objective-c runtime 开发详情

目录

  • 概述
  • 对象与类的实质
    • 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

A bit field of num bits

^type

A pointer to type

?

An unknown type (among other things, this code is used for function pointers)

  2.就是怎么表示一个类的属性,我们先来看代码

 // 添加 类的属性
    objc_property_attribute_t type          = {"T", "@\"NSString\""};
    objc_property_attribute_t ownership     = { "C", "" };
    objc_property_attribute_t nonatomic     = { "N", "" };
    objc_property_attribute_t backingivar   = { "V", "_name"};
    objc_property_attribute_t attrs[] = {type, ownership,nonatomic, backingivar};
    class_addProperty(Person, "name", attrs, 4);

  用一个objc_property_attribute_t数组来描述,objc_property_attribute_t是一个结构体

typedef struct {
    const char *name;           /**< The name of the attribute */
    const char *value;          /**< The value of the attribute (usually empty) */
} objc_property_attribute_t;

  有一个name与一个value。name 可以取下表2的值,value根据需要填,不需要可以填空字符串。

Code

Meaning

R

The property is read-only (readonly).

C

The property is a copy of the value last assigned (copy).

&

The property is a reference to the value last assigned (retain).

N

The property is non-atomic (nonatomic).

G<name>

The property defines a custom getter selector name. The name follows the G (for example, GcustomGetter,).

S<name>

The property defines a custom setter selector name. The name follows the S (for example, ScustomSetter:,).

D

The property is dynamic (@dynamic).

W

The property is a weak reference (__weak).

P

The property is eligible for garbage collection.

t<encoding>

Specifies the type using old-style encoding.   


  需要注意这个objc_property_attribute_t数组必须以name为T的objc_property_attribute_t开头,name 为T 的item表示这个属性的类型。可以通过@encode(<#type-name#>)返回一个类型的字符串表示。

  必须以name为V的objc_property_attribute_t为结尾。name为V的item的value表示这个属性的名字。  

四、OC类与runtime

  一般来说,NSObject类是大多数的OC类的基类(少部分是继承自NSProxy,后面会介绍)。通过继承NSObject类,oc类获得了runtime系统接口和OC对象的能力。所以说NSObject是OC类与runtime的桥梁。

 NSObjectProtocol

  值得注意的是OC定义了一个名叫NSObject的协议,这个协议符号与NSObject类是完全同名的。NSObject协议定义了一系列的方法,看下列代码

@protocol NSObject

- (BOOL)isEqual:(id)object;
@property (readonly) NSUInteger hash;

@property (readonly) Class superclass;
- (Class)class OBJC_SWIFT_UNAVAILABLE("use 'anObject.dynamicType' instead");
- (instancetype)self;

- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;

- (BOOL)isProxy;

- (BOOL)isKindOfClass:(Class)aClass;
- (BOOL)isMemberOfClass:(Class)aClass;
- (BOOL)conformsToProtocol:(Protocol *)aProtocol;

- (BOOL)respondsToSelector:(SEL)aSelector;

- (instancetype)retain OBJC_ARC_UNAVAILABLE;
- (oneway void)release OBJC_ARC_UNAVAILABLE;
- (instancetype)autorelease OBJC_ARC_UNAVAILABLE;
- (NSUInteger)retainCount OBJC_ARC_UNAVAILABLE;

- (struct _NSZone *)zone OBJC_ARC_UNAVAILABLE;

@property (readonly, copy) NSString *description;
@optional
@property (readonly, copy) NSString *debugDescription;

@end

  正如前面所说的,OC语言有两个基类,一个是NSObject,另一个是NSProxy,这两个基类都实现是NSObject协议。

  @interface NSObject<NSObject>

  @interface NSProxy<NSObject>

  所以在OC里所有类都能直接用NSObject协议的所有方法,因为基类NSObject类、NSProxy类已经实现了。

 NSObject

  NSObject不仅是OC类的基础还是OC类与runtime交互的桥梁。接下来我们直接来学习NSObject重要的方法,来加深对runtime的理解。

  load 函数

+ (void)load;
  • 一个类、一个类别被添加到runtime的时候被调用
  • 父类的load在子类的load之前被调用,类别的load在类别的宿主类的load之后被调用

  initialize 函数

+ (void)initialize;
  • 在给一个类发第一条消息时,会先于第一条消息执行initialize函数。
  • 父类的initialize在子类initialize之前被调用
  • 如果子类没事覆盖initialize函数,会调用父类的initialize函数,所以说一个父类的initialize有可以会被调用多次。这个问题可以通过下列代码来解决
+ (void)initialize {
    if (self == [ClassName self]) {
        // ... do the initialization ...
    }
}

   alloc 函数

+ (instancetype)alloc OBJC_SWIFT_UNAVAILABLE("use object initializers instead");
  • 初始化isa,把成员变量内存都置为0,并返回一个实例

  一般来说在调用alloc之后,还必须调用init函数来完成整个初始化过程。

TheClass *newObject = [[TheClass alloc] init];

  performSelector:函数

- (id)performSelector:(SEL)sel {
    if (!sel) [self doesNotRecognizeSelector:sel];
    return ((id(*)(id, SEL))objc_msgSend)(self, sel);
}

- (id)performSelector:(SEL)sel withObject:(id)obj {
    if (!sel) [self doesNotRecognizeSelector:sel];
    return ((id(*)(id, SEL, id))objc_msgSend)(self, sel, obj);
}

- (id)performSelector:(SEL)sel withObject:(id)obj1 withObject:(id)obj2 {
    if (!sel) [self doesNotRecognizeSelector:sel];
    return ((id(*)(id, SEL, id, id))objc_msgSend)(self, sel, obj1, obj2);
}
  • performSelector函数的功能就是给自己发一个消息,接受这个消息的SEL只支持0、1、2个参数。
  • performSelector内部是直接调用objc_msgSend这个runtime api。

  当给一个对象发送它不支持的消息时,OC是怎么处理的呢?整个过程涉及下面4个函数

+(BOOL)resolveInstanceMethod:(SEL)sel;             
-(id)forwardingTargetForSelector:(SEL)aSelector;
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
-(void)forwardInvocation:(NSInvocation *)anInvocation;

  整个过程是处理的

  1.OC首先调用的是resolveInstanceMethod函数,给个机会你动态地添加响应这个消息的函数实现,并且把YES当做返回值返回,这时OC会直接调用你新添加的函数。如果返回NO,就执行2。

  2.OC会再调用forwardingTargetForSelector:,咨询你是否有其他对象othterObject可以处理这个消息,如果有就把othterObject当住返回值返回,这时OC就会把消息转发给othterObject。如果返回nil,将会执行3。

  3.OC调用methodSignatureForSelector:,咨询你是否要响应这个消息,如果响应麻烦返回响应函数的methodSignature,然后会继续执行4。如果返回nil将执行5.

  4.OC调用forwardInvocation:这个函数会传入一个NSInvocation,你可以通过NSInvocation来指定由哪个target来执行。就算你不执行NSInvocation,OC也不管你了,它的任务已经完成了。

  5.调用doesNotRecognizeSelector抛出异常。

 NSProxy

  NSProxy到底是什么?有什么用?怎么用?我们这里只要讲个大概,大家有个概念就好。

  NSProxy是一个虚类并不能直接用,需要创建一个类并继承它才能使用。从名字上来看,NSProxy应该是一个代理,你可以向NSProxy发送一个NSProxy并不响应的消息,NSProxy把这个消息转发给其他响应这个消息的对象。

  那NSProxy有什么用呢,你可以通过NSProxy来实现多继承(OC是单继承关系)。你也可以通过NSProxy来调用远程的类(其他进程类,通过NSconnection来发生,比如 NSDistantObject类)。

   创建继承NSProxy类,必须实现两个方法

    forwardInvocation:      --  转发消息

    methodSignatureForSelector:  --  返回一个消息的响应函数类型

  我们来看下列代码,TestNSProxy类是模拟多继承的一个例子

//
//  TestNSProxy.h
//  IOSTest
//
//  Created by 朱国清 on 17/1/22.
//  Copyright © 2017年 朱国清. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface TestNSProxy : NSProxy

+(instancetype)testProxy;

@end
//
//  TestNSProxy.m
//  IOSTest
//
//  Created by 朱国清 on 17/1/22.
//  Copyright © 2017年 朱国清. All rights reserved.
//

#import "TestNSProxy.h"

@implementation TestNSProxy{
    NSString * stringObjc;
    NSMutableArray * arrayObjc;
}
+(instancetype)testProxy{
    // NSProxy 只能通过静态方法alloc来生成实例
    return [[TestNSProxy alloc]init];
}
-(instancetype)init{
    // 分别创建两个不同的对象
    stringObjc  = @"";
    arrayObjc   = [[NSMutableArray alloc]init];
    return self;
}
// forwardInvocation、methodSignatureForSelector这两个函数来处理不存在的message
-(void)forwardInvocation:(NSInvocation *)invocation{
    SEL sel = invocation.selector;
    if ([stringObjc respondsToSelector:sel]) {
        [invocation invokeWithTarget:stringObjc];
        return;
    }
    if ([arrayObjc respondsToSelector:sel]) {
        [invocation invokeWithTarget:arrayObjc];
        return ;
    }
    [super forwardInvocation:invocation];
}
-(NSMethodSignature *)methodSignatureForSelector:(SEL)sel{
    if ([stringObjc respondsToSelector:sel]) {
        return [stringObjc methodSignatureForSelector:sel];
    }
    if ([arrayObjc respondsToSelector:sel]) {
        return [arrayObjc methodSignatureForSelector:sel];
    }
    return [super methodSignatureForSelector:sel];
}
@end

  使用TestNSProxy

TestNSProxy * proxy = (TestNSProxy *)[TestNSProxy testProxy];
    // 强制转换为NSStirng * 只是骗过编译器,给TestNSProxy发TestNSProxy并没有实现的stringByAppendingString:方法。
    NSString * outStr = [(NSString *)proxy stringByAppendingString:@"append String"];
    NSLog(@"strObjc=%@",outStr);
    // 输出 strObjc=append String
    
    
    // 强制转换为NSMutableArray * 只是骗过编译器,给TestNSProxy发TestNSProxy并没有实现的addObject:方法。
    [(NSMutableArray *)proxy addObject:@"item1"];
    [(NSMutableArray *)proxy addObject:@"item2"];
    NSString * lastObject = [(NSMutableArray *)proxy lastObject];
    NSLog(@"lastObject=%@",lastObject);
    // 输出 lastObject=item2
posted @ 2017-01-17 11:39  水谷  阅读(260)  评论(0编辑  收藏  举报