zoukankan      html  css  js  c++  java
  • 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)]
    "  ,"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("
    ");
    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("
    ");
    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 
    ",class_getName(metaClass),metaClass->isa);
    35         metaClass = class_getSuperclass(metaClass);
    36     }
    37     // 展示类变量列表
    38     printf("
    var List:
    ");
    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]
    ",ivar_getName(var),ivar_getOffset(var));
    44     }
    45     // 展示属性列表
    46     printf("
    property List:
    ");
    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]
    ",property_getName(property),property_getAttributes(property));
    52     }
    53     
    54     // 展示类方法
    55     printf("
    class method List:
    ");
    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]
    ",sel_getName(method_getName(method)));
    62     }
    63 
    64     // 展示实例类方法
    65     printf("
    class instance method List:
    ");
    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]
    ",sel_getName(method_getName(method)));
    71     }
    72     printf("
    
    ");
    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
    ",[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 
    ",class_getName([self class]),sel_getName(_cmd),[name UTF8String],(int)age.integerValue);
     33 }
     34 void isPerson(){
     35     printf("is person.
    ");
     36 }
     37 void add(id self,SEL _cmd,int x, int y){
     38     printf("x + y = %d
    ",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
    ",[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
  • 相关阅读:
    决策树理解
    堆排序
    glove理解
    PHP图片水印类
    宝塔nginx安装rtmp模块实现推拉流
    nginx安装配置
    结构体,位域,共用体
    指针
    升级mac Catalina版本后无操作权限
    脚本连接linux服务器
  • 原文地址:https://www.cnblogs.com/shuigu/p/6292406.html
Copyright © 2011-2022 走看看