1、什么是Runtime
- Objective-C是一门动态性比较强的编程语言,跟C、C++等语言有着很大的不同
- Objective-C的动态性是由Runtime API来支撑的
- Runtime API提供的接口基本都是C语言的,源码由C\C++\汇编语言编写
2、isa详解
- 在arm64架构之前,isa就是一个普通的指针,存储着Class、Meta-Class对象的内存地址
- 从arm64架构开始,对isa进行了优化,变成了一个共用体(union)结构,还使用位域来存储更多的信息
union isa_t
{
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
struct {
uintptr_t nonpointer : 1;
uintptr_t has_assoc : 1;
uintptr_t has_cxx_dtor : 1;
uintptr_t shiftcls : 33; // MACH_VM_MAX_ADDRESS 0x1000000000
uintptr_t magic : 6;
uintptr_t weakly_referenced : 1;
uintptr_t deallocating : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 19;
};
};
-
nonpointer
0代表普通的指针,存储着Class、Meta-Class对象的内存地址; 1代表优化过,使用位域存储更多的信息。 -
has_assoc
是否有设置过关联对象,如果没有,释放时会更快。 -
has_cxx_dtor
是否有C++的析构函数(.cxx_destruct),如果没有,释放时会更快。 -
shiftcls
存储着Class、Meta-Class对象的内存地址信息。 -
magic
用于在调试时分辨对象是否未完成初始化。 -
weakly_referenced
是否有被弱引用指向过,如果没有,释放时会更快。 -
deallocating
对象是否正在释放。 -
has_sidetable_rc
引用计数器是否过大无法存储在isa中。如果为1,那么引用计数会存储在一个叫SideTable
的类的属性中 -
extra_rc
里面存储的值是引用计数器减1。
3、Class的结构
struct objc_class {
Class isa;
Class superclass;
cache_t cache; // formerly cache pointer and vtable, 方法缓存
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags, 用于获取具体的类信息
class_rw_t *data() {
return bits.data();
}
};
class_rw_t
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint32_t version;
const class_ro_t *ro; //编译期的只读信息
method_array_t methods; //方法列表
property_array_t properties; //属性列表
protocol_array_t protocols; //协议列表
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
};
-
class_rw_t
里面的methods、properties、protocols是二维数组,是可读可写的,包含了类的初始内容、分类的内容。
class_rw_t的method_array_t.jpg
class_ro_t
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize; //instance对象占用的内存空间
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name; //类名
method_list_t * baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars; //成员变量列表
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
};
-
class_ro_t
里面的baseMethodList、baseProtocols、ivars、baseProperties是一维数组,是只读的,包含了类的初始内容。
class_ro_t的method_list_t.jpg
method_t
-
method_t
是对方法\函数的封装
struct method_t {
SEL name; //函数名
const char *types; //编码 (返回类型、参数类型)
IMP imp; //指向函数的指针(函数地址)
};
-
IMP
代表函数的具体实现
typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...);
-
SEL
代表方法\函数名,一般叫做选择器,底层结构跟char *
类似- 可以通过
@selector()
和sel_registerName()
获得 - 可以通过
sel_getName()
和NSStringFromSelector()
转成字符串 - 不同类中相同名字的方法,所对应的方法选择器是相同的
- 可以通过
typedef struct objc_selector *SEL;
- types包含了函数返回值、参数编码的字符串
返回值 | 参数1 | 参数2 | ...... | 参数n |
---|
- Type Encoding
iOS中提供了一个叫做@encode
的指令,可以将具体的类型表示成字符串编码
方法缓存
- Class内部结构中有个方法缓存
cache_t
,用散列表(哈希表)来缓存曾经调用过的方法,可以提高方法的查找速度。
struct cache_t {
struct bucket_t *_buckets; //散列表
mask_t _mask; //散列表的长度减1
mask_t _occupied; //已经缓存的方法数量
};
struct bucket_t {
private:
cache_key_t _key; //SEL作为Key
IMP _imp; //函数的内存地址
};
- 缓存查找
//objc-cache.mm
bucket_t * cache_t::find(cache_key_t k, id receiver)
4、objc_msgSend执行流程
- OC中的方法调用,其实都是转换为
objc_msgSend
函数的调用 -
objc_msgSend
的执行流程可以分为3大阶段 - 消息发送
- 动态方法解析
- 消息转发
- objc_msgSend源码执行流程
//objc-msg-arm64.s
ENTRY _objc_msgSend
b.le LNilOrTagged
CacheLookup NORMAL
.macro CacheLookup
.macro CheckMiss
STATIC_ENTRY __objc_msgSend_uncached
.macro MethodTableLookup
__class_lookupMethodAndLoadCache3
STATIC_ENTRY __objc_msgForward_impcache
ENTRY __objc_msgForward
//objc-runtime-new.mm
_class_lookupMethodAndLoadCache3
lookUpImpOrForward
getMethodNoSuper_nolock、search_method_list、log_and_fill_cache
cache_getImp、log_and_fill_cache、getMethodNoSuper_nolock、log_and_fill_cache
_class_resolveInstanceMethod
_objc_msgForward_impcache
//Core Foundation
__forwarding__(不开源)
4.1 objc_msgSend 消息发送流程
objc_msgSend执行流程01-消息发送.jpg
- 如果是从
class_rw_t
中查找方法
已经排序的,二分查找
没有排序的,遍历查找 - receiver通过
isa
指针找到receiverClass - receiverClass通过
superclass
指针找到superClass
4.2 objc_msgSend 动态方法解析
objc_msgSend执行流程02-动态方法解析.jpg
- 可以实现以下方法,来动态添加方法实现
+resolveInstanceMethod:
+resolveInstanceMethod:
- 动态解析过后,会重新走“消息发送”的流程
从receiverClass的cache中查找方法这一步开始执行
动态添加类方法
@interface Person : NSObject
+ (void)test;
@end
@implementation Person
+ (BOOL)resolveClassMethod:(SEL)sel {
if (sel == @selector(test)) {
Class metaClass = object_getClass(self);//元类
Method method = class_getClassMethod(metaClass, @selector(other));
// 动态添加test方法的实现
class_addMethod(metaClass, sel,
method_getImplementation(method),
method_getTypeEncoding(method));
// 返回YES代表有动态添加方法
return YES;
}
return [super resolveClassMethod:sel];
}
+ (void)other {
NSLog(@"%s", __func__);
}
@end
动态添加实例方法
@interface Person : NSObject
- (void)test;
@end
@implementation Person
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(test)) {
Class class = self;//Class类
Method method = class_getInstanceMethod(class, @selector(other));
// 动态添加test方法的实现
class_addMethod(class, sel,
method_getImplementation(method),
method_getTypeEncoding(method));
// 返回YES代表有动态添加方法
return YES;
}
return [super resolveInstanceMethod:sel];
}
- (void)other {
NSLog(@"%s", __func__);
}
@end
4.3 objc_msgSend 消息转发
objc_msgSend的执行流程03-消息转发.jpg
- 可以在
forwardInvocation:
方法中自定义任何逻辑 - 以上方法都有对象方法、类方法2个版本(前面可以是加号+,也可以是减号-)
forwardingTargetForSelector
实例方法
@interface Cat : NSObject
- (void)test;
@end
@implementation Cat
- (void)test {
NSLog(@"%s", __func__);
}
@end
@interface Person : NSObject
- (void)test;
@end
@implementation Person
- (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(test)) {
return [[Cat alloc] init];
}
return [super forwardingTargetForSelector:aSelector];
}
@end
类方法
@interface Cat : NSObject
+ (void)test;
@end
@implementation Cat
+ (void)test {
NSLog(@"%s", __func__);
}
@end
@interface Person : NSObject
+ (void)test;
@end
@implementation Person
+ (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(test)) {
return [Cat class];
}
return [super forwardingTargetForSelector:aSelector];
}
@end
forwardInvocation
实例方法
@interface Cat : NSObject
- (void)test;
@end
@implementation Cat
- (void)test {
NSLog(@"%s", __func__);
}
@end
@interface Dog : NSObject
- (void)test;
@end
@implementation Dog
- (void)test {
NSLog(@"%s", __func__);
}
@end
@interface Person : NSObject
- (void)test;
@end
@implementation Person
// 方法签名:返回值类型、参数类型
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if (aSelector == @selector(test)) {
return [NSMethodSignature signatureWithObjCTypes:"v16@0:8"];
}
return [super methodSignatureForSelector:aSelector];
}
/*
NSInvocation封装了一个方法调用,包括:方法调用者、方法名、方法参数
anInvocation.target 方法调用者
anInvocation.selector 方法名
[anInvocation getArgument:NULL atIndex:0]
*/
- (void)forwardInvocation:(NSInvocation *)anInvocation {
[anInvocation invokeWithTarget:[[Cat alloc] init]];
[anInvocation invokeWithTarget:[[Dog alloc] init]];
}
@end
类方法
@interface Cat : NSObject
+ (void)test;
@end
@implementation Cat
+ (void)test {
NSLog(@"%s", __func__);
}
@end
@interface Dog : NSObject
+ (void)test;
@end
@implementation Dog
+ (void)test {
NSLog(@"%s", __func__);
}
@end
@interface Person : NSObject
+ (void)test;
@end
@implementation Person
// 方法签名:返回值类型、参数类型
+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if (aSelector == @selector(test)) {
return [NSMethodSignature signatureWithObjCTypes:"v16@0:8"];
}
return [super methodSignatureForSelector:aSelector];
}
+ (void)forwardInvocation:(NSInvocation *)anInvocation {
[anInvocation invokeWithTarget:[Cat class]];
[anInvocation invokeWithTarget:[Dog class]];
}
@end
4.4 降低doesNotRecognizeSelector崩溃
@interface Person : NSObject
- (void)run;
- (void)test;
- (void)other;
@end
@implementation Person
- (void)run {
NSLog(@"run-123");
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
// 本来能调用的方法
if ([self respondsToSelector:aSelector]) {
return [super methodSignatureForSelector:aSelector];
}
// 找不到的方法
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
// 找不到的方法,都会来到这里
- (void)forwardInvocation:(NSInvocation *)anInvocation {
NSLog(@"找不到%@方法", NSStringFromSelector(anInvocation.selector));
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [[Person alloc] init];
[person run];
[person test];
[person other];
}
return 0;
}
找不到方法输出.jpg
写一个NSObject的分类解决找不到方法问题
@implementation NSObject (ForwardInvocation)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class cls = self;
Method method1 = class_getInstanceMethod(cls, @selector(methodSignatureForSelector:));
Method method2 = class_getInstanceMethod(cls, @selector(fi_methodSignatureForSelector:));
method_exchangeImplementations(method1, method2);
Method method3 = class_getClassMethod(cls, @selector(methodSignatureForSelector:));
Method method4 = class_getClassMethod(cls, @selector(fi_methodSignatureForSelector:));
method_exchangeImplementations(method3, method4);
});
}
- (NSMethodSignature *)fi_methodSignatureForSelector:(SEL)aSelector {
if ([self respondsToSelector:aSelector]) {
return [self fi_methodSignatureForSelector:aSelector];
}
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
+ (NSMethodSignature *)fi_methodSignatureForSelector:(SEL)aSelector {
if ([self respondsToSelector:aSelector]) {
return [self fi_methodSignatureForSelector:aSelector];
}
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
NSLog(@"找不到%@方法", NSStringFromSelector(anInvocation.selector));
}
+ (void)forwardInvocation:(NSInvocation *)anInvocation {
NSLog(@"找不到%@方法", NSStringFromSelector(anInvocation.selector));
}
@end
5、super的本质
- super调用,底层会转换为objc_msgSendSuper2函数的调用,接收2个参数
struct objc_super2
SEL
struct objc_super2 {
id receiver;
Class current_class;
};
-
receiver
是消息接收者 -
current_class
是receiver
的Class
对象
@interface Person : NSObject
@end
@implementation Person
@end
@interface Student : Person
@end
@implementation Student
- (instancetype)init {
if (self = [super init]) {
NSLog(@"[self class] = %@", [self class]);
NSLog(@"[self superclass] = %@", [self superclass]);
NSLog(@"[super class] = %@", [super class]);
NSLog(@"[super superclass] = %@", [super superclass]);
}
return self;
}
@end
superClass输出.jpg
看objc源码
+ (Class)class {
return self;
}
- (Class)class {
return object_getClass(self);
}
+ (Class)superclass {
return self->superclass;
}
- (Class)superclass {
return [self class]->superclass;
}
struct objc_super {
__unsafe_unretained _Nonnull id receiver; // 消息接收者
__unsafe_unretained _Nonnull Class super_class; // 消息接收者的父类
};
-
[super message]
的底层实现- 消息接收者仍然是子类对象
- 从父类开始查找方法的实现
//[super run]转化的源码
struct __rw_objc_super {
struct objc_object *object;
struct objc_object *superClass;
};
static void _I_Student_run(Student * self, SEL _cmd) {
((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Student"))}, sel_registerName("run"));
}
简化下相当于
struct __rw_objc_super arg = {self, [Person class]};
objc_msgSendSuper(arg, @selector(run));
[super class]
输出Student的原因
[super superclass]
输出Person的原因,
-
[super class]
中的class
方法只在NSObject
才有。 - 调用的是
object_getClass(self);
, -
self
实际上是Student对象
,对应Student类 - 同理
[super superclass]
--->[self class]->superclass
, 对应Person类
6、isKindOfClass、isMemberOfClass方法
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 这句代码的方法调用者不管是哪个类(只要是NSObject体系下的),都返回YES
NSLog(@"%d", [NSObject isKindOfClass:[NSObject class]]); // 1
NSLog(@"%d", [NSObject isMemberOfClass:[NSObject class]]); // 0
NSLog(@"%d", [Person isKindOfClass:[Person class]]); // 0
NSLog(@"%d", [Person isMemberOfClass:[Person class]]); // 0
}
return 0;
}
+ (BOOL)isMemberOfClass:(Class)cls {
return object_getClass((id)self) == cls;
}
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
isa-superclass.png
7、Runtime应用
- 利用关联对象(
AssociatedObject
)给分类添加属性 - 遍历类的所有成员变量(修改
textfield
的占位文字颜色、字典转模型、自动归档解档) - 利用消息转发机制解决方法找不到的异常问题
- 交换方法实现(交换系统的方法)
Method Swizzling
8、练习
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
- (void)print;
@end
@implementation Person
- (void)print {
NSLog(@"name = %@", self.name);
}
@end
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
id cls = [Person class];
void *obj = &cls;
[(__bridge id)obj print];
}
@end
调用输出
练习输出.jpg
1、为什么print能打印成功
- obj存储的是cla的地址,cls存储的地址是
[Person class]
的地址。 - 与实例对象调用类似,person存储的是isa的地址,通过isa找到
[Person class]
,调用print方法。所以能调用成功
Person *person = [[Person alloc] init];
[person print];
cls指向问题.jpg
2、输出为什么变为ViewController
或者其他的
[super viewDidLoad];
转化为C++函数,其实有个临时的self变量
struct abc = {
self,
[ViewController class]
};
objc_msgSendSuper2(abc, sel_registerName("viewDidLoad"));
viewDidLoad
方法各个临时变量在函数中的内存地址是在栈中的,栈的地址是从高到低的,地址分布如下:
栈地址.jpg
cls相当于isa, 其下面一个地址就是_name的地址,所以会输出ViewController
。