runtime介绍:
runtime 简称运行时, 是一套纯c编写的API.
objective-c是基于c的,为c添加了面向对象的特性,它将很多静态语言在编译和链接时期做的事放到了runtime运行时来处理.可以说runtime是oc的幕后工作者.
我们都知道高级编程语言想要成为可执行文件,需先编译为汇编语言再汇编为机器语言,机器语言也是计算机能够识别的唯一语言,但是oc并不能直接编译为汇编语言,而是先要转写为纯C语言在进行编译和汇编操作,从oc到c语言的过渡就是就是由runtime实现的.
我们使用oc进行面向对象开发,而c语言更多的是面向过程开发,这就需要将面向对象的类转变为面向过程的的结构体.其实我们创建的一个对象或实例就是一个结构体,这个结构体里定义了很多变量,有, 指向元类的isa, 指向父类的指针,类的名字,版本,实例大小,实例变量列表,方法列表,缓存,租售的协议列表,结构体如下:
structobjc_class {
Class isa OBJC_ISA_AVAILABILITY;
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
}
runtime最主要的是消息机制.,
对于c语言,函数的调用在编译的时候会决定调用哪个函数.
oc的函数调用称为消息发送,属于动态调用过程.在编译的时候并不能决定真正调用哪个函数,只要声明过就不会报错,只有当运行的时候才会报错,这是因为oc是运行时动态调用的.而c语言调用未实现的函数就会报错.
runtime消息机制
NSMutableString *str = [[NSMutableString alloc] initWithString: @"hello"];
[str appendString:@" world"];
上述代码的str称为消息接受者,appendString:称作选择子,也就是我们常用的selector,selector和参数共同构成了消息,所以第二句话可以理解为将消息:"增加一个字符串: is a good guy"发送给消息的接受者str。
objc_msgSend的工作原理, 为了匹配消息的接受者和选择子首先会在消息的接受者所在的类中去搜索这个struct objc_cache,如果能找到就可以直接跳到相关的具体实现中去调用,如果找不到,再去 struct objc_method_list方法列表搜索,如果能找到就可以直接跳到相关的具体实现中去调用,如果找不到,就会通过super_class指针沿着继承树向上搜索,如果找到就跳转,如果到了继承树的根部(通常是NSObject)还没找到,那就会包unrecongnized selector错误(其实在条用这个方法之前还会进行消息转发,还有三次机会处理, 这就体现了runtime的强大).
三次机会:
1,所属类动态方法解析
如果沿继承树没有搜索到相关方法,则会向接受者所属类进行一次请求,看是否能动态添加一个方法,注意这是一个类方法,因为是是想接受者所属的类进行请求
+(BOOL)resolveInstanceMethod:(SEL)name
2,备援接受者
当对象所属类不能动态添加方法后,runtime就会询问当前的接受者是否有其他对象可以处理这个位置的selector:
- (id)forwardingTargetForSelector:(SEL)aSelector;
3,消息重定向
当没有北苑接受者事,就只剩下最后一次机会了,那就是消息重定向,这个时候runtime会将未知消息的所有细节都封装为NSInvocation对象,方法如下:
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
- (void)forwardInvocation:(NSInvocation *)anInvocation
runtime的其他作用: 动态添加属性,动态添加方法,交换方法(method swizzling)
获取属性
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person* p = [[Person alloc] init];
p.cjmName = @"Jiaming Chen";
unsigned int propertyCount = 0;
objc_property_t *propertyList = class_copyPropertyList([p class], &propertyCount);
for (int i = 0; i < propertyCount; i++) {
const char* name = property_getName(propertyList[i]);
const char* attributes = property_getAttributes(propertyList[i]);
NSLog(@"%s %s", name, attributes);
}
}
return 0;
}
动态添加属性
class_addProperty
方法交换: 本质是修改selector对应的_imp,也就是修改实例方法的具体实现
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *p = [[Person alloc] initWithName:@"Jiaming Chen" age:22];
Method method1 = class_getInstanceMethod([p class], @selector(helloWorld));
Method method2 = class_getInstanceMethod([p class], @selector(showMyself));
method_exchangeImplementations(method1, method2);
[p showMyself];
[p helloWorld];
}
return 0;
}
iOS runtime探究(一): 从runtime开始理解面向对象的类到面向过程的结构体
iOS runtime探究(二): 从runtime开始深入理解OC消息转发机制
iOS runtime探究(三): 从runtime开始理解OC的属性property
iOS runtime探究(四): 从runtiem开始实践Category添加属性与黑魔法method swizzling