目录
简介
数据结构
与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.获取某类的属性(可用于字典->模型)/方法/成员变量/协议
// 如果报错,导入 #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)。
返回值以上都不是,返回值未定义。
- 动态添加方法
(使用拦截调用)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
网友评论