Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Objective-C Internals

Objective-C Internals

This talk is about how Objective-C archives object orientation with plain C.

Suvrat Apte

October 28, 2017
Tweet

More Decks by Suvrat Apte

Other Decks in Programming

Transcript

  1. Facts - Designed by Brad Cox and Tom Love in

    1984 - 33 Years ago (A decade before I was born) - Inspired by Smalltalk’s message passing - Major implementations: Clang and GCC - First major use at NeXTSTEP in their OS - AppKit and FoundationKit at NeXTSTEP (NS!) - “Strict Superset“ of C
  2. A Dynamic Language Defers many decisions (as much as possible)

    from 1. Compile time, to 2. Link time, to 3. Run time
  3. Example C++ class Example { public: void methodOne(); void methodTwo();

    }; Example.h void Example::methodOne() { std::cout << "Method one"; } Example.cpp int main() { Example example; example.methodOne(); example.methodTwo(); return 0; } main.cpp $ g++ Example.cpp main.cpp Linker error ❌
  4. Example Objective-C @interface Example : NSObject - (void) methodOne; -

    (void) methodTwo; @end Example.h @implementation Example - (void) methodOne { NSLog(@"Method one"); } @end Example.m int main() { Example *example = [[Example alloc] init]; [example methodOne]; [example methodTwo]; return 0; } main.m $ clang -fobjc-arc Example.m main.m
  5. Example Objective-C @interface Example : NSObject - (void) methodOne; -

    (void) methodTwo; @end Example.h @implementation Example - (void) methodOne { NSLog(@"Method one"); } @end Example.m int main() { Example *example = [[Example alloc] init]; [example methodOne]; [example methodTwo]; return 0; } main.m $ ./a.out -[Example methodTwo]: unrecognized selector sent to instance
  6. Example C++ Compiled code gets inserted directly into the flow

    of the binary Objective-C objc_msgSend(example, @selector(methodTwo));
  7. The Runtime What is a Runtime? - The language needs

    the runtime to execute - It is like an operating system for the language Library : libobjc.dylib Header : objc/runtime.h A very good example of how you would write object oriented code using C.
  8. The Runtime Data Structures (Types) - Selectors: Internal identifier for

    a method. A null terminated string of the method name. - Class: Structure representing a class. - Method: Structure representing a method. - IMP: A function pointer.
  9. The Runtime Class class = [Example class]; SEL selector =

    @selector(methodOne); Method method = class_getInstanceMethod(class, selector); unsigned int nArgs = method_getNumberOfArguments(method); @interface Example : NSObject - (void) methodOne; - (void) methodTwo; @end Example.h @implementation Example - (void) methodOne { NSLog(@"Method one"); } @end Example.m 2⃣
  10. The Runtime @interface Example : NSObject - (void) methodOne; -

    (void) methodTwo; @end Example.h @implementation Example - (void) methodOne { NSLog(@"Method one"); } @end Example.m void methodOne(id self, SEL _cmd) { NSLog(@"Method one”); } Dispatch Table Addresses @selector(methodOne) @selector(methodTwo) NULL
  11. The Runtime void methodOne(id self, SEL _cmd) { NSLog(@"Method one”);

    } Dispatch Table Addresses @selector(methodOne) @selector(methodTwo) NULL [example methodOne]; objc_msgSend(example, @selector(methodOne)); struct objc_object { Class isa; }; Example Why is overloading not possible? How categories work? How method swizzling works?
  12. Categories @interface NSString (SAString) - (NSString *) trimmedString; @end @implementation

    NSString (SAString) - (NSString *) trimmedString { return [self stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]]; } @end Dispatch Table Addresses . . . . . . . . . . . . @selector(trimmedString) Dispatch table of NSString
  13. Categories @interface NSString (SAString) - (NSString *) trimmedStringSA; @end @implementation

    NSString (SAString) - (NSString *) trimmedStringSA { return [self stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]]; } @end Dispatch Table Addresses . . . . . . . . . . . . @selector(trimmedStringSA) Dispatch table of NSString
  14. Categories @interface NSString (SAString) - (NSString *) SAtrimmedString; @end @implementation

    NSString (SAString) - (NSString *) SAtrimmedString { return [self stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]]; } @end Dispatch Table Addresses . . . . . . . . . . . . @selector(SAtrimmedString) Dispatch table of NSString
  15. Method Swizzling @interface Example : NSObject - (void) methodOne; -

    (void) methodTwo; @end Example.h @implementation Example - (void) methodOne { NSLog(@"Method one"); } - (void) methodTwo { NSLog(@"Method two"); [self methodTwo]; } @end Example.m void methodOne(id self, SEL _cmd) { . . . } void methodTwo(id self, SEL _cmd) { . . . } Dispatch Table Addresses @selector(methodOne) @selector(methodTwo)
  16. Method Swizzling Class class = [Example class]; SEL s1 =

    @selector(methodOne); SEL s2 = @selector(methodTwo); Method m1 = class_getInstanceMethod(class, s1)); Method m2 = class_getInstanceMethod(class, s2)); method_exchangeImplementations(m1, m2); void methodOne(id self, SEL _cmd) { . . . } void methodTwo(id self, SEL _cmd) { . . . } Dispatch Table Addresses @selector(methodOne) @selector(methodTwo)
  17. Method Swizzling Class class = [Example class]; SEL s1 =

    @selector(methodOne); SEL s2 = @selector(methodTwo); Method m1 = class_getInstanceMethod(class, s1)); Method m2 = class_getInstanceMethod(class, s2)); method_exchangeImplementations(m1, m2); void methodOne(id self, SEL _cmd) { . . . } void methodTwo(id self, SEL _cmd) { . . . } Dispatch Table Addresses @selector(methodOne) @selector(methodTwo)
  18. Method Swizzling void methodOne(id self, SEL _cmd) { . .

    . } void methodTwo(id self, SEL _cmd) { . . . } Dispatch Table Addresses @selector(methodOne) @selector(methodTwo) @interface Example : NSObject - (void) methodOne; - (void) methodTwo; @end Example.h @implementation Example - (void) methodOne { NSLog(@"Method one"); } - (void) methodTwo { NSLog(@"Method two"); [self methodTwo]; } @end Example.m
  19. isa Swizzling struct objc_object { Class isa; }; Objective-C Object

    employeeX employeeY salary [employeeX addObserver:employeeY forKeyPath:@"salary" options:NSKeyValueObservingOptionNew context:NULL]; - (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { // . . . }
  20. isa Swizzling [employeeX addObserver:employeeY forKeyPath:@“salary" options:NSKeyValueObservingOptionNew context:NULL]; NSLog(@"%@", NSStringFromClass([employeeX class]));

    NSLog(@"%@", class_getClassName(employeeX)); NSLog(@"Started observing..."); NSLog(@"%@", NSStringFromClass([employeeX class])); NSLog(@"%@", class_getClassName(employeeX)); Employee Employee Started observing... Employee NSKVONotifying_Employee
  21. And much more class_getInstanceMethod class_addProtocol class_addIvar class_replaceProperty class_addProperty class_getClassName method_getNumberOfArguments

    method_getSizeOfArguments method_exchangeImplementations objc_setAssociatedObject objc_msgSend objc_msgForward
  22. Why? - Deeper understanding of how the language works -

    Better reasoning - Crazy debugging - Internal data structures of object oriented languages - Many parts of the Linux kernel use similar data structures - Fun! Advanced