I was inspired the other day while talking with a friend about how to access private instance variables in a subclass. There are several methods to do this - some useful in production and some error prone.
@interface Foo : NSObject
@end
@implementation Foo {
@private
NSString *_name;
}
@end
Pointer arithmetic
Since an Objective-C object is a pointer to allocated memory in the heap, it is possible to calculate offset of the ivar and use basic pointer arithmetic to access it.
In this basic example _name is the first field in the class Foo, it will be offset of 4 from the base pointer of the instance.
NSString *name = (__bridge id)*(void **)((__bridge void *)aFoo + 4)
In production code this approach isn’t so nice but an interesting thought experiment.
Runtime
The runtime provides the following functions to access instance variables on an object.
Ivar class_getInstanceVariable(Class cls, const char *name)
id object_getIvar(id obj, Ivar ivar)
Which can be used to access public, private, or protected instance variables:
Ivar nameIVar = class_getInstanceVariable([aFoo class], "_name");
NSString *name = object_getIvar(aFoo, nameIVar);
For non ARC code, there is also a 1 liner to do this: Ivar object_getInstanceVariable(id obj, const char *name, void **outValue);
Key value coding
KVC is an object oriented way to access fields of an object and can be used to direcly access private instance variables. KVC instance variable access is enabled if the target class returns YES from +[NSObject accessInstanceVariablesDirectly].
Many classes in UIKit don’t support this by default but we over can over ride that with method swizzeling or a category.
@implementation Foo (Exposed)
+ (BOOL)accessInstanceVariablesDirectly {
return YES;
}
@end
In production code this category isn’t so safe, as another class could be load and override our new category. A safer approach with KVC would check if the class supports direct instance variable access before invoking valueForKey: and if not then swizzeling before and then swizzeling it back. This wax on wax off approach really negates the elegance of using KVC.
The KVC approach is useful if the class already supports this by default.
Redeclaring to public
A straight forward way to access private instance variables is to create a class continuation and redeclare the instance variable as public. This is clean because it is handled by the compiler and enables using the dereference operator to access the now public instance variable.
@interface Foo() {
@public
NSString *_name;
}
@end
aFoo->_name;