在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
目录
Objective-C是开发 osx/ios 的主要语言,它是C语言的一个超集。Objective-C继承了C的基本语法特性,并添加了面对象的特性,所以在Objective-C代码里可以直接写C代码的。在Objective-C里面的对象是一个动态类型,只有在运行时才会被确定下来。在运行时你可以为一个类添加新的方法、属性,也可以修改这个类原有的方法、属性。提供动态添加、修改类的api我们管它叫做runtime api。这一篇文章我们就来学runtime api 、runtime与Objective-C的关系。Objective-C的runtime源码可以从这里获得 runtime 。每个版本的runtime实现不一样,本文以objc4-706.tar.gz为标板。 二、对象与类的实质id与class在Objective-C里面 id 代表着任意对象,Class代表着任意的类,我们可以看源码的定义: typedef struct objc_class *Class; typedef struct objc_object *id; 可以看出id的实质是机构体objc_object的指针,Class的实质是结构体objc_class的指针。我们在查看结构体objc_object、objc_class的源码(隐藏了一些代码) objc_object: struct objc_object { private: isa_t isa; // 唯一的成员变量 代表当前对象所指向的类的信息 public: isa_t: 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; }; } objc_class: struct objc_class : objc_object { Class superclass; const char *name; uint32_t version; uint32_t info; uint32_t instance_size; struct old_ivar_list *ivars; struct old_method_list **methodLists; Cache cache; struct old_protocol_list *protocols; // CLS_EXT only const uint8_t *ivar_layout; struct old_class_ext *ext; // 下面还有一系列相关函数 ...... } 从上面我们可以看出2点 1.objc_object有一个成员变量isa用来保存这个对象指向的类的信息 2.objc_class继承objc_object,意思就是说class也是对象,我们管它叫做类对象 既然类对象也是对象,那么类对象自然也有isa成员变量了,oc里面把类对象的isa成员变量指向的类叫做元类(meta class) 元类是用来实现类方法的,那么元类对象的isa有指向什么呢?oc里面把元类对象的isa指向基类的元类。元类也是有父类...感觉无穷无尽了 我们还是来看图1吧:
我们来说明上图 1.Cat类继承Animal类,Animal类继承NSObject类,cat是Cat的一个实例 2.cat的对象的isa指向 Cat 类 3.Cat类的isa指向Cat类的元类(Cat meta class,相应的Animal类的isa指向Animal类的元类(Animal meta class),NSObject类的isa指向NSObject类的元类(NSObject meta class) 4.Cat类的元类继承Animal类的元类,Animal类的元类继承NSObject类的元类,NSObject类的元类继承NSObject类。 5.Cat meta class、Animal meta class、NSObject meta class这三个元类的isa都指向根元类(root metaclass)。大部分时候根元类就是NSObject meta class。 继承关系与isa这些继承关系,还有isa构成了oc的对象与类整系统,它们到底怎么发生作用呢,请看下面例子。 @interface Animal : NSObject +(BOOL)isAnimal; // 是否动物 永远返回ture -(void)move; // 移动 @end @interface Cat :Animal -(void)eatFish; @end 上面是Cat与Animal类的实现,下面代码将展示对象、类怎么调用一个方法的。 Cat *cat = [Cat new]; [cat eatFish]; [cat move]; id newCat = [cat copy]; BOOL isAnimal = [Cat isAnimal]; 首先是创建一个Cat的实例 Cat *cat = [Cat new];
分析上面这句代码前我们先确认一件事:实例方法是在类里实现的,类方法是在类的元类里实现的 确认了这些事,我们就可以开始分析了 1.因为new是类方法,所以第一步当然是查询Cat类的isa指向的Cat类的元类(Cat meta class)有没有这个方法,发现Cat meta class 并没有实现new方法。 2.接着查询Cat meta class的父类即Animal类的元类(Animal meta class)有没有这个方法,发现还是new方法。 3.再接着查询Animal meta class的父类既NSObject类的元类(NSObject meta class),发现NSObject meta class是有实现new方法的,所以就调用new方法返回一个实例cat。 [cat eatFish]; 这句代码就比较简单了 先查询cat对象的isa指向的Cat类,有没有实现eatFish这个方法,发现Cat类已经实现了,直接调用既可。 [cat move]; 这个跟[cat eatFish]类似, 1.先查询cat对象的isa指向的Cat类,发现并没有实现move方法 2.在查询Cat类的父类Animal类,发现Animal类已经实现了move,直接调用 id newCat = [cat copy];
这句代码与[cat move]类似 1.先查询cat对象的isa指向的Cat类,发现并没有实现copy方法 2.再查询Cat类的父类Animal类,发现并没有实现copy方法 3.继续查询Animal类的父类NSObject类,发现NSObject类有实现copy方法,直接调用 BOOL isAnimal = [Cat isAnimal]; 这句代码与[Cat new]类似,大家可以试着分析。 总结在Objectative-c里面一个类的实例是一个对象,一个类也是一个对象叫类对象,对象用结构体struct objc_objectl来描述,对象都有一个isa成员变量用来指向这个对象的类。对象实例的方法都走这个对象的类里实现。 类对象的isa指向这个类对象的类,类对象的类叫做元类。类的方法都在元类里实现。 元类它也是一个对象叫做元类对象。元类对象的isa指向根元类(root metaclass),根元类在大部分时候都是NSObject meta class。 当然,类的成员变量、属性、方法、协议等都由相应的机构体来表示,这里就不一一展开了,下文遇到会展开。
三、C函数创建一个OC类这节我们用runtime的c函数来创建一个oc类,通过这种方式来加深对oc类的理解。 首先我写了一个方法用于打印一个id对象的类的相关信息, 1 void rtl_Class(id object){ 2 3 // 获取一个对象的class 4 Class cls = object_getClass(object); 5 // 获取class 名字 6 const char * className = class_getName(cls); 7 printf("%20s :[%p (%s)]\n" ,"class Name",cls,className); 8 9 // 展示类的继承关系 10 // 获取父类 11 Class superClass = class_getSuperclass(cls); 12 printf("%20s :[%p (%s)]" ,"class Inherit",cls,className); 13 while (superClass) { 14 printf(" => [%p (%s)]",superClass,class_getName(superClass)); 15 superClass = class_getSuperclass(superClass); 16 } 17 printf("\n"); 18 19 // 展示元类的继承关系 20 // 获取一个类的元类 21 Class metaClass = objc_getMetaClass(class_getName(cls)); 22 printf("%20s :[%p (%s)]" ,"meta class Inherit",metaClass,class_getName(metaClass)); 23 metaClass = class_getSuperclass(metaClass); 24 while (metaClass) { 25 printf(" => [%p (%s)]",metaClass, class_getName(metaClass)); 26 metaClass = class_getSuperclass(metaClass); 27 } 28 printf("\n"); 29 30 // 展示元类的isa信息 31 // isa成员变量在arm64下并不是一个Class的指针,所以不能直接这样使用 Class metaClass = cls->isa;但是可以打印cls->isa的指针 32 metaClass = objc_getMetaClass(class_getName(cls)); 33 while (metaClass) { 34 printf("%10s meta class isa :%p \n",class_getName(metaClass),metaClass->isa); 35 metaClass = class_getSuperclass(metaClass); 36 } 37 // 展示类变量列表 38 printf("\nvar List:\n"); 39 unsigned int count = 0; 40 Ivar * varList = class_copyIvarList(cls, &count); 41 for (int i=0; i<count; i++) { 42 Ivar var = varList[i]; 43 printf("[%30s] [offset:%ld]\n",ivar_getName(var),ivar_getOffset(var)); 44 } 45 // 展示属性列表 46 printf("\nproperty List:\n"); 47 count = 0; 48 objc_property_t * propList = class_copyPropertyList(cls, &count); 49 for (int i=0; i<count; i++) { 50 objc_property_t property = propList[i]; 51 printf("[%30s] [%s]\n",property_getName(property),property_getAttributes(property)); 52 } 53 54 // 展示类方法 55 printf("\nclass method List:\n"); 56 Class metaCls = objc_getMetaClass(class_getName(cls)); 57 count = 0; 58 Method * methodList = class_copyMethodList(metaCls, &count); 59 for (int i = 0; i < count; i++) { 60 Method method = methodList[i]; 61 printf("[%30s]\n",sel_getName(method_getName(method))); 62 } 63 64 // 展示实例类方法 65 printf("\nclass instance method List:\n"); 66 count = 0; 67 methodList = class_copyMethodList(cls, &count); 68 for (int i = 0; i < count; i++) { 69 Method method = methodList[i]; 70 printf("[%30s]\n",sel_getName(method_getName(method))); 71 } 72 printf("\n\n"); 73 } 继续讲之前先,大家应该先记住runtime函数的命名规则 class_xxx --- 与类相关的函数 objc_xxxx --- 与对象相关的函数 object_xxx --- 与类实例相关的函数 property_xxx --- 与属性相关的函数 ivar_xxx --- 与类成员变量相关的函数 method_xxx --- 与方法相关的函数 sel_xxxx --- 与seletor相关的函数 imp_xxx --- 与imp相关的函数 protocol_xxx --- 与协议相关的函数 runtime api函数的的命名比较规范,一看名字就大概知道函数的功能了,后续将不再讲解runtime api,特殊除外。 所有的runtime api 都可以在这里 查到 runtime 下面我们就用runtime api来创建一个Person类,Person类继承Biology类,Biology继承NSObject类。 1 // 2 // RuntimeTestVC.m 3 // IOSTest 4 // 5 // Created by 朱国清 on 17/1/16. 6 // Copyright © 2017年 朱国清. All rights reserved. 7 // 8 9 #import "RuntimeTestVC.h" 10 #import <objc/runtime.h> 11 #import "runtime_log.h" 12 #import <objc/message.h> 13 14 15 NSString * name(id self,SEL _cmd){ 16 // kvo coding 17 Ivar nameVar = class_getInstanceVariable([self class], "_name"); 18 return object_getIvar(self,nameVar); 19 } 20 void setName(id self,SEL _cmd, NSString *name){ 21 // kvo coding 22 Ivar nameVar = class_getInstanceVariable([self class], "_name"); 23 object_setIvar(self, nameVar, name); 24 printf("setName : %s\n",[name UTF8String]); 25 } 26 void sayHello(id self,SEL _cmd){ 27 Ivar ageVar = class_getInstanceVariable([self class], "age"); 28 NSNumber * age = object_getIvar(self, ageVar); 29 Ivar nameVar = class_getInstanceVariable([self class], "_name"); 30 NSString * name = object_getIvar(self, nameVar); 31 32 printf("[%s][%s] my name is %s age is %d \n",class_getName([self class]),sel_getName(_cmd),[name UTF8String],(int)age.integerValue); 33 } 34 void isPerson(){ 35 printf("is person.\n"); 36 } 37 void add(id self,SEL _cmd,int x, int y){ 38 printf("x + y = %d\n",x+y); 39 } 40 41 @interface RuntimeTestVC () 42 43 @end 44 45 @implementation RuntimeTestVC 46 47 - (void)viewDidLoad { 48 [super viewDidLoad]; 49 50 [self newClass]; 51 52 } 53 -(void)newClass{ 54 55 // 创建Biology类,继承NSObject类 56 Class Biology = objc_allocateClassPair([NSObject class], "Biology", 0); 57 // 注册Biology类,只有注册了,才能用Biology类 58 objc_registerClassPair(Biology); 59 60 // 创建 Person,继承Biology类 61 Class Person = objc_allocateClassPair(Biology, "Person", 0); 62 63 // 添加 类实例变量 64 class_addIvar(Person, "age", sizeof(NSInteger), 0, "i"); 65 class_addIvar(Person, "_name", sizeof(NSString *), 0, "@"); 66 67 68 // 添加 类的属性 69 objc_property_attribute_t type = {"T", "@\"NSString\""}; 70 objc_property_attribute_t ownership = { "C", "" }; 71 objc_property_attribute_t nonatomic = { "N", "" }; 72 objc_property_attribute_t backingivar = { "V", "_name"}; 73 objc_property_attribute_t attrs[] = {type, ownership,nonatomic, backingivar}; 74 class_addProperty(Person, "name", attrs, 4); 75 76 // 实例方法 77 // 属性name 的setter getter 方法 78 class_addMethod(Person, @selector(name), (IMP)name , "v"); 79 class_addMethod(Person, @selector(setName:),(IMP)setName, "v@"); 80 81 class_addMethod(Person, @selector(say), (IMP)sayHello, "v"); 82 class_addMethod(Person, @selector(addWithX:y:), (IMP)add, "vii"); 83 84 85 // 注册Person类 86 objc_registerClassPair(Person); 87 88 // 添加类方法 89 // 只有注册Person后才能通过objc_getMetaClass获取Person类的元类 90 Class personMetaClass = objc_getMetaClass(class_getName(Person)); 91 class_addMethod(personMetaClass, @selector(isPerson), (IMP)isPerson, "v"); 92 93 // 创建一个Person的实例 94 id zhang = [[Person alloc]init]; 95 // 打印zhang的信息 96 rtl_Class(zhang); 97 98 // 设置实例变量 99 Ivar ageVar = class_getInstanceVariable([zhang class], "age"); 100 object_setIvar(zhang, ageVar, @19); 101 102 // 5种调用一个实例的方法 103 // 1. 调用objc_msgSend函数得把 enable strict checking of objc_msgSend calls 设置为NO 104 // arm64下,需要显式转换函数原型 105 ((void(*)(id,SEL,int,int))objc_msgSend)(zhang, @selector(addWithX:y:),1,3); 106 ((void(*)(id,SEL,id))objc_msgSend)(zhang,@selector(setName:),@"zhangsan1"); 107 108 // 2.performSelector 109 [zhang performSelector:@selector(setName:) withObject:@"zhangsan2"]; 110 111 // 3.直接调用函数 112 setName(zhang, @selector(setName:), @"zhangsan3"); 113 114 // 4.直接调用Method 需要显式转换函数原型 115 Method setNameMethod = class_getInstanceMethod([zhang class], @selector(setName:)); 116 ((void (*)(id, Method, id)) method_invoke)(zhang, setNameMethod,@"zhangsan4"); 117 118 // 5.直接调用IMP 119 IMP setNameIMP = class_getMethodImplementation([zhang class],@selector(setName:)); 120 ((void (*)(id, SEL, id))setNameIMP)(zhang,@selector(setName:),@"zhangsan5"); 121 122 123 // 带返回值 124 NSString * name = ((id(*)(id,SEL))objc_msgSend)(zhang, @selector(name)); 125 printf("name = %s\n",[name UTF8String]); 126 127 [zhang performSelector:@selector(say)]; 128 129 // 调用类方法与调用实例方法是一样的,只是第一个参数设置为类对象 130 ((void(*)(id,SEL))objc_msgSend)((id)Person,@selector(isPerson)); 131 132 // 133 // objc_disposeClassPair(runtimeClass); 134 135 } 136 /* 137 输出 138 139 class Name :[0x7f9932da8830 (Person)] 140 class Inherit :[0x7f9932da8830 (Person)] => [0x7f9932d0b540 (Biology)] => [0x102ab9170 (NSObject)] 141 meta class Inherit :[0x7f9932da8330 (Person)] => [0x7f9932dc8e80 (Biology)] => [0x102ab9198 (NSObject)] => [0x102ab9170 (NSObject)] 142 Person meta class isa :0x102ab9198 143 Biology meta class isa :0x102ab9198 144 NSObject meta class isa :0x102ab9198 145 NSObject meta class isa :0x102ab9198 146 147 var List: 148 [ age] [offset:8] 149 [ _name] [offset:16] 150 151 property List: 152 [ name] [T@"NSString",C,N,V_name] 153 154 class method List: 155 [ isPerson] 156 157 class instance method List: 158 [ addWithX:y:] 159 [ say] 160 [ setName:] 161 [ name] 162 163 164 x + y = 4 165 setName : zhangsan1 166 setName : zhangsan2 167 setName : zhangsan3 168 setName : zhangsan4 169 setName : zhangsan5 170 name = zhangsan5 171 [Person][say] my name is zhangsan5 age is 19 172 is person. 173 174 */ 175 176 @end 代码都注释的很清楚了,耐心看完的理解应该没有问题。有几点需要说明一下 1.在arc下如果需要调用objc_msgSend等一些函数,必须做两件事。第一引入objc/runtime.h,第二把编译选项enable strict checking of objc_msgSend calls 设置为NO。 2.在ram64下调用objc_msgSend函数、method_invoke函数、IMP变量时必须显式地转化成相应的函数原型,才能正确的调用相应的函数。 3.上面代码的输出结果片段1证明图1的正确性。 4.通过objc_getMetaClass函数来获取一个Class的元类是,被获取的Class必须已经被linked进来,否则返回空。
还有两点需要解释 1.就是在runtime api中 类成员变量、给函数的参数、返回值怎么用字符串表示。具体看表1
全部评论
专题导读
热门推荐
热门话题
阅读排行榜
|
请发表评论