via http://www.lijianfei.cn/2016/07/13/learnYYModelSomeGain/?utm_source=tuicool&utm_medium=referral
YYModel 作者性能优化的几个 Tip:
缓存
Model JSON 转换过程中需要很多类的元数据,如果数据足够小,则全部缓存到内存中。
查表
当遇到多项选择的条件时,要尽量使用查表法实现,比如 switch/case,C Array,如果查表条件是对象,则可以用 NSDictionary 来实现。
避免 KVC
Key-Value Coding 使用起来非常方便,但性能上要差于直接调用 Getter/Setter,所以如果能避免 KVC 而用 Getter/Setter 代替,性能会有较大提升。
避免 Getter/Setter 调用
如果能直接访问 ivar,则尽量使用 ivar 而不要使用 Getter/Setter 这样也能节省一部分开销。
避免多余的内存管理方法
在 ARC 条件下,默认声明的对象是 strong 类型的,赋值时有可能会产生 retain/release 调用,如果一个变量在其生命周期内不会被释放,则使用 unsafe_unretained 会节省很大的开销。
访问具有 weak 属性的变量时,实际上会调用 objc_loadWeak() 和 objc_storeWeak() 来完成,这也会带来很大的开销,所以要避免使用 weak 属性。
创建和使用对象时,要尽量避免对象进入 autoreleasepool,以避免额外的资源开销。
遍历容器类时,选择更高效的方法
相对于 Foundation 的方法来说,CoreFoundation 的方法有更高的性能,用 CFArrayApplyFunction() 和 CFDictionaryApplyFunction() 方法来遍历容器类能带来不少性能提升,但代码写起来会非常麻烦。
尽量用纯 C 函数、内联函数
使用纯 C 函数可以避免 ObjC 的消息发送带来的开销。如果 C 函数比较小,使用 inline 可以避免一部分压栈弹栈等函数调用的开销。
减少遍历的循环次数
在 JSON 和 Model 转换前,Model 的属性个数和 JSON 的属性个数都是已知的,这时选择数量较少的那一方进行遍历,会节省很多时间。
Runtime MetaClass
Objective-c 中的 每一个类 如: Person类 其实包含两部分
普通类(类本身)如 person 类本身(我们创建的类)
元类(系统创建)Meta Person类,由系统默认创建
这个创建Meta Person类的过程,我们不知道而已
关于meta class的系统方法
判断一个objc_class实例是否是Meta类的Class
BOOL class_isMetaClass(Class cls)
获取一个NSObject类对应的Meta类的Class
Class objc_getMetaClass(const char *name)
类 与 Meta类
类,存放对象的相关数据实例成员变量
实例方法
Meta类,存放类的先关数据类的成员变量(static 类成员变量)
类方法(+开头的方法)
区分 对象方法 与 类方法 调用过程向一个对象发送消息通过对象的 isa指针找到 对象所属类对应的的 objc_class结构体实例
然后开始查找objc_class实例的 method_list 查找 Method
向一个类发送消息通过类Class的 isa指针找到 Meta类对应的 objc_class结构体实例
然后从objc_class实例的 method_list 查找 Method
import "RuntimeViewController.h"
@interface RuntimeViewController ()
@end
void ReportFunction(id self, SEL _cmd)
{
//1. 对象
NSLog(@"This object is %p.", self);
//2. 对象所属的类
NSLog(@"Class is %@", [self class]);
//3. 所属类的父类
NSLog(@"super is %@.",[self superclass]);
//4. 每一个类都有两部分
//类的第一部分、类本身
//类的第二部分、元类
Class currentClass = [self class];
for (int i = 1; i < 10; i++)//i的次数随便改都可以
{
NSLog(@"Following the isa pointer times = %d, ClassValue = %@, ClassAddress = %p", i, currentClass, currentClass);
//通过Class的 isa指针 找到 MetaClass
currentClass = object_getClass(currentClass);
}
//5. NSObject类本身
NSLog(@"NSObject's class is %p", [NSObject class]);
//6. NSObject类的元类
NSLog(@"NSObject's meta class is %p", object_getClass([NSObject class]));
}
@implementation RuntimeViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self setOSProps];
//运行时创建类
[self createClass];
[self imp_implementationWithBlock];
}
(void)setOSProps
{
self.view.backgroundColor = [UIColor whiteColor];
self.navigationItem.title = @"Runtime Meta Learn";
self.navigationController.navigationBar.translucent = YES;
}(void)createClass
{
//1. 创建一个Class
Class MyClass = objc_allocateClassPair([NSObject class],
"myclass",
0);
//2. 添加一个NSString的变量,第四个参数是对其方式,第五个参数是参数类型
if (class_addIvar(MyClass, "itest", sizeof(NSString *), 0, "@")) {
NSLog(@"add ivar success");
}
//3. 添加一个函数
class_addMethod(MyClass,
@selector(report),
(IMP)ReportFunction,
"v@:");
//4. 注册这个类到runtime系统中就可以使用他了
objc_registerClassPair(MyClass);
//5. 测试创建的类
[self test:MyClass];
}
(void)report {
//什么都不做,只是为了OC对象能够调用到c函数
}(void)test:(Class)class {
//1.
id obj = [[class alloc] init];
//2.
[obj report];
}
控制台输出如下
2016-07-12 11:37:23.281 LJFSourceCodeLearn[1353:75617] add ivar success
2016-07-12 11:37:23.281 LJFSourceCodeLearn[1353:75617] This object is 0x7ff42bc291d0.
2016-07-12 11:37:23.281 LJFSourceCodeLearn[1353:75617] Class is myclass
2016-07-12 11:37:23.282 LJFSourceCodeLearn[1353:75617] super is NSObject.
2016-07-12 11:37:23.282 LJFSourceCodeLearn[1353:75617] Following the isa pointer times = 1, ClassValue = myclass, ClassAddress = 0x7ff42bf0f180
2016-07-12 11:37:23.282 LJFSourceCodeLearn[1353:75617] Following the isa pointer times = 2, ClassValue = myclass, ClassAddress = 0x7ff42bf36550
2016-07-12 11:37:23.282 LJFSourceCodeLearn[1353:75617] Following the isa pointer times = 3, ClassValue = NSObject, ClassAddress = 0x110ecb198
2016-07-12 11:37:23.282 LJFSourceCodeLearn[1353:75617] Following the isa pointer times = 4, ClassValue = NSObject, ClassAddress = 0x110ecb198
2016-07-12 11:37:23.282 LJFSourceCodeLearn[1353:75617] Following the isa pointer times = 5, ClassValue = NSObject, ClassAddress = 0x110ecb198
2016-07-12 11:37:23.282 LJFSourceCodeLearn[1353:75617] Following the isa pointer times = 6, ClassValue = NSObject, ClassAddress = 0x110ecb198
2016-07-12 11:37:23.304 LJFSourceCodeLearn[1353:75617] Following the isa pointer times = 7, ClassValue = NSObject, ClassAddress = 0x110ecb198
2016-07-12 11:37:23.304 LJFSourceCodeLearn[1353:75617] Following the isa pointer times = 8, ClassValue = NSObject, ClassAddress = 0x110ecb198
2016-07-12 11:37:23.304 LJFSourceCodeLearn[1353:75617] Following the isa pointer times = 9, ClassValue = NSObject, ClassAddress = 0x110ecb198
2016-07-12 11:37:23.304 LJFSourceCodeLearn[1353:75617] NSObject's class is 0x110ecb170
2016-07-12 11:37:23.304 LJFSourceCodeLearn[1353:75617] NSObject's meta class is 0x110ecb198
方法object_getClass(id obj)会根据 Class的私有成员变量isa指针 找到一个类,有两种情况:对象的isa >>> 所属类
类的isa >>> Meta 类
Meta类的isa >>> Meta NSObject
Meta NSObject的isa >>> 永远指向自己,形成环路
times = 2时、根据myclass->isa指针,找到其对应的 Meta myclass
times = 3时、根据 Meta myclass->isa 指针,找到了 Meta NSObject
times = 4,5,6,7,8…、根据 元类NSObject 的 isa指针,最后都是指向自己
使用imp_implementationWithBlock()替代上面例子需要一个辅助的static c函数来完成运行时创建Class.
- (void)imp_implementationWithBlock
{
//////////////////////////////////////////////////////
///创建一个类
//////////////////////////////////////////////////////
Class People = objc_allocateClassPair([NSObject class], "People", 0);
//////////////////////////////////////////////////////
///添加两个变量
//////////////////////////////////////////////////////
if (class_addIvar(People, "name", sizeof(NSString *), 0, @encode(NSString *))) {
NSLog(@"add ivar name success");
}
if (class_addIvar(People, "age", sizeof(int), 0, @encode(int))) {
NSLog(@"add ivar age success");
}
//////////////////////////////////////////////////////
///创建方法的SEL
//////////////////////////////////////////////////////
SEL selector = sel_registerName("talk:");
//////////////////////////////////////////////////////
///创建方法的IMP指针,并指向Block给出的代码
//////////////////////////////////////////////////////
IMP impl = imp_implementationWithBlock(^(id self, NSString *arg1){
//age变量值
//通过KVC
//int age = (int)[[self valueForKey:@"age"] integerValue];
//通过Ivar
Ivar ageIvar = class_getInstanceVariable([self class], "age");
int age = (int)[object_getIvar(self, ageIvar) integerValue];
//name变量值
//通过KVC
//NSString *name = [self valueForKey:@"name"];
//通过Ivar
Ivar nameIvar = class_getInstanceVariable([self class], "name");
NSString *name = object_getIvar(self, nameIvar);
NSLog(@"age = %d, name = %@, msgSay = %@", age, name, arg1);
});
//////////////////////////////////////////////////////
///添加一个方法, 将SEL与IMP组装成一个Method结构体实例,添加到Class中的 method_list数组
//////////////////////////////////////////////////////
class_addMethod(People, selector, impl, "v@:@");
//////////////////////////////////////////////////////
///注册这个类到系统
//////////////////////////////////////////////////////
objc_registerClassPair(People);
//////////////////////////////////////////////////////
///生成一个实例
//////////////////////////////////////////////////////
id instanceP = [[People alloc] init];
//////////////////////////////////////////////////////
///给Ivar赋值
//////////////////////////////////////////////////////
//通过KVC赋值
[instanceP setValue:@"变量字符串值" forKey:@"name"];
//[instanceP setValue:@19 forKey:@"age"];
//通知Ivar赋值
Ivar ageIvar = class_getInstanceVariable(People, "age");
object_setIvar(instanceP, ageIvar, @19);
//////////////////////////////////////////////////////
///发送消息
//////////////////////////////////////////////////////
((void (*)(id, SEL, NSString *))(void *) objc_msgSend)(instanceP, selector, @"参数值");
//////////////////////////////////////////////////////
///释放对象、销毁类
//////////////////////////////////////////////////////
instanceP = nil;
objc_disposeClassPair(People);
}
方法的编码格式:v@: >>> 返回值void,参数1:self,参数2:SEL >>> - (void)funcName;
v@:@ >>> 返回值void,参数1:self,参数2:SEL, 参数3:NSString* >>> - (void)funcName:(NSSring *)name;
对应的Objective-C方法的SEL也应该是 >>> talk:带一个参数
imp_implementationWithBlock()接收一个添加方法被调用时的回调Block其格式为: method_return_type ^(id self, method_args …)
Class objc_allocateClassPair(Class superclass,
const char *name,
size_t extraBytes)
函数名 objc_allocateClassPair.. 中的 ClassPair意思是说一对Class.
但是该函数只返回了一个Class
没有被返回的另一个Class就是 MetaClass 元类,由系统创建.
Meta Class与普通类Class 都是结构体 objc_class实例
Objective-c中的 对象 与 类
类
经常写获取一个对象的类型Class的代码
Class cls = [Person class];
就用到了Class这个数据结构,其定义为如下,可以得到Class是 结构体objc_class实例的 指针类型
typedef struct objc_class *Class;
那么能够成为一个类(Class)的数据结构为
struct objc_class {
/*
又指向一个结构体objc_class实例
类的isa指针,指向类的 元类 MetaClass
*/
Class isa OBJC_ISA_AVAILABILITY;
if !OBJC2
/**
指向其父类(普通类)
*/
Class super_class OBJC2_UNAVAILABLE;
const char *name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;
/**
方法列表
*/
struct objc_method_list **methodLists OBJC2_UNAVAILABLE;
/**
方法缓存
*/
struct objc_cache *cache OBJC2_UNAVAILABLE;
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;
endif
} OBJC2_UNAVAILABLE;
/* Use Class
instead of struct objc_class *
*/
而Class数据结构中最重要的一个数据项是 Class isa; 这样的isa指针.
对象
能够成为一个对象(Object)的数据结构如下,可以看到对象数据结构中最重要的就是Class isa;指针项
struct objc_object {
/**
对象的isa指针,指向其 所属类本身(不是元类)
*/
Class isa OBJC_ISA_AVAILABILITY;
};
Class是struct objc_class的指针类型,那么struct objc_object也有其对于的指针类型
调用类方法与对象方法给对象发送消息时,消息是在对象所属类的方法列表method_list或cache中查询Method
给类发消息时,消息是在这个类的元类 meta class的方法列表或缓存中查询Method
所以也就是说对象方法与类方法存放地点的不同
对象方法、存放在 类本身
类方法、存放在 类的元类MetaClass
所以,元类是必不可少的,它存储了类的所有类方法
对象、类、元类三者之间的关系图
对象的 isa指针 指向 所属类
每一个类,都有一个 isa指针 指向一个 唯一的 MetaClass
每一个Meta Class,拥有的 isa指针 都指向 顶层 NSObject Meta Class
最上层的Meta Class(Meta NSObject)的isa指针指向自己,形成一个 回路
类本身的 super_class 指向其父类,如果该类为根类则值为 nil
每一个 MetaClass 的 super_class指针 都指向其 原本Class的superClass对应的 MetaClass特列: 但是最上层的 Meta Class 的 super_class 指针,指向的却是 NSObject 原本 Class
所有的 类 NSObject子类 都是 objc_class结构体 的实例
NSObject类获取Class的源码如下:
- (Class)class {
return self;
}
Class是 objc_class结构体实例的指针变量类型
那么+[NSObject class]返回就是一个 objc_class结构体实例
而 self 一般是 一个指向对象地址的指针
而此时的 self 所代表的就是 NSObject类本身
那么可以猜测: NSObject 是 objc_class的实例
@implementation RuntimeViewController
- (void)viewDidLoad {
[super viewDidLoad];
//名字
NSLog(@"%@", [RuntimeViewController class]);
//多次输出其地址
NSLog(@"%p", [self class]);
NSLog(@"%p", [self class]);
NSLog(@"%p", [RuntimeViewController class]);
}
输出结果
2016-07-12 13:30:27.264 LJFSourceCodeLearn[1640:121986] RuntimeViewController
2016-07-12 13:30:27.265 LJFSourceCodeLearn[1640:121986] 0x101d03cd0
2016-07-12 13:30:27.265 LJFSourceCodeLearn[1640:121986] 0x101d03cd0
2016-07-12 13:30:27.265 LJFSourceCodeLearn[1640:121986] 0x101d03cd0
每一个NSObject类在运行阶段,是按照一个单例保存