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

Roy Marmelstein: THE OBJECTIVE C RUNTIME AND SWIFT DYNAMISM

1fa9cb8c7997c8c4d3d251fb5e41f749?s=47 Realm
October 22, 2016

Roy Marmelstein: THE OBJECTIVE C RUNTIME AND SWIFT DYNAMISM

A new talk introducing advanced techniques with the Objective C runtime and assessing their relevance in an increasingly Swifty world.

1fa9cb8c7997c8c4d3d251fb5e41f749?s=128

Realm

October 22, 2016
Tweet

Transcript

  1. The Objective C Runtime and Swift dynamism A 2016 Perspective

  2. 5 months

  3. None
  4. “I’m documenting problems that Mac and iOS developers solve using

    the dynamic features of the Objective-C runtime. […] The point is that these problems will need solving in a possible future world without the Objective-C runtime. The answers don’t have to be the same answers as Objective- C – but they need to be good answers.” Brent Simmons
  5. None
  6. None
  7. - ObjC runtime functions and dynamism. - The 2016 Swift

    perspective. - The future
  8. Cat gif!

  9. VS

  10. The Objective C Runtime

  11. - #import <objc/runtime.h> - Written mostly in C & Assembler.

    - Classes. - Method dispatching. - Method forwarding. - Protocols - Open source!
  12. Dynamism!

  13. None
  14. typedef struct objc_class *Class; struct objc_object { Class isa; };

  15. struct objc_class { Class isa; Class super_class; const char *name;

    long version; long info; long instance_size; struct objc_ivar_list *ivars; struct objc_method_list **methodLists; struct objc_cache *cache; struct objc_protocol_list *protocols; };
  16. - Create a class at runtime. Dynamism!

  17. Class myClass = objc_allocateClassPair([NSObject class], "MyClass", 0); // Add ivars,

    methods, protocols. objc_registerClassPair(myClass); // ivars are locked after registration. [[myClass alloc] init];
  18. Categories

  19. - Create a class at runtime. - Add a stored

    property to an existing class. Dynamism!
  20. @implementation NSObject (AssociatedObject) @dynamic associatedObject; - (void)setAssociatedObject:(id)object { objc_setAssociatedObject(self, @selector(associatedObject),

    object, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (id)associatedObject { return objc_getAssociatedObject(self, @selector(associatedObject)); }
  21. - Create a class at runtime. - Add a stored

    property to an existing class. - Figure out what a class can do. Dynamism!
  22. Introspection

  23. [myObject isMemberOfClass:NSObject.class]; [myObject respondsToSelector:@selector (doStuff:)]; // isa == class class_respondsToSelector(myObject.class,

    @selector(doStuff:));
  24. None
  25. unsigned int count; Method *methods = class_copyMethodList(myObject.class, &count); //Ivar *list

    = class_copyIvarList(myObject.class, &count); for(unsigned i = 0; i < count; i++) { SEL selector = method_getName(methods[i]); NSString *selectorString = NSStringFromSelector(selector); if ([selectorString containsString:@"test"]) { [myObject performSelector:selector]; } } free(methods);
  26. struct objc_ivar { char *ivar_name; char *ivar_type; int ivar_offset; }

    struct objc_method { SEL method_name; char *method_types; IMP method_imp; }
  27. - Create a class at runtime. - Add a stored

    property to an existing class. - Figure out what a class can do. - Add a method at runtime. Dynamism!
  28. Method doStuff = class_getInstanceMethod(self.class, @selector(doStuff)); IMP doStuffImplementation = method_getImplementation(doStuff); const

    char *types = method_getTypeEncoding(doStuff); //“v@:@" class_addMethod(myClass.class, @selector(doStuff:), doStuffImplementation, types);
  29. None
  30. [self doStuff]; [self performSelector:@selector(doStuff)]; objc_msgSend(self, @selector(message));

  31. None
  32. None
  33. - Create a class at runtime. - Add a stored

    property to an existing class. - Figure out what a class can do. - Add a method at runtime. - Forward a method to another target. Dynamism!
  34. // 1 +(BOOL)resolveInstanceMethod:(SEL)sel{ // A chance to add the instance

    method and return YES. It will then try sending the message again. } // 2 - (id)forwardingTargetForSelector:(SEL)aSelector{ // Return an object that can handle the selector. } // 3 - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{ // You need to implement this for the creation of an NSInvocation. } - (void)forwardInvocation:(NSInvocation *)invocation { // Invoke the selector on a target of your choice. [invocation invokeWithTarget:target]; }
  35. - Create a class at runtime. - Add a stored

    property to an existing class. - Figure out what a class can do. - Add a method at runtime. - Forward a method to another target. - Replace / exchange an implementation. Dynamism!
  36. None
  37. + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ Class class

    = [self class]; SEL originalSelector = @selector(doSomething); SEL swizzledSelector = @selector(mo_doSomething); Method originalMethod = class_getInstanceMethod(class, originalSelector); Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)); if (didAddMethod) { class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); } else { method_exchangeImplementations(originalMethod, swizzledMethod); } }); }
  38. Foundation

  39. KVC + KVO

  40. - Create a class at runtime. - Add a stored

    property to an existing class. - Figure out what a class can do. - Add a method at runtime. - Forward a method to another target. - Replace / exchange an implementation. - Bind UI to Data. Dynamism!
  41. @property (nonatomic, strong) NSNumber *number; [myClass valueForKey:@"number"]; [myClass setValue:@(4) forKey:@"number"];

    KVC
  42. [myClass addObserver:self forKeyPath:@"number" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:nil]; - (void)observeValueForKeyPath:(NSString *)keyPath

    ofObject:(id)object change: (NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{ // Respond to observation. } KVO
  43. - Create a class at runtime. - Add a stored

    property to an existing class. - Figure out what a class can do. - Add a method at runtime. - Forward a method to another target. - Replace / exchange an implementation. - Bind UI to Data. Dynamism!
  44. -(void)attentionClassDumpUser: yesItsUsAgain: althoughSwizzlingAndOverriding PrivateMethodsIsFun: itWasntMuchFunWhenYour AppStoppedWorking: pleaseRefrainFromDoing SoInTheFutureOkayThanksBye: UIViewController

  45. None
  46. Swift is strongly typed and kind of statically typed. Dynamism

    available via ObjC runtime.
  47. “Swift on Linux does not depend on the Objective-C runtime

    nor includes it.”
  48. without the objective?

  49. @objc / NSObject @dynamic

  50. “While the @objc attribute exposes your Swift API to the

    Objective-C runtime, it does not guarantee dynamic dispatch. The Swift compiler may still devirtualize or inline member access to optimize the performance of your code, bypassing the Objective-C runtime. When you mark a member declaration with the dynamic modifier, access to that member is always dynamically dispatched. Declarations marked with the dynamic… [are] implicitly marked with the @objc attribute.”
  51. - Create a class at runtime. - Add a stored

    property to an existing class. - Add a method at runtime. - Figure out what a class can do. - Forward a method to another target. - Replace / exchange an implementation. - Bind UI to Data. Dynamism!
  52. // 1 override class func resolveInstanceMethod(_ sel: Selector!) -> Bool

    { // A chance to add the instance method and return YES. It will then try sending the message again. } // 2 override func forwardingTarget(for aSelector: Selector!) -> Any? { // Return an object that can handle the selector. } // 3 - NSInvocation is not available in Swift Forwarding *NSObject
  53. // - Implement on public override class func initialize() instead

    of + (void)load because load is never called in Swift Swizzling ☹
  54. if self is MyClass { // YAY } Introspection let

    myString = "myString"; let mirror = Mirror(reflecting: myString) print(mirror.subjectType) // “String" let string = String(reflecting: type(of: myString)) // Swift.String // No native method introspection
  55. XCTest - Linux static var allTests = { return [

    ("test_URLStrings", test_URLStrings), ("test_fileURLWithPath_relativeToURL", test_fileURLWithPath_relativeToURL), ("test_fileURLWithPath", test_fileURLWithPath), ("test_fileURLWithPath_isDirectory", test_fileURLWithPath_isDirectory), // Other tests go here ] }()
  56. NSObject + @dynamic + only ObjC types KVO/KVC?

  57. None
  58. None
  59. “I personally think that adding “dynamic” features to Swift is

    absolutely essential. I don’t think that it is interesting to provide the “equivalent” features to Objective-C, but I do think it is important that Swift be able to solve the same sorts of problems (including your list of Responder Chain, NSUndoManager, KVC/ KVO/Bindings, …) in a fluent/expressive way, even if it works differently. Chris Lattner
  60. Swift 4 Stage 2 “The core team is committed to

    adding powerful dynamic features to Swift.”
  61. ObjectiveKit coming soon…

  62. - Dynamism in ObjC is powerful, useful and dangerous. -

    Swift currently doesn’t offer good enough alternatives for most runtime functions. - This will likely improve with future versions of Swift. Conclusion
  63. None
  64. @marmelro y #yatusabes