目录
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. 结构体+函数
- 结构体
结构体类型 | 定义( |
---|---|
类 | 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; // 协议
- 函数
#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. 动态添加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);
- 动态添加方法
(使用拦截调用)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中选择一个来调用