Runtime运行时之iOS笔记摘录

目录

    1. 简介
    2. 结构体+函数
    3. 与RunTime交互的3方式
    4. Runtime 运用

前言

OC的核心是运行时,运行时的核心是objc_msgSend函数。
运行时库:由一系列 结构体、函数 组成的动态共享库(基于C语言和汇编语言编写的API)。

源代码 >>> 预处理器 > 编译器 > 汇编器 > 链接器 >>> 可执行文件

预处理阶段: 
    处理 头文件包含、预编译指令、宏定义

编译阶段:
    编译器对代码进行词法分析和语法分析,出错则停止,成功则将代码翻译成汇编语言。

汇编阶段:
    将汇编语言转换为机器语言。

链接阶段:
    链接C语言运行库及各个文件为可执行文件。
    Other linker flags就是ld链接命令的参数:
        -ObjC:链接器会加载静态库中所有的Objective-C类和分类。
        -all_load:链接器把所有找到的目标文件都加载到可执行文件中。不能随便使用,不同的库文件有相同的目标文件会报ld: duplicate symbol错误。
        -force_load:和-all_load一样,但需要指定要进行全部加载的库文件的路径。
编译器
  词法分析器
  语法分析器
  语义分析及中间代码生成

1. 简介

使用时导入头文件(存放于/usr/include/objc下 )
    #import  
    #import 
    
可用于
    1. 全局 避免数组字典崩溃(越界)
    避免出现未识别方法崩溃(unrecognized selector)
    字典转模型
    快速归档
    动态添加方法(拦截调用)
    给分类添加动态属性
    等

常见的 数组、字典崩溃 情况

    NSString *str=nil;
    NSArray *arr=@[@"s"];
    // NSMutableArray(注意muArr[1]调用的不是objectAtIndex方法)
    NSMutableArray *muArr=[NSMutableArray arrayWithArray:@[@""]];
    NSMutableDictionary *muDic=[NSMutableDictionary dictionaryWithCapacity:10];

数组崩溃情况
// >>>>数组越界
    arr[1];
    muArr[1];
// >>>>数组越界
    [arr objectAtIndex:1];
    [muArr objectAtIndex:1];
// >>>>元素为nil
    NSArray *arrTwo=@[str];
    NSMutableArray *muArrTwo=[NSMutableArray arrayWithArray:@[str]];
    [muArr addObject:str];  

字典崩溃情况
// >>>>键、值为nil
    NSDictionary *dic=@{str:@"s"};
    NSDictionary *dicTwo=@{@"s":str};
    [muDic setObject:nil forKey:@"s"];
    [muDic setObject:@"s" forKey:nil];

解决:建立相应分类(使用运行时机制的交换方法) 见下方 5.3 例1

2. 结构体+函数

  1. 结构体
结构体类型 定义( 中)
typedef struct objc_class *Class;
分类 typedef struct objc_category *Category;
变量 typedef struct objc_ivar *Ivar;
属性 typedef struct objc_property *objc_property_t;
方法 typedef struct objc_method *Method;

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_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();
                ... 此处省略其他方法声明
            }

分类

            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_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               
                unsigned int occupied                                    
                Method buckets[1]                                        
            };
            _buckets 存储 IMP,_mask 和 _occupied 对应 vtable
            typedef struct objc_method *Method;   // 见上
            objc_msgSend 每调用一次方法后,就会把该方法缓存到cache列表中

协议

objc_protocol_list 协议列表

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

#import

获取 类、父类
    Class cls=objc_getClass(self);      
    Class cls=objc_getClass("DogModel"); 
    Class cls=class_getSuperclass(self);
获取 类名
    const char *className=class_getName(cls);
获取/设置 类version
    int version=class_getVersion(cls);
    class_setVersion(cls, 1);
获取 类size
    size_t size=class_getInstanceSize(cls)
是否是元类
    BOOL isMetaClass=class_isMetaClass(cls);

创建类
    Class MyClass = objc_allocateClassPair([NSObject class], "Person", 0);
注册类
    objc_registerClassPair(MyClass);
释放类
    objc_disposeClassPair(MyClass);


    =》[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 库创建了一种叫做元类 (Meta Class) 的东西,对象的isa所指向是类对象,类对象(每个类仅有一个类对象,所属类型叫做元类,它用来表述类对象本身所具备的元数据)的isa指向元类,元类的isa指向根元类,根元类的isa指向自己。
    当 [NSObject alloc] 这条消息发给类对象的时候,objc_msgSend() 会去它的类对象里面去查找能够响应消息的方法,如果找到了,然后对这个类对象执行方法调用。如果没有找到,则从该类的父类的类对象找,直到根元类的类对象。
获取 属性列表
    unsigned int outCount;
    objc_property_t *properties=class_copyPropertyList(cls, &outCount); 
获取 某类的属性
    objc_property_t property=class_getProperty(Class cls, const char *name);
获取 属性名
    char *name=property_getName(property);
获取 属性的属性
    char *attributes=property_getAttributes(property);
    objc_property_attribute_t * attribute=property_copyAttributeList(property,&outCount);
    attribute.name、attribute.value
    char *att=property_copyAttributeValue(property,"name");
添加属性
    objc_property_attribute_t attributes[]=[@{"T":"NSString"}]; 
    BOOL isSuccess=class_addProperty(cls, "name",
                  attributes,
                  1);


获取 所有成员变量(可用于快速归档)
    unsigned int outCount;
    Ivar *ivars = class_copyIvarList(dogClass, &outCount); 
获取 变量名、变量类型、变量基地址偏移量
    Ivar ivar=ivars[0];         
    const char *ivarName=ivar_getName(ivar);
    const char *typeEncoding=ivar_getTypeEncoding(ivar);
    ptrdiff_t offset=ivar_getOffset(ivar);
添加实例变量
    1.不能向编译后得到的类增加实例变量
      编译后的类已经注册在runtime中,类结构体中的objc_ivar_list实例变量的链表和instance_size实例变量的内存大小已经确定。只能在 objc_allocateClassPair 新建的类上添加。
    2.能向运行时创建的类中添加实例变量
      但是必须在调用objc_allocateClassPair之后,objc_registerClassPair之前
    // (类名,属性名,占字节大小,对齐方式参数,参数类型)
    BOOL isSuccess = class_addIvar(Person, "name", sizeof(NSString *), 0, "@");
获取 协议
    Protocol *protocol=objc_getProtocol("dogProtocol");       
获取 协议名  
    const char *protocolName=protocol_getName(protocol);  
获取 遵守的协议列表
    Protocol * __unsafe_unretained _Nonnull *protocolList=protocol_copyProtocolList([Person class],&outCount);
获取 协议属性列表       
    unsigned int outCount;  
    objc_property_t *properties=protocol_copyPropertyList(protocol, &outCount);    
获取 协议的指定属性
    objc_property_t property=protocol_getProperty(protocol, const char *name, BOOL isRequiredProperty, BOOL isInstanceProperty);
    const char *propertyName=property_getName(property);
是否相等
    BOOL protocolIsEqual=protocol_isEqual(protocol1, protocol2);
是否遵守另一个协议
    BOOL isCon=protocol_conformsToProtocol(protocol1, protocol2);

添加协议
    BOOL isSuccess=class_addProtocol(Class _Nullable cls, Protocol * _Nonnull protocol);
获取 所有方法
    unsigned int outCount;
    Method *methods=class_copyMethodList(dogClass,  &outCount); 

    Method method=methods[0];   
方法索引
    SEL sel=method_getName(method);
方法实现
    IMP imp=method_getImplementation(method); 
    IMP imp=method_setImplementation(method, imp);
方法名
    const char * methodName=sel_getName(sel);
方法参数返回类型
    const char * typeE=method_getTypeEncoding(method); 
交换方法实现
    method_exchangeImplementations(method1, method2);
sel是否相同
    BOOL isEqual=sel_isEqual(sel1, sel2);
注册sel
    SEL rSel=sel_registerName("run");
    
获取 指定类方法        
    Method method=class_getClassMethod(dogClass, @selector(countOfLegs));
获取 指定实例方法   
    Method method=class_getInstanceMethod(dogClass, @selector(run));
获取 指定方法的实现 (类方法或实例方法)
    IMP imp=class_getMethodImplementation(object_getClass(self), @selector(myClassMethod:));    

交换 两个方法的实现   (给系统功能添加功能)
    method_exchangeImplementations(method, method2);
添加 方法
    BOOL isSuccess=class_addMethod(object_getClass(self), sel, imp,"v@:*"); 
    // 最后一个参数可由以下获取
    char *methodType=method_getTypeEncoding(replaceMethod);
替换 SEL和实现方法
    IMP imp=class_replaceMethod(cls,replaceSel,method_getImplementation(originMethod),method_getTypeEncoding(originMethod));


block->imp
    IMP runImp=imp_implementationWithBlock(^{
        NSLog(@"run run....");
    });
imp->block
    void (^runBlock)(id self,SEL _cmd)=imp_getBlock(runImp);
删除
    BOOL isSuccess=imp_removeBlock(IMP _Nonnull anImp);

动态获取/设置/移除属性

static NSString *name;

设置
objc_setAssociatedObject(self,&name,nameStr,OBJC_ASSOCIATION_COPY_NONATOMIC);
/*
OBJC_ASSOCIATION_ASSIGN
OBJC_ASSOCIATION_RETAIN_NONATOMIC
OBJC_ASSOCIATION_COPY_NONATOMIC
OBJC_ASSOCIATION_RETAIN
OBJC_ASSOCIATION_COPY
*/

获取
NSString *name=objc_getAssociatedObject(self, &name);

移除
objc_removeAssociatedObjects(self)

    // 获取类指针(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);

例2

    Class personClass=objc_allocateClassPair([NSObject class], "PersonM", 0);
    BOOL isSuccess = class_addIvar(personClass, "name", sizeof(NSString *), log2(sizeof(NSString *)), @encode(NSString *));
    if(isSuccess){
        NSLog(@"");
    }
    SEL runSel=sel_registerName("run");
    IMP runImp=imp_implementationWithBlock(^{
        NSLog(@"run run....");
    });
    class_addMethod(personClass, runSel, runImp, "v@");
    objc_registerClassPair(personClass);
    id personM=[personClass new];
    [personM setValue:@"hi" forKey:@"name"];
    //
    void (^runBlock)(id self,SEL _cmd)=imp_getBlock(runImp);
    runBlock(personM,runSel);
    //
    [personM performSelector:@selector(run)];
    personM=nil;
    objc_disposeClassPair(personClass);

3. 与RunTime交互的3方式

方式1、runtime函数

见2.2

方式2、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;

方式3、Objc代码

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

4. Runtime 运用

  1. 动态添加属性
>>>>> 1.1. 动态添加category属性

@interface Person(Name)
@property (nonatomic,copy) NSString *name;
@end
@implementation Person(Name)
static NSString *name;
- (void)setName:(NSString *)nameStr{
    objc_setAssociatedObject(self,&name,nameStr,OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *) name{
    return objc_getAssociatedObject(self, &name);
}
@end
>>>>> 1.2.获取某类的属性、方法、成员变量、协议

    // 如果报错,导入 #import 
    unsigned int count;
    // 获取属性列表(可用于字典->模型)
    objc_property_t *propertyList = class_copyPropertyList([self class], &count);
    for (unsigned int i=0; i%@", [NSString stringWithUTF8String:propertyName]);
    }
    // 获取方法列表
    Method *methodList = class_copyMethodList([self class], &count);
    for (unsigned int i=0; i%@", NSStringFromSelector(method_getName(method)));
    }
    
    // 获取成员变量列表
    Ivar *ivarList = class_copyIvarList([self class], &count);
    for (unsigned int i=0; i%@", [NSString stringWithUTF8String:ivarName]);
    }
    
    // 获取协议列表
    __unsafe_unretained Protocol **protocolList = class_copyProtocolList([self class], &count);
    for (unsigned int i=0; i%@", [NSString stringWithUTF8String:protocolName]);
    }
>>>>> 1.3.动态添加属性

    // 定义一个全局变量(用它的地址存储 关联对象的属性)
    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. 动态添加方法
(使用拦截调用)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 
@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 {
        @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 {
        }
}
@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 

@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 
@interface NSObject (Extension)
- (NSArray *)ignoredNames;
- (void)encode:(NSCoder *)aCoder;
- (void)decode:(NSCoder *)aDecoder;
@end


#import "NSObject+Extension.h"
#import 
@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 
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 
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

其他

/**
 属性 是否是类类型
 */
+(BOOL)isObjWithPropertyType:(NSString *)property {
    objc_property_t p = class_getProperty(self, property.UTF8String);
    const char *attrs = property_getAttributes(p);
    if (attrs[0] == 'T' && attrs[1] != '@') {
        return false;
    }
    return true;
}

其他

调用方法

    [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]];
健壮的实例变量 (Non Fragile ivars)
    当一个类被编译时,实例变量的空间位置也就形成了。从对象头部开始,实例变量依次根据自己所占空间而产生位移。当超类发生变动,runtime 系统检测到变化后会调整实例变量的位移。
    不能使用sizeof(SomeClass),offsetof(SomeClass, SomeIvar)而是用class_getInstanceSize([SomeClass class]),ivar_getOffset(class_getInstanceVariable([SomeClass class], "SomeIvar"))来代替。
#import
        需要在  buildSettings |  Enable Strict Checking of objc_msgSend Calls 置为 NO
        编译器会根据情况在objc_msgSend, objc_msgSend_stret, objc_msgSendSuper, 或 objc_msgSendSuper_stret中选择一个来调用

你可能感兴趣的:(Runtime运行时之iOS笔记摘录)