Engineering
“Instead of this absurd division into sexes they ought to class people as static and dynamic.” – Evelyn Waugh

 

The standard Objective-C introduction usually mentions its Smalltalk heritage along with its strict C dependence. So how can a language running over the hallmark of procedural programming be dynamic? The Objective-C runtime. It’s the only dynamic library that doesn’t have to be included in an Xcode project’s frameworks, as it is loaded automatically by the OS. It allows for object orientation, dynamic and duck typing, messaging, protocols, introspection, etc. It simply puts the objective in Objective-C.

 

“The Objective-C language defers as many decisions as it can from compile time and link time to runtime. Whenever possible, it does things dynamically. This means that the language requires not just a compiler, but also a runtime system to execute the compiled code. The runtime system acts as a kind of operating system for the Objective-C language; it’s what makes the language work.” – Objective-C Runtime Programming Guide

 

 

Why consider writing more dynamic code?

 

Write less code: Objective-C is a verbose language that loves boilerplate code, and while that’s not always a bad thing, it can get in the way of getting things done. When Swift was introduced, a lot of the excitement was exclusively over how much could be accomplished with so little code. Writing less Objective-C code is appealing not only because it’s less work — even though one should definitely “work smart, not hard” — but also because it means reducing cost of maintenance and change.

 

Write more SOLID code: The open/closed principle (OCP) urges developers to write “software entities” that are open to extension but closed for modification. While this issue is usually addressed in classes (with subclassing), methods are also entities that should ideally be stabilized. Not, for example, by adding a new conditional statement every time a new file type is introduced to a class that handles files.

 

Implement design patterns more easily: A computer scientist found that 16 out of the 23 design patterns found in Gang of Four’s Design Patterns (the Gray’s Anatomy of OOP design books) are invisible or simpler in dynamic languages. So it’s a better, more fit tool for the job.

 

Have more fun: Writing dynamic code is just more fun! Having less limitations, writing less control flow, less casting, etc., just makes writing code a more elegant and less repetitive process.

 

 

What’s the catch?

 

Runtime overhead: With all the power the Objective-C runtime provides, sometimes the effect is cost of performance. For example, the dynamic resolution of a selector is often a more expensive operation than calling the same method directly, due to compiler optimizations. That being said, unless it’s a very resource intensive app/game and every millisecond counts, the boost in code quality and developer productivity outweighs the minuscule performance hit.

 

Safety: Dynamic code, by design, has fewer compiler checks, as it is evaluated at runtime, which means it also fails at runtime. So any code path or environment configuration that is not tested might have a nasty bug that would only present itself in product, a less than ideal situation. This would be even more obvious in “stringly-typed” features like KVC, KVO, and selectors.

 

Code readability: While this might be subjective, dynamic code is usually less self-documenting than its static counterpart. The run flow might be less pronounced than a familiar if-else structure, at least at first glance.

 

 

Examples

 

Scenario #1: An app requires comprehensive logging of all events performed on UIButtons; every touch, tap, etc., should be reported somewhere central.

 

Static code:

“`objc
@interface IBGLoggingButton : UIButton
@end
@implementation IBGLoggingButton
– (void)sendAction:(SEL)action
to:(nullable id)target
forEvent:(nullable UIEvent *)event {
[super sendAction:action to:target forEvent:event];
// the logger implementation is not relevant
[self.logger addEvent:[NSString stringWithFormat:@"%@ was called on %@",
NSStringFromSelector(action),
NSStringFromClass(self)]];
}

// Somewhere in a view controller far far away…
IBGLoggingButton *button = [IBGLoggingButton buttonWithType:UIButtonTypeCustom]; // etc.
“`

 

 

Dynamic code:

“`objc
@implementation UIControl (Logging)
// kudos http://nshipster.com/method-swizzling/
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(& onceToken, ^{
Class class = [self class];
SEL originalSelector = @selector(sendAction:to:from:forEvent:);
SEL swizzledSelector = @selector(IBG_sendAction:to:from:forEvent:);
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
BOOL didAddMethod =
class_addMethod(class,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod(class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
– (void)IBG_sendAction:(SEL)action
to:(id)target
forEvent:(UIEvent *)event {
[self IBG_sendAction:action to:target forEvent:event];
[self.logger addEvent:[NSString stringWithFormat:@"%@ was called on %@",
NSStringFromSelector(action),
NSStringFromClass(self)]];
}
@end
“`

 

Static approach has the apparent disadvantage of introducing a new subclass that requires changing every class that has button code; it also fails to solve the problem all together if third party code is used, as it cannot be changed to accommodate the new subclasses. Another downer is that said solution cannot be generalized on all controls to pickers, switches, etc.

The dynamic solution is much more plug and play; it swizzles all sendAction:to:from:forEvent: messages to a category (more on that later) and calls a custom method that does the logging after. Swizzling would work on all subclasses of UIControl, even third party code. The technique has endless applications, from mocking objects for testing to overriding behaviors (monkey-patching) in Cocoa Touch or third-party code beyond what is allowed in its public API.

 

 

Scenario #2: Parsing and storing JSON API that is expected to change.

 

Static code:

“`objc
@interface IBGUser : NSObject
– (instancetype)initWithDictionary:(NSDictionary *)dictionary;
@property(nonatomic, copy) NSString *firstName;
@property(nonatomic, copy) NSString *middleName;
@property(nonatomic, copy) NSString *lastName;
// :/…
@property(nonatomic, copy) NSString *mothersMaidenName;
@end
@implementation IBGUser
– (instancetype)initWithDictionary:(NSDictionary *)dictionary {
if (self = [super init]) {
self.firstName = dictionary[@"firstName"];
self.middleName = dictionary[@"middleName"];
self.lastName = dictionary[@"lastName"];
// and 20 more of that here
// 40 more when implementing NSCoding, NSCopying… etc.
}
return self;
}
@end
“`

 

Dynamic code:

“`objc
@interface IBGUser : NSObject
– (instancetype)initWithDictionary:(NSDictionary *)dictionary;
// not private for simplification
@property(nonatomic, strong) NSMutableDictionary *data;
@end
@implementation IBGUser
– (instancetype)initWithDictionary:(NSDictionary *)dictionary {
if (self = [super init]) {
self.data = [NSMutableDictionary dictionary];
for (id obj in dictionary) {
NSString *key = [[dictionary allKeysForObject:obj] firstObject];
[self.data setObject:obj forKey:key];
}
}
return self;
}

// “Return accurate descriptions of the methods that respond to forwarded messages”
// https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtForwarding.html
– (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
NSString *selectorString = NSStringFromSelector(selector);
BOOL selectorHasSet = [selectorString rangeOfString:@"set"].location == 0;
const char *types = selectorHasSet ? "v@:@" : "@@:";
return [NSMethodSignature signatureWithObjCTypes:types];
}

// All setters are forwarded to keep the value in self.data
// Example: [user setName:@”Jon Snow”] // All getters fetch their returned objects from self.data
// Example: [user motherName] // Lyanna?
– (void)forwardInvocation:(NSInvocation *)invocation {
NSString *key = NSStringFromSelector([invocation selector]);
if ([key rangeOfString:@"set"].location == 0) {
key = [[key substringWithRange:NSMakeRange(3, [key length] – 4)] lowercaseString];
NSString *obj;
[invocation getArgument:& obj atIndex:2];
[self.data setObject:obj forKey:key];
} else {
NSString *obj = [self.data objectForKey:key];
[invocation setReturnValue:& obj];
}
}
@end
“`

Static approach both requires knowledge of returned JSON keys and needs to configure corresponding properties in the model class. Also, whenever the the returned JSON structure changes, the model would need to be updated, tested, and deployed again, which is less than ideal.

The disadvantages of this should be apparent. The dynamic approach is worlds apart in terms of flexibility; it adapts to the API the way a model should, and responds to getters and setters the way properties should. This can be the starting point to creating a full-fledged dynamic model layer à la Github’s Mantle from scratch.

 

 

Scenario #3: Checking if an API is authenticated without having a callback (delegate, block, etc.).

 

Static code:

“`objc
– (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[[BadlyDesignedSDK sharedInstance] authenticate];
// If called right away isUserAuthenticated
// would still be NO, until it finished authenticating
[sdk performSelector:@selector(fetchUserInfo)
withObject:nil
afterDelay:1.0];
return YES;
}
“`

 

Dynamic code:

“`objc
– (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
BadlyDesignedSDK *sdk = [BadlyDesignedSDK sharedInstance];
[sdk authenticate];
[sdk addObserver:self
forKeyPath:@"isUserAuthenticated"
options:NSKeyValueObservingOptionNew
context:nil];
return YES;
}
– (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context {
if ([keyPath isEqualToString:@"isUserAuthenticated"]) {
BOOL userDidAuthenticate =
[[change objectForKey:NSKeyValueChangeNewKey] boolValue];
if (userDidAuthenticate) {
[[BadlyDesignedSDK sharedInstance] fetchUserInfo];
}
}
}
“`

 

Static approach not only exhibits the usual ugliness, but is also prone to error; if authentication takes longer than one second, it would just fall apart. KVO on the other hand, while still not pretty, would only get called when the value of isUserAuthenticated is changed. KVO is best suited to create event-driven implementations and binding model to view and can be a good introducing to functional programming with ReactiveCocoa, Swift etc.

 

 

Scenario #4: Having multiple parents for a single class.

 

Dynamic code:

“`objc
@interface Ninja : NSObject {
NSString *clanName;
}
@property(nonatomic, strong) NSString *clanName;
– (void)doNinjaStuff;
@end
@implementation Ninja
@synthesize clanName;
– (void)doNinjaStuff {
self.clanName = @"Iga";
NSLog(@"I'm a %@ and my clan name is %@",
[[self class] description], self.clanName);
}
@end

// When the multiple inheritance is needed…
Turtle *turtle = [[Turtle alloc] init];
[Mixin from:[Ninja class] into:[Turtle class]];
// Prints "I'm a Turtle and my clan name is Iga"
[(id)turtle doNinjaStuff];
“`

 

A static approach was simply not possible in Objective-C; it was never intended to support multiple inheritance. The dynamic solution comes from and uses ObjectiveMixin open source implementation. It’s a very compact (~100 LoC) class that uses the Objective-C runtime to copy method implementations from one class to another, thus allowing pseudo multiple parents to a single class, similar to Ruby’s mixins. The fact that this is also done at runtime allows dynamic subclassing on the spot, after classes are compiled. This scenario demonstrates best how dynamism can totally change the patterns and paradigms used to write iOS/Mac solutions.

 

 

Scenario #5: Handling different object types in the same UITableView. A backend API returns different kinds of media files that need to be handled and presented differently.

 

Static code:

“`objc
// tableView setup and boilerplate code
– (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell;
id obj = [self modelForIndexPath:indexPath];
if ([obj isKindOfClass:[NSString class]]) {
cell = [[IBGMediaCellString alloc] init];
} else if ([obj isKindOfClass:[UIImage class]]) {
cell = [[IBGMediaCellImage alloc] init];
}
// more copied and pasted code…
// configure cell
return cell;
}
“`

 

Dynamic code:

“`objc
– (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
id obj = [self modelForIndexPath:indexPath];
Class cellClass = [self classForContent:obj];
UITableViewCell *cell = [[cellClass alloc] init];
// configure…
return cell;
}
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
– (Class)classForContent:(id)content {
NSString *className = NSStringFromClass([content class]);
NSString *contentMethodName = [NSString stringWithFormat:@"cellClassFor%@", className];
return [self.class performSelector:NSSelectorFromString(contentMethodName)];
}
– (Class)cellClassFor__NSCFString {
return [IBGTextCell class];
}
– (Class)cellClassForUIImage {
return [IBGImageCell class];
}
“`

 

The static approach should look very familiar to most developers, and it’s not “wrong” by any means. Actually, calling it static is slightly misleading because it does use introspection feature of the runtime (isKindOfClass:), but the dynamic approach is still a little better. Not only does it offer a lower cyclomatic complexity (the number of independent code paths), which in turn increases code readability, but it makes the tableView:cellForRowAtIndexPath: method more open and closed in the OCP sense as well. It doesn’t need to be modified when handling a new object type later on.

 

 

Conclusion

 

This is not great entry-level material for learning the runtime APIs, but is valuable for engineers who are comfortable with the tools yet want to polish their software design. The examples in this article are not meant to explain features as much as to advocate a paradigm shift, thinking more of dynamism and metaprogramming while designing the little things in code.

This only touches the surface of what one can do with Objective-C using rather than simple runtime APIs. Features like introspection, posing, dynamic method resolution and forwarding, dynamic class generation, ISA swizzling, and much more weren’t even mentioned. The runtime opens a lot of possibilities that were simply not feasible by just the language syntax, and it helps create a sexier, more-maintainable code for the next generation.

 

 

NOW IT’S YOUR TURN: How do you feel about dynamism? Share your thoughts in the comments below.
  • Ahmed

    good objective-c defending article but your examples of static and dynamic code are wrong because both are written by objective-c and both are dynamic