• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

objective-cruntime开发详情

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

目录

  • 概述
  • 对象与类的实质
    • id与class
    • 继承关系与isa
    • 总结    
  • C函数创建一个OC类
  • OC类与runtime
    • NSObjectProtocol
    • NSObject
    • NSProxy

  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的Calss指针  
  Class getIsa();
// 下面还有一系列相关的函数 ...... }

  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 }
View Code

  继续讲之前先,大家应该先记住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
View Code

  代码都注释的很清楚了,耐心看完的理解应该没有问题。有几点需要说明一下

  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

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

bnum


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
OBJECTIVE-C入门(2) 类的声明和定义发布时间:2022-07-18
下一篇:
Objective-C 协议和运行时检查方法、类是否存在发布时间:2022-07-18
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap