Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Just some fun experiments to see how this really works.
Objective-C
tree: 5728f66114

Fetching latest commit…

Cannot retrieve the latest commit at this time

Failed to load latest commit information.
ObjcPlayground
.gitignore
F-Script20100614.zip
ObjCRuntimeGuide.pdf
README
iOS_PTL.zip
objc4-493.11.tar.gz

README

Dynamic Languages - They're not just for Rubyists (and Pythonistas)

All credit to:
- iOS 5 Programming Pushing the Limits, by Rob Napier, Mugunth Kumar
  - Show a picture and link to book: http://iosptl.com/

Assumptions: 
- Not covering Core Foundation
- 

What should we cover?
- Creating new methods and classes at runtime
- introspection
- message passing
- KVO
- blocks
- at runtime
  - inspect and modify class hierarchy
  - create new classes and methods
  - change class and superclass (ISA Swizzling)

Tools:
- F-Script: sort of an Objective-C REPL
  - Smalltalk dialect
  - http://www.fscript.org/
    - syntax tutorial: http://www.fscript.org/documentation/ExploringCocoaWithFScript/index.htm


Basics / Intro:
- Like C++, no multiple inheritance, no operator overloading
- It all comes from libobjc, a collection of C functions
  objc_msgSend --> [object message]
  
- Objective-C runtime is open source (opensource.apple.com)
  - got objc4-493.11 here: http://opensource.apple.com/release/mac-os-x-1073/

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:    
  typedef struct objc_class *Class;
  struct objc_class {
      Class isa;
      ...
    
- A Quick Intro to C Structs
  - http://heather.cs.ucdavis.edu/~matloff/UnixAndC/CLanguage/PointersI.html

- Class structure contains a metaclass pointer(?), superclass pointer, data about the class
  - data: name, ivars, methods, properties, protocols

- Superclass pointer createes 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


Review
- 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



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



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: http://mikeash.com/pyblog/friday-qa-2009-03-20-objective-c-messaging.html)
  
    struct objc_method {
      SEL method_name                                          OBJC2_UNAVAILABLE;
      char *method_types                                       OBJC2_UNAVAILABLE;
      IMP method_imp                                           OBJC2_UNAVAILABLE;
    }                                                          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 itand 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?
  
- 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
    - Call resolveInstanceMethod: (or resolveClassMethod:), if returns YES, start over
      - starts over and assumes method has been added
    - Call forwardingTargetForSelector:, if returns non-nil, send message to object (other than self)
      - starts over with new target
    - 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 implementation: @dynamic synthesis of properites
      - uses resolveInstanceMethod: and resolveClassMethod:
      - See ch-20 Person.m/h
    - Dynamic loading (not allowed in iOS)
    - Fast Forwarding
      - forwardingTargetForSelector: useful for proxy objects, such as CacheProxy.h/m
      - See ch-20 CacheProxy.m/h
    - Normal Forwarding
      - Slower, but more flexible
    - Forwarding Failure w/ doesNotRecognizeSelector:

A (Slightly) Deep(er) Dive into objc_msgSend()
- README: http://www.friday.com/bbum/2009/12/18/objc_msgsend-part-1-the-road-map/ (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.
  - Three notable optimizations:
      Don’t Mess With Registers (unless absolutely necessary)
      Tail Call Optimized
      Don’t Mess with the Argument List
- 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
- 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:
  - Through ObjC source
    - write and complile 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

- Messaging
  - [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.
    
- Dynamic Method Resolution
  - @dynamic propertyName;
    - Core Data NSManagedObject does this.
  - You can implement resolvedInstanceMethod: and resolveClassMethod:

- Dynamic Loading
  - System Preferences modules
  - NSBundle
  
- Message Forwarding
  - forwardInvocation: (it's kind of like Ruby's method_missing)
    - (void)forwardInvocation:(NSInvocation *)anInvocation
    {
        if ([someOtherObject respondsToSelector:
                [anInvocation selector]])
            [anInvocation invokeWithTarget:someOtherObject];
        else
    [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

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
- NSObject Category RNSwizzle.m/h example (ch-20)

- 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:
    http://robnapier.net/blog/hijacking-methodexchangeimplementations-502
      - NSNotificationCenter Category Example:
        @interface NSNotificationCenter (RNHijack)
        + (void)hijack;
        @end

        @implementation NSNotificationCenter (RNHijack)
        + (void)hijackSelector:(SEL)originalSelector withSelector:(SEL)newSelector
        {
            Class class = [NSNotificationCenter class];
            Method originalMethod = class_getInstanceMethod(class, originalSelector);
            Method categoryMethod = class_getInstanceMethod(class, newSelector);
            method_exchangeImplementations(originalMethod, categoryMethod);
        }

        + (void)hijack
        {
            [self hijackSelector:@selector(removeObserver:)
                withSelector:@selector(RNHijack_removeObserver:)];
        }

        - (void)RNHijack_removeObserver:(id)notificationObserver
        {
            NSLog(@"Removing observer: %@", notificationObserver);
            [self RNHijack_removeObserver:notificationObserver];
        }
  
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
  



WHAT ABOUT...?
- Class structure metaclass pointer
- KVO refresher




CREDITS:
- Everything:
  http://iosptl.com/
  The Objective-C Runtime Programming Guide
    https://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Introduction/Introduction.html
- C Basics:
  http://heather.cs.ucdavis.edu/~matloff/UnixAndC/CLanguage/PointersI.html
- Messaging:
  http://www.friday.com/bbum/2009/12/18/objc_msgsend-part-1-the-road-map/
  http://mikeash.com/pyblog/friday-qa-2009-03-20-objective-c-messaging.html
- Swizzling:
  http://robnapier.net/blog/hijacking-methodexchangeimplementations-502
Something went wrong with that request. Please try again.