美文网首页iOS开发之笔记摘录
OC Runtime运行时机制---OC语法进阶摘录

OC Runtime运行时机制---OC语法进阶摘录

作者: 平安喜乐698 | 来源:发表于2017-09-18 09:18 被阅读12493次
目录

    简介
    数据结构
    与RunTime交互的3方式
    其他
    Runtime使用

简介

Runtime系统是一个由一系列 数据结构 和 函数 组成的具有公共接口的动态共享库。

    Objc中的类、方法和协议等在 runtime 中都由一些数据结构来定义
    头文件存放于/usr/include/objc目录下 
    基于C语言和汇编语言编写的API
    #import <objc/runtime.h> , #import <objc/message.h>


用于
    避免数组越界(替换方法)
    字典转模型
    快速归档
    动态添加方法(拦截调用)
    等
数据结构
类
    typedef struct objc_class *Class;   
方法
    typedef struct objc_method *Method;
变量
    typedef struct objc_ivar *Ivar;
Category
    typedef struct objc_category *Category;
属性
    typedef struct objc_property *objc_property_t;
类(类本身也是对象)
typedef struct objc_class *Class;  
            struct objc_class{
                Class isa;            // 指针
            #if !__OBJC2__
                Class super_class;    // 指向父类
                const char *name;     // 类名
                long version;
                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_ivar_list 成员变量列表
            struct objc_ivar_list {
                int ivar_count
            #ifdef __LP64__
                int space
            #endif
                /* variable length structure */
                struct objc_ivar ivar_list[1]
            }
objc_ivar 成员变量
            struct objc_ivar {
                char *ivar_name
                char *ivar_type
                int ivar_offset
            #ifdef __LP64__
                int space
            #endif
            }


objc_method_list 方法列表
            struct objc_method_list {
                struct objc_method_list *obsolete
                int method_count
            #ifdef __LP64__
                int space
            #endif
                /* variable length structure */
                struct objc_method method_list[1]
            }
objc_method 方法
            struct objc_method {
                SEL method_name                 // 方法索引
                char *method_types              // 方法的参数类型和返回值类型
                IMP method_imp                  // 方法具体实现
            }   


objc_cache 缓存
            struct objc_cache {
                unsigned int mask /* total = mask + 1 */                 
                unsigned int occupied                                    
                Method buckets[1]                                        
            };
            _buckets 存储 IMP,_mask 和 _occupied 对应 vtable
            typedef struct objc_method *Method;   // 见上




objc_category 类别
            struct objc_category {
                char *category_name                                     // 扩展名
                char *class_name                                        // 被扩展的类名
                struct objc_method_list *instance_methods               // 实例方法列表 
                struct objc_method_list *class_methods                  // 类方法列表
                struct objc_protocol_list *protocols                    // 协议列表
            }

objc_protocol_list 协议列表
            struct objc_protocol_list {
                struct objc_protocol_list *next;              
                long count;                                              // 个数
                __unsafe_unretained Protocol *list[1];                   // 列表
            };
            typedef struct objc_object Protocol;        // 协议

objc_object 对象 (objc_class:objc_object)
            struct objc_object {
            private:
                isa_t isa;
                // isa 指针不总是指向实例对象所属的类,不能依靠它来确定类型.KVO时指向中间类
            public:
                // ISA() assumes this is NOT a tagged pointer object
                Class ISA();
                
                // getIsa() allows this to be a tagged pointer object
                Class getIsa();
                ... 此处省略其他方法声明
            }
  


objc_ivar 变量
            struct objc_ivar {
                char *ivar_name                   // 变量名                       
                char *ivar_type                   // 变量类型                      
                int ivar_offset                                          
            #ifdef __LP64__
                int space                                                
            #endif
            }
函数
与RunTime交互的3方式: runtime函数/  NSObject方法 /Objc代码

Runtime函数

#import<objc/runtime.h>     

获取所有成员变量(快速归档)
    class_copyIvarList              
获取所有方法
    class_copyMethodList            
获取某个具体的类方法  
    class_getClassMethod            
交换两个方法的实现   (给系统功能添加功能)
    method_exchangeImplementations  

获取某实例方法的实现 
    class_getMethodImplementation(object_getClass(self), @selector(myClassMethod:));    
添加方法
    class_addMethod(object_getClass(self), sel, (IMP)runAddMethod, "v@:*");  

获取类
    Class objc_getClass(const char *name);      
    class_getSuperclass(Class cls);
获取属性列表
    objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount); 
获取某类的属性
    objc_property_t class_getProperty(Class cls, const char *name);
获取属性名
    const char *property_getName(objc_property_t property);
获取属性的属性?
    const char *property_getAttributes(objc_property_t property);
            
获取协议
    Protocol *objc_getProtocol(const char *name);           
获取协议列表         
    objc_property_t *protocol_copyPropertyList(Protocol *proto, unsigned int *outCount);    
获取某协议的属性
    objc_property_t protocol_getProperty(Protocol *proto, const char *name, BOOL isRequiredProperty, BOOL isInstanceProperty);





简单使用
    // 获取类指针(id 可以代表任意类型)
    id dogClass = objc_getClass("DogModel");
    unsigned int outCount;
    // 获取属性列表
    objc_property_t *properties = class_copyPropertyList(dogClass, &outCount);
    for (i = 0; i < outCount; i++) {
        // 属性
        objc_property_t property = properties[I];
        // 获取属性的一些属性
        fprintf(stdout, "%s %s\n", property_getName(property), property_getAttributes(property));
    }

    // 获取协议指针
    id protocol=objc_getProtocol("DogProtocol");
    // 获取协议列表
    objc_property_t *protocols=protocol_copyPropertyList(protocol, &outCount);
#import<objc/message.h>
        需要在  buildSettings |  Enable Strict Checking of objc_msgSend Calls 置为 NO
        Objc 中发送消息是用中括号([])把接收者和消息括起来,而直到运行时才会把消息与方法实现绑定
        编译器会根据情况在objc_msgSend, objc_msgSend_stret, objc_msgSendSuper, 或 objc_msgSendSuper_stret中选择一个来调用



        objc_msgSend(dogC, @selector(run))     
        消息发送步骤:
            1.检测 selector 是否要忽略
            2.检测 target 是否是nil (target为nil时 不会崩溃)
            3.查找IMP:具体参见方法的执行过程(见下)

NSObject (绝大多数类继承自NSObject)

    自定义类继承 NSObject 是因为 1.自动完成复杂的内存分配2.能够使用Runtime 系统带来的便利。

    + (NSString *)description;                // 子类覆写(自定义本类描述字符串)
    + (Class)class;                           // 返回本类类型
    + (Class)superclass;                      // 返回父类类型
    + (BOOL)isSubclassOfClass:(Class)aClass;  // 是否是某类型的子类
    - (BOOL)isKindOfClass:(Class)aClass;      // 是否是某一种类
    - (BOOL)isMemberOfClass:(Class)aClass;    // 是否是某一成员类
    
    - (BOOL)conformsToProtocol:(Protocol *)aProtocol;   // 是否实现了某协议
    - (BOOL)respondsToSelector:(SEL)aSelector;          // 是否实现了某方法

    - (IMP)methodForSelector:(SEL)aSelector;             // 方法具体实现的地址
    
    + (void)load;                                    //
    + (void)initialize;                              //
    - (instancetype)init;                            //
    - (void)dealloc;                                 // 销毁时调用


    // 用于调用方法
    - (id)performSelector:(SEL)aSelector;
    - (id)performSelector:(SEL)aSelector withObject:(id)object;
    - (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;


    // 覆写,用于实现真正的单例
    - (id)copy;
    - (id)mutableCopy;
    + (id)copyWithZone:(struct _NSZone *)zone;
    + (id)mutableCopyWithZone:(struct _NSZone *)zone;


    // 用于拦截调用
    // 当调用不存在的类方法时调用(默认:false)可作相应处理后返回true
    + (BOOL)resolveClassMethod:(SEL)sel;
    // 当调用不存在的实例方法时调用(默认:false)可作相应处理后返回true
    + (BOOL)resolveInstanceMethod:(SEL)sel;
    // 转发给拥有该方法的实例
    - (id)forwardingTargetForSelector:(SEL)aSelector;
    // 作相应处理后,调用invokeWithTarget:将invocation传给拥有该方法的实例
    - (void)forwardInvocation:(NSInvocation *)anInvocation;

Objc代码

Runtime运行时机制 会将OC代码转为Runtime函数再执行。

方法

    一个方法名对应一个SEL(不同类相同方法的SEL相同),每个类只存储SEL。
    项目中所有方法的SEL放在一个表中。

   
    SEL x1=@selector(method:);       // 方法选择器将方法名hash成一个唯一标识
    SEL x2=NSSelectorFromString(@"method:");
    SEL x3=sel_registerName("method:");

    // 调用方法
    [self method];
    [self performSelector:@selector(method)];
    [self performSelector:@selector(method) withObject:nil];
    [self performSelector:@selector(method) withObject:nil withObject:nil];
    [self performSelector:@selector(method) withObject:nil afterDelay:2.0];
    [self performSelectorInBackground:@selector(method) withObject:nil];
    [self performSelectorOnMainThread:@selector(method) withObject:nil waitUntilDone:true];
    [self performSelector:@selector(method) withObject:nil afterDelay:2.0 inModes:@[NSRunLoopCommonModes]];
    [self performSelector:@selector(method) onThread:[NSThread new] withObject:nil waitUntilDone:true];
    [self performSelector:@selector(method) onThread:[NSThread new] withObject:nil waitUntilDone:true modes:@[NSRunLoopCommonModes]];
    [self performSelectorOnMainThread:@selector(method) withObject:nil waitUntilDone:true modes:@[NSRunLoopCommonModes]];
    
SEL 
    struct objc_selector *SEL;     一种数据类型
IMP  
    typedef id (*IMP)(id, SEL, ...); (类实例或元类,SEL,参数)  函数地址
Method 
    每个类有一个方法链表
    struct objc_method {
        SEL method_name                                          OBJC2_UNAVAILABLE;
        char *method_types                                       OBJC2_UNAVAILABLE;
        IMP method_imp                                           OBJC2_UNAVAILABLE;
    }

方法执行过程

     // (实例对象,区分方法的id,参数列表)
     id objc_msgSend(id self,SEL op, ...);   
         // 第一个参数id
         typedef struct objc_object *id;
         // 第二个参数SEL
         typedef struct objc_selector *SEL;   


[person run];   
会被编译器转为  
objc_msgSend(person,run);
【方法执行过程】
    1:首先,在对象的缓存方法列表中寻找被调用的方法,如果找到则转向相应方法实现并执行。objc_cache
    2:如果没找到,在对象中的方法列表中寻找被调用的方法,如果找到则转向相应方法实现并执行。 objc_method_list
    根据类id和SEL->IMP
    3:如果没找到,去父类指针所指向的对象中执行1,2。
    4:以此类推,如果一直到根类还没找到则转向拦截调用。
    5:如果没有重写拦截调用的方法,程序报错。



【拦截调用 即消息转发 弥补不能多继承】
    当程序找不到相应方法,崩溃之前会调用.

1 
    // 当调用不存在的类方法时调用(默认:false,调用2(2未实现则崩))可作相应处理后返回true
    + (BOOL)resolveClassMethod:(SEL)sel {
        if (sel == @selector(learnClass:)) {
            class_addMethod(object_getClass(self), sel, class_getMethodImplementation(object_getClass(self), @selector(myClassMethod:)), "v@:");
            return YES;
        }
        return [class_getSuperclass(self) resolveClassMethod:sel];
    }
2


1 
    // 当调用不存在的实例方法时调用(默认:false 去调用2(2未实现则崩))
    // 可作相应处理后返回true
    + (BOOL)resolveInstanceMethod:(SEL)aSEL{
        if (aSEL == @selector(goToSchool:)) {
            class_addMethod([self class], aSEL, class_getMethodImplementation([self class], @selector(myInstanceMethod:)), "v@:");
            return YES;
        }
        return [super resolveInstanceMethod:aSEL];
    }
2 
    // 转发给拥有该方法的实例
    // 返回nil 则调用3(3未实现则崩)
    - (id)forwardingTargetForSelector:(SEL)aSelector{
        if(aSelector == @selector(mysteriousMethod:)){
            return alternateObject;
        }
        return [super forwardingTargetForSelector:aSelector];
    }
3 
    // 作相应处理后,调用invokeWithTarget:将invocation传给拥有该方法的实例
    - (void)forwardInvocation:(NSInvocation *)anInvocation{
        // invocation封装了原始的消息和消息的参数
        if ([someOtherObject respondsToSelector:[anInvocation selector]]){
            [anInvocation invokeWithTarget:someOtherObject];
        }else{
            [super forwardInvocation:anInvocation];
        }
    }
    /* 在forwardInvocation:消息发送前,Runtime系统会向对象发送methodSignatureForSelector:消息,并取到返回的方法签名用于生成NSInvocation对象。所以在重写forwardInvocation:的同时也要重写methodSignatureForSelector:方法,否则会抛异常。
        - (NSMethodSignature*)methodSignatureForSelector:(SEL)selector{
            NSMethodSignature* signature = [super methodSignatureForSelector:selector];
            if (!signature) {
                signature = [surrogate methodSignatureForSelector:selector];
            }
            return signature;
        }
    */

其他

健壮的实例变量 (Non Fragile ivars)
    当一个类被编译时,实例变量的布局(访问类的实例变量的位置)也就形成了。从对象头部开始,实例变量依次根据自己所占空间而产生位移。当超类发生变动,runtime 系统检测到变化后会调整实例变量的位移。
    在健壮的实例变量下,不能使用sizeof(SomeClass),offsetof(SomeClass, SomeIvar)而是用class_getInstanceSize([SomeClass class]),ivar_getOffset(class_getInstanceVariable([SomeClass class], "SomeIvar"))来代替。


类和对象的关系
    为了处理类和对象的关系,runtime 库创建了一种叫做元类 (Meta Class) 的东西,类对象所属类型叫做元类,它用来表述类对象本身所具备的元数据。每个类仅有一个类对象,而每个类对象仅有一个与之相关的元类。
    当发出一个类似 [NSObject alloc] 的消息时,事实上是把这个消息发给了一个类对象 (Class Object) ,该类对象必须是一个元类的实例,而且这个元类同时也是一个根元类 (root meta class,他的根父类) 的实例。
    所有的元类最终都指向根元类为其超类。所有的元类的方法列表都有能够响应消息的类方法。
    所以当 [NSObject alloc] 这条消息发给类对象的时候,objc_msgSend() 会去它的元类里面去查找能够响应消息的方法,如果找到了,然后对这个类对象执行方法调用。


[self class]
    当 self 为实例对象时,[self class] 与 object_getClass(self) 等价,因为前者会调用后者。object_getClass([self class]) 得到元类。
    当 self 为类对象时,[self class] 返回值为自身还是 self。object_getClass(self) 与 object_getClass([self class]) 等价。
    + (Class)class {    return self; }
    - (Class)class {    return object_getClass(self); }
Runtime 运用
  1. 动态添加属性
>>>>> 1.1.获取某类的属性(可用于字典->模型)/方法/成员变量/协议

    // 如果报错,导入 #import <objc/runtime.h>
    unsigned int count;
    // 获取属性列表
    objc_property_t *propertyList = class_copyPropertyList([self class], &count);
    for (unsigned int i=0; i<count; i++) {
        const char *propertyName = property_getName(propertyList[i]);
        NSLog(@"property---->%@", [NSString stringWithUTF8String:propertyName]);
    }
    // 获取方法列表
    Method *methodList = class_copyMethodList([self class], &count);
    for (unsigned int i=0; i<count; i++) {
        Method method = methodList[i];
        NSLog(@"method---->%@", NSStringFromSelector(method_getName(method)));
    }
    
    // 获取成员变量列表
    Ivar *ivarList = class_copyIvarList([self class], &count);
    for (unsigned int i=0; i<count; i++) {
        Ivar myIvar = ivarList[i];
        const char *ivarName = ivar_getName(myIvar);
        NSLog(@"Ivar---->%@", [NSString stringWithUTF8String:ivarName]);
    }
    
    // 获取协议列表
    __unsafe_unretained Protocol **protocolList = class_copyProtocolList([self class], &count);
    for (unsigned int i=0; i<count; i++) {
        Protocol *myProtocal = protocolList[i];
        const char *protocolName = protocol_getName(myProtocal);
        NSLog(@"protocol---->%@", [NSString stringWithUTF8String:protocolName]);
    }



>>>>> 1.2.动态添加属性

    // 定义一个全局变量(用它的地址存储 关联对象的属性)
    static char associatedObjectKey;

    // 添加属性
    objc_setAssociatedObject(target, &associatedObjectKey, @"属性名", OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    /*
        OBJC_ASSOCIATION_ASSIGN
        OBJC_ASSOCIATION_RETAIN_NONATOMIC
        OBJC_ASSOCIATION_COPY_NONATOMIC
        OBJC_ASSOCIATION_RETAIN
        OBJC_ASSOCIATION_COPY
    */

    // 获取属性值
    NSString *string = objc_getAssociatedObject(target, &associatedObjectKey);
    // 删除动态添加的属性
    objc_removeAssociatedObjects (target);


>>>>> 1.3.动态添加category属性

    @interface Person (Student)
    @property(nonatomic,copy) NSString *name;
    @end
    @implementation Person (Student)
    -(void)setName:(NSString*)name{
        // self绑定的name属型
        objc_setAssociatedObject(self,@”name”,name,OBJC_ASSOCIATION_COPY_NONATOMIC);
    }
    -(NSString*)name{
        // 返回和self绑定的name属性
        return objc_getAssociatedObject(self,@“name”);
    }
    @end
创建类
Class MyClass = objc_allocateClassPair([NSObject class], "Person", 0);

添加实例变量
    1.不能向编译后得到的类增加实例变量
      编译后的类已经注册在runtime中,类结构体中的objc_ivar_list实例变量的链表和instance_size实例变量的内存大小已经确定。
    2.能向运行时创建的类中添加实例变量
      但是要在调用objc_allocateClassPair之后,objc_registerClassPair之前
    // (类名,属性名,占字节大小,对齐方式参数,参数类型)
    BOOL isSuccess = class_addIvar(Person, "name", sizeof(NSString *), 0, "@");

    // 向nil发消息(不会蹦)
    返回值是对象,返nil
    返回值为指针类型,返回0。
    返回值为结构体,返回0(结构体中各个字段的值将都是0)。
    返回值以上都不是,返回值未定义。
  1. 动态添加方法
(使用拦截调用)respondsToSelector是不检测这种方法的(除非自定义respondsToSelector)

@dynamic
    @dynamic propertyName;  // 由程序员动态提供存取方法,编译器不再自动生成set/get。
    自行实现set/get方法,
    或者
    不实现set/get而使用拦截调用动态添加方法
 
隐式调用不存在的方法
    [target performSelector:@selector(resolveAdd:) withObject:@"test"];


    // 
    void runAddMethod(id self, SEL _cmd, NSString *string){
        NSLog(@"add C IMP ", string);
    }
    // 拦截
    + (BOOL)resolveInstanceMethod:(SEL)sel{
        if ([NSStringFromSelector(sel) isEqualToString:@"resolveAdd:"]) {

            // 给本类动态添加一个方法  (C方法直接可写,OC则 [Class instanceMethodForSelector:@selector(ss:)])
            // <#__unsafe_unretained Class cls#>  参数1 给哪个类添加方法
            // <#SEL name#>                       参数2 添加哪个方法
            // <#IMP imp#>                        参数3 添加方法函数实现 (函数地址)
            // <#const char *types#>              参数4 函数的类型 (返回值 + 参数类型、参数类型) 
                                                            返回值          v 表示void 
                                                            参数一          @ 表示id类型对象 
                                                            参数二          : 表示SEL方法
            class_addMethod(self, sel, (IMP)runAddMethod, "v@:*");
        }
        return YES;
    }

3.替换方法(Method Swizzling)

可对系统方法做额外的操作。
一般添加到类别的load中,使用到如下函数:
    // 获取该类方法的实现(类,selector方法)
    Method systemMethod = class_getClassMethod(self, @selector(ImageOriginalWithStrName:));
    // 获取该实例方法的实现
    Method myMethod = class_getInstanceMethod(self, @selector(method:));
    // 替换
    method_exchangeImplementations(systemMethod, myMethod);
例1:
/*
 避免数组越界直接崩溃
 */


#import <Foundation/Foundation.h>
@interface NSArray (YTCusArray)
@end


#import "NSArray+YTCusArray.h"
#import "objc/runtime.h"
@implementation NSArray (YTCusArray)
+ (void)load {
    [super load];
    
/*
  对于以下,不能直接修改,应修改右侧
    NSArray               __NSArrayI
    NSMutableArray        __NSArrayM
    NSDictionary          __NSDictionaryI
    NSMutableDictionary   __NSDictionaryM
*/
    // 系统方法
    Method fromMethod = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(objectAtIndex:));
    // 自定义方法
    Method toMethod = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(my_objectAtIndex:));
    // 替换
    method_exchangeImplementations(fromMethod, toMethod);
}
// 自定义方法
-(id)my_objectAtIndex:(NSUInteger)index {
    //
    if (self.count-1 < index) { // 越界
        @try {
            return [self my_objectAtIndex:index];
        }
        @catch (NSException *exception) {
            NSLog(@"---------- %s Crash Because Method %s  ----------\n", class_getName(self.class), __func__);
            NSLog(@"%@", [exception callStackSymbols]);
            return nil;
        }
        @finally {
        }
    } else {
        return [self my_objectAtIndex:index];
    }
}
@end
例2:
    // load方法会在类第一次加载的时候被调用
    + (void)load{
        // 方法交换应该只执行一次
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            
            // 系统方法
            SEL systemSel = @selector(viewWillAppear:);
            // 自定义方法
            SEL mySel = @selector(myViewWillAppear:);
            // 两个方法的Method实现(此处self为元类)
            Method systemMethod = class_getInstanceMethod([self class], systemSel);
            Method myMethod = class_getInstanceMethod([self class], mySel);
            
            // 动态添加自定义方法
            BOOL isAdd = class_addMethod(self, systemSel, method_getImplementation(mySel), method_getTypeEncoding(mySel));
            if (isAdd) {
                // 添加成功,表示类中不存在自定义方法的实现

                // 将系统方法的实现放在自定义方法的实现(调用自定义的方法时,调用系统方法)
                class_replaceMethod(self, mySel, method_getImplementation(systemMethod), method_getTypeEncoding(systemMethod));
                // 本质直接调用class_addMethod向类中添加该自定义方法的实现
            }else{
                // 添加失败,表示类中有该自定义方法

                // 调换实现(调用自定义方法时,调的是系统;调系统方法时,调的是自定义)                
                method_exchangeImplementations(systemMethod, myMethod);
                /* 本质是:
                    IMP imp1 = method_getImplementation(m1);
                    IMP imp2 = method_getImplementation(m2);
                    method_setImplementation(m1, imp2);
                    method_setImplementation(m2, imp1);
                */
            }
        });
    }

4.字典转模型

#import "NSObject+Model.h"
#import <objc/runtime.h>

@implementation NSObject (Model)


+ (instancetype)modelWithDictionary:(NSDictionary *)dictionary{
    
    
    // 思路:遍历模型中所有属性-》使用运行时
    
    // 0.创建对应的对象
    id objc = [[self alloc] init];
    
    // 1.利用runtime给对象中的成员属性赋值
    
    // class_copyIvarList:获取类中的所有成员属性
    // Ivar:成员属性的意思
    // 第一个参数:表示获取哪个类中的成员属性
    // 第二个参数:表示这个类有多少成员属性,传入一个Int变量地址,会自动给这个变量赋值
    // 返回值Ivar *:指的是一个ivar数组,会把所有成员属性放在一个数组中,通过返回的数组就能全部获取到。
    /* 类似下面这种写法
     
     Ivar ivar;
     Ivar ivar1;
     Ivar ivar2;
     // 定义一个ivar的数组a
     Ivar a[] = {ivar,ivar1,ivar2};
     
     // 用一个Ivar *指针指向数组第一个元素
     Ivar *ivarList = a;
     
     // 根据指针访问数组第一个元素
     ivarList[0];
     
     */
    unsigned int count;
    
    // 获取类中的所有成员属性
    Ivar *ivarList = class_copyIvarList(self, &count);
    
    for (int i = 0; i < count; i++) {
        // 根据角标,从数组取出对应的成员属性
        Ivar ivar = ivarList[i];
        
        // 获取成员属性名
        NSString *name = [NSString stringWithUTF8String:ivar_getName(ivar)];
        
        // 处理成员属性名->字典中的key
        // 从第一个角标开始截取
        NSString *key = [name substringFromIndex:1];
        
        // 根据成员属性名去字典中查找对应的value
        id value = dictionary[key];
        
        // 二级转换:如果字典中还有字典,也需要把对应的字典转换成模型
        // 判断下value是否是字典
        if ([value isKindOfClass:[NSDictionary class]]) {
            // 字典转模型
            // 获取模型的类对象,调用modelWithDict
            // 模型的类名已知,就是成员属性的类型
            
            // 获取成员属性类型
            NSString *type = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
            // 生成的是这种@"@\"User\"" 类型 -》 @"User"  在OC字符串中 \" -> ",\是转义的意思,不占用字符
            // 裁剪类型字符串
            NSRange range = [type rangeOfString:@"\""];
            
            type = [type substringFromIndex:range.location + range.length];
            
            range = [type rangeOfString:@"\""];
            
            // 裁剪到哪个角标,不包括当前角标
            type = [type substringToIndex:range.location];
            
            
            // 根据字符串类名生成类对象
            Class modelClass = NSClassFromString(type);
            
            
            if (modelClass) { // 有对应的模型才需要转
                
                // 把字典转模型
                value  =  [modelClass modelWithDictionary:value];
            }
            
            
        }
        
        // 三级转换:NSArray中也是字典,把数组中的字典转换成模型.
        // 判断值是否是数组
        if ([value isKindOfClass:[NSArray class]]) {
            // 判断对应类有没有实现字典数组转模型数组的协议
            if ([self respondsToSelector:@selector(arrayContainModelClass)]) {
                
                // 转换成id类型,就能调用任何对象的方法
                id idSelf = self;
                
                // 获取数组中字典对应的模型
                NSString *type =  [idSelf arrayContainModelClass][key];
                
                // 生成模型
                Class classModel = NSClassFromString(type);
                NSMutableArray *arrM = [NSMutableArray array];
                // 遍历字典数组,生成模型数组
                for (NSDictionary *dict in value) {
                    // 字典转模型
                    id model =  [classModel modelWithDictionary:dict];
                    [arrM addObject:model];
                }
                
                // 把模型数组赋值给value
                value = arrM;
                
            }
        }  
        if (value) { // 有值,才需要给模型的属性赋值
            // 利用KVC给模型中的属性赋值
            [objc setValue:value forKey:key];
        }
        
    }
    
    return objc;
}
@end


5.快速归档

#import <Foundation/Foundation.h>
@interface NSObject (Extension)
- (NSArray *)ignoredNames;
- (void)encode:(NSCoder *)aCoder;
- (void)decode:(NSCoder *)aDecoder;
@end


#import "NSObject+Extension.h"
#import <objc/runtime.h>
@implementation NSObject (Extension)
// 拿到所有属性名,然后解档
- (void)decode:(NSCoder *)aDecoder {
    // 一层层父类往上查找,对父类的属性执行归解档方法
    Class c = self.class;
    while (c &&c != [NSObject class]) {

        unsigned int outCount = 0;    // 存储属性个数
        Ivar *ivars = class_copyIvarList(c, &outCount);  // 获取属性列表
        for (int i = 0; i < outCount; i++) {
            Ivar ivar = ivars[i];    // 获取每一个属性
            NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];  // 获取属性名
            if ([self respondsToSelector:@selector(ignoredNames)]) {  // 是否是忽略属性(不用归档)
                if ([[self ignoredNames] containsObject:key]) continue;
            }
            
            id value = [aDecoder decodeObjectForKey:key];    
            [self setValue:value forKey:key];
        }
        free(ivars);
        c = [c superclass];
    }
}

// 拿到所有属性名,然后归档
- (void)encode:(NSCoder *)aCoder {
    // 一层层父类往上查找,对父类的属性执行归解档方法
    Class c = self.class;
    while (c &&c != [NSObject class]) {
        
        unsigned int outCount = 0;
        Ivar *ivars = class_copyIvarList([self class], &outCount);
        for (int i = 0; i < outCount; i++) {
            Ivar ivar = ivars[i];
            NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
            if ([self respondsToSelector:@selector(ignoredNames)]) {
                if ([[self ignoredNames] containsObject:key]) continue;
            }
            
            id value = [self valueForKeyPath:key];
            [aCoder encodeObject:value forKey:key];
        }
        free(ivars);
        c = [c superclass];
    }
}
// 设置需要忽略的属性(不需要被归档的)
- (NSArray *)ignoredNames {
    return @[@"bone"];
}
【使用快速归档】
// 在需要实现归档的类中+
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    if (self = [super init]) {
        [self decode:aDecoder];    // 调用自定义的接档方法
    }
    return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
    [self encode:aCoder];     // 调用自定义的归档方法
}

6.快捷创建手势

#import <UIKit/UIKit.h>
typedef void(^XXWGestureBlock)(id gestureRecognizer);
@interface UIGestureRecognizer (Block)
/**
 *  使用类方法 初始化 添加手势
 *
 *  @param block 手势回调
 *
 *  @return block 内部 action
 *
 *
 *  使用 __unsafe_unretained __typeof(self) weakSelf = self;
 *  防止循环引用
 *
 */
+ (instancetype)xxw_gestureRecognizerWithActionBlock:(XXWGestureBlock)block;
@end



#import "UIGestureRecognizer+Block.h"
#import <objc/runtime.h>
static const int target_key;
@implementation UIGestureRecognizer (Block)
+ (instancetype)xxw_gestureRecognizerWithActionBlock:(XXWGestureBlock)block {
    return [[self alloc]initWithActionBlock:block];
}

- (instancetype)initWithActionBlock:(XXWGestureBlock)block {
    self = [self init];
    [self addActionBlock:block];
    [self addTarget:self action:@selector(invoke:)];
    return self;
}

/**
 * Returns the value associated with a given object for a given key.
 *
 * @param object The source object for the association.
 * @param key The key for the association.
 *
 * @return The value associated with the key \e key for \e object.
 *
 * @see objc_setAssociatedObject
 */

//OBJC_EXPORT id objc_getAssociatedObject(id object, const void *key)
//__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);

- (void)addActionBlock:(XXWGestureBlock)block {
    if (block) {
        objc_setAssociatedObject(self, &target_key, block, OBJC_ASSOCIATION_COPY_NONATOMIC);
    }
}

- (void)invoke:(id)sender {
    XXWGestureBlock block = objc_getAssociatedObject(self, &target_key);
    if (block) {
        block(sender);
    }
}
@end

相关文章

网友评论

    本文标题:OC Runtime运行时机制---OC语法进阶摘录

    本文链接:https://www.haomeiwen.com/subject/cvuvsxtx.html