你要知道的runtime都在这里
转载请注明出处 http://blog.csdn.net/u014205968/article/details/67639289
本文主要讲解runtime
相关知识,从原理到实践,由于包含内容过多分为以下五篇文章详细讲解,可自行选择需要了解的方向:
- 从runtime开始: 理解面向对象的类到面向过程的结构体
- 从runtime开始: 深入理解OC消息转发机制
- 从runtime开始: 理解OC的属性property
- 从runtime开始: 实践Category添加属性与黑魔法method swizzling
- 从runtime开始: 深入weak实现机理
本文是系列文章的第二篇文章从runtime开始: 深入理解OC消息转发机制,主要从runtime
出发讲解OC的消息传递和消息转发机制。
你不知道的msg_send
我们知道在OC中的实例对象调用一个方法称作消息传递
,比如有如下代码:
NSMutableString *str = [[NSMutableString alloc] initWithString: @"Jiaming Chen"];
[str appendString:@" is a good guy."];
上述代码中的第二句str
称为消息的接受者,appendString:
称作选择子
也就是我们常用的selector
,selector
和参数
共同构成了消息
,所以第二句话可以理解为将消息:"增加一个字符串: is a good guy"
发送给消息的接受者str
。
OC中里的消息传递
采用动态绑定机制来决定具体调用哪个方法,OC的实例方法在转写为C语言后实际就是一个函数,但是OC并不是在编译期决定调用哪个函数,而是在运行期决定,因为编译期根本不能确定最终会调用哪个函数,这是由于运行期可以修改方法的实现,在后文会有讲解。举个栗子,有如下代码:
id num = @123;
//输出123
NSLog(@"%@", num);
//程序崩溃,报错[__NSCFNumber appendString:]: unrecognized selector sent to instance 0x7b27
[num appendString:@"Hello World"];
上述代码在编译期没有任何问题,因为id
类型可以指向任何类型的实例对象,NSString
有一个方法appendString:
,在编译期不确定这个num
到底具体指代什么类型的实例对象,并且在运行期还可以给NSNumber
类型添加新的方法,因此编译期发现有appendString:
的函数声明就不会报错,但在运行时找不到在NSNumber
类中找不到appendString:
方法,就会报错。这也就是消息传递的强大之处和弊端,编译期无法检查到未定义的方法,运行期可以添加新的方法。
讲了这么多OC究竟是怎么将实例方法转换为C语言的函数,又是如何调用这些函数的呢?这些都依靠强大的runtime
。
在深入代码之前介绍一个clang
编译器的命令:
clang -rewrite-objc main.m
该命令可以将.m的OC文件转写为.cpp文件
有如下代码:
@interface Person : NSObject
@property (nonatomic, copy) NSString* name;
@property (nonatomic, assign) NSUInteger age;
- (void)showMyself;
@end
@implementation Person
@synthesize name = _name;
@synthesize age = _age;
- (void)showMyself {
NSLog(@"My name is %@ I am %ld years old.", self.name, self.age);
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
//为了方便查看转写后的C语言代码,将alloc和init分两步完成
Person *p = [Person alloc];
p = [p init];
p.name = @"Jiaming Chen";
[p showMyself];
}
return 0;
}
通过上述clang
命令可以转写代码,然后找到如下定义: