iOS底层-Runtime系列三
super的本质
-
实例:
#import "Student.h" @implementation Student - (instancetype)init { self = [super init]; if (self) { NSLog(@"%@",[self class]);//Student NSLog(@"%@",[self superclass]);//Person NSLog(@"----------"); NSLog(@"%@",[super class]);//Student NSLog(@"%@",[super superclass]);//Person } return self; } @end
- 为何
[super class]
与[super superclass]
返回的是Student与Person,难道不该是Person与Nsobject吗?
- 为何
-
分析super的底层:将下面代码编译成C++,核心代码如下
-
OC代码
#import "Student.h" @implementation Student -(void)run{ [super run]; } @end
-
C++核心代码如下
//super结构体 struct __rw_objc_super { struct objc_object *object; struct objc_object *superClass; __rw_objc_super(struct objc_object *o, struct objc_object *s) : object(o), superClass(s) {} }; //run函数 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, class_getSuperclass(objc_getClass("Student")) //本质就是[Person class]; }; //objc_msgSendSuper(arg, sel_registerName("run")); objc_msgSendSuper(arg, @selector(run));
-
-
- objc公开源码分析:
-
找到objc_super结构体对象的定义
/// Specifies the superclass of an instance. struct objc_super { /// Specifies an instance of a class. //一个类的实例 __unsafe_unretained _Nonnull id receiver; /// Specifies the particular superclass of the instance to message. //一个类实例的父类,而且仅仅是第一个父类 __unsafe_unretained _Nonnull Class super_class; //哪里去搜索方法的实现 /* super_class is the first class to search */ };
-
- 分析:
-
[super class]
,本质是调用这个函数:OBJC_EXPORT void objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */ )
-
第一个参数是
objc_super
结构体对象{ self, [Person class] }
-
[super class]
本质是:struct objc_super arg = { self, [Person class] }; objc_msgSendSuper(arg, @selector(run));
-
-
最开始的实例本质如下:
- (instancetype)init { self = [super init]; if (self) { NSLog(@"%@",[self class]);//Student NSLog(@"%@",[self superclass]);//Person NSLog(@"----------"); //本质:objc_msgSendSuper({self,[Student class]},@selector(class)); NSLog(@"%@",[super class]);//Student //本质:objc_msgSendSuper({self,[Student superclass]},@selector(class)); NSLog(@"%@",[super superclass]);//Person } return self; }
- 总结
[super message]
的底层实现- 消息接收者receiver仍然是子类对象
- 从父类开始查找方法
- 注意:class与superclass方法
- 都是在NSObject.mm中实现的
-
在开源objec中的NSObject.mm可以查到:
//class的底层实现 //因此class这个方法的返回值取决于self,即receiver -(Class)class{ return object_getClass(self); } //superclass的底层实现 -(Class)superclass{ return class_getSuperclass(object_getClass(self)); }
- 汇编本质(以这个为准)
- super调用,底层会转换为objc_msgSendSuper2函数的调用,接收2个参数
- struct objc_super2
- SEL
- 与编译成C++区别,名称不一样(objc_msgSendSuper2、objc_super2)
- super调用,底层会转换为objc_msgSendSuper2函数的调用,接收2个参数
isMemberOfClass、isKindOfClass
-
这两个方法在objc开源的NSObject.mm中已经开源,如下:
//object_getClass这个函数如果参数cls是类对象那么返回的是元类对象了 + (BOOL)isMemberOfClass:(Class)cls { return object_getClass((id)self) == cls; } - (BOOL)isMemberOfClass:(Class)cls { return [self class] == cls; } //object_getClass这个函数如果参数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; }
-
对象方法:
- isMemberOfClass :左边的对象,是否是右边的类型 - isKindOfClass :边的对象,对否是右边的类型,或者是他的子类型
-
类方法
+ isMemberOfClass :左边类的元类,是否是右边的类型 + isKindOfClass : 左边类的元类,对否是右边的类型,或者是他的子类型
-
-
示例:
/*********对象方法**********/ //- isMemberOfClass :左边的对象,是否是右边的类型 // - isKindOfClass : 左边的对象,对否是右边的类型,或者是他的子类型 id person = [[Person alloc] init]; NSLog(@"====%d",[person isMemberOfClass:[Person class]]); //1 NSLog(@"====%d",[person isMemberOfClass:[NSObject class]]); //0 NSLog(@"====%d",[person isKindOfClass:[Person class]]); //1 NSLog(@"====%d",[person isKindOfClass:[NSObject class]]); //1 /*********类方法**********/ //+ isMemberOfClass :左边类的元类,是否是右边的类型 // + isKindOfClass : 左边类的元类,对否是右边的类型,或者是他的子类型 NSLog(@"====%d",[Person isMemberOfClass:[Person class]]); //0 //左边传类,右边传元类才是正确的比较 NSLog(@"====%d",[Person isMemberOfClass:object_getClass([Person class])]); //1 NSLog(@"====%d",[Person isMemberOfClass:[NSObject class]]); //0 NSLog(@"====%d",[Person isKindOfClass:[Person class]]); //0 //NSObject的元类等于它本身 NSLog(@"====%d",[Person isKindOfClass:[NSObject class]]); //1
-
总结:
- 区别:
isMemberOfClass
:判断两个类是否正好相等isKindOfClass
:判断2个类是否是继承体系
- 如何使用:(两个方法同样适用)
- 如果是对象方法:左边一定要是实例对象,右边参数传类对象
- 如果是类方法:左边一定是类,右边参数传元类对象
- 区别:
LLVM的中间代码(IR)
- Objective-C在变为机器代码之前,会被LLVM编译器转换为中间代码(Intermediate Representation)(注意了,可不是转成C++代码)
- OC->llvm转成的中间代码->汇编->机器码
- C++仅仅是OC底层方法的实现
-
可以使用以下命令行指令生成中间代码(.ll文件)
clang -emit-llvm -S main.m
-
.ll文件内部是一种全新的语法
@ - 全局变量 % - 局部变量 alloca - 在当前执行的函数的堆栈帧中分配内存,当该函数返回到其调用者时,将自动释放内存 i32 - 32位4字节的整数 align - 对齐 load - 读出,store 写入 icmp - 两个整数值比较,返回布尔值 br - 选择分支,根据条件来转向label,不根据条件跳转的话类似 goto label - 代码标签 call - 调用函数
- 具体可以参考官方文档:https://llvm.org/docs/LangRef.html
runtime的API
类
-
相关API如下:
动态创建一个类(参数:父类,类名,额外的内存空间) Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes) 注册一个类(要在类注册之前添加成员变量) void objc_registerClassPair(Class cls) 销毁一个类 void objc_disposeClassPair(Class cls) 获取isa指向的Class Class object_getClass(id obj) 设置isa指向的Class Class object_setClass(id obj, Class cls) 判断一个OC对象是否为Class BOOL object_isClass(id obj) 判断一个Class是否为元类 BOOL class_isMetaClass(Class cls) 获取父类 Class class_getSuperclass(Class cls)
-
示例代码:
// object_getClass:获取对象isa指向的Class,参数可以是实例对象(获取的为类对象),类对象(获取的为元类对象) void test1(){ Person *person = [[Person alloc] init]; [person run]; //0x100001190====0x100001190 NSLog(@"%p====%p",object_getClass(person),[Person class]); //元类地址: ====0x100001188 NSLog(@"====%p",object_getClass([Person class])); } //object_setClass: 设置对象的isa指向的Class,参数可以是实例对象(设置的是类对象),类对象(设置的元类对象) void test2(){ Person *person = [[Person alloc] init]; //====person run===== [person run]; object_setClass(person, [Car class]); //打印: ====Car NSLog(@"====%@",[person class]); //打印:====Car run===== [person run]; } void test3(){ Person *person = [[Person alloc] init]; //====person run===== [person run]; ; //object_isClass:判断一个对象是否为一个类对象或者元类对象 //打印:0====1===1 ,元类对象也是一种特殊的类对象 NSLog(@"%d====%d===%d", object_isClass(person),object_isClass([Person class]),object_isClass(object_getClass([Person class]))); //class_isMetaClass: 判断一个Class是否为元类 //打印: 0====1 NSLog(@"%d====%d",class_isMetaClass([Person class]),class_isMetaClass(object_getClass([Person class]))); //class_getSuperclass: 获取父类 //NSObject NSLog(@"====%@",class_getSuperclass([person class])); } void run(id self,SEL _cmd){ NSLog(@"====%@===%@==",self,NSStringFromSelector(_cmd)); } void test4(){ //动态创建一个类(参数:父类,类名,额外的内存空间) Class NewCls = objc_allocateClassPair([NSObject class], "Dog", 0); //给一个类添加一个成员变量 class_addIvar(NewCls, "_age", 4, 1, @encode(int)); class_addIvar(NewCls, "_weight", 4, 1, @encode(int)); //给一个类添加一个方法 class_addMethod(NewCls, @selector(run),(IMP)run, "v@:"); //注册一个类(要在类注册之前添加成员变量) objc_registerClassPair(NewCls); id dog = [[NewCls alloc] init]; //==16======= NSLog(@"==%zd=======",class_getInstanceSize(NewCls)); [dog setValue:@(6) forKey:@"_age"]; [dog setValue:@(50) forKey:@"_weight"]; //==6===50==== NSLog(@"==%@===%@====",[dog valueForKey:@"_age"],[dog valueForKey:@"_weight"]); //====<Dog: 0x102011160>===run== [dog run]; //销毁一个类objc_disposeClassPair objc_disposeClassPair(NewCls); }
成员变量
-
相关API如下:
获取一个实例变量信息 Ivar class_getInstanceVariable(Class cls, const char *name) 拷贝实例变量列表(最后需要调用free释放) Ivar *class_copyIvarList(Class cls, unsigned int *outCount) 设置和获取成员变量的值 void object_setIvar(id obj, Ivar ivar, id value) id object_getIvar(id obj, Ivar ivar) 动态添加成员变量(已经注册的类是不能动态添加成员变量的) BOOL class_addIvar(Class cls, const char * name, size_t size, uint8_t alignment, const char * types) 获取成员变量的相关信息 const char *ivar_getName(Ivar v) const char *ivar_getTypeEncoding(Ivar v)
-
示例:
//成员变量相关 void test5(){ //获取一个成员变量信息 Ivar ageIvar = class_getInstanceVariable([Person class], "_age"); //打印: _age===i NSLog(@"%s===%s",ivar_getName(ageIvar),ivar_getTypeEncoding(ageIvar)); Person *person = [[Person alloc] init]; //设置实例成员变量的值 object_setIvar(person, ageIvar, @(20)); //获取实例成员变量的值 NSLog(@"====%@",object_getIvar(person, ageIvar)); NSLog(@"====%d",person.age); //class_copyIvarList: 拷贝实例变量列表(最后需要调用free释放) unsigned int count = 0; //数组指针 Ivar * ivarList = class_copyIvarList([Person class], &count); //遍历所有的成员变量 for (int i =0; i<count; i++) { Ivar ivar = ivarList[i]; NSLog(@"%s===%s",ivar_getName(ivar),ivar_getTypeEncoding(ivar)); } //runtime中如果用copy或者create创建出来的都要用free释放掉 free(ivarList); }
属性
-
相关API如下:
获取一个属性 objc_property_t class_getProperty(Class cls, const char *name) 拷贝属性列表(最后需要调用free释放) objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount) 动态添加属性 BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount) 动态替换属性 void class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount) 获取属性的一些信息 const char *property_getName(objc_property_t property) const char *property_getAttributes(objc_property_t property)
方法
-
相关API如下:
获得一个实例方法、类方法 Method class_getInstanceMethod(Class cls, SEL name) Method class_getClassMethod(Class cls, SEL name) 方法实现相关操作 IMP class_getMethodImplementation(Class cls, SEL name) IMP method_setImplementation(Method m, IMP imp) void method_exchangeImplementations(Method m1, Method m2) 拷贝方法列表(最后需要调用free释放) Method *class_copyMethodList(Class cls, unsigned int *outCount) 动态添加方法 BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types) 动态替换方法 IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types) 获取方法的相关信息(带有copy的需要调用free去释放) SEL method_getName(Method m) IMP method_getImplementation(Method m) const char *method_getTypeEncoding(Method m) unsigned int method_getNumberOfArguments(Method m) char *method_copyReturnType(Method m) char *method_copyArgumentType(Method m, unsigned int index) 选择器相关 const char *sel_getName(SEL sel) SEL sel_registerName(const char *str) 用block作为方法实现 IMP imp_implementationWithBlock(id block) id imp_getBlock(IMP anImp) BOOL imp_removeBlock(IMP anImp)
-
示例:
void myrun(){ NSLog(@"====myrun====="); } void test6(){ Person *person = [[Person alloc] init]; //方法替换 // class_replaceMethod([Person class], @selector(run), (IMP)myrun, "v"); class_replaceMethod([Person class], @selector(run), imp_implementationWithBlock(^{ NSLog(@"====block"); }),"v"); [person run]; //方法交换 Method runimp = class_getInstanceMethod([Person class], @selector(run)); Method testimp = class_getInstanceMethod([Person class], @selector(test)); method_exchangeImplementations(runimp, testimp); //====person test===== [person run]; }
黑魔法的使用
method_exchangeImplementations
俗称黑魔法,使用场合很多- 监听整个项目的按钮点击事件
- 监听可变数组添加nil值(防止崩溃)
- 监听可变字典key值为nil(峰值崩溃)
- 等等
-
代码举例:
//NSObject+ZHExtension.m #import <UIKit/UIKit.h> #import <objc/runtime.h> /** UIControl的分类,用于拦截所有的点击事件 */ @implementation UIControl (Extension) +(void)load{ Method method1 = class_getInstanceMethod([self class], @selector(sendAction:to:forEvent:)); Method method2 = class_getInstanceMethod([self class], @selector(zh_sendAction:to:forEvent:)); method_exchangeImplementations(method1, method2); } /** 这个方法是UIControl的,一点点击事件触发,优先调用这个方法,然后才会去调用action方法 */ //-(void)sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event{ // NSLog(@"============="); //} -(void)zh_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event{ [self zh_sendAction:action to:target forEvent:event]; if ([self isKindOfClass:[UIButton class]]) { //这里监听所有按钮的点击 NSLog(@"======按钮点击======="); } } @end /** UIViewController的分类,用于监听所有控制器的delloc情况 */ @implementation UIViewController (Extension) -(void)dealloc{ NSLog(@"=======%@----delloc---",self.class); } @end /** 监听NSMutableArray添加空值 */ @implementation NSMutableArray (Extension) //addObject:的本质还是调用这个方法 //-(void)insertObject:(id)anObject atIndex:(NSUInteger)index{ // //} +(void)load{ //类簇:他们的真实类型是其他类型 NSString、 NSArray、NSDictionary //NSMutableArray的真实类型是:__NSArrayM Class cls = NSClassFromString(@"__NSArrayM"); Method method1 = class_getInstanceMethod(cls, @selector(insertObject:atIndex:)); Method method2 = class_getInstanceMethod(cls, @selector(zh_insertObject:atIndex:)); method_exchangeImplementations(method1, method2); } -(void)zh_insertObject:(id)anObject atIndex:(NSUInteger)index{ if (!anObject) { NSLog(@"加入的数据为空值!!!!===%s====%d",__func__,__LINE__); }else{ [self zh_insertObject:anObject atIndex:index]; } } @end /** 监听key值是否为nil dic[@"key"] = obj; 底层会调用setObject:forKeyedSubscript: */ @implementation NSMutableDictionary (Extension) +(void)load{ //类簇:他们的真实类型是其他类型 NSString、 NSArray、NSDictionary //NSMutableArray的真实类型是:__NSArrayM Class cls = NSClassFromString(@"__NSDictionaryM"); Method method1 = class_getInstanceMethod(cls, @selector(setObject:forKeyedSubscript:)); Method method2 = class_getInstanceMethod(cls, @selector(zh_setObject:forKeyedSubscript:)); method_exchangeImplementations(method1, method2); } -(void)zh_setObject:(id)obj forKeyedSubscript:(id<NSCopying>)key{ if (!key) { NSLog(@"==当前key值为空!!!=="); }else{ [self zh_setObject:obj forKeyedSubscript:key]; } } @end
-
使用:
@implementation ViewController - (IBAction)btnAction:(id)sender { NSLog(@"=====btn========"); } - (IBAction)btnAction2:(id)sender { NSLog(@"=====btn2========"); } - (IBAction)btnAction3:(id)sender { NSLog(@"=====btn3========"); } - (void)btnAction4:(id)sender { NSLog(@"=====btn4========"); } - (void)viewDidLoad { [super viewDidLoad]; [self.BTN addTarget:self action:@selector(btnAction4:) forControlEvents:UIControlEventTouchUpInside]; //监听可变数组添加是否为nil NSMutableArray *muarray = [NSMutableArray array]; //__NSArrayM NSLog(@"%@",[muarray class]); [muarray addObject:nil]; //可变字典key为nil id key = nil; NSMutableDictionary *dic = [[NSMutableDictionary alloc] init]; dic[@"jack"] = nil; dic[key] = @"123"; }
相关面试题
- 讲一下 OC 的消息机制
- OC中的方法调用其实都是转成了objc_msgSend函数的调用,给receiver(方法调用者)发送了一条消息(selector方法名)
- objc_msgSend底层有3大阶段
- 消息发送(当前类、父类中查找)、动态方法解析、消息转发
- 什么是Runtime?平时项目中有用过么?
- OC是一门动态性比较强的编程语言,允许很多操作推迟到程序运行时再进行
- OC的动态性就是由Runtime来支撑和实现的,Runtime是一套C语言的API,封装了很多动态性相关的函数
- 平时编写的OC代码,底层都是转换成了Runtime API进行调用