Slide 1

Slide 1 text

[Objective-C Internals] [Suvrat Apte] [Helpshift Inc]

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

Legacy Photo Booth Mail Xcode and Instruments UIKit Foundation Core frameworks Kernel modules

Slide 4

Slide 4 text

A Dynamic Language Defers many decisions (as much as possible) from 1. Compile time, to 2. Link time, to 3. Run time

Slide 5

Slide 5 text

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 ❌

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

Example C++ Compiled code gets inserted directly into the flow of the binary Objective-C objc_msgSend(example, @selector(methodTwo));

Slide 9

Slide 9 text

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.

Slide 10

Slide 10 text

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.

Slide 11

Slide 11 text

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⃣

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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?

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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)

Slide 18

Slide 18 text

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)

Slide 19

Slide 19 text

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)

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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 { // . . . }

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

isa Swizzling EmployeeX Employee NSKVONotifying_ Employee

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

Open sourced! https://github.com/opensource-apple/objc4 https://opensource.apple.com/

Slide 27

Slide 27 text

Thank you! @suvratapte