Objective-C
ivar
programming concepts
software development
memory management

Why would you use an ivar?

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

In Objective-C, an instance variable (ivar) is a variable stored directly in an object's memory layout. While properties (with @property and @synthesize) are now the standard for most use cases, ivars still have valid uses: they provide direct memory access without getter/setter overhead, they keep implementation details truly private (not visible in the header), and they are necessary in certain performance-critical or low-level scenarios. Use ivars when you need private storage without KVO, no accessor overhead, or compatibility with legacy code.

Ivars vs Properties

Property (Standard Approach)

objectivec
1// MyClass.h
2@interface MyClass : NSObject
3@property (nonatomic, strong) NSString *name;
4@property (nonatomic, assign) NSInteger count;
5@end
6
7// MyClass.m
8@implementation MyClass
9// @synthesize generates _name and _count ivars + getter/setter methods
10@end

Properties create getter and setter methods. Accessing self.name calls -name (getter) or -setName: (setter), which may involve retain/release, KVO notifications, and thread locking (atomic).

Ivar (Direct Access)

objectivec
1// MyClass.h
2@interface MyClass : NSObject {
3    @private
4    NSString *_internalState;  // Declared in header (visible but private)
5}
6@end
7
8// MyClass.m — ivars in implementation block (truly private)
9@implementation MyClass {
10    NSInteger _cachedValue;
11    BOOL _initialized;
12}
13
14- (void)setup {
15    _cachedValue = 42;       // Direct memory access — no method call
16    _initialized = YES;
17}
18@end

Ivars declared in the @implementation block are invisible to subclasses and external code — they are truly private.

When to Use Ivars

1. Performance-Critical Code

objectivec
1@implementation ParticleSystem {
2    float *_positions;   // Raw C array for performance
3    NSInteger _count;
4}
5
6- (void)updatePositions {
7    // Tight loop — direct ivar access avoids method call overhead
8    for (NSInteger i = 0; i < _count; i++) {
9        _positions[i * 2] += _velocities[i * 2];      // x
10        _positions[i * 2 + 1] += _velocities[i * 2 + 1]; // y
11    }
12    // Using self.count in a tight loop would call the getter thousands of times
13}
14@end

In tight loops processing thousands of items per frame, the overhead of property getter/setter calls (even with nonatomic) can be measurable.

2. Truly Private State

objectivec
1// MyClass.h — clean public interface
2@interface MyClass : NSObject
3- (void)performAction;
4@end
5
6// MyClass.m — implementation details hidden
7@implementation MyClass {
8    NSMutableArray *_pendingOperations;  // Not in header at all
9    dispatch_queue_t _workQueue;
10    BOOL _isProcessing;
11}
12
13- (void)performAction {
14    if (_isProcessing) return;
15    _isProcessing = YES;
16    // ...
17}
18@end

Ivars in the @implementation block do not appear in the header file. They are invisible to consumers of the class, keeping the public API clean.

3. Avoiding KVO Side Effects

objectivec
1@implementation ViewModel {
2    NSString *_internalState;  // Not KVO-observable
3}
4
5// Property would trigger KVO notifications
6// @property NSString *internalState;
7
8- (void)updateState:(NSString *)newState {
9    _internalState = newState;  // No KVO notification, no setter overhead
10    // Manually notify only when needed
11    [self willChangeValueForKey:@"displayState"];
12    // Update derived properties...
13    [self didChangeValueForKey:@"displayState"];
14}
15@end

Properties are automatically KVO-compliant. If you modify a property frequently during internal computation and do not want observers triggered on every change, use an ivar.

4. C Types and Structs

objectivec
1@implementation MapRenderer {
2    CGRect _visibleRect;
3    CGAffineTransform _currentTransform;
4    struct {
5        unsigned int needsRedraw: 1;
6        unsigned int isAnimating: 1;
7        unsigned int didLayout: 1;
8    } _flags;  // Bitfield struct — no property equivalent
9}
10
11- (void)setNeedsRedraw {
12    _flags.needsRedraw = 1;
13}
14@end

C structs, bitfields, and unions cannot be meaningfully wrapped in properties. Ivars are the natural choice for these types.

5. Delegate Flag Caching

objectivec
1// Common pattern in Apple's frameworks
2@implementation MyTableView {
3    struct {
4        unsigned int delegateHeightForRow: 1;
5        unsigned int delegateWillDisplayCell: 1;
6        unsigned int delegateDidSelectRow: 1;
7    } _delegateFlags;
8}
9
10- (void)setDelegate:(id<MyTableViewDelegate>)delegate {
11    _delegate = delegate;
12    // Cache which methods the delegate implements (once, not per call)
13    _delegateFlags.delegateHeightForRow = [delegate respondsToSelector:@selector(tableView:heightForRowAtIndexPath:)];
14    _delegateFlags.delegateWillDisplayCell = [delegate respondsToSelector:@selector(tableView:willDisplayCell:forRowAtIndexPath:)];
15}
16
17- (CGFloat)heightForRow:(NSIndexPath *)indexPath {
18    if (_delegateFlags.delegateHeightForRow) {
19        return [_delegate tableView:self heightForRowAtIndexPath:indexPath];
20    }
21    return 44.0;  // Default height
22}
23@end

This is a well-known performance pattern from Apple's UIKit source. The bitfield ivar caches respondsToSelector: results to avoid repeated message sending.

Property vs Ivar Summary

FeaturePropertyIvar
Accessor methodsYes (getter/setter)No (direct access)
KVO compatibleYes (automatic)No
Memory managementManaged by setter (strong/weak/copy)Manual in MRC, automatic in ARC
Visible in headerYes (public API)Optional (can be in .m)
Subclass accessYesOnly if in header/@protected
Thread safetyOptional (atomic)None built-in
PerformanceSlight overheadDirect memory access

Common Pitfalls

  • Forgetting memory management (MRC): Under Manual Reference Counting, assigning to an ivar does not retain the object. _name = newName leaks the old value and does not retain the new one. Under ARC, this is handled automatically. Always use ARC for new code.
  • Accessing ivars from outside the class: Ivars declared in the header with @public are accessible as object->_ivar, which breaks encapsulation. Always declare ivars as @private or put them in the @implementation block.
  • Using self->_ivar in blocks: Under ARC, _ivar inside a block implicitly captures self, creating a potential retain cycle. Use __weak typeof(self) weakSelf = self and access weakSelf->_ivar or convert to a property accessed through weakSelf.property.
  • Subclass cannot access implementation ivars: Ivars declared in @implementation MyClass { ... } are invisible to subclasses. If a subclass needs access, move the ivar to the header's @protected section or provide a property.
  • Using ivars when properties are clearer: Overusing ivars reduces code clarity. Properties document the intent (strong vs weak vs copy, atomic vs nonatomic). Only use ivars when you have a specific reason — properties are the default best practice.

Summary

  • Use ivars for truly private state, performance-critical paths, C structs/bitfields, and KVO avoidance
  • Ivars declared in @implementation are invisible outside the class — cleaner headers
  • Properties are preferred for most use cases — they provide memory management, KVO, and thread safety
  • Direct ivar access (_name) avoids getter/setter overhead, useful in tight loops
  • Under ARC, ivar memory management is automatic — no manual retain/release needed
  • Apple's own frameworks use ivars extensively for delegate flag caching and internal state

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.