它是指向某个类的实例的指针,它是指向某个类的实例的指针

一、Runtime简介

一、Runtime简介

iOS,

1.1 简单介绍

  • Runtime简称运行时。OC就是运作时机制,也就是在运作时候的有的编制,其中最要害的是音讯机制;
  • 对此C语言,函数的调用在编译的时候会控制调用哪个函数;
  • 对此OC的函数,属于动态调用的历程,在编译的时候并不可以操纵真正调用哪个函数,唯有在真的运行的时候才会按照函数的称呼找到呼应的函数来调用;
  • 在编译阶段,OC可以调用任何函数,即使那一个函数并未已毕,只要申明过就不会报错;在编译阶段,C语言调用未兑现的函数就会报错。

1.1 不难介绍

  • Runtime简称运行时。OC就是运行时机制,也就是在运作时候的有些建制,其中最要紧的是音讯机制;
  • 对于C语言,函数的调用在编译的时候会决定调用哪个函数;
  • 对于OC的函数,属于动态调用的经过,在编译的时候并不能操纵真正调用哪个函数,只有在真的运行的时候才会按照函数的称呼找到呼应的函数来调用;
  • 在编译阶段,OC可以调用任何函数,纵然这几个函数并未落成,只要评释过就不会报错;在编译阶段,C语言调用未落到实处的函数就会报错。

一、Runtime简介

1.2 可以用来做哪些

  • 在程序运行的进程中,动态的创始一个类(比如KVO的底层完成)
  • 在程序运行进程中,动态地为某个类添加属性/方法,可以用于封装框架(想怎么改就怎么改),那也是大家runtime机制的第一使用趋势
  • 遍历一个类中具有的积极分子变量(属性)/所有办法。比如字典转模型:利用runtime遍历模型对象的持有属性,按照属性名从字典中取出对应的值,设置到模型的属性上;还有归档和平解决档,利用runtime遍历模型对象的装有属性。

1.2 可以用来做哪些

  • 在程序运行的进程中,动态的始建一个类(比如KVO的底层完成)
  • 在程序运行进程中,动态地为某个类添加属性/方法,可以用于封装框架(想怎么改就怎么改),那也是我们runtime机制的主要性使用趋势
  • 遍历一个类中兼有的积极分子变量(属性)/所有办法。比如字典转模型:利用runtime遍历模型对象的有所属性,根据属性名从字典中取出对应的值,设置到模型的属性上;还有归档和平解决档,利用runtime遍历模型对象的兼具属性。

1.1 简单介绍

  • Runtime简称运行时。OC就是运作时机制,也就是在运转时候的有的机制,其中最重大的是新闻机制;
  • 对于C语言,函数的调用在编译的时候会操纵调用哪个函数;
  • 对此OC的函数,属于动态调用的进度,在编译的时候并无法控制真正调用哪个函数,唯有在真正运行的时候才会依据函数的名目找到相应的函数来调用;
  • 在编译阶段,OC可以调用任何函数,即便那几个函数并未完毕,只要表明过就不会报错;在编译阶段,C语言调用未落实的函数就会报错。

二、Runtime常用的措施

// 获取类
Class PersonClass = object_getClass([Person class]);

// SEL是selector在Objc中的标示 method 是方法名
SEL oriSEL = @selector(method);

// 获取类方法
Method oriMethod = class_getClassMethod(Class cls , SEL name);

// 获取实例方法
Method insMethod = class_getInstanceMethod(Class cls, SEL name);

// 添加一个新方法
BOOL addSucc = class_addMethod(xiaomingClass, oriSEL, method_getImplementation(cusMethod), method_getTypeEncoding(cusMethod));

// 替换原方法实现
class_replaceMethod(toolClass, cusSEL, method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod));

// 交换两个方法
method_exchangeImplementations(oriMethod, cusMethod);

// 获取一个类的属性列表(返回值是一个数组)
objc_property_t *propertyList = class_copyPropertyList([self class], &count);

// 获取一个类的方法列表(返回值是一个数组)
Method *methodList = class_copyMethodList([self class], &count);

// 获取一个类的成员变量列表 (返回值是一个数组)
Ivar *ivarList = class_copyIvarList([self class], &count);

// 获取成员变量的名字
const char *ivar_getName(Ivar v)

// 获取成员变量的类型
const char *ivar_getTypeEndcoding(Ivar v)

// 获取一个类的协议列表(返回值是一个数组)
__unsafe_unretained Protocol **protocolList = class_copyProtocolList([self class], &count);

// set方法
//将值value 跟对象object 关联起来(将值value 存储到对象object 中)
//参数 object:给哪个对象设置属性
//参数 key:一个属性对应一个Key,将来可以通过key取出这个存储的值,key 可以是任何类型:double、int 等,建议用char 可以节省字节
//参数 value:给属性设置的值
//参数policy:存储策略 (assign 、copy 、 retain就是strong)
void objc_setAssociatedObject(id object , const void *key ,id value ,objc_AssociationPolicy policy)

// get方法,利用参数key将对象object中存储的对应值取出来
id objc_getAssociatedObject(id object , const void *key)

二、Runtime常用的艺术

// 获取类
Class PersonClass = object_getClass([Person class]);

// SEL是selector在Objc中的标示 method 是方法名
SEL oriSEL = @selector(method);

// 获取类方法
Method oriMethod = class_getClassMethod(Class cls , SEL name);

// 获取实例方法
Method insMethod = class_getInstanceMethod(Class cls, SEL name);

// 添加一个新方法
BOOL addSucc = class_addMethod(xiaomingClass, oriSEL, method_getImplementation(cusMethod), method_getTypeEncoding(cusMethod));

// 替换原方法实现
class_replaceMethod(toolClass, cusSEL, method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod));

// 交换两个方法
method_exchangeImplementations(oriMethod, cusMethod);

// 获取一个类的属性列表(返回值是一个数组)
objc_property_t *propertyList = class_copyPropertyList([self class], &count);

// 获取一个类的方法列表(返回值是一个数组)
Method *methodList = class_copyMethodList([self class], &count);

// 获取一个类的成员变量列表 (返回值是一个数组)
Ivar *ivarList = class_copyIvarList([self class], &count);

// 获取成员变量的名字
const char *ivar_getName(Ivar v)

// 获取成员变量的类型
const char *ivar_getTypeEndcoding(Ivar v)

// 获取一个类的协议列表(返回值是一个数组)
__unsafe_unretained Protocol **protocolList = class_copyProtocolList([self class], &count);

// set方法
//将值value 跟对象object 关联起来(将值value 存储到对象object 中)
//参数 object:给哪个对象设置属性
//参数 key:一个属性对应一个Key,将来可以通过key取出这个存储的值,key 可以是任何类型:double、int 等,建议用char 可以节省字节
//参数 value:给属性设置的值
//参数policy:存储策略 (assign 、copy 、 retain就是strong)
void objc_setAssociatedObject(id object , const void *key ,id value ,objc_AssociationPolicy policy)

// get方法,利用参数key将对象object中存储的对应值取出来
id objc_getAssociatedObject(id object , const void *key)

1.2 可以用来做如何

  • 在程序运行的长河中,动态的始建一个类(比如KVO的平底完成)
  • 在程序运行进程中,动态地为某个类添加属性/方法,可以用来封装框架(想怎么改就怎么改),那也是我们runtime机制的严重性选取趋势
  • 遍历一个类中装有的分子变量(属性)/所有办法。比如字典转模型:利用runtime遍历模型对象的拥有属性,按照属性名从字典中取出对应的值,设置到模型的性能上;还有归档和平解决档,利用runtime遍历模型对象的具有属性。

三、Runtime相关术语的数据结构

本人倍感照旧有须求看一下理论知识,如若不看跳过—

三、Runtime相关术语的数据结构

自我感觉到仍旧有必不可少看一下理论知识,借使不看跳过—

二、Runtime常用的不二法门

// 获取类
Class PersonClass = object_getClass([Person class]);

// SEL是selector在Objc中的标示 method 是方法名
SEL oriSEL = @selector(method);

// 获取类方法
Method oriMethod = class_getClassMethod(Class cls , SEL name);

// 获取实例方法
Method insMethod = class_getInstanceMethod(Class cls, SEL name);

// 添加一个新方法
BOOL addSucc = class_addMethod(xiaomingClass, oriSEL, method_getImplementation(cusMethod), method_getTypeEncoding(cusMethod));

// 替换原方法实现
class_replaceMethod(toolClass, cusSEL, method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod));

// 交换两个方法
method_exchangeImplementations(oriMethod, cusMethod);

// 获取一个类的属性列表(返回值是一个数组)
objc_property_t *propertyList = class_copyPropertyList([self class], &count);

// 获取一个类的方法列表(返回值是一个数组)
Method *methodList = class_copyMethodList([self class], &count);

// 获取一个类的成员变量列表 (返回值是一个数组)
Ivar *ivarList = class_copyIvarList([self class], &count);

// 获取成员变量的名字
const char *ivar_getName(Ivar v)

// 获取成员变量的类型
const char *ivar_getTypeEndcoding(Ivar v)

// 获取一个类的协议列表(返回值是一个数组)
__unsafe_unretained Protocol **protocolList = class_copyProtocolList([self class], &count);

// set方法
//将值value 跟对象object 关联起来(将值value 存储到对象object 中)
//参数 object:给哪个对象设置属性
//参数 key:一个属性对应一个Key,将来可以通过key取出这个存储的值,key 可以是任何类型:double、int 等,建议用char 可以节省字节
//参数 value:给属性设置的值
//参数policy:存储策略 (assign 、copy 、 retain就是strong)
void objc_setAssociatedObject(id object , const void *key ,id value ,objc_AssociationPolicy policy)

// get方法,利用参数key将对象object中存储的对应值取出来
id objc_getAssociatedObject(id object , const void *key)

3.1 SEL

它是selector(方法接纳器)在Objc中的标示(斯维夫特中是Selector类)。Objc在同等的类中不会有命名相同的七个艺术。selector对艺术名展开打包,以便找到相应的章程达成,它的数据结构:

// 它是个映射到方法的C语言字符串,可以通过Objc编译器命令@selector 或者Runtime 系统的 sel_registerName 函数来获取一个 SEL 类型的方法选择器。
typedef struct objc_selector *SEL;

3.1 SEL

它是selector(方法选拔器)在Objc中的标示(Swift中是Selector类)。Objc在相同的类中不会有命名相同的多少个艺术。selector对艺术名举办包装,以便找到呼应的点子完毕,它的数据结构:

// 它是个映射到方法的C语言字符串,可以通过Objc编译器命令@selector 或者Runtime 系统的 sel_registerName 函数来获取一个 SEL 类型的方法选择器。
typedef struct objc_selector *SEL;

三、Runtime相关术语的数据结构

自家感觉照旧有须要看一下理论知识,假若不看跳过—

3.2 id

id 是一个参数类型,它是指向某个类的实例的指针。定义如下:

// 我们可以看到 objc_object 结构体包含一个isa指针,根据isa指针就可以找到对象所属的类。
// isa 指针在代码运行时并不总指向实例对象所属的类型,所以不能依靠它来确定类型,要想确定类型还是需要用对象的 -class方法。
// KVO的实现原理就是将被观察对象的isa指针指向了一个中间动态创建的中间类,而不是真实类型。
typedef struct objc_object *id;
struct objc_object {Class isa;};

3.2 id

id 是一个参数类型,它是指向某个类的实例的指针。定义如下:

// 我们可以看到 objc_object 结构体包含一个isa指针,根据isa指针就可以找到对象所属的类。
// isa 指针在代码运行时并不总指向实例对象所属的类型,所以不能依靠它来确定类型,要想确定类型还是需要用对象的 -class方法。
// KVO的实现原理就是将被观察对象的isa指针指向了一个中间动态创建的中间类,而不是真实类型。
typedef struct objc_object *id;
struct objc_object {Class isa;};

3.1 SEL

它是selector(方法拔取器)在Objc中的标示(Swift中是Selector类)。Objc在一如既往的类中不会有命名相同的三个点子。selector对章程名展开打包,以便找到相应的主意完结,它的数据结构:

// 它是个映射到方法的C语言字符串,可以通过Objc编译器命令@selector 或者Runtime 系统的 sel_registerName 函数来获取一个 SEL 类型的方法选择器。
typedef struct objc_selector *SEL;

 3.3 Class

typedef struct objc_class *Class;

 Class其实是指向objc_class 结构体的指针。objc_class 的数据结构如下:

struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;
#if !__OBJC2_
    Class super_class// 父类            
    const char *name // 类名                                       
    long version     // 类的版本信息,默认为0

    long info        // 类信息,供运行期间使用的一些位标识                            
    long instance_size  // 类的实例变量大小                                 
    struct objc_ivar_list *ivars       // 累的成员变量链表                     
    struct objc_method_list **methodLists   // 方法链表     
    struct objc_cache *cache                // 方法缓存                
    struct objc_protocol_list *protocols    // 协议链表      
#endif
} OBJC2_UNAVAILABLE;

 从 objc_class
可以见见,一个运转时类中涉及了它的父类指针、类名、成员变量、方法、缓存以及直属的合计。

其中 objc_ivar_list 和 objc_method_list
分别是成员变量列表和章程列表:

// 成员变量列表
struct objc_ivar_list {
    int ivar_count                                           OBJC2_UNAVAILABLE;
#ifdef __LP64__
    int space                                                OBJC2_UNAVAILABLE;
#endif
    /* variable length structure */
    struct objc_ivar ivar_list[1]                            OBJC2_UNAVAILABLE;
}                                                            OBJC2_UNAVAILABLE;
// 方法列表
struct objc_method_list {
    struct objc_method_list *obsolete                        OBJC2_UNAVAILABLE;
    int method_count                                         OBJC2_UNAVAILABLE;
#ifdef __LP64__
    int space                                                OBJC2_UNAVAILABLE;
#endif
    /* variable length structure */
    struct objc_method method_list[1]                        OBJC2_UNAVAILABLE;
}

 一句话来说,大家可以动态修改 *methodList 的值来添加成员方法,那也是
Category 完成的原理,同样解释了 Category 不可能添加属性的原故。

objc_ivar_list 结构体用来存储成员变量的列表,而 objc_ivar
则是储存了单个成员变量的音讯;同理,objc_method_list
结构体存储着艺术数组的列表,而单个方法的新闻则由 objc_method
结构体存储。

值得注意的是,objc_class 中也有一个 isa 指针,那表明 Objc
类本身也是一个对象。为了处理类和目的的关联,Runtime库成立了一种叫做 Meta
Class(元类)
的事物,类对象所属的类就称为元类。元类表述了类对象自我所负有的元数据。

我们所耳熟能详的类形式,就源自于 Meta
Class。我们可以知道为类措施就是类对象的实例方法。每个类仅有一个类对象,而各样类对象仅有一个与之相关的元类。

当您生出一个近似 [NSObject alloc]
(类措施)的音讯时,实际上,那么些音讯被发送给了一个类对象,这么些类对象必须是一个元类的实例,而以此元类同时也是一个根元类的实例,所有元类的isa指针最终都指向根元类。

所以当 [NSObject alloc] 那条新闻发送给类对象的时候,运行时代码
objc_msgSend()
会去它元类中找找可以响应音信的主意落成,如若找到了,就会对那几个类对象实施格局调用。

 3.3 Class

typedef struct objc_class *Class;

 Class其实是指向objc_class 结构体的指针。objc_class 的数据结构如下:

struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;
#if !__OBJC2_
    Class super_class// 父类            
    const char *name // 类名                                       
    long version     // 类的版本信息,默认为0

    long info        // 类信息,供运行期间使用的一些位标识                            
    long instance_size  // 类的实例变量大小                                 
    struct objc_ivar_list *ivars       // 累的成员变量链表                     
    struct objc_method_list **methodLists   // 方法链表     
    struct objc_cache *cache                // 方法缓存                
    struct objc_protocol_list *protocols    // 协议链表      
#endif
} OBJC2_UNAVAILABLE;

 从 objc_class
可以看出,一个运作时类中涉嫌了它的父类指针、类名、成员变量、方法、缓存以及直属的协商。

其中 objc_ivar_list 和 objc_method_list
分别是成员变量列表和章程列表:

// 成员变量列表
struct objc_ivar_list {
    int ivar_count                                           OBJC2_UNAVAILABLE;
#ifdef __LP64__
    int space                                                OBJC2_UNAVAILABLE;
#endif
    /* variable length structure */
    struct objc_ivar ivar_list[1]                            OBJC2_UNAVAILABLE;
}                                                            OBJC2_UNAVAILABLE;
// 方法列表
struct objc_method_list {
    struct objc_method_list *obsolete                        OBJC2_UNAVAILABLE;
    int method_count                                         OBJC2_UNAVAILABLE;
#ifdef __LP64__
    int space                                                OBJC2_UNAVAILABLE;
#endif
    /* variable length structure */
    struct objc_method method_list[1]                        OBJC2_UNAVAILABLE;
}

 显而易见,大家可以动态修改 *methodList 的值来添加成员方法,那也是
Category 完结的原理,同样解释了 Category 不可能添加属性的原故。

objc_ivar_list 结构体用来存储成员变量的列表,而 objc_ivar
则是储存了单个成员变量的音讯;同理,objc_method_list
结构体存储着艺易学组的列表,而单个方法的音信则由 objc_method
结构体存储。

值得注意的是,objc_class 中也有一个 isa 指针,那阐明 Objc
类本身也是一个目的。为了处理类和目标的关联,Runtime库成立了一种叫做 Meta
Class(元类)
的东西,类对象所属的类就称为元类。元类表述了类对象自我所怀有的元数据。

咱们所熟习的类格局,就源自于 Meta
Class。大家可以领略为类措施就是类对象的实例方法。每个类仅有一个类对象,而各类类对象仅有一个与之有关的元类。

当您发生一个近乎 [NSObject alloc]
(类措施)的音讯时,实际上,这一个音讯被发送给了一个类对象,这些类对象必须是一个元类的实例,而以此元类同时也是一个根元类的实例,所有元类的isa指针最后都指向根元类。

所以当 [NSObject alloc] 那条音信发送给类对象的时候,运行时代码
objc_msgSend()
会去它元类中寻觅可以响应音讯的办法已毕,要是找到了,就会对这几个类对象举行办法调用。

3.2 id

id 是一个参数类型,它是指向某个类的实例的指针。定义如下:

// 我们可以看到 objc_object 结构体包含一个isa指针,根据isa指针就可以找到对象所属的类。
// isa 指针在代码运行时并不总指向实例对象所属的类型,所以不能依靠它来确定类型,要想确定类型还是需要用对象的 -class方法。
// KVO的实现原理就是将被观察对象的isa指针指向了一个中间动态创建的中间类,而不是真实类型。
typedef struct objc_object *id;
struct objc_object {Class isa;};

3.4 Method

Method 是一个办法结构体的指针:

typedef struct objc_method *Method;

struct objc_method {
    SEL method_name                                          OBJC2_UNAVAILABLE;
    char *method_types    //  方法的返回值,和各个参数类型的字符串描述;
    IMP method_imp                                           OBJC2_UNAVAILABLE;
}

 objc_method 存储了章程名,方法类型和艺术完结:

  • 情势名类型为SEL
  • 办法类型 method_types 是个char指针,存储方法的参数类型和重返值类型
  • method_imp 指向了措施的达成,本质是一个函数指针

3.4 Method

Method 是一个主意结构体的指针:

typedef struct objc_method *Method;

struct objc_method {
    SEL method_name                                          OBJC2_UNAVAILABLE;
    char *method_types    //  方法的返回值,和各个参数类型的字符串描述;
    IMP method_imp                                           OBJC2_UNAVAILABLE;
}

 objc_method 存储了主意名,方法类型和方式完成:

  • 办法名类型为SEL
  • 主意类型 method_types 是个char指针,存储方法的参数类型和再次来到值类型
  • method_imp 指向了措施的兑现,本质是一个函数指针

 3.3 Class

typedef struct objc_class *Class;

 Class其实是指向objc_class 结构体的指针。objc_class 的数据结构如下:

struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;
#if !__OBJC2_
    Class super_class// 父类            
    const char *name // 类名                                       
    long version     // 类的版本信息,默认为0

    long info        // 类信息,供运行期间使用的一些位标识                            
    long instance_size  // 类的实例变量大小                                 
    struct objc_ivar_list *ivars       // 累的成员变量链表                     
    struct objc_method_list **methodLists   // 方法链表     
    struct objc_cache *cache                // 方法缓存                
    struct objc_protocol_list *protocols    // 协议链表      
#endif
} OBJC2_UNAVAILABLE;

 从 objc_class
可以见见,一个运行时类中涉嫌了它的父类指针、类名、成员变量、方法、缓存以及直属的协议。

其中 objc_ivar_list 和 objc_method_list
分别是成员变量列表和章程列表:

// 成员变量列表
struct objc_ivar_list {
    int ivar_count                                           OBJC2_UNAVAILABLE;
#ifdef __LP64__
    int space                                                OBJC2_UNAVAILABLE;
#endif
    /* variable length structure */
    struct objc_ivar ivar_list[1]                            OBJC2_UNAVAILABLE;
}                                                            OBJC2_UNAVAILABLE;
// 方法列表
struct objc_method_list {
    struct objc_method_list *obsolete                        OBJC2_UNAVAILABLE;
    int method_count                                         OBJC2_UNAVAILABLE;
#ifdef __LP64__
    int space                                                OBJC2_UNAVAILABLE;
#endif
    /* variable length structure */
    struct objc_method method_list[1]                        OBJC2_UNAVAILABLE;
}

 不问可知,大家可以动态修改 *methodList 的值来添加成员方法,那也是
Category 落成的原理,同样解释了 Category 不可以添加属性的案由。

objc_ivar_list 结构体用来存储成员变量的列表,而 objc_ivar
则是储存了单个成员变量的音信;同理,objc_method_list
结构体存储着办法数组的列表,而单个方法的音讯则由 objc_method
结构体存储。

值得注意的是,objc_class 中也有一个 isa 指针,那表达 Objc
类本身也是一个目的。为了处理类和对象的关系,Runtime库创造了一种名叫 Meta
Class(元类)
的东西,类对象所属的类就叫做元类。元类表述了类对象自我所持有的元数据。

俺们所熟稔的类措施,就源自于 Meta
Class。大家得以知道为类措施就是类对象的实例方法。每个类仅有一个类对象,而各样类对象仅有一个与之皮之不存毛将焉附的元类。

当你生出一个近乎 [NSObject alloc]
(类措施)的新闻时,实际上,这几个音信被发送给了一个类对象,这一个类对象必须是一个元类的实例,而以此元类同时也是一个根元类的实例,所有元类的isa指针末了都指向根元类。

所以当 [NSObject alloc] 那条音讯发送给类对象的时候,运行时代码
objc_msgSend()
会去它元类中找寻可以响应消息的法子达成,即使找到了,就会对这几个类对象进行办法调用。

3.5 Ivar

Ivar 是意味成员变量的类型。

typedef struct objc_ivar *Ivar;
struct objc_ivar {
    char *ivar_name                                          OBJC2_UNAVAILABLE;
    char *ivar_type                                          OBJC2_UNAVAILABLE;
    int ivar_offset                                          OBJC2_UNAVAILABLE;
#ifdef __LP64__
    int space                                                OBJC2_UNAVAILABLE;
#endif
}

其中,有成员变量的名称,类型,空间, ivar_offset是基地址偏移字节

3.5 Ivar

Ivar 是象征成员变量的项目。

typedef struct objc_ivar *Ivar;
struct objc_ivar {
    char *ivar_name                                          OBJC2_UNAVAILABLE;
    char *ivar_type                                          OBJC2_UNAVAILABLE;
    int ivar_offset                                          OBJC2_UNAVAILABLE;
#ifdef __LP64__
    int space                                                OBJC2_UNAVAILABLE;
#endif
}

其中,有成员变量的名称,类型,空间, ivar_offset是基地址偏移字节

3.4 Method

Method 是一个艺术结构体的指针:

typedef struct objc_method *Method;

struct objc_method {
    SEL method_name                                          OBJC2_UNAVAILABLE;
    char *method_types    //  方法的返回值,和各个参数类型的字符串描述;
    IMP method_imp                                           OBJC2_UNAVAILABLE;
}

 objc_method 存储了措施名,方法类型和方法完毕:

  • 措施名类型为SEL
  • 艺术类型 method_types 是个char指针,存储方法的参数类型和再次回到值类型
  • method_imp 指向了办法的完毕,本质是一个函数指针

 3.6 IMP

IMP在objc.h中的定义是:

typedef id (*IMP)(id, SEL,...);

 它就是一个函数指针,这是由编译器生成的。当你发起一个ObjC新闻随后,最后它会执行的那段代码就是由这么些函数指针指定的。而
IMP 那些函数指针就针对了那么些方法的完结。

即便获得了履行某个实例某个方法的输入,大家就足以绕开音讯传递阶段,直接执行措施,那在前面Cache中会提到。

您会发现 IMP 指向的方法与 objc_msgSend 函数类型相同,参数都含有 id 和
SEL 类型。每个方法对应一个 SEL
类型的艺术接纳器,而种种实例对象中的SEL对应的不二法门已毕自然是唯一的,通过id和
SEL 参数就能确定唯一的方法达成地方,而一个规定的法子也唯有唯一的一组 id
和 SEL 参数。

 3.6 IMP

IMP在objc.h中的定义是:

typedef id (*IMP)(id, SEL,...);

 它就是一个函数指针,那是由编译器生成的。当你发起一个ObjC音信随后,最后它会实施的那段代码就是由那么些函数指针指定的。而
IMP 这些函数指针就对准了那么些法子的兑现。

只要得到了实践某个实例某个方法的入口,大家就可以绕开音讯传递阶段,直接实施措施,那在背后Cache中会提到。

您会发现 IMP 指向的格局与 objc_msgSend 函数类型相同,参数都蕴含 id 和
SEL 类型。每个方法对应一个 SEL
类型的主意选取器,而各样实例对象中的SEL对应的法门完毕自然是绝无仅有的,通过id和
SEL 参数就能确定唯一的措施已毕地点,而一个确定的艺术也唯有唯一的一组 id
和 SEL 参数。

3.5 Ivar

Ivar 是意味着成员变量的项目。

typedef struct objc_ivar *Ivar;
struct objc_ivar {
    char *ivar_name                                          OBJC2_UNAVAILABLE;
    char *ivar_type                                          OBJC2_UNAVAILABLE;
    int ivar_offset                                          OBJC2_UNAVAILABLE;
#ifdef __LP64__
    int space                                                OBJC2_UNAVAILABLE;
#endif
}

其中,有成员变量的名称,类型,空间, ivar_offset是基地址偏移字节

3.7 Cache 

Cache 定义如下:

typedef struct objc_cache *Cache

struct objc_cache {
    unsigned int mask /* total = mask + 1 */                 OBJC2_UNAVAILABLE;
    unsigned int occupied                                    OBJC2_UNAVAILABLE;
    Method buckets[1]                                        OBJC2_UNAVAILABLE;
};

 Cache
为艺术调用的性质进行优化,每当实例对象收取到一个新闻时,它不会直接在 isa
指针指向的类的不二法门列表中遍历查找到可以响应的方式,因为每便都要寻找作用太低了,而是优先在
Cache 中寻找。

Runtime 系统会把被调用的法门存到 Cache
中,借使一个措施被调用,那么它有可能未来还会被调用,下次寻觅的收就会效用更高。

3.7 Cache 

Cache 定义如下:

typedef struct objc_cache *Cache

struct objc_cache {
    unsigned int mask /* total = mask + 1 */                 OBJC2_UNAVAILABLE;
    unsigned int occupied                                    OBJC2_UNAVAILABLE;
    Method buckets[1]                                        OBJC2_UNAVAILABLE;
};

 Cache
为艺术调用的特性举办优化,每当实例对象吸收到一个音信时,它不会直接在 isa
指针指向的类的法门列表中遍历查找到可以响应的章程,因为每一遍都要物色功能太低了,而是优先在
Cache 中查找。

Runtime 系统会把被调用的方法存到 Cache
中,假诺一个主意被调用,那么它有可能未来还会被调用,下次寻找的收就会效用更高。

 3.6 IMP

IMP在objc.h中的定义是:

typedef id (*IMP)(id, SEL,...);

 它就是一个函数指针,那是由编译器生成的。当你发起一个ObjC音信之后,最后它会履行的那段代码就是由这几个函数指针指定的。而
IMP 这几个函数指针就针对了这些办法的落到实处。

一经得到了实施某个实例某个方法的入口,我们就足以绕开音信传递阶段,间接执行办法,这在末端Cache中会提到。

您会发觉 IMP 指向的不二法门与 objc_msgSend 函数类型相同,参数都富含 id 和
SEL 类型。每个方法对应一个 SEL
类型的主意接纳器,而各样实例对象中的SEL对应的法门完成自然是唯一的,通过id和
SEL 参数就能确定唯一的章程落成地方,而一个规定的措施也唯有唯一的一组 id
和 SEL 参数。

四、音讯发送和信息转载

四、信息发送和音讯转载

3.7 Cache 

Cache 定义如下:

typedef struct objc_cache *Cache

struct objc_cache {
    unsigned int mask /* total = mask + 1 */                 OBJC2_UNAVAILABLE;
    unsigned int occupied                                    OBJC2_UNAVAILABLE;
    Method buckets[1]                                        OBJC2_UNAVAILABLE;
};

 Cache
为方式调用的习性举办优化,每当实例对象收取到一个新闻时,它不会一贯在 isa
指针指向的类的不二法门列表中遍历查找到可以响应的格局,因为老是都要寻找效能太低了,而是优先在
Cache 中追寻。

Runtime 系统会把被调用的法门存到 Cache
中,假如一个措施被调用,那么它有可能未来还会被调用,下次寻觅的收就会功能更高。

4.1 音讯发送

消息发送举例:

[person read:book];

 会被编译成:

objc_msgSend(person, @selector(read:), book);

 objc_msgSend的有血有肉流程如下:

  • 透过 isa 指针找到所属的类
  • 查找类的 cache 列表,如若没有则下一步
  • 查找类的艺术列表
  • 一旦能找到与选用子名称符合的方法,就跳至其落到实处代码
  • 找不到的话,就本着继承体系接轨上扬查找
  • 假使可以找到与选用子名称相符的章程,就调至其落到实处代码
  • 如果还没找到,就执行 “音讯转载”

4.1 音讯发送

新闻发送举例:

[person read:book];

 会被编译成:

objc_msgSend(person, @selector(read:), book);

 objc_msgSend的实际流程如下:

  • 经过 isa 指针找到所属的类
  • 查找类的 cache 列表,若是没有则下一步
  • 查找类的不二法门列表
  • 假诺能找到与接纳子名称符合的法子,就跳至其落成代码
  • 找不到的话,就本着继承连串接轨前行查找
  • 假诺可以找到与拔取子名称符合的措施,就调至其完成代码
  • 比方还没找到,就推行 “新闻转载”

四、信息发送和音讯转载

4.2 音讯转载

此处会给接收者最后几回机遇把这些形式处理了,搞不定程序就会崩溃

- (void)forwardInvocation:(NSInvocation *)invocation
// invocation : 封装了与那条尚未处理的消息相关的所有细节的对象

 在那边能做的相比较现实的事就是:在触及音讯前,先以某种格局改变信息内容,比如伸张其它一个参数,或是改变信息等等。完成此办法时,倘诺发现某调用操作不应有由本类处理,可以调用超类的同名方法,则继续体系中的每个类都有时机处理该请求,知道
NSObject ,如若NSObject 搞不定,则还会调用 doesNotRecognizeSelector:
来抛出格外,此时你就会在控制台看到娴熟的 unrecognized selector sent to
instance..1

图片 1

地点那4个格局均是模板方法,开发者可以override,由runtime来调用。最广泛的兑现音讯转载,就是重写3和4。

4.2 音信转载

那边会给接收者最终一次机会把这么些点子处理了,搞不定程序就会崩溃

- (void)forwardInvocation:(NSInvocation *)invocation
// invocation : 封装了与那条尚未处理的消息相关的所有细节的对象

 在此处能做的可比现实的事就是:在触发新闻前,先以某种形式改变音信内容,比如扩展别的一个参数,或是改变新闻等等。完毕此办法时,若是发现某调用操作不应当由本类处理,可以调用超类的同名方法,则一连连串中的每个类都有空子处理该请求,知道
NSObject ,若是NSObject 搞不定,则还会调用 doesNotRecognizeSelector:
来抛出极度,此时您就会在控制台看到熟知的 unrecognized selector sent to
instance..1

图片 2

上边那4个方法均是模板方法,开发者可以override,由runtime来调用。最广泛的达成音信转载,就是重写3和4。

4.1 信息发送

信息发送举例:

[person read:book];

 会被编译成:

objc_msgSend(person, @selector(read:), book);

 objc_msgSend的切实可行流程如下:

  • 通过 isa 指针找到所属的类
  • 查找类的 cache 列表,假若没有则下一步
  • 查找类的方法列表
  • 一旦能找到与选取子名称相符的点子,就跳至其落到实处代码
  • 找不到的话,就顺着继承种类接轨上扬查找
  • 倘使可以找到与接纳子名称相符的不二法门,就调至其落到实处代码
  • 假设还没找到,就举办 “音讯转发”

五、Runtime的作用

五、Runtime的作用

4.2 音信转载

那边会给接收者最终一遍机会把这一个主意处理了,搞不定程序就会崩溃

- (void)forwardInvocation:(NSInvocation *)invocation
// invocation : 封装了与那条尚未处理的消息相关的所有细节的对象

 在此处能做的可比实际的事就是:在触及音信前,先以某种格局改变音讯内容,比如扩充其余一个参数,或是改变音信等等。落成此方式时,假使发现某调用操作不应有由本类处理,可以调用超类的同名方法,则继续种类中的每个类都有机会处理该请求,知道
NSObject ,若是NSObject 搞不定,则还会调用 doesNotRecognizeSelector:
来抛出卓殊,此时您就会在控制台看到熟谙的 unrecognized selector sent to
instance..1

图片 3

上边那4个章程均是模板方法,开发者可以override,由runtime来调用。最普遍的贯彻新闻转载,就是重写3和4。

5.1 发送音讯

  • 艺术调用的本来面目,就是让对象发送音讯;
  • objc_msgSend,只有对象才能发送音讯,因而以objc早先;
  • 选用新闻机制的前提,必须导入 #import<objc/message.h>
  • 音信机制不难利用

    // 比如创造一个Person对象,我们平时会如下写

    Person *p = [[Person alloc] init];
    
    // 其实方法的调用就是向这个对方发送消息,所以上面的代码底层实现的主要代码就是:
    // 由于iOS5以后,苹果不支持这样写,所以需要在 Build Setting 中 将 Enable Strick Checking 设置为 NO
    Person *p = objc_msgSend(objc_msgSend(objc_getClass("Person"),sel_registerName("alloc")),sel_registerName("init"));
    

    新闻机制的原理:对象依照章程编号SEL去映射表查找对应的法子完成。

图片 4

 

5.1 发送新闻

  • 措施调用的原形,就是让对象发送音信;
  • objc_msgSend,只有对象才能发送音讯,由此以objc早先;
  • 运用音信机制的前提,必须导入 #import<objc/message.h>
  • 音讯机制简单利用

    // 比如创建一个Person对象,我们不足为奇会如下写

    Person *p = [[Person alloc] init];
    
    // 其实方法的调用就是向这个对方发送消息,所以上面的代码底层实现的主要代码就是:
    // 由于iOS5以后,苹果不支持这样写,所以需要在 Build Setting 中 将 Enable Strick Checking 设置为 NO
    Person *p = objc_msgSend(objc_msgSend(objc_getClass("Person"),sel_registerName("alloc")),sel_registerName("init"));
    

    新闻机制的法则:对象依据办法编号SEL去映射表查找对应的措施完毕。

图片 5

 

五、Runtime的作用

5.2 交流方法

粗略的话就是将四个法子完毕举行交流,比如系统自带的办法效果不够,大家想给系统自带的点子增添部分作用,并且维持原有的效率,咱们就足以动用Runtie,调换方法。

比如说:我们想在 NSMutableArray 添加新对象的时候,举行置空判断。

// 需求:给 addObject: 方法提供功能,当添加一个新的对象时,进行为空判断
    // 1 先弄个分类 定义一个 能添加新对象又能进行为空判断的方法
    // 2 和系统的方法进行交换

#import <objc/message.h>
@implementation NSMutableArray (Category)
// 加载分类到内存的时候调用,会比main函数先调用
+(void)load{
    // 交换方法 获取类名,如果使用[self class] 获取的是 NSMutableArray ,因为NSMutableArray 是类簇,这个可以看看了解一下
    Class selfClass = NSClassFromString(@"__NSArrayM"); 
    // 获取系统 addObject: 方法地址
    SEL sel_add = @selector(addObject:);
    Method addObject1 = class_getInstanceMethod(selfClass, sel_add);

    // 获取自定义 safeAddObject: 方法地址
    SEL sel_safeAdd = @selector(safeAddObject:);
    Method safeAddObject2 = class_getInstanceMethod(selfClass, sel_safeAdd);

    // 然后交换方法地址,相当于交换实现方式
    method_exchangeImplementations(addObject1,safeAddObject2);
}

// 我们这里定义一个新的添加对象的方法 , 不能在分类中重写系统方法 addObject 这样会覆盖系统的addObject ,并且分类中也不能使用 super
- (void)safeAddObject:(id)anObject{

    // 我们可以加判断,如果对象为空
    if (!anObject) {
        NSLog(@"对象为空,添加新对象失败");
        return;
    }

    // 这里调用 safeAddObject 其实是调用 addObject: 因为在load 方法中,系统的方法和你自己实现的方法已经交换了
    [self safeAddObject:anObject];
}

5.2 调换方法

大概的话就是将多个主意落成举行置换,比如系统自带的点子效果不够,我们想给系统自带的主意增加部分功效,并且维持原有的作用,我们就可以利用Runtie,交流方法。

比如说:咱们想在 NSMutableArray 添加新对象的时候,进行置空判断。

// 需求:给 addObject: 方法提供功能,当添加一个新的对象时,进行为空判断
    // 1 先弄个分类 定义一个 能添加新对象又能进行为空判断的方法
    // 2 和系统的方法进行交换

#import <objc/message.h>
@implementation NSMutableArray (Category)
// 加载分类到内存的时候调用,会比main函数先调用
+(void)load{
    // 交换方法 获取类名,如果使用[self class] 获取的是 NSMutableArray ,因为NSMutableArray 是类簇,这个可以看看了解一下
    Class selfClass = NSClassFromString(@"__NSArrayM"); 
    // 获取系统 addObject: 方法地址
    SEL sel_add = @selector(addObject:);
    Method addObject1 = class_getInstanceMethod(selfClass, sel_add);

    // 获取自定义 safeAddObject: 方法地址
    SEL sel_safeAdd = @selector(safeAddObject:);
    Method safeAddObject2 = class_getInstanceMethod(selfClass, sel_safeAdd);

    // 然后交换方法地址,相当于交换实现方式
    method_exchangeImplementations(addObject1,safeAddObject2);
}

// 我们这里定义一个新的添加对象的方法 , 不能在分类中重写系统方法 addObject 这样会覆盖系统的addObject ,并且分类中也不能使用 super
- (void)safeAddObject:(id)anObject{

    // 我们可以加判断,如果对象为空
    if (!anObject) {
        NSLog(@"对象为空,添加新对象失败");
        return;
    }

    // 这里调用 safeAddObject 其实是调用 addObject: 因为在load 方法中,系统的方法和你自己实现的方法已经交换了
    [self safeAddObject:anObject];
}

5.1 发送音信

  • 主意调用的真面目,就是让对象发送音讯;
  • objc_msgSend,唯有对象才能发送音信,由此以objc开端;
  • 动用音讯机制的前提,必须导入 #import<objc/message.h>
  • 音信机制简单利用

// 比如创建一个Person对象,我们通常会如下写
    Person *p = [[Person alloc] init];

    // 其实方法的调用就是向这个对方发送消息,所以上面的代码底层实现的主要代码就是:
    // 由于iOS5以后,苹果不支持这样写,所以需要在 Build Setting 中 将 Enable Strick Checking 设置为 NO
    Person *p = objc_msgSend(objc_msgSend(objc_getClass("Person"),sel_registerName("alloc")),sel_registerName("init"));

    消息机制的规律:对象根据章程编号SEL去映射表查找对应的法子完成。

图片 6

 

5.3 动态增进方法 

如果一个类措施至极多,加载类到内存的时候也正如花费资源,须求给各样方法生成映射表,可以利用动态给某个类,添加情势解决。

NSMutableArray *mArr = [NSMutableArray array];
    // WCE_addObject 方法只是在分类中声明,但是并没有实现
    [mArr performSelector:@selector(WCE_addObject)];

// 现在我们在分类中默认去实现
/ 当对象调用一个未实现的对象方法,会进入这个方法,并把对应的方法列表传过来

+(BOOL)resolveInstanceMethod:(SEL)sel{

    if (sel == @selector(WCE_addObject)) {

        // 动态添加 WCE_addObject 方法

        // 第一个参数:给哪个类添加方法

        // 第二个参数:添加方法的方法编号

        // 第三个参数:添加方法的函数实现(函数地址) 这里需要转换一下

        // 第四个参数:函数的类型,(返回值+参数类型) v:void @:对象-self SEL:_cmd

        class_addMethod(self, @selector(WCE_addObject), (void *)WCE_addObject, "v@:");

    }    

    return [super resolveInstanceMethod:sel];

}

// 默认方法都有两个隐式参数 调用方法者 调用的方法

void WCE_addObject(id self,SEL sel){

    NSLog(@"这个方法被默认执行了");

}

5.3 动态增进方法 

只要一个类措施卓殊多,加载类到内存的时候也正如用度资源,必要给每个方法生成映射表,可以利用动态给某个类,添加方式解决。

NSMutableArray *mArr = [NSMutableArray array];
    // WCE_addObject 方法只是在分类中声明,但是并没有实现
    [mArr performSelector:@selector(WCE_addObject)];

// 现在我们在分类中默认去实现
/ 当对象调用一个未实现的对象方法,会进入这个方法,并把对应的方法列表传过来

+(BOOL)resolveInstanceMethod:(SEL)sel{

    if (sel == @selector(WCE_addObject)) {

        // 动态添加 WCE_addObject 方法

        // 第一个参数:给哪个类添加方法

        // 第二个参数:添加方法的方法编号

        // 第三个参数:添加方法的函数实现(函数地址) 这里需要转换一下

        // 第四个参数:函数的类型,(返回值+参数类型) v:void @:对象-self SEL:_cmd

        class_addMethod(self, @selector(WCE_addObject), (void *)WCE_addObject, "v@:");

    }    

    return [super resolveInstanceMethod:sel];

}

// 默认方法都有两个隐式参数 调用方法者 调用的方法

void WCE_addObject(id self,SEL sel){

    NSLog(@"这个方法被默认执行了");

}

5.2 交流方法

简单的说的话就是将八个法子完成举办置换,比如系统自带的法子效果不够,我们想给系统自带的办法增加部分效用,并且维持原有的效应,我们就可以运用Runtie,交流方法。

譬如说:我们想在 NSMutableArray 添加新对象的时候,进行置空判断。

// 需求:给 addObject: 方法提供功能,当添加一个新的对象时,进行为空判断
    // 1 先弄个分类 定义一个 能添加新对象又能进行为空判断的方法
    // 2 和系统的方法进行交换

#import <objc/message.h>
@implementation NSMutableArray (Category)
// 加载分类到内存的时候调用,会比main函数先调用
+(void)load{
    // 交换方法 获取类名,如果使用[self class] 获取的是 NSMutableArray ,因为NSMutableArray 是类簇,这个可以看看了解一下
    Class selfClass = NSClassFromString(@"__NSArrayM"); 
    // 获取系统 addObject: 方法地址
    SEL sel_add = @selector(addObject:);
    Method addObject1 = class_getInstanceMethod(selfClass, sel_add);

    // 获取自定义 safeAddObject: 方法地址
    SEL sel_safeAdd = @selector(safeAddObject:);
    Method safeAddObject2 = class_getInstanceMethod(selfClass, sel_safeAdd);

    // 然后交换方法地址,相当于交换实现方式
    method_exchangeImplementations(addObject1,safeAddObject2);
}

// 我们这里定义一个新的添加对象的方法 , 不能在分类中重写系统方法 addObject 这样会覆盖系统的addObject ,并且分类中也不能使用 super
- (void)safeAddObject:(id)anObject{

    // 我们可以加判断,如果对象为空
    if (!anObject) {
        NSLog(@"对象为空,添加新对象失败");
        return;
    }

    // 这里调用 safeAddObject 其实是调用 addObject: 因为在load 方法中,系统的方法和你自己实现的方法已经交换了
    [self safeAddObject:anObject];
}

 5.4 给分类添加属性

给一个类注解属性,其实本质就是给那几个类添加关系,并不是向来把这么些值的内存空间添加到类存空间。

譬如大家想给NSMutableArr类动态拉长一个wce_name属性:

// wce_name 是动态的给NSMutableArray 添加的属性
    NSMutableArray *mArr = [NSMutableArray array];
    mArr.wce_name = @"这是我的东西哦";
    NSLog(@"%@",mArr.wce_name);

// 下面是具体实现
// 定义关联的key
static const char *key = "wce_name";

@implementation NSMutableArray (Property)

-(NSString *)wce_name{
    // 根据关联的key,获取关联的值
    return objc_getAssociatedObject(self, key);
}

-(void)setWce_name:(NSString *)wce_name{
    // 第一个参数:给哪个对象添加关联
    // 第二个参数:关联的key,通过这个key获取
    // 第三个参数:关联的value
    // 第四个参数:关联的策略 retain copy strong
    objc_setAssociatedObject(self, key, wce_name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

 5.4 给分类添加属性

给一个类注明属性,其实本质就是给这么些类添加关系,并不是直接把这么些值的内存空间添加到类存空间。

譬如说我们想给NSMutableArr类动态拉长一个wce_name属性:

// wce_name 是动态的给NSMutableArray 添加的属性
    NSMutableArray *mArr = [NSMutableArray array];
    mArr.wce_name = @"这是我的东西哦";
    NSLog(@"%@",mArr.wce_name);

// 下面是具体实现
// 定义关联的key
static const char *key = "wce_name";

@implementation NSMutableArray (Property)

-(NSString *)wce_name{
    // 根据关联的key,获取关联的值
    return objc_getAssociatedObject(self, key);
}

-(void)setWce_name:(NSString *)wce_name{
    // 第一个参数:给哪个对象添加关联
    // 第二个参数:关联的key,通过这个key获取
    // 第三个参数:关联的value
    // 第四个参数:关联的策略 retain copy strong
    objc_setAssociatedObject(self, key, wce_name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

5.3 动态拉长方法 

设若一个类措施至极多,加载类到内存的时候也正如费用资源,须求给各样方法生成映射表,可以使用动态给某个类,添加措施解决。

NSMutableArray *mArr = [NSMutableArray array];
    // WCE_addObject 方法只是在分类中声明,但是并没有实现
    [mArr performSelector:@selector(WCE_addObject)];

// 现在我们在分类中默认去实现
/ 当对象调用一个未实现的对象方法,会进入这个方法,并把对应的方法列表传过来

+(BOOL)resolveInstanceMethod:(SEL)sel{

    if (sel == @selector(WCE_addObject)) {

        // 动态添加 WCE_addObject 方法

        // 第一个参数:给哪个类添加方法

        // 第二个参数:添加方法的方法编号

        // 第三个参数:添加方法的函数实现(函数地址) 这里需要转换一下

        // 第四个参数:函数的类型,(返回值+参数类型) v:void @:对象-self SEL:_cmd

        class_addMethod(self, @selector(WCE_addObject), (void *)WCE_addObject, "[email protected]:");

    }    

    return [super resolveInstanceMethod:sel];

}

// 默认方法都有两个隐式参数 调用方法者 调用的方法

void WCE_addObject(id self,SEL sel){

    NSLog(@"这个方法被默认执行了");

}

 5.5 获取对象的具有属性

例如大家想把一个字典转化成对象,可以选择KVC,setValuesForKeysWithDictionary:
,但是必须求保管:模型中的属性和字典中key一一对应,如若不雷同,就会报错。

由此,大家能够动用Runtime,遍历对象中的所有属性,根据模型的属性名,去字典中追寻key,取出对应的值,给模型的特性赋值。建一个NSObject分类,专门字典转模型,未来所有的模型都得以由此这么些分类转。

// 字典转model
+ (instancetype)WCE_objectWithKeyValues:(NSDictionary *)dict{

    NSArray *propertyList = [self WCE_propertyList];

    id object = [[self alloc] init];

    for (NSString *key in propertyList) {
        if (dict[key]) {
            [object setValue:dict[key] forKey:key];
        }
    }
    return object;
}

// 获取所有的属性列表
+ (NSArray *)WCE_propertyList{
    NSMutableArray *mArr = [NSMutableArray array];

    // 获取所有的属性列表
    unsigned int count = 0;
    // 获取各种所有成员属性,无论是属性还是成员变量。私有的也可以,这里假设Person只有三个属性
  // 如果只想获取属性可以用 class_copyPropertyList
    Ivar *ivarList = class_copyIvarList(self, &count);

    for (int i = 0; i < count; i++) {
        Ivar ivar = ivarList[i];
        // 获取成员属性名
        NSString *name = [NSString stringWithUTF8String:ivar_getName(ivar)];
        // 因为获取的是属性,成员变量就是 _属性名,这里是去除下划线
        name = [name substringFromIndex:1];
        // 添加到数组
        [mArr addObject:name];
    }
    return mArr;
}

 5.5 获取对象的装有属性

比如我们想把一个字典转化成对象,可以利用KVC,setValuesForKeysWithDictionary:
,然则必需求确保:模型中的属性和字典中key一一对应,若是不等同,就会报错。

为此,大家可以使用Runtime,遍历对象中的所有属性,根据模型的属性名,去字典中搜索key,取出对应的值,给模型的性质赋值。建一个NSObject分类,专门字典转模型,未来所有的模型都可以经过那么些分类转。

// 字典转model
+ (instancetype)WCE_objectWithKeyValues:(NSDictionary *)dict{

    NSArray *propertyList = [self WCE_propertyList];

    id object = [[self alloc] init];

    for (NSString *key in propertyList) {
        if (dict[key]) {
            [object setValue:dict[key] forKey:key];
        }
    }
    return object;
}

// 获取所有的属性列表
+ (NSArray *)WCE_propertyList{
    NSMutableArray *mArr = [NSMutableArray array];

    // 获取所有的属性列表
    unsigned int count = 0;
    // 获取各种所有成员属性,无论是属性还是成员变量。私有的也可以,这里假设Person只有三个属性
  // 如果只想获取属性可以用 class_copyPropertyList
    Ivar *ivarList = class_copyIvarList(self, &count);

    for (int i = 0; i < count; i++) {
        Ivar ivar = ivarList[i];
        // 获取成员属性名
        NSString *name = [NSString stringWithUTF8String:ivar_getName(ivar)];
        // 因为获取的是属性,成员变量就是 _属性名,这里是去除下划线
        name = [name substringFromIndex:1];
        // 添加到数组
        [mArr addObject:name];
    }
    return mArr;
}

 5.4 给分类添加属性

给一个类申明属性,其实本质就是给这几个类添加关系,并不是平昔把这些值的内存空间添加到类存空间。

比如说大家想给NSMutableArr类动态拉长一个wce_name属性:

// wce_name 是动态的给NSMutableArray 添加的属性
    NSMutableArray *mArr = [NSMutableArray array];
    mArr.wce_name = @"这是我的东西哦";
    NSLog(@"%@",mArr.wce_name);

// 下面是具体实现
// 定义关联的key
static const char *key = "wce_name";

@implementation NSMutableArray (Property)

-(NSString *)wce_name{
    // 根据关联的key,获取关联的值
    return objc_getAssociatedObject(self, key);
}

-(void)setWce_name:(NSString *)wce_name{
    // 第一个参数:给哪个对象添加关联
    // 第二个参数:关联的key,通过这个key获取
    // 第三个参数:关联的value
    // 第四个参数:关联的策略 retain copy strong
    objc_setAssociatedObject(self, key, wce_name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

5.6 动态增进一个类

KVO(键值观看)的贯彻就是选择的runtime能够动态添加类

当你对一个对象举行观看时,系统会自动新建一个类继承自源类,然后重写被观看属性的setter方法,然后重写的setter方法会负责在调用原setter方法前后布告观望者,然后把原对象的isa指针指向那几个新类,大家知道,对象是经过isa指针去寻找自己是属于哪个类,并去所在类的章程列表中寻觅方法的,所以那一个时候那几个目标就自然地成为了新类的实例对象。

就像KVO一样,系统是在程序运行的时候,根据你要监听的类,动态增进一个新类继承自该类,然后重写原类的setter方法并在里头通告observer的。

那么,怎样动态增加一个类呢?

// 创建一个类(size_t extraBytes该参数通常指定为0, 该参数是分配给类和元类对象尾部的索引ivars的字节数。)
Class clazz = objc_allocateClassPair([NSObject class], "GoodPerson", 0);

// 添加ivar
// @encode(aType) : 返回该类型的C字符串
class_addIvar(clazz, "_name", sizeof(NSString *), log2(sizeof(NSString *)), @encode(NSString *));

class_addIvar(clazz, "_age", sizeof(NSUInteger), log2(sizeof(NSUInteger)), @encode(NSUInteger));

// 注册该类
objc_registerClassPair(clazz);

// 创建实例对象
id object = [[clazz alloc] init];

// 设置ivar
[object setValue:@"Tracy" forKey:@"name"];

Ivar ageIvar = class_getInstanceVariable(clazz, "_age");
object_setIvar(object, ageIvar, @18);

// 打印对象的类和内存地址
NSLog(@"%@", object);

// 打印对象的属性值
NSLog(@"name = %@, age = %@", [object valueForKey:@"name"], object_getIvar(object, ageIvar));

// 当类或者它的子类的实例还存在,则不能调用objc_disposeClassPair方法
object = nil;

// 销毁类
objc_disposeClassPair(clazz);

 

5.6 动态增进一个类

KVO(键值观望)的贯彻就是运用的runtime可以动态添加类

当你对一个对象进行观测时,系统会活动新建一个类继承自源类,然后重写被观看属性的setter方法,然后重写的setter方法会负责在调用原setter方法前后公告观看者,然后把原对象的isa指针指向那些新类,大家领略,对象是经过isa指针去寻找自己是属于哪个类,并去所在类的点子列表中寻觅方法的,所以那一个时候这么些目的就自然地改成了新类的实例对象。

就像是KVO一样,系统是在程序运行的时候,按照你要监听的类,动态增加一个新类继承自该类,然后重写原类的setter方法并在里面通告observer的。

那就是说,如何动态增加一个类呢?

// 创建一个类(size_t extraBytes该参数通常指定为0, 该参数是分配给类和元类对象尾部的索引ivars的字节数。)
Class clazz = objc_allocateClassPair([NSObject class], "GoodPerson", 0);

// 添加ivar
// @encode(aType) : 返回该类型的C字符串
class_addIvar(clazz, "_name", sizeof(NSString *), log2(sizeof(NSString *)), @encode(NSString *));

class_addIvar(clazz, "_age", sizeof(NSUInteger), log2(sizeof(NSUInteger)), @encode(NSUInteger));

// 注册该类
objc_registerClassPair(clazz);

// 创建实例对象
id object = [[clazz alloc] init];

// 设置ivar
[object setValue:@"Tracy" forKey:@"name"];

Ivar ageIvar = class_getInstanceVariable(clazz, "_age");
object_setIvar(object, ageIvar, @18);

// 打印对象的类和内存地址
NSLog(@"%@", object);

// 打印对象的属性值
NSLog(@"name = %@, age = %@", [object valueForKey:@"name"], object_getIvar(object, ageIvar));

// 当类或者它的子类的实例还存在,则不能调用objc_disposeClassPair方法
object = nil;

// 销毁类
objc_disposeClassPair(clazz);

 

 5.5 获取对象的装有属性

比如我们想把一个字典转化成对象,可以动用KVC,setValuesForKeysWithDictionary:
,可是必须求力保:模型中的属性和字典中key一一对应,假使分裂,就会报错。

从而,大家可以行使Runtime,遍历对象中的所有属性,依据模型的属性名,去字典中找找key,取出对应的值,给模型的质量赋值。建一个NSObject分类,专门字典转模型,未来所有的模型都得以经过那一个分类转。

// 字典转model
+ (instancetype)WCE_objectWithKeyValues:(NSDictionary *)dict{

    NSArray *propertyList = [self WCE_propertyList];

    id object = [[self alloc] init];

    for (NSString *key in propertyList) {
        if (dict[key]) {
            [object setValue:dict[key] forKey:key];
        }
    }
    return object;
}

// 获取所有的属性列表
+ (NSArray *)WCE_propertyList{
    NSMutableArray *mArr = [NSMutableArray array];

    // 获取所有的属性列表
    unsigned int count = 0;
    // 获取各种所有成员属性,无论是属性还是成员变量。私有的也可以,这里假设Person只有三个属性
  // 如果只想获取属性可以用 class_copyPropertyList
    Ivar *ivarList = class_copyIvarList(self, &count);

    for (int i = 0; i < count; i++) {
        Ivar ivar = ivarList[i];
        // 获取成员属性名
        NSString *name = [NSString stringWithUTF8String:ivar_getName(ivar)];
        // 因为获取的是属性,成员变量就是 _属性名,这里是去除下划线
        name = [name substringFromIndex:1];
        // 添加到数组
        [mArr addObject:name];
    }
    return mArr;
}

 六、项目中的具体行使

 六、项目中的具体使用

5.6 动态增进一个类

KVO(键值观望)的兑现就是行使的runtime可以动态添加类

当您对一个目的开展考察时,系统会活动新建一个类继承自源类,然后重写被考察属性的setter方法,然后重写的setter方法会负责在调用原setter方法前后通告观察者,然后把原对象的isa指针指向那几个新类,大家精晓,对象是通过isa指针去寻觅自己是属于哪个类,并去所在类的艺术列表中找寻方法的,所以这么些时候这么些目的就自然地改为了新类的实例对象。

就像KVO一样,系统是在程序运行的时候,根据你要监听的类,动态增加一个新类继承自该类,然后重写原类的setter方法并在里面通告observer的。

那么,怎么样动态增加一个类呢?

// 创建一个类(size_t extraBytes该参数通常指定为0, 该参数是分配给类和元类对象尾部的索引ivars的字节数。)
Class clazz = objc_allocateClassPair([NSObject class], "GoodPerson", 0);

// 添加ivar
// @encode(aType) : 返回该类型的C字符串
class_addIvar(clazz, "_name", sizeof(NSString *), log2(sizeof(NSString *)), @encode(NSString *));

class_addIvar(clazz, "_age", sizeof(NSUInteger), log2(sizeof(NSUInteger)), @encode(NSUInteger));

// 注册该类
objc_registerClassPair(clazz);

// 创建实例对象
id object = [[clazz alloc] init];

// 设置ivar
[object setValue:@"Tracy" forKey:@"name"];

Ivar ageIvar = class_getInstanceVariable(clazz, "_age");
object_setIvar(object, ageIvar, @18);

// 打印对象的类和内存地址
NSLog(@"%@", object);

// 打印对象的属性值
NSLog(@"name = %@, age = %@", [object valueForKey:@"name"], object_getIvar(object, ageIvar));

// 当类或者它的子类的实例还存在,则不能调用objc_disposeClassPair方法
object = nil;

// 销毁类
objc_disposeClassPair(clazz);

 

6.1 替换系统的方法

也就是将系统的点子和融洽定义的主意替换,比如可变数组添加新对象、按标取值时的增多的防备崩溃的论断,当大家接手一个新品类时,想更快掌握界面跳转,可以替换系统的view威尔Appear:方法。

6.1 替换系统的方法

也就是将系统的主意和融洽定义的主意替换,比如可变数组添加新对象、按标取值时的增多的防范崩溃的判定,当大家接手一个新类型时,想更快明白界面跳转,可以轮换系统的view威尔Appear:方法。

 六、项目中的具体采纳

6.2 落成分类也足以添加属性

MJRefresh 就是动态的给UIScrollView增添的mj_header和mj_footer

6.2 达成分类也能够添加属性

MJRefresh 就是动态的给UIScrollView伸张的mj_header和mj_footer

6.1 替换系统的方式

也就是将系统的点子和协调定义的点子替换,比如可变数组添加新对象、按标取值时的增多的警备崩溃的判定,当我们接手一个新品类时,想更快了然界面跳转,可以替换系统的view威尔Appear:方法。

6.3 字典和模型的全自动转换

事实上根本的也是采用了,遍历属性这一个法子。

6.3 字典和模型的电动转换

实际上主要的也是运用了,遍历属性这一个措施。

6.2 完毕分类也可以添加属性

MJRefresh 就是动态的给UIScrollView扩展的mj_header和mj_footer

6.4 动态的充实方法

动态的为某个类增添一个措施,能够对某一个效能,做扩张。

6.4 动态的增多方法

动态的为某个类增加一个主意,可以对某一个效益,做伸张。

6.3 字典和模型的自行转换

事实上主要的也是应用了,遍历属性这几个方法。

6.5 自动归档和活动解档

大家如若有个Moviel类,有多个特性 id name url
,寻常在归解档的时候,大家都会

兑现这七个探讨形式:

- (void)encodeWithCoder:(NSCoder *)aCoder
{
    [aCoder encodeObject:_movieId forKey:@"id"];
    [aCoder encodeObject:_movieName forKey:@"name"];
    [aCoder encodeObject:_pic_url forKey:@"url"];

}

- (id)initWithCoder:(NSCoder *)aDecoder
{
    if (self = [super init]) {
        self.movieId = [aDecoder decodeObjectForKey:@"id"];
        self.movieName = [aDecoder decodeObjectForKey:@"name"];
        self.pic_url = [aDecoder decodeObjectForKey:@"url"];
    }
    return self;
}
@end

 即使那里有100个特性,那么大家也只可以把100个属性都给写一回,

只是用runtime之后,就简单了:

#import "Movie.h"
#import <objc/runtime.h>
@implementation Movie

- (void)encodeWithCoder:(NSCoder *)encoder

{
    unsigned int count = 0;
    Ivar *ivars = class_copyIvarList([Movie class], &count);

    for (int i = 0; i<count; i++) {
        // 取出i位置对应的成员变量
        Ivar ivar = ivars[i];
        // 查看成员变量
        const char *name = ivar_getName(ivar);
        // 归档
        NSString *key = [NSString stringWithUTF8String:name];
        id value = [self valueForKey:key];
        [encoder encodeObject:value forKey:key];
    }
    free(ivars);
}

- (id)initWithCoder:(NSCoder *)decoder
{
    if (self = [super init]) {
        unsigned int count = 0;
        Ivar *ivars = class_copyIvarList([Movie class], &count);
        for (int i = 0; i<count; i++) {
        // 取出i位置对应的成员变量
        Ivar ivar = ivars[i];
        // 查看成员变量
        const char *name = ivar_getName(ivar);
       // 归档
       NSString *key = [NSString stringWithUTF8String:name];
      id value = [decoder decodeObjectForKey:key];
       // 设置到成员变量身上
        [self setValue:value forKey:key];

        }
        free(ivars);
    } 
    return self;
}
@end

 倘使还嫌麻烦,我们可以把encodeWithCoder 和 initWithCoder
那四个点子抽成宏

#import "Movie.h"
#import <objc/runtime.h>

#define encodeRuntime(A) \
\
unsigned int count = 0;\
Ivar *ivars = class_copyIvarList([A class], &count);\
for (int i = 0; i<count; i++) {\
Ivar ivar = ivars[i];\
const char *name = ivar_getName(ivar);\
NSString *key = [NSString stringWithUTF8String:name];\
id value = [self valueForKey:key];\
[encoder encodeObject:value forKey:key];\
}\
free(ivars);\
\

#define initCoderRuntime(A) \
\
if (self = [super init]) {\
unsigned int count = 0;\
Ivar *ivars = class_copyIvarList([A class], &count);\
for (int i = 0; i<count; i++) {\
Ivar ivar = ivars[i];\
const char *name = ivar_getName(ivar);\
NSString *key = [NSString stringWithUTF8String:name];\
id value = [decoder decodeObjectForKey:key];\
[self setValue:value forKey:key];\
}\
free(ivars);\
}\
return self;\
\

@implementation Movie

- (void)encodeWithCoder:(NSCoder *)encoder

{
    encodeRuntime(Movie)
}

- (id)initWithCoder:(NSCoder *)decoder
{
    initCoderRuntime(Movie)
}
@end

6.5 自动归档和电动解档

俺们只要有个Moviel类,有多个属性 id name url
,平时在归解档的时候,大家都会

完毕那多个探讨情势:

- (void)encodeWithCoder:(NSCoder *)aCoder
{
    [aCoder encodeObject:_movieId forKey:@"id"];
    [aCoder encodeObject:_movieName forKey:@"name"];
    [aCoder encodeObject:_pic_url forKey:@"url"];

}

- (id)initWithCoder:(NSCoder *)aDecoder
{
    if (self = [super init]) {
        self.movieId = [aDecoder decodeObjectForKey:@"id"];
        self.movieName = [aDecoder decodeObjectForKey:@"name"];
        self.pic_url = [aDecoder decodeObjectForKey:@"url"];
    }
    return self;
}
@end

 倘使那里有100个属性,那么我们也不得不把100个特性都给写四遍,

而是用runtime之后,就大约了:

#import "Movie.h"
#import <objc/runtime.h>
@implementation Movie

- (void)encodeWithCoder:(NSCoder *)encoder

{
    unsigned int count = 0;
    Ivar *ivars = class_copyIvarList([Movie class], &count);

    for (int i = 0; i<count; i++) {
        // 取出i位置对应的成员变量
        Ivar ivar = ivars[i];
        // 查看成员变量
        const char *name = ivar_getName(ivar);
        // 归档
        NSString *key = [NSString stringWithUTF8String:name];
        id value = [self valueForKey:key];
        [encoder encodeObject:value forKey:key];
    }
    free(ivars);
}

- (id)initWithCoder:(NSCoder *)decoder
{
    if (self = [super init]) {
        unsigned int count = 0;
        Ivar *ivars = class_copyIvarList([Movie class], &count);
        for (int i = 0; i<count; i++) {
        // 取出i位置对应的成员变量
        Ivar ivar = ivars[i];
        // 查看成员变量
        const char *name = ivar_getName(ivar);
       // 归档
       NSString *key = [NSString stringWithUTF8String:name];
      id value = [decoder decodeObjectForKey:key];
       // 设置到成员变量身上
        [self setValue:value forKey:key];

        }
        free(ivars);
    } 
    return self;
}
@end

 借使还嫌麻烦,大家得以把encodeWithCoder 和 initWithCoder
这个章程抽成宏

#import "Movie.h"
#import <objc/runtime.h>

#define encodeRuntime(A) \
\
unsigned int count = 0;\
Ivar *ivars = class_copyIvarList([A class], &count);\
for (int i = 0; i<count; i++) {\
Ivar ivar = ivars[i];\
const char *name = ivar_getName(ivar);\
NSString *key = [NSString stringWithUTF8String:name];\
id value = [self valueForKey:key];\
[encoder encodeObject:value forKey:key];\
}\
free(ivars);\
\

#define initCoderRuntime(A) \
\
if (self = [super init]) {\
unsigned int count = 0;\
Ivar *ivars = class_copyIvarList([A class], &count);\
for (int i = 0; i<count; i++) {\
Ivar ivar = ivars[i];\
const char *name = ivar_getName(ivar);\
NSString *key = [NSString stringWithUTF8String:name];\
id value = [decoder decodeObjectForKey:key];\
[self setValue:value forKey:key];\
}\
free(ivars);\
}\
return self;\
\

@implementation Movie

- (void)encodeWithCoder:(NSCoder *)encoder

{
    encodeRuntime(Movie)
}

- (id)initWithCoder:(NSCoder *)decoder
{
    initCoderRuntime(Movie)
}
@end

6.4 动态的加码方法

动态的为某个类增添一个办法,可以对某一个功能,做扩充。

 6.6 界面指定跳转

在收取推送公告的时候,大家期待得以点击那条公告跳转到对应的页面,简单的不二法门就是,判断,在认清。不过我们得以运用runtime动态变化对象、属性、方法那特色,大家得以先跟服务端探讨好,定义跳转规则,比如要跳转到A控制器,要求传属性
id、type,那么服务端重回字典给我,里面有决定器名,多少个属性名跟属性值,客户端就足以依照控制器名生成对象,再用kvc给目的赋值,那样就搞定了。

诸如,根据推送规则跳转对应页面 HSFeedsViewController

// 进入该页面需要穿的属性

@interface HSFeedsViewController : UIViewController
// 注:根据下面的两个属性,可以从服务器获取对应的频道列表数据
/** 频道ID */
@property (nonatomic, copy) NSString *ID;
/** 频道type */
@property (nonatomic, copy) NSString *type;
@end

 AppDelegate.m 中添加以下代码片段:

// 这个规则肯定事先跟服务端沟通好,跳转对应的界面需要对应的参数
NSDictionary *userInfo = @{
                           @"class": @"HSFeedsViewController",
                           @"property": @{
                                        @"ID": @"123",
                                        @"type": @"12"
                                   }
                           };

 接收到推送的新闻后:

- (void)push:(NSDictionary *)params
{
    // 类名
    NSString *class =[NSString stringWithFormat:@"%@", params[@"class"]];
    const char *className = [class cStringUsingEncoding:NSASCIIStringEncoding];
    // 从一个字串返回一个类
    Class newClass = objc_getClass(className);
    if (!newClass)
    {
        // 创建一个类
        Class superClass = [NSObject class];
        newClass = objc_allocateClassPair(superClass, className, 0);
        // 注册你创建的这个类
        objc_registerClassPair(newClass);
    }
    // 创建对象
    id instance = [[newClass alloc] init];
    // 对该对象赋值属性
    NSDictionary * propertys = params[@"property"];
    [propertys enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
        // 检测这个对象是否存在该属性
        if ([self checkIsExistPropertyWithInstance:instance verifyPropertyName:key]) {
            // 利用kvc赋值
            [instance setValue:obj forKey:key];
        }
    }];
    // 获取导航控制器
    UITabBarController *tabVC = (UITabBarController *)self.window.rootViewController;
    UINavigationController *pushClassStance = (UINavigationController *)tabVC.viewControllers[tabVC.selectedIndex];
    // 跳转到对应的控制器
    [pushClassStance pushViewController:instance animated:YES];
}

 // 检查对象是或不是留存该属性:

- (BOOL)checkIsExistPropertyWithInstance:(id)instance verifyPropertyName:(NSString *)verifyPropertyName
{
    unsigned int outCount, i;
    // 获取对象里的属性列表
    objc_property_t * properties = class_copyPropertyList([instance
                                                           class], &outCount);
    for (i = 0; i < outCount; i++) {
        objc_property_t property =properties[i];
        //  属性名转成字符串
        NSString *propertyName = [[NSString alloc] initWithCString:property_getName(property) encoding:NSUTF8StringEncoding];
        // 判断该属性是否存在
        if ([propertyName isEqualToString:verifyPropertyName]) {
            free(properties);
            return YES;
        }
    }
    free(properties); // 用copy的时候记得释放
    return NO;
}

 

// 官方文档翻译

http://blog.csdn.net/coyote1994/article/details/52441513

 6.6 界面指定跳转

在吸纳推送通告的时候,大家盼望得以点击那条布告跳转到对应的页面,简单的主意就是,判断,在认清。不过大家得以选拔runtime动态变化对象、属性、方法那特色,大家可以先跟服务端研讨好,定义跳转规则,比如要跳转到A控制器,须要传属性
id、type,那么服务端重临字典给自身,里面有决定器名,七个属性名跟属性值,客户端就可以依据控制器名生成对象,再用kvc给目标赋值,那样就搞定了。

例如,按照推送规则跳转对应页面 HSFeedsViewController

// 进入该页面需要穿的属性

@interface HSFeedsViewController : UIViewController
// 注:根据下面的两个属性,可以从服务器获取对应的频道列表数据
/** 频道ID */
@property (nonatomic, copy) NSString *ID;
/** 频道type */
@property (nonatomic, copy) NSString *type;
@end

 AppDelegate.m 中添加以下代码片段:

// 这个规则肯定事先跟服务端沟通好,跳转对应的界面需要对应的参数
NSDictionary *userInfo = @{
                           @"class": @"HSFeedsViewController",
                           @"property": @{
                                        @"ID": @"123",
                                        @"type": @"12"
                                   }
                           };

 接收到推送的新闻后:

- (void)push:(NSDictionary *)params
{
    // 类名
    NSString *class =[NSString stringWithFormat:@"%@", params[@"class"]];
    const char *className = [class cStringUsingEncoding:NSASCIIStringEncoding];
    // 从一个字串返回一个类
    Class newClass = objc_getClass(className);
    if (!newClass)
    {
        // 创建一个类
        Class superClass = [NSObject class];
        newClass = objc_allocateClassPair(superClass, className, 0);
        // 注册你创建的这个类
        objc_registerClassPair(newClass);
    }
    // 创建对象
    id instance = [[newClass alloc] init];
    // 对该对象赋值属性
    NSDictionary * propertys = params[@"property"];
    [propertys enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
        // 检测这个对象是否存在该属性
        if ([self checkIsExistPropertyWithInstance:instance verifyPropertyName:key]) {
            // 利用kvc赋值
            [instance setValue:obj forKey:key];
        }
    }];
    // 获取导航控制器
    UITabBarController *tabVC = (UITabBarController *)self.window.rootViewController;
    UINavigationController *pushClassStance = (UINavigationController *)tabVC.viewControllers[tabVC.selectedIndex];
    // 跳转到对应的控制器
    [pushClassStance pushViewController:instance animated:YES];
}

 // 检核对象是或不是留存该属性:

- (BOOL)checkIsExistPropertyWithInstance:(id)instance verifyPropertyName:(NSString *)verifyPropertyName
{
    unsigned int outCount, i;
    // 获取对象里的属性列表
    objc_property_t * properties = class_copyPropertyList([instance
                                                           class], &outCount);
    for (i = 0; i < outCount; i++) {
        objc_property_t property =properties[i];
        //  属性名转成字符串
        NSString *propertyName = [[NSString alloc] initWithCString:property_getName(property) encoding:NSUTF8StringEncoding];
        // 判断该属性是否存在
        if ([propertyName isEqualToString:verifyPropertyName]) {
            free(properties);
            return YES;
        }
    }
    free(properties); // 用copy的时候记得释放
    return NO;
}

 

// 官方文档翻译

http://blog.csdn.net/coyote1994/article/details/52441513

6.5 自动归档和自行解档

大家只要有个Moviel类,有多少个属性 id name url
,平常在归解档的时候,我们都会

落到实处那五个协议章程:

- (void)encodeWithCoder:(NSCoder *)aCoder
{
    [aCoder encodeObject:_movieId forKey:@"id"];
    [aCoder encodeObject:_movieName forKey:@"name"];
    [aCoder encodeObject:_pic_url forKey:@"url"];

}

- (id)initWithCoder:(NSCoder *)aDecoder
{
    if (self = [super init]) {
        self.movieId = [aDecoder decodeObjectForKey:@"id"];
        self.movieName = [aDecoder decodeObjectForKey:@"name"];
        self.pic_url = [aDecoder decodeObjectForKey:@"url"];
    }
    return self;
}
@end

 若是那里有100个属性,那么大家也只可以把100个特性都给写三回,

可是用runtime之后,就概括了:

#import "Movie.h"
#import <objc/runtime.h>
@implementation Movie

- (void)encodeWithCoder:(NSCoder *)encoder

{
    unsigned int count = 0;
    Ivar *ivars = class_copyIvarList([Movie class], &count);

    for (int i = 0; i<count; i++) {
        // 取出i位置对应的成员变量
        Ivar ivar = ivars[i];
        // 查看成员变量
        const char *name = ivar_getName(ivar);
        // 归档
        NSString *key = [NSString stringWithUTF8String:name];
        id value = [self valueForKey:key];
        [encoder encodeObject:value forKey:key];
    }
    free(ivars);
}

- (id)initWithCoder:(NSCoder *)decoder
{
    if (self = [super init]) {
        unsigned int count = 0;
        Ivar *ivars = class_copyIvarList([Movie class], &count);
        for (int i = 0; i<count; i++) {
        // 取出i位置对应的成员变量
        Ivar ivar = ivars[i];
        // 查看成员变量
        const char *name = ivar_getName(ivar);
       // 归档
       NSString *key = [NSString stringWithUTF8String:name];
      id value = [decoder decodeObjectForKey:key];
       // 设置到成员变量身上
        [self setValue:value forKey:key];

        }
        free(ivars);
    } 
    return self;
}
@end

 如若还嫌麻烦,大家得以把encodeWithCoder 和 initWithCoder
那七个法子抽成宏

#import "Movie.h"
#import <objc/runtime.h>

#define encodeRuntime(A) \
\
unsigned int count = 0;\
Ivar *ivars = class_copyIvarList([A class], &count);\
for (int i = 0; i<count; i++) {\
Ivar ivar = ivars[i];\
const char *name = ivar_getName(ivar);\
NSString *key = [NSString stringWithUTF8String:name];\
id value = [self valueForKey:key];\
[encoder encodeObject:value forKey:key];\
}\
free(ivars);\
\

#define initCoderRuntime(A) \
\
if (self = [super init]) {\
unsigned int count = 0;\
Ivar *ivars = class_copyIvarList([A class], &count);\
for (int i = 0; i<count; i++) {\
Ivar ivar = ivars[i];\
const char *name = ivar_getName(ivar);\
NSString *key = [NSString stringWithUTF8String:name];\
id value = [decoder decodeObjectForKey:key];\
[self setValue:value forKey:key];\
}\
free(ivars);\
}\
return self;\
\

@implementation Movie

- (void)encodeWithCoder:(NSCoder *)encoder

{
    encodeRuntime(Movie)
}

- (id)initWithCoder:(NSCoder *)decoder
{
    initCoderRuntime(Movie)
}
@end

 6.6 界面指定跳转

在接收推送布告的时候,大家期待得以点击那条通告跳转到对应的页面,容易的主意就是,判断,在认清。不过大家得以选择runtime动态变化对象、属性、方法那特色,大家得以先跟服务端切磋好,定义跳转规则,比如要跳转到A控制器,须求传属性
id、type,那么服务端重临字典给自家,里面有决定器名,多少个属性名跟属性值,客户端就足以依照控制器名生成对象,再用kvc给目的赋值,那样就搞定了。

诸如,依照推送规则跳转对应页面 HSFeedsViewController

// 进入该页面需要穿的属性

@interface HSFeedsViewController : UIViewController
// 注:根据下面的两个属性,可以从服务器获取对应的频道列表数据
/** 频道ID */
@property (nonatomic, copy) NSString *ID;
/** 频道type */
@property (nonatomic, copy) NSString *type;
@end

 AppDelegate.m 中添加以下代码片段:

// 这个规则肯定事先跟服务端沟通好,跳转对应的界面需要对应的参数
NSDictionary *userInfo = @{
                           @"class": @"HSFeedsViewController",
                           @"property": @{
                                        @"ID": @"123",
                                        @"type": @"12"
                                   }
                           };

 接收到推送的音讯后:

- (void)push:(NSDictionary *)params
{
    // 类名
    NSString *class =[NSString stringWithFormat:@"%@", params[@"class"]];
    const char *className = [class cStringUsingEncoding:NSASCIIStringEncoding];
    // 从一个字串返回一个类
    Class newClass = objc_getClass(className);
    if (!newClass)
    {
        // 创建一个类
        Class superClass = [NSObject class];
        newClass = objc_allocateClassPair(superClass, className, 0);
        // 注册你创建的这个类
        objc_registerClassPair(newClass);
    }
    // 创建对象
    id instance = [[newClass alloc] init];
    // 对该对象赋值属性
    NSDictionary * propertys = params[@"property"];
    [propertys enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
        // 检测这个对象是否存在该属性
        if ([self checkIsExistPropertyWithInstance:instance verifyPropertyName:key]) {
            // 利用kvc赋值
            [instance setValue:obj forKey:key];
        }
    }];
    // 获取导航控制器
    UITabBarController *tabVC = (UITabBarController *)self.window.rootViewController;
    UINavigationController *pushClassStance = (UINavigationController *)tabVC.viewControllers[tabVC.selectedIndex];
    // 跳转到对应的控制器
    [pushClassStance pushViewController:instance animated:YES];
}

 // 检核对象是否存在该属性:

- (BOOL)checkIsExistPropertyWithInstance:(id)instance verifyPropertyName:(NSString *)verifyPropertyName
{
    unsigned int outCount, i;
    // 获取对象里的属性列表
    objc_property_t * properties = class_copyPropertyList([instance
                                                           class], &outCount);
    for (i = 0; i < outCount; i++) {
        objc_property_t property =properties[i];
        //  属性名转成字符串
        NSString *propertyName = [[NSString alloc] initWithCString:property_getName(property) encoding:NSUTF8StringEncoding];
        // 判断该属性是否存在
        if ([propertyName isEqualToString:verifyPropertyName]) {
            free(properties);
            return YES;
        }
    }
    free(properties); // 用copy的时候记得释放
    return NO;
}

 

// 官方文档翻译

http://blog.csdn.net/coyote1994/article/details/52441513

http://www.bkjia.com/IOSjc/1266256.htmlwww.bkjia.comtruehttp://www.bkjia.com/IOSjc/1266256.htmlTechArticleiOS, 一、Runtime简介 1.1 简单介绍
Runtime简称运行时。OC就是运作时机制,也就是在运行时候的有的编制,其中最重大的是新闻机制;
对于…

相关文章