Skip to content
Find file
Fetching contributors…
Cannot retrieve contributors at this time
499 lines (406 sloc) 19.1 KB
Dynamic Languages - They're not just for Rubyists (and Pythonistas)
Objective-C is a popular language as it is a building block of all iOS applications. It also has powerful dynamic features, while running on C. This talk will introduce you to the basics of Objective-C, with an eye on how it is similar to other dynamic languages, such as Ruby.
Credit for the outline of this talk goes to:
- iOS 5 Programming Pushing the Limits, by Rob Napier, Mugunth Kumar
- Show a picture and link to book:
- Not covering Core Foundation
What We'll Cover:
- The Basics
- Objective-C w/ source code
- Mention of ARC
- objects and classes
- An intro to C Structs, and how Objective-C uses them
- instances, classes, superclasses, & meta-classes
- Categories, this is how they work.
- Messaging
- Basics: methods, messages, selectors, message sending
- Sending Messages
- What is a method?
- Disecting objc_msgSend()
- Dynamic usage
- @dynamic, dynamic loading, fast forwarding, normal forwarding, forwarding failure
- Core Data, relies on this
- A deep(er) dive into objc_msgSend
- Sidenote: The Objective-C Runtime
- What is it?
- How you interact with it
- Message forwarding examples
- Swizzling
- Method swizzling: swapping method implementations at runtime
- ISA swizzling : changing classes at runtime
- This is how KVO works
- That's cool, but what do I do now?
Basics / Intro:
- Primer?
- Like C++, no multiple inheritance, no operator overloading
- A Quick Intro to C Structs
The Objective-C Object:
- All Objective-C objects are C structs
typedef struct objc_object {
Class isa;
} *id;
- ISA pointer (defines the object's class)
- Root class' ivars
- penultimate superclass' ivars
- ...
- superclass' ivars
- Class' ivars
- And the Class:
- The old way:
typedef struct objc_class *Class;
struct objc_class {
Class isa;
Classes and Metaclasses
- A class is like an object, you can pass it messages
- [MyClass alloc]
- Class methods are stored in the metaclass, which is where the Class isa pointer goes
- diagram:
- Class is instance of metaclass
- metaclass describes methods of class, just as class describes methods of instance
- Meta-classes are instances of the root class' metaclass
- which is also an instance of the root class' metaclass (ending in a cycle)
- Meta-classes superclasses are the meta-classes of their corresponding class' superclass
- but, the root meta-class' superclass is the root class
- so, class objects respond to the root class' instance methods
- Meta-classes are hidden from you ([NSObject class] #=> [NSObject self]), and are rarely accessed
- The new way (OS X 64 & iOS)
From objc-runtime-new.h
typedef struct class_ro_t {
const char * name;
const method_list_t * baseMethods;
const protocol_list_t * baseProtocols;
const ivar_list_t * ivars;
const property_list_t *baseProperties;
} class_ro_t;
typedef struct class_rw_t {
const class_ro_t *ro;
method_list_t **methods;
struct chained_property_list *properties;
const protocol_list_t ** protocols;
struct class_t *firstSubclass;
struct class_t *nextSiblingClass;
} class_rw_t;
typedef struct class_t {
struct class_t *isa;
struct class_t *superclass;
Cache cache;
IMP *vtable;
uintptr_t data_NEVER_USE; // class_rw_t * plus flags
bool isRootClass() const {
return superclass == NULL;
bool isRootMetaclass() const {
return isa == this;
} class_t;
- Class structure contains a metaclass pointer(?), superclass pointer, data about the class
- data: name, ivars, methods, properties, protocols
- Superclass pointer creates the hierarchy of classes
( Categories )
- Methods, properties, and protocols define what the class can do
- stored in writable section of class definition, which can be changed at runtime
- this is how categories work (Ruby: Monkey-Patching)
- Ivars are stored in the read-only section, unmodifiable as this would impact existing instances
- thus, categories cannot change add ivars
- objc_object isa pointer is not const --> change class at runtime
- Class superclass pointer also not const --> change class hierarcy at runtime
- Now with ARC
- In general: Messaging
- Method: An actual piece of code associated with a class, and given a particular name
- Message: A name and parameters sent to an object
- Selector: A particular way of representing the name of a message or method
- Message send: Taking a message and finding and executing the correct method
- SEL, IMP, method
SEL - Selector, or name of method
Method - A selector on a class
IMP - The function itself
- just a function that accepts an object pointer and selector
- It all comes from libobjc, a collection of C functions
objc_msgSend --> [object message]
Sending Messages
- What is a method?
Ex: - (int)foo:(NSString *)str { ...
Is really:
int SomeClass_method_foo_(SomeClass *self, SEL _cmd, NSString *str) { ...
Ex: int result = [obj foo:@"hello"];
Is really:
int result = ((int (*)(id, SEL, NSString *))objc_msgSend)(obj, @selector(foo:), @"hello");
(Examples from:
struct objc_method {
char *method_types OBJC2_UNAVAILABLE;
- So, a method is
- a name (selector, SEL)
- a string containing argument and return types (created by @encode)
- an IMP, or function pointer:
typedef id (*IMP)(id, SEL, ...);
- Messaging:
objc_msgSend does the following
- look up class of given object, be dereferencing it and grabbing ISA member
- look at method list of class, search for selector
- if not found, move to superclass and do the same
- when found, jump to IMP
- There is also a method cache, to speed up this lookup process for future messages
- What about when no method found for a selector?
- Messaging (from The Objective-C Runtime Programming Guide):
- [receiver message]
objc_msgSend(receiver, selector, arg1, arg2, ...)
- dynamic binding:
- find procedure (method implementation) for selector
- call procedure, passing receiver and args
- return the procedure's return value
- isa / messaging framwork diagram p12 (Figure 3-1)
- method implementations chosen at runtime == methods dynamically bound to messages
- Hidden Arguments
- receiver and selector are called "hidden" because aren't delared in the source code
- source code can still refer to them:
- self = receiver
- _cmd = selector
- You can use methodForSelector: to bypass dynamic binding... but, it's kind of cheating since this method is provided by Cocoa.
- See Xcode project examples
- What happens in objc_msgSend?
- It's written in opcode in objc-msg-arm.s (and -i386.s, -simulator-i386.s, x86_64.s)
* id objc_msgSend(id self,
* SEL op,
* ...)
* On entry: a1 is the message receiver,
* a2 is the selector
ENTRY objc_msgSend
# check whether receiver is nil
teq a1, #0
itt eq
moveq a2, #0
bxeq lr
- Order of operations:
- Check if the receiver is nil --> nil-handler
- If garbage collection available, short-circuit on certain selectors (retain, release, autorelease, retainCount) --> return self
- Check class' cache for implementation, call it
- Compare requested selector to selectors defined in class, call it
- Compare to superclass, and up the chain
- Lazy method resolution
- Call resolveInstanceMethod: (or resolveClassMethod:), if returns YES, start over
- starts over and assumes method has been added
- Fast forwarding path
- Call forwardingTargetForSelector:, if returns non-nil, send message to object (other than self)
- starts over with new target
- Normal forwarding path
- Call methodSignatureForSelector:, if returns non-nil, create an NSInvocation and pass to forwardInvocation:
- Call doesNotRecognizeSelector: --> throws exception by default
- How to use this in a dynamic way?
- Dynamic Method Resolution:
- @dynamic synthesis of properites
- Core Data NSManagedObject does this.
- uses resolveInstanceMethod: and resolveClassMethod:
- See ch-20 Person.m/h
- Dynamic loading (not allowed in iOS)
- System Preferences modules
- NSBundle
- Fast Forwarding
- forwardingTargetForSelector: useful for proxy objects, such as CacheProxy.h/m
- See ch-20 CacheProxy.m/h (not an awesome example)
- The Higher Order Messaging (HOM) example is much more interesting
(more on this below)
- Normal Forwarding
- Slower, but more flexible
- Forwarding Failure w/ doesNotRecognizeSelector:
A (Slightly) Deep(er) Dive into objc_msgSend()
- README: (all 4 parts!)
- Now we're getting crazy.
- objc_msgSend() is really a family of functions
"each written to handle the calling conventions required to deal with various return types under the x86_64 ABI (Application Binary Interface). Oh, and the vtable dispatch stuff (different post)."
- called 10s of millions of times just launching your app
- That's why it's carefully crafted in assembly.
- Show objc_msgSend() assemby just to make the point that it's written in assembly?
- Method calls in ObjC are really just C function calls to objc_msgSend(), in which it figures out C function to call
These are equivalent, generating the functionaly equivalent assembly:
- (id) doSomething: (NSUInteger) index;
id doSomething(id self, SEL _cmd, NSUInteger index) { ... }
The Objective-C Runtime Programming Guide
- Objective-C runtime is open source (
- got objc4-493.11 here:
- From the introduction:
"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."
"You should read this document to gain an understanding of how the Objective-C runtime system works and how you can take advantage of it. Typically, though, there should be little reason for you to need to know and understand this material to write a Cocoa application."
- Runtime interaction (from The Objective-C Runtime Programming Guide):
- Through ObjC source
- write and compile ObjC source
- NSObject methods
- allow introspection:
class, isMemberOfClass:, isKindOfClass:, respondsToSelector:, conformsToProtocol:, methodForSelector:
- direct calls to runtime functions
- Most ObjC boil down to these functions, allowing writing C code to replicate what ObjC compiles down to
- dynamic shared library with a public interface consisting of a set of functions and data structures
- Header files located in /usr/include/objc
- Message Forwarding (our concrete example?)
- forwardInvocation: (it's kind of like Ruby's method_missing)
- (void)forwardInvocation:(NSInvocation *)anInvocation
if ([someOtherObject respondsToSelector:
[anInvocation selector]])
[anInvocation invokeWithTarget:someOtherObject];
[super forwardInvocation:anInvocation];
- Forwarding mimics multiple inheritance
- provides features of multiple inheritance
- but, multiple inheritance combines capabilities in single object
- forwarding provides abilities in smaller objects, associated in transparent way to the message sender
- Implement "Array#map"
- HOM Example:
- The Complete Friday Q&A p 104
- Super not-fun to implement, here's the simpler way...
(This is old code and doesn't work... look at this just to illustrate concept)
@interface ArrayMapProxyNormal : NSProxy
NSArray *_array;
- (id)initWithArray:(NSArray *)array;
@implementation ArrayMapProxyNormal
- (id)initWithArray:(NSArray *)array
_array = array;
return self;
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
return [[_array lastObject] methodSignatureForSelector:sel];
- (void)forwardInvocation:(NSInvocation *)inv
NSMutableArray *newArray = [NSMutableArray array];
for(id obj in _array)
id retval;
[inv invokeWithTarget:obj];
[inv getReturnValue:&retval;];
[newArray addObject:retval];
[inv setReturnValue:&newArray;];
OnNSArray Category:
- (id)mapNormal
return [[[ArrayMapProxyNormal alloc] initWithArray:self] autorelease];
Method Swizzling
- Swizzling: transparently replacing one thing with another at runtime
in IOS, usually this is methods
- Warning: May cause App Rejection.
- Allow you to change the behaviors of Apple frameworks
- Why not use a category?
- Category method replaces original method, with no way to call original method
- Original method you wish to replace might have been implemented by category
- There is no way to determine which category method "wins"
- Option 1 (more bad): method_exhangeImplementations
- modifies selector, can break things
- pseudo-recursive call can be misleading
- Use funtion pointer approach in RNSwizzle instead
- If still interested, read this:
- NSNotificationCenter Category Example:
#import <objc/runtime.h>
#import <objc/message.h>
@interface NSNotificationCenter (RNHijack)
+ (void)hijack;
@implementation NSNotificationCenter (RNHijack)
+ (void)hijackSelector:(SEL)originalSelector withSelector:(SEL)newSelector
Class class = [NSNotificationCenter class];
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method overrideMethod = class_getInstanceMethod(class, newSelector);
// method_exchangeImplementations(originalMethod, overrideMethod); // Can't do this!
if (class_addMethod(c, origSEL, method_getImplementation(overrideMethod), method_getTypeEncoding(overrideMethod)))
class_replaceMethod(c, overrideSEL, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
method_exchangeImplementations(origMethod, overrideMethod);
+ (void)hijack
[self hijackSelector:@selector(removeObserver:)
- (void)RNHijack_removeObserver:(id)notificationObserver
NSLog(@"Removing observer: %@", notificationObserver);
[self RNHijack_removeObserver:notificationObserver];
- Option 2 (less bad): NSObject Category RNSwizzle.m/h example (ch-20)
ISA Swizzling
- ISA pointer defines the object's class
- Modifying an object's class at runtime
- NSObject Category SetClass (ISASwizzle)
- accomplishes same goal as RNSwizzle, but uses ISA swizzling, instead of method swizzling
- Makes sure to check instance size before replacing
- clobbering ISA pointer of object after this object is difficult to debug
- hence the NSAssert ensuring both Class' instances are the same size
- This is a good solution for classes that are meant to be subclassed
- KVO is implemented with ISA swizzling
- Allows frameworks to inject code into your classes
- Now you can do the reverse
Downsides to Swizzling
- why might this be bad?
- tight coupling with implementation details
- difficult to debug
- unfamiliar to others
- we're going to talk about it because it reveals the dynamic nature of Objective-C
- Rob Napier suggests using ISA over method swizzling
- only impacts specific objects, rather than all instances of a class
- use method swizzling if you actually want to affect every instance of a class
Rob Napier's talbe of Swizzling differences - P387 iOS-PTL
- KVO refresher
- Complete Friday Q&A V1: How Key-Value Observing Works
- KVO Done Right (Take 2):
- Blocks
- Complete Friday Q&A V1:
- Practical Blocks
- Blocks in Objective-C
- Implement Clojure/Ruby collection methods with trampolining or blocks?
- Higher order messaging:
- Complete Friday Q&A V1: Fast Objective-C Forwarding
- implement HOM: map?
- F-Script: sort of an Objective-C REPL
- Smalltalk dialect
- syntax tutorial:
- Everything:
The Objective-C Runtime Programming Guide
- C Basics:
- Messaging:
- Swizzling:
Something went wrong with that request. Please try again.