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. [Objective-C Internals]
    [Suvrat Apte]
    [Helpshift Inc]

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  11. 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⃣

    View Slide

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

    View Slide

  13. 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?

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  17. 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)

    View Slide

  18. 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)

    View Slide

  19. 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)

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  23. isa Swizzling
    EmployeeX Employee
    NSKVONotifying_
    Employee

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  27. Thank you!
    @suvratapte

    View Slide