Objective-C的Runtime

版权所有,禁止匿名转载;禁止商业使用。

Objective-C语言是开源的,虽然现在Swift红红火火,但是Runtime是以前就容易被忽略的特性之一,所以在这个弥留之际,做一个总结还是有一些怀旧意义的


首先Runtime的概念就是编译器会把编译和链接的逻辑尽可能的放在运行的时候运行,这样提升灵活性。Objective-C 的 Runtime 是一个运行时库(Runtime Library),它是一个主要使用 C 和汇编写的库,为 C 添加了面相对象的能力并创造了 Objective-C。这就是说它在类信息(Class information) 中被加载,完成所有的方法分发,方法转发,等等。Objective-C runtime 创建了所有需要的结构体,让 Objective-C 的面相对象编程变为可能。


Objc方法的调用机制举个例子,下面会说具体的


[myObject doSomethingWith:parameter];


//编译时会变成
objc_msgSend(myObject,@selector(doSomethingWith:),parameter);
Selector是一个C的结构体,定义是这样的
typedef struct objc_selector *SEL;
//这么用
SEL aSel = @selector(movieTitle);

每个对象有一个isa指针,用于判断指向的类,这样比如你给一个对象传消息,调用方法,就会先通过isa指针找到这个对象是什么类,能否响应这个消息


//类是一个结构体
typedef struct objc_class *Class;
//对象也是一个结构体,含有一个isa指针
typedef struct objc_object {
Class isa;
} *id;

类也是有isa指针的,objc2.0以前,还有除了isa指针一些其他信息,比如superclass,方法list之类


struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE;
const char *name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;
struct objc_method_list **methodLists OBJC2_UNAVAILABLE;
struct objc_cache *cache OBJC2_UNAVAILABLE;
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;

初始化的过程


一个类调用alloc方法,过程和普通方法一样,这个类的isa指针会判断这个类能不能响应alloc方法,因为不能,会调用superClass的alloc方法,举个例子


id obj1 = [NSMutableArray alloc]; 
id obj2 = [[NSMutableArray alloc] init]; 
id obj3 = [NSArray alloc]; 
id obj4 = [[NSArray alloc] initWithObjects:@"Hello",nil];
NSLog(@"obj1 class is %@",NSStringFromClass([obj1 class])); //__NSPlaceholderArray
NSLog(@"obj2 class is %@",NSStringFromClass([obj2 class])); //__NSArrayM
NSLog(@"obj3 class is %@",NSStringFromClass([obj3 class])); //__NSPlaceholderArray
NSLog(@"obj4 class is %@",NSStringFromClass([obj4 class])); //__NSArrayI
id obj5 = [MyObject alloc]; 
id obj6 = [[MyObject alloc] init];
NSLog(@"obj5 class is %@",NSStringFromClass([obj5 class]));  //MyObject
NSLog(@"obj6 class is %@",NSStringFromClass([obj6 class])); //MyObject

class cache的作用应该是把method记住,便于下次调用方法或者传递消息的时候寻找这个method更快


调用方法的过程


前面说过了,调用方法会发送objc_msgSend消息,那么具体都发生了什么呢?


检查忽略的 Selector 和短路(Short Circut)

检查 nil 对象(target)。和其他的语言不一样的是,在 Objective-C 中向 nil 发送消息是完全合法的,并且有些原因下你会愿意这么做的。

然后我们需要在这个类上找到 IMP,所以我们先从 class cache 中找起,如果找到了就沿着指针跳到这个函数。

如果没有在缓存中找到 IMP,然后去查找类的分发表,如果找到了,就沿着指针跳到这个函数。

如果 IMP 没有在缓存和类的分发表中找到,然后我们跳到转发机制。这意味着最终你的代码被编译器转换为 C 函数。你写的方法会像这样…

-(int)doComputeWithNum:(int)aNum


//转换为
int aClass_doComputeWithNum(aClass *self,SEL _cmd,int aNum)

这里其实我并没有特别看懂IMP没有找到后会是怎么样的,待解答


动态添加方法


如果这个类没这个方法,响应不到收到的消息,那么可以临时起意给这个类添加方法,比如


void sayHello(id self, SEL _cmd) {
NSLog(@"Hello");
}
- (void)addMethod {
class_addMethod([EmptyClass class], @selector(sayHello2), (IMP)sayHello, "v@:");
// Test Method
EmptyClass *instance = [[EmptyClass alloc] init];
[instance sayHello2];
[instance release];
}

调用没实现的方法一般会crash,这时候可以重写下面这个方法来动态处理添加或是重定向

- (id)forwardingTargetForSelector:(SEL)aSelector

补一些关于Runtime题目的内容


一个题目

//输出什么?
@implementation Son : Father
- (id)init
{
self = [super init];
if (self)
{
NSLog(@"%@", NSStringFromClass([self class]));
NSLog(@"%@", NSStringFromClass([super class]));
}
return self;
}
@end

结果都会输出Son,那么为什么呢?


self在被调用的时候指向的是当前调用方法这个类的实例,super不是想当然的当前类的父类,是一个“Magic Keyword”,和self指向同一个消息接受者,它的本质是一个编译器标识符,所以调用[self class]或者[super class]都是Son *xxx这个对象接受的消息。


深入讨论一下,用clang -rewrite-objc ViewController.m命令可以看到[self class]和[super class]分别被转换成了什么样的C/C++代码,会发现:


[Son class]转换成objc_msgSend,这个前面已经提过,就像下面的代码,self自己去找自己的方法


i

d objc_msgSend(id self, SEL op, ...)

[Super class]转换成了objc_msgSendSuper,实现是这样

id objc_msgSendSuper(struct objc_super *super, SEL op, ...)

里面的objc_super是一个结构体


struct objc_super {
__unsafe_unretained id receiver;
__unsafe_unretained Class super_class;
};

那么调用起来的顺序是[self class]调用objc_msgSend,第一个参数是Son,在Son类里找-(Class)class方法,Son是没实现这个class方法的,去父类Father找也没有,最后在NSObject找找到了Class方法,执行,我们看下-(Class)class方法的实现


- (Class)class {
return object_getClass(self);
}

所以[self class]返回的其实是通过NSObject找到的class方法,返回self,自然就是Son了,然后看一下[super class]调用转换为objc_msgSendSuper, 构造objc_super结构体,第一个成员是self,第二个成员返回的是father,实际上这个objc_msgSendSuper返回的类结果是father,但是father没有class这个方法,NSObject类里找到了

objc_msgSend(objc_super->receiver, @selector(class))

等同于[self class] ,所以输出Son


0 0