YYModel--源码解析

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

结合 Demo 中 Runtime_MataClass

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 …)

Meta Class 元类

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

所以,元类是必不可少的,它存储了类的所有类方法
对象、类、元类三者之间的关系图

YYModel--源码解析_第1张图片

YYModel--源码解析_第2张图片

对象的 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类在运行阶段,是按照一个单例保存

你可能感兴趣的:(YYModel--源码解析)