forked from mudphone/ObjcPlayground
-
Notifications
You must be signed in to change notification settings - Fork 0
hicapacity/ObjcPlayground
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
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
About
Just some fun experiments to see how this really works.
Resources
Stars
Watchers
Forks
Releases
No releases published
Packages 0
No packages published