The Objective-c Runtime
Павел Альбицкий
p.albitsky@gmail.com
Open source
libobjc.dylib
http://www.opensource.apple.com/source/objc
4/objc4-680/
Functions
class_getName
class_getSuperclass
class_setSuperclass
class_isMetaClass
class_getInstanceSize
class_getInstanceVariable
class_getClassVariable
class_addIvar
class_copyIvarList
class_getIvarLayout
class_setIvarLayout
class_getWeakIvarLayout
class_setWeakIvarLayout
class_getProperty
class_copyPropertyList
class_addMethod
class_getInstanceMethod
class_getClassMethod
class_copyMethodList
class_replaceMethod
class_getMethodImplementation
class_getMethodImplementation_stret
class_respondsToSelector
class_addProtocol
class_addProperty
class_replaceProperty
class_conformsToProtocol
class_copyProtocolList
class_getVersion
class_setVersion
objc_getFutureClass
objc_setFutureClass
Working with Classes
Adding Classes
objc_allocateClassPair
objc_disposeClassPair
objc_registerClassPair
objc_duplicateClass
Instantiating Classes
class_createInstance
objc_constructInstance
objc_destructInstance
Working with Instances
object_copy
object_dispose
object_setInstanceVariable
object_getInstanceVariable
object_getIndexedIvars
object_getIvar
object_setIvar
object_getClassName
object_getClass
object_setClass
Obtaining Class Definitions
objc_getClassList
objc_copyClassList
objc_lookUpClass
objc_getClass
objc_getRequiredClass
objc_getMetaClass
Working with Instance Variables
ivar_getName
ivar_getTypeEncoding
ivar_getOffset
Associative References
objc_setAssociatedObject
objc_getAssociatedObject
objc_removeAssociatedObjects
NSObject.mm
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
- (BOOL)respondsToSelector:(SEL)sel {
if (!sel) return NO;
return class_respondsToSelector_inst([self class], sel, self);
}
- (BOOL)conformsToProtocol:(Protocol *)protocol {
if (!protocol) return NO;
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (class_conformsToProtocol(tcls, protocol)) return YES;
}
return NO;
}
- (id)performSelector:(SEL)sel {
if (!sel) [self doesNotRecognizeSelector:sel];
return ((id(*)(id, SEL))objc_msgSend)(self, sel);
}
NSObject
@interface NSObject <NSObject> {
Class isa;
}
typedef struct objc_class *Class;
struct objc_class {
Class isa;
#if !__OBJC2__
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
#endif
};
NSObject
typedef struct objc_object *id;
typedef struct objc_selector *SEL;
typedef struct objc_method *Method;
typedef struct objc_ivar *Ivar;
typedef struct objc_category *Category;
typedef struct objc_property *objc_property_t;
typedef struct objc_object Protocol;
Class hierarchy
@interface BaseExample : NSObject {
NSString *_ivarBase;
}
@property(nonatomic, strong) NSString *baseProperty;
- (void)test;
+ (NSString *)baseClassMethod;
@end
#import "BaseExample.h"
@interface Example : BaseExample {
NSString *_ivarExample;
}
@property(nonatomic, strong) NSString *exampleProperty;
- (void)test;
- (NSString *)execWithParam:(NSString *)param;
+ (NSString *)exampleClassMethod;
@end
Instance of Example Class Example Metaclass Example
isa isa
Class BaseExample
isa
Class NSObject
isa
Metaclass
BaseExample
Metaclass NSObject
super_class
super_class
super_class
super_class
Class hierarchy
struct objc_method {
SEL method_name; // signature of method
char *method_types; // types of the parameters to the method
IMP method_imp; // memory address of the start of code block
};
typedef struct objc_method *Method;
A selector is the name used to select a method to execute for an object, or the
unique identifier that replaces the name when the source code is compiled.
Selector – C-string;
IMP - typedef id (*IMP)(id self,SEL _cmd,...);
Method
NSString *result = [example execWithParam:@"param"];
id objc_msgSend(id self, SEL op, ...)
result = objc_msgSend(example, @selector(execWithParam:), @"param");
Tail-call: typedef id (*IMP)(id self,SEL _cmd,...);
void objc_msgSend(void /* id self, SEL op, ... */ )
NSString *result = ((NSString* (*)(id, SEL, NSString *))objc_msgSend)(example,
@selector(execWithParam:), @"param");
Sending Messages
id objc_msgSend(id self, SEL op, ...)
Sends a message with a simple return value to an instance of a class.
double objc_msgSend_fpret(id self, SEL op, ...)
Sends a message with a floating-point return value to an instance of a class.
void objc_msgSend_stret(void * stretAddr, id theReceiver, SEL theSelector, ...)
Sends a message with a data-structure return value to an instance of a class.
id objc_msgSendSuper(struct objc_super *super, SEL op, ...)
Sends a message with a simple return value to the superclass of an instance of a class.
void objc_msgSendSuper_stret(struct objc_super *super, SEL op, ...) Sends a message with a
data-structure return value to the superclass of an instance of a class.
objc_msgSend
Message forwarding
resolveInstanceMethod
forwardingTargetForSelector
forwardInvocation
Message handled
Message
not handled
Return NO
Return YES
Return nil
Return
replacement
receiver
resolveInstanceMethod
1. Class cache -> class dispatch table -> all super classes
2. resolveInstanceMethod for @dynamic properties
id dynamicGetter(id self, SEL _cmd);
void dynamicSetter(id self, SEL _cmd, id value);
+ (BOOL)isDynamicProperty:(NSString *)selectorString;
+ (BOOL)resolveInstanceMethod:(SEL)sel {
NSString *selectorString = NSStringFromSelector(sel);
if ([self isDynamicProperty:selectorString]) {
if ([selectorString hasPrefix:@"set"]) {
class_addMethod(self, sel, (IMP)dynamicSetter, "v@:@");
} else {
class_addMethod(sel, sel, (IMP)dynamicGetter, "@@:");
}
return YES;
}
return [super resolveInstanceMethod:sel];
}
// "v@:@", "@@:" - Objective-C type encodings
forwardingTargetForSelector
3. The Runtime then calls forwardingTargetForSelector
Returns the object to which unrecognized messages should first be directed.
- (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(magicMethod:)) {
return magicObject;
} else {
return [super forwardingTargetForSelector:aSelector];
}
}
forwardInvocation
@implementation InvClass
- (NSString *)invWithFirst:(NSString *)first second:(NSNumber *)second
third:(NSArray *)third {
return [NSString stringWithFormat:@"first: %@, second: %@, third: %@", first, second,
third];
}
@end
// perform unrecognized selector
Example *example = [Example new];
SEL selector = @selector(invWithFirst:);
NSString *result = [example performSelector:selector
withObject:@"first"];
forwardInvocation
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(invWithFirst:)) {
return NO;
}
return YES;
}
- (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(invWithFirst:)) {
return nil;
}
return [super forwardingTargetForSelector:aSelector];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
NSMethodSignature* signature = [super methodSignatureForSelector:aSelector];
if (!signature) {
SEL customSelector = @selector(invWithFirst:second:third:);
signature = [_invObject methodSignatureForSelector:customSelector];
}
return signature;
}
forwardInvocation
- (void)forwardInvocation:(NSInvocation *)anInvocation {
SEL selector = anInvocation.selector;
if ([_invObject respondsToSelector:selector]) {
[anInvocation setTarget:_invObject];
SEL customSelector = @selector(invWithFirst:second:third:);
[anInvocation setSelector:customSelector];
NSString *first = @"first";
NSNumber *second = @2;
NSArray *third = @[@”3", @”33”, @”333"];
[anInvocation setArgument:&first atIndex:2];
[anInvocation setArgument:&second atIndex:3];
[anInvocation setArgument:&third atIndex:4];
[anInvocation invoke];
} else {
[self doesNotRecognizeSelector:selector];
}
}
Method Swizzling
#import <objc/runtime.h>
@import Crashlytics;
@implementation UIViewController (Tracking)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
SEL originalSelector = @selector(viewWillAppear:);
SEL swizzledSelector = @selector(swizzled_viewWillAppear:);
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);
}
});
}
- (void)swizzled_viewWillAppear:(BOOL)animated {
CLS_LOG(@"CLSLOG viewWillAppear: %@", self);
[self swizzled_viewWillAppear:animated];
}
@end
URLs
• Mike Ash
• Objective-C Runtime Reference
• Objective-C Runtime Programming Guide
• opensource.apple.com
Спасибо за внимание!
Павел Альбицкий
p.albitsky@gmail.com

Objective-c Runtime

  • 1.
    The Objective-c Runtime ПавелАльбицкий p.albitsky@gmail.com
  • 2.
  • 3.
  • 4.
    Adding Classes objc_allocateClassPair objc_disposeClassPair objc_registerClassPair objc_duplicateClass Instantiating Classes class_createInstance objc_constructInstance objc_destructInstance Workingwith Instances object_copy object_dispose object_setInstanceVariable object_getInstanceVariable object_getIndexedIvars object_getIvar object_setIvar object_getClassName object_getClass object_setClass Obtaining Class Definitions objc_getClassList objc_copyClassList objc_lookUpClass objc_getClass objc_getRequiredClass objc_getMetaClass Working with Instance Variables ivar_getName ivar_getTypeEncoding ivar_getOffset Associative References objc_setAssociatedObject objc_getAssociatedObject objc_removeAssociatedObjects
  • 5.
    NSObject.mm + (BOOL)isKindOfClass:(Class)cls { for(Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) { if (tcls == cls) return YES; } return NO; } - (BOOL)respondsToSelector:(SEL)sel { if (!sel) return NO; return class_respondsToSelector_inst([self class], sel, self); } - (BOOL)conformsToProtocol:(Protocol *)protocol { if (!protocol) return NO; for (Class tcls = [self class]; tcls; tcls = tcls->superclass) { if (class_conformsToProtocol(tcls, protocol)) return YES; } return NO; } - (id)performSelector:(SEL)sel { if (!sel) [self doesNotRecognizeSelector:sel]; return ((id(*)(id, SEL))objc_msgSend)(self, sel); }
  • 6.
    NSObject @interface NSObject <NSObject>{ Class isa; } typedef struct objc_class *Class; struct objc_class { Class isa; #if !__OBJC2__ 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 #endif };
  • 7.
    NSObject typedef struct objc_object*id; typedef struct objc_selector *SEL; typedef struct objc_method *Method; typedef struct objc_ivar *Ivar; typedef struct objc_category *Category; typedef struct objc_property *objc_property_t; typedef struct objc_object Protocol;
  • 8.
    Class hierarchy @interface BaseExample: NSObject { NSString *_ivarBase; } @property(nonatomic, strong) NSString *baseProperty; - (void)test; + (NSString *)baseClassMethod; @end #import "BaseExample.h" @interface Example : BaseExample { NSString *_ivarExample; } @property(nonatomic, strong) NSString *exampleProperty; - (void)test; - (NSString *)execWithParam:(NSString *)param; + (NSString *)exampleClassMethod; @end
  • 9.
    Instance of ExampleClass Example Metaclass Example isa isa Class BaseExample isa Class NSObject isa Metaclass BaseExample Metaclass NSObject super_class super_class super_class super_class Class hierarchy
  • 10.
    struct objc_method { SELmethod_name; // signature of method char *method_types; // types of the parameters to the method IMP method_imp; // memory address of the start of code block }; typedef struct objc_method *Method; A selector is the name used to select a method to execute for an object, or the unique identifier that replaces the name when the source code is compiled. Selector – C-string; IMP - typedef id (*IMP)(id self,SEL _cmd,...); Method
  • 11.
    NSString *result =[example execWithParam:@"param"]; id objc_msgSend(id self, SEL op, ...) result = objc_msgSend(example, @selector(execWithParam:), @"param"); Tail-call: typedef id (*IMP)(id self,SEL _cmd,...); void objc_msgSend(void /* id self, SEL op, ... */ ) NSString *result = ((NSString* (*)(id, SEL, NSString *))objc_msgSend)(example, @selector(execWithParam:), @"param"); Sending Messages
  • 12.
    id objc_msgSend(id self,SEL op, ...) Sends a message with a simple return value to an instance of a class. double objc_msgSend_fpret(id self, SEL op, ...) Sends a message with a floating-point return value to an instance of a class. void objc_msgSend_stret(void * stretAddr, id theReceiver, SEL theSelector, ...) Sends a message with a data-structure return value to an instance of a class. id objc_msgSendSuper(struct objc_super *super, SEL op, ...) Sends a message with a simple return value to the superclass of an instance of a class. void objc_msgSendSuper_stret(struct objc_super *super, SEL op, ...) Sends a message with a data-structure return value to the superclass of an instance of a class. objc_msgSend
  • 13.
  • 14.
    resolveInstanceMethod 1. Class cache-> class dispatch table -> all super classes 2. resolveInstanceMethod for @dynamic properties id dynamicGetter(id self, SEL _cmd); void dynamicSetter(id self, SEL _cmd, id value); + (BOOL)isDynamicProperty:(NSString *)selectorString; + (BOOL)resolveInstanceMethod:(SEL)sel { NSString *selectorString = NSStringFromSelector(sel); if ([self isDynamicProperty:selectorString]) { if ([selectorString hasPrefix:@"set"]) { class_addMethod(self, sel, (IMP)dynamicSetter, "v@:@"); } else { class_addMethod(sel, sel, (IMP)dynamicGetter, "@@:"); } return YES; } return [super resolveInstanceMethod:sel]; } // "v@:@", "@@:" - Objective-C type encodings
  • 15.
    forwardingTargetForSelector 3. The Runtimethen calls forwardingTargetForSelector Returns the object to which unrecognized messages should first be directed. - (id)forwardingTargetForSelector:(SEL)aSelector { if (aSelector == @selector(magicMethod:)) { return magicObject; } else { return [super forwardingTargetForSelector:aSelector]; } }
  • 16.
    forwardInvocation @implementation InvClass - (NSString*)invWithFirst:(NSString *)first second:(NSNumber *)second third:(NSArray *)third { return [NSString stringWithFormat:@"first: %@, second: %@, third: %@", first, second, third]; } @end // perform unrecognized selector Example *example = [Example new]; SEL selector = @selector(invWithFirst:); NSString *result = [example performSelector:selector withObject:@"first"];
  • 17.
    forwardInvocation + (BOOL)resolveInstanceMethod:(SEL)sel { if(sel == @selector(invWithFirst:)) { return NO; } return YES; } - (id)forwardingTargetForSelector:(SEL)aSelector { if (aSelector == @selector(invWithFirst:)) { return nil; } return [super forwardingTargetForSelector:aSelector]; } - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { NSMethodSignature* signature = [super methodSignatureForSelector:aSelector]; if (!signature) { SEL customSelector = @selector(invWithFirst:second:third:); signature = [_invObject methodSignatureForSelector:customSelector]; } return signature; }
  • 18.
    forwardInvocation - (void)forwardInvocation:(NSInvocation *)anInvocation{ SEL selector = anInvocation.selector; if ([_invObject respondsToSelector:selector]) { [anInvocation setTarget:_invObject]; SEL customSelector = @selector(invWithFirst:second:third:); [anInvocation setSelector:customSelector]; NSString *first = @"first"; NSNumber *second = @2; NSArray *third = @[@”3", @”33”, @”333"]; [anInvocation setArgument:&first atIndex:2]; [anInvocation setArgument:&second atIndex:3]; [anInvocation setArgument:&third atIndex:4]; [anInvocation invoke]; } else { [self doesNotRecognizeSelector:selector]; } }
  • 19.
  • 20.
    #import <objc/runtime.h> @import Crashlytics; @implementationUIViewController (Tracking) + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ Class class = [self class]; SEL originalSelector = @selector(viewWillAppear:); SEL swizzledSelector = @selector(swizzled_viewWillAppear:); 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); } }); } - (void)swizzled_viewWillAppear:(BOOL)animated { CLS_LOG(@"CLSLOG viewWillAppear: %@", self); [self swizzled_viewWillAppear:animated]; } @end
  • 21.
    URLs • Mike Ash •Objective-C Runtime Reference • Objective-C Runtime Programming Guide • opensource.apple.com
  • 22.
    Спасибо за внимание! ПавелАльбицкий p.albitsky@gmail.com