苹果公开的源代码
Runtime的源代码
Runloop的源代码
GitHub在线查看Runtime
GitHub在线查看Runloop
iOS开发:认识一下Runtime
iOS开发:Runtime常见方法
iOS开发:Runtime解决UITapGesture重复点击问题 iOS开发:Runtime解决UIButton重复点击问题
什么是Runtime
- OC语言是一门动态语言,它将很多静态语言在编译和链接时期做的事放到了运行时来处理。运行时系统就像一个操作系统一样:它让所有的工作可以正常的运行。这个运行时系统即Objc Runtime。Objc Runtime基本上是用C和汇编写的,这个库使得C语言有了面向对象的能力。
- Runtime其实有两个版本: “
modern ” 和 “legacy ”。我们现在用的 Objective-C 2.0 采用的是现行 (Modern ) 版的 Runtime 系统,只能运行在 iOS 和 macOS 10.5 之后的 64 位程序中。而 macOS 较老的32位程序仍采用 Objective-C 1 中的(早期)Legacy 版本的 Runtime 系统。这两个版本最大的区别在于当你更改一个类的实例变量的布局时,在早期版本中你需要重新编译它的子类,而现行版就不需要。
- runtime(简称运行时),是一套纯C(C和汇编)编写的API。而OC就是运行时机制,也就是在运行时候的一些机制,其中最主要的是消息机制 objc_msgSend。
- 对于 C 语言,函数的调用在编译的时候会决定调用哪个函数。
- 运行时机制原理:OC的函数调用称为消息发送,属于动态调用过程。在编译的时候 并不能决定真正调用哪个函数,只有在真 正运行的时候才会根据函数的名称找到对应的函数来调用。Runtime将数据类型的确定由编译时推迟到了运行时。
- 事实证明:在编译阶段,OC可以调用任何函数,即使这个函数并未实现,只要声明过就不会报错,只有当运行的时候才会报错,这是因为OC是运行时动态调用的。而C语言调用未实现的函数就会报错。
Runtime的组成
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
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;
struct objc_method_list {
struct objc_method_list *obsolete OBJC2_UNAVAILABLE;
int method_count OBJC2_UNAVAILABLE;
#ifdef __LP64__
int space OBJC2_UNAVAILABLE;
#endif
struct objc_method method_list[1] OBJC2_UNAVAILABLE;
} OBJC2_UNAVAILABLE;
struct objc_method {
SEL method_name OBJC2_UNAVAILABLE;
char *method_types OBJC2_UNAVAILABLE;
IMP method_imp OBJC2_UNAVAILABLE;
}
struct category_t {
const char *name;
classref_t cls;
struct method_list_t *instanceMethods;
struct method_list_t *classMethods;
struct protocol_list_t *protocols;
struct property_list_t *instanceProperties;
};
Runtime消息传递
- 实例对象调用方法后,底层调用
[objc performSelector:@selector(SEL)]; 方法,编译器将代码转化为objc_msgSend(receiver, selector) 。 - 在
objc_msgSend 函数中,首先通过objc 的isa 指针找到objc 对应的class ,在class 中先去cache 中通过SEL 查找对应函数的 method ,如果找到则通过 method 中的函数指针跳转到对应的函数中去执行。 - 如果在
cacha 中未找到,再去methodList 中查找,如果能找到,则将method 加入到cache 中,以方便下次查找,并通过method 中的函数指针跳转到对应的函数中去执行。 - 如果在
methodlist 中未找到,则去superClass 中去查找,如果能找到,则将method 加入到cache中,以方便下次查找,并通过method 中的函数指针跳转到对应的函数中去执行。
- objc在向一个对象发送消息时,runtime库会根据对象的isa指针找到该对象实际所属的类,然后在该类中的方法列表以及其父类方法列表中寻找方法运行,即:objc_msgSend(receiver, selector)。如果,在最顶层的父类中依然找不到相应的方法时,程序在运行时会挂掉并抛出异常unrecognized selector sent to XXX 。但是在这之前,objc的运行时会给出三次拯救程序崩溃的机会:
- objc运行时会调用+resolveInstanceMethod:或者 +resolveClassMethod:(实例方法和类方法),让你有机会提供一个函数实现。如果你添加了函数,那运行时系统就会重新启动一次消息发送的过程,否则 ,运行时就会移到下一步,消息转发(Message Forwarding)。
- 如果目标对象实现了-forwardingTargetForSelector:,Runtime 这时就会调用这个方法,给你把这个消息转发给其他对象的机会。 只要这个方法返回的不是nil和self,整个消息发送的过程就会被重启,当然发送的对象会变成你返回的那个对象。否则,就会继续Normal Fowarding。 这里叫Fast,只是为了区别下一步的转发机制。因为这一步不会创建任何新的对象,但下一步转发会创建一个NSInvocation对象,所以相对更快点。
- 这一步是Runtime最后一次给你挽救的机会。首先它会发送-methodSignatureForSelector:消息获得函数的参数和返回值类型。如果-methodSignatureForSelector:返回nil,Runtime则会发出-doesNotRecognizeSelector:消息,程序这时也就挂掉了。如果返回了一个函数签名,Runtime就会创建一个NSInvocation对象并发送-forwardInvocation:消息给目标对象。
一个完整转发的例子:
#import "ViewController.h"
#import "objc/runtime.h"
@interface Person: NSObject
@end
@implementation Person
- (void)foo {
NSLog(@"Doing foo");
}
@end
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self performSelector:@selector(foo)];
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
return YES;
}
- (id)forwardingTargetForSelector:(SEL)aSelector {
return nil;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if ([NSStringFromSelector(aSelector) isEqualToString:@"foo"]) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
SEL sel = anInvocation.selector;
Person *p = [Person new];
if([p respondsToSelector:sel]) {
[anInvocation invokeWithTarget:p];
}
else {
[self doesNotRecognizeSelector:sel];
}
}
@end
Objective-C type encodings
??Objective-C type encodings
Code | Meaning |
---|
c
| A char | i
| An int | s
| A short | l
| A long l is treated as a 32-bit quantity on 64-bit programs.
| q
| A long long | C
| An unsigned char | I
| An unsigned int | S
| An unsigned short | L
| An unsigned long | Q
| An unsigned long long | f
| A float | d
| A double | B
| A C++ bool or a C99 _Bool | v
| A void | *
| A character string (char * ) | @
| An object (whether statically typed or typed id ) | #
| A class object (Class ) | :
| A method selector (SEL ) | [array type] | An array | {name=type...} | A structure | (name=type...) | A union | b num
| A bit field of num bits | ^ type
| A pointer to type | ?
| An unknown type (among other things, this code is used for function pointers) |
|