Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Just some fun experiments to see how this really works.

branch: master
README
Dynamic Languages - They're not just for Rubyists (and Pythonistas)

ABSTRACT
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.

WHY?
http://www.tiobe.com/index.php/paperinfo/tpci/Objective-C.html



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: http://iosptl.com/

Assumptions: 
- 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? http://developer.apple.com/library/ios/#referencelibrary/GettingStarted/Learning_Objective-C_A_Primer/_index.html
- Like C++, no multiple inheritance, no operator overloading
  
  
  
  - A Quick Intro to C Structs
    - http://heather.cs.ucdavis.edu/~matloff/UnixAndC/CLanguage/PointersI.html


  
  
  
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: http://www.sealiesoftware.com/blog/archive/2009/04/14/objc_explain_Classes_and_metaclasses.html
        - 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)
    http://cocoawithlove.com/2010/01/getting-subclasses-of-objective-c-class.html
    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


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
  
- 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: 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 IN DETAIL (IN KEYNOTE, STARTS HERE)

- 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: 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.
- 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 (opensource.apple.com)
  - got objc4-493.11 here: http://opensource.apple.com/release/mac-os-x-1073/

- 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];
        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 
  - Implement "Array#map"
    - HOM Example: http://www.mikeash.com/pyblog/friday-qa-2009-04-24-code-generation-with-llvm-part-2-fast-objective-c-forwarding.html
      - 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;
          @end
          @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;];
          }
          @end

          
        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:
    http://mikeash.com/pyblog/friday-qa-2010-01-29-method-replacement-for-fun-and-profit.html
      - NSNotificationCenter Category Example:
        #import <objc/runtime.h> 
        #import <objc/message.h>
      
        @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 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));
            }     
            else
            {
                method_exchangeImplementations(origMethod, overrideMethod);
            }
        }

        + (void)hijack
        {
            [self hijackSelector:@selector(removeObserver:)
                withSelector:@selector(RNHijack_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
  



WHAT ABOUT...?
- KVO refresher
  - Complete Friday Q&A V1: How Key-Value Observing Works
  - KVO Done Right (Take 2): http://www.mikeash.com/pyblog/friday-qa-2012-03-02-key-value-observing-done-right-take-2.html
- Blocks
  - Complete Friday Q&A V1: 
    - Practical Blocks
    - Blocks in Objective-C

- Implement Clojure/Ruby collection methods with trampolining or blocks?
- Higher order messaging: http://cocoadev.com/index.pl?HigherOrderMessaging

- Complete Friday Q&A V1: Fast Objective-C Forwarding
  - implement HOM: map?
  
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




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
  http://mikeash.com/book.html
- Swizzling:
  http://robnapier.net/blog/hijacking-methodexchangeimplementations-502
Something went wrong with that request. Please try again.