1 | #import "objc/runtime.h" |
Runtime术语
objc_msgSend
的方法定义: 1
id objc_msgSend(id self, SEL op, ...);
SEL
1 | typedef struct objc_selector *SEL; |
SEL即selector,本质是方法名的常量字符串,所以同名方法就是同一个selector
id
1 | typedef struct objc_object *id; |
而objc_object为: 1
struct objc_object { Class isa; };
Class
1 | typedef struct objc_class *Class; |
1 | struct objc_class { |
1 | struct objc_ivar_list { |
Ivar
1 | typedef struct objc_ivar *Ivar; |
1 | struct objc_ivar { |
Method
Method代表类中某个方法: 1
typedef struct objc_method *Method;
1
2
3
4
5struct objc_method {
SEL method_name OBJC2_UNAVAILABLE;
char *method_types OBJC2_UNAVAILABLE;
IMP method_imp OBJC2_UNAVAILABLE;
}
IMP
1 | typedef id (*IMP)(id, SEL, ...); |
该函数指针指向的方法与objc_msgSend
函数类型相同
Cache
1 | typedef struct objc_cache *Cache |
1 | struct objc_cache { |
Runtime会把被调用的方法存到Cache中,为方法调用进行性能优化。当实例对象接收到一个消息时先在Cache中查找,若未命中再去isa指向的类的方法列表中遍历查找。
Property
1 | typedef struct objc_property *Property; |
可以通过class_copyPropertyList
和protocol_copyPropertyList
方法来获取类和协议中的属性,返回类型是指向objc_property_t数组的指针: 1
2objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
objc_property_t *protocol_copyPropertyList(Protocol *proto, unsigned int *outCount)property_getName
函数来查找属性名称: 1
const char *property_getName(objc_property_t property)
class_getProperty
和protocol_getProperty
通过给出的名称来在类和协议中获取属性的引用: 1
2objc_property_t class_getProperty(Class cls, const char *name)
objc_property_t protocol_getProperty(Protocol *proto, const char *name, BOOL isRequiredProperty, BOOL isInstanceProperty)property_getAttributes
函数来获得属性的名称和@encode类型字符串: 1
const char *property_getAttributes(objc_property_t property)
1
2
3
4
5
6
7id LenderClass = objc_getClass("Lender");
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList(LenderClass, &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
fprintf(stdout, "%s %s\n", property_getName(property), property_getAttributes(property));
}
消息传递
objc_msgSend的流程
- 检测selector可不可以忽略,比如Mac开发中开启GC就会忽略retain/release调用
- 检测target是不是nil,ObjC的会忽略向nil发的消息
- 在target的Class中根据selector查找IMP
- 先从cache里面找,找得到就跳到对应的函数去执行
- cache里找不到就去找Class的方法列表
- 还找不到,就沿着继承链找超类的方法列表,直到NSObject类为止
- 实在找不到,进入动态方法解析和消息转发机制
方法的隐藏参数
每个方法都有两个隐藏参数:self
为接收者对象,_cmd
为方法本身的selector
获取方法地址
NSObject中有个methodForSelector:实例方法,可以用来获取selector对应的IMP: 1
2
3void (*setter)(id, SEL, BOOL);
setter = (void (*)(id, SEL, BOOL))[target methodForSelector:@selector(setFilled:)];
for (int i = 0; i < 1000; i++) setter(targetList[i], @selector(setFilled:), YES);
动态方法解析
用@dynamic
修饰属性: 1
@dynamic propertyName;
resolveInstanceMethod:
或resolveClassMethod:
来给程序员一次动态添加方法实现的机会,需要用class_addMethod
函数向某类添加方法实现: 1
2
3
4
5
6
7
8
9
10
11
12void dynamicMethodIMP(id self, SEL _cmd) {
// implementation ....
}
@implementation MyClass
+ (BOOL)resolveInstanceMethod:(SEL)aSEL {
if (aSEL == @selector(resolveThisMethodDynamically)) {
class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, "v@:");
return YES;
}
return [super resolveInstanceMethod:aSEL];
}
@end
动态方法解析在消息转发机制之前执行,要让消息进入转发机制,就让resolveInstanceMethod:
返回NO
消息转发
重定向
在消息转发之前,还有一次偷梁换柱的机会,通过重载forwardingTargetForSelector:
可以替换消息的接收者: 1
2
3
4
5
6- (id)forwardingTargetForSelector:(SEL)aSelector {
if(aSelector == @selector(mysteriousMethod:)){
return alternateObject;
}
return [super forwardingTargetForSelector:aSelector];
}
转发
在对象无法正常响应消息时才会调用forwardInvocation:
,重载这个方法来定义任何你想要的转发逻辑: 1
2
3
4
5
6- (void)forwardInvocation:(NSInvocation *)anInvocation {
if ([someOtherObject respondsToSelector:[anInvocation selector]])
[anInvocation invokeWithTarget:someOtherObject];
else
[super forwardInvocation:anInvocation];
}forwardInvocation:
之前,会先调用methodSignatureForSelector:
来生成这里的anInvocation参数,所以在重写forwardInvocation:
之前还要重写methodSignatureForSelector:
,否则会因找不到signature而抛异常
转发可以实现类似多继承的效果,把消息转发给另一个对象,就好像借用或“继承”了另一个对象的方法
替代者对象
尽管转发能模拟多继承,但respondsToSelector:
、isKindOfClass:
等方法只会考虑继承链,不会考虑转发链。
如果处于某种意图需要模拟继承,得重载respondsToSelector:
、isKindOfClass:
、instancesRespondToSelector:
来加入转发逻辑: 1
2
3
4
5
6
7
8
9
10- (BOOL)respondsToSelector:(SEL)aSelector {
if ([super respondsToSelector:aSelector])
return YES;
else {
/* Here, test whether the aSelector message can *
* be forwarded to another object and whether that *
* object can respond to it. Return YES if it can. */
}
return NO;
}conformsToProtocol:
同样地,转发还得重载methodSignatureForSelector:
,比如一个对象为给它的替代者对象转发消息,需要实现: 1
2
3
4
5
6
7- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector {
NSMethodSignature* signature = [super methodSignatureForSelector:selector];
if (!signature) {
signature = [surrogate methodSignatureForSelector:selector];
}
return signature;
}
Assiciate Objects
给category添加变量涉及下面三个函数: 1
2
3void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);
id objc_getAssociatedObject(id object, const void *key);
void objc_removeAssociatedObjects(id object);1
2
3
4// NSObject+AssociatedObject.h
@interface NSObject (AssociatedObject)
@property (nonatomic, strong) id associatedObject;
@end1
2
3
4
5
6
7
8
9
10// NSObject+AssociatedObject.m
@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));
}
几个细节
[super class] == [self class]
[self class]
会转成 1
id objc_msgSend(id self, SEL op, ...)
[super class]
会转成 1
id objc_msgSendSuper(struct objc_super *super, SEL op, ...)
1 | struct objc_super { |
其中receiver就是self,super_class就是实际超类。同样向self发消息,但从超类的方法列表开始,沿继承链向上找@selector(class)的实现。由于没重载过这个方法,最终调用NSObject中的实现。
Object & Class & MetaClass

Class也是个对象,Class的类叫MetaClass。有几个特点:
- MetaClass的superclass继承链,和对应的原本Class的superclass继承链类似;只有根类特殊:NSObject的superclass为nil,MetaClass-of-NSObject的superclass为NSObject
- Class的isa指向自己的MetaClass,所有MetaClass的isa指向MetaClass-of-NSObject
Category的加载
Category中的方法在加载时被加入对应的方法列表中:实例方法加入Class的方法列表, 类方法加入MetaClass的方法列表
如果有多个Category,则按加载的逆序把方法加入最终方法列表。比如某类O,按序加载了Category A和B,则最终方法列表是:[B中的方法, A中的方法, O中的方法]