关于源码学习自己的一些感悟
第一层:熟练使用;
第二层:读懂代码;
第三层:通晓原理;
第四层:如何设计;
自己学到了什么,还留有什么问题;
关于分享
关于线下演讲分享和线上文章分享,我一直觉得技术领域要学东西的话线上文章分享是最好的形式,一是它传播广,触达用户多;二是耗时少,写一篇文章或看一篇文章都比听一个分享花的时间少很多;三是可沉淀,读者可以反复看细节,可以沉淀下来不断被人搜索到。 – Bang (JSPatch 作者)
YYModel 作者性能优化的几个 Tip:
缓存Model JSON 转换过程中需要很多类的元数据,如果数据足够小,则全部缓存到内存中。
查表当遇到多项选择的条件时,要尽量使用查表法实现,比如 switch/case,C Array,如果查表条件是对象,则可以用 NSDictionary 来实现。
避免 KVCKey-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 的属性个数都是已知的,这时选择数量较少的那一方进行遍历,会节省很多时间。
YYModel 源码学习篇
把不熟练的一些代码挑出来,弄清楚
NS_ASSUME_NONNULL_BEGIN / NS_ASSUME_NONNULL_END
为了防止写一大堆 nonnull(不可为nil),Foundation 还提供了一对儿宏,包在里面的对象默认加 nonnull 修饰符,只需要把 nullable 的指出来就行
#define force_inline inline attribute((always_inline))
普通函数
调用时,会出现流程跳转来回
调用时,直接 将内联函数的代码全部复制到调用的地点;
内联函数的定义必须出现在调用之前;
牺牲内存空间,来提高函数执行速度;
内联函数的不足
通常,编译器比程序设计者更清楚对于一个特定的函数是否合适进行内联扩展;一些情况下,对于程序员指定的某些内联函数,编译器可能更倾向于不使用内联甚至根本无法完成内联。
代码比较长的,即使声明为inline,也不会最终内联
而有的一些比较短的小函数,即使没有声明inline,也会由c/c++编译器最终内联
而如果函数使用强制内联,那么最终就一定是内联
对于一些开发中的函数,它们可能从原来的不适合内联扩展变得适合或者倒过来。尽管内联函数或者非内联函数的转换易于宏的转换,但增加的维护开支还是使得它的优点显得更不突出了。
对于基于C的编译系统,内联函数的使用可能大大增加编译时间,因为每个调用该函数的地方都需要替换成函数体,代码量的增加也同时带来了潜在的编译时间的增加。
函数调用中堆栈的个人理解
NSCoder
iOS开发-数据存储NSCoder NSCoding是一个protocol. 如果实现了NSCoding,需要实现其中的两个方法:
方法中的主要的参数就是NSCoder,它是archivie字节流的抽象类.可以将数据写入一个coder,也可以从coder中读取我们写入的数据. NSCoder是一个抽象类,不能直接使用它来创建对象. 但是可以通过其子类NSKeyedUnarchiver从字节流中读取数据,NSKeyedArchiver将对象写入到字节流。
@package
@package变量,对于framework内部,相当于@protected, 对于framework外部,相当于@privat。
这个特性,很适合用于开发第三方的静态类库,因为多数人并不希望让别人知道自己属性的值。
.(点)和->(箭头)的区别
.(点语法)是访问类的属性,本质是调用set、get方法。
->是访问成员变量,但成员变量默认受保护,所以常常报错,手动设为public即可解决
Core Foundation框架和Cocoa Foundation框架区别
Core Foundation框架 (CoreFoundation.framework) 是一组C语言接口,它们为iOS应用程序提供基本数据管理和服务功能。下面列举该框架支持进行管理的数据以及可提供的服务:
群体数据类型 (数组、集合等)
程序包
字符串管理
日期和时间管理
原始数据块管理
偏好管理
URL及数据流操作
线程和RunLoop
端口和soket通讯Core Foundation框架和Foundation框架紧密相关,它们为相同功能提供接口,但Foundation框架提供Objective-C接口。如果您将Foundation对象和Core Foundation类型掺杂使用,则可利用两个框架之间的 “toll-free bridging”。所谓的Toll-free bridging是说您可以在某个框架的方法或函数同时使用Core Foundatio和Foundation 框架中的某些类型。很多数据类型支持这一特性,其中包括群体和字符串数据类型。每个框架的类和类型描述都会对某个对象是否为 toll-free bridged,应和什么对象桥接进行说明。
CoreFoundation 对于 ios开发的作用
对于corefoundation一直存在疑问,一直感觉他是作为ios开发的底层接口,可是为什么很多开发者习惯于实用corefoundation接口而不是用NS库,比如CFUUIDRef,NSUUID,都可以产生UUID,但是看到的大部分代码都是使用CFUUIDRef,这是为什么呢,直接使用corefoundation接口有什么好处呢?
核心是和其他加框架和架构方便“沟通”。The programming interfaces of Core Foundation objects have been designed for ease of use and reuse. At a general level, Core Foundation:
Enables sharing of code and data among various frameworks and libraries
Makes some degree of operating-system independence possible
Supports internationalization with Unicode strings
Provides common API and other useful capabilities, including a plug-in architecture, XML property lists, and preferences
Core Foundation makes it possible for the different frameworks and libraries on OS X to share code and data. Applications, libraries, and frameworks can define C routines that incorporate Core Foundation types in their external interfaces; they can thus communicate data—as Core Foundation objects—to each other through these interfaces.
__bridge 相关知识点
bridge,bridge_transfer和__bridge_retained详解
__bridge只做类型转换,但是不修改对象(内存)管理权;
__bridge_retained(也可以使用CFBridgingRetain)将Objective-C的对象转换为Core Foundation的对象,同时将对象(内存)的管理权交给我们,后续需要使用CFRelease或者相关方法来释放对象;
__bridge_transfer(也可以使用CFBridgingRelease)将Core Foundation的对象转换为Objective-C的对象,同时将对象(内存)的管理权交给ARC。
CFMutableDictionaryRef with ARC
如何创建并使用一个 CFMutableDictionaryRef
如何根据 NSMutableDictionary 创建 CFMutableDictionaryRef
CoreFoundation、CFDictionaryApplyFunction
Context
回调c函数
示例
输出
类似还有CFArrayApplyFunction使用
C函数
输出
__unsafe_unretained 修饰符
ARC 修饰符 相关知识点 而 unsafe_unretained是跟 weak类似的用法,但是__unsafe_unretained会更易造成野指针,
并且需要注意的是,尽管ARC式的内存管理是编译器的工作,但是附有_unsafe_unretained修饰符的变量不属于编译器的内存管理对象
kCFNull
Nil NSNull NULL kCFNull
kCFNull,其宏定义,其实就是NSNull的单例
测试下
输出信息
其实 kCFNull这个宏 === [NSNull null]这一句代码
isnan 和 isinf 函数
math.h 文件中定义isnan(x) not a number 此宏返回一个非零值;isinf(x) 当x是正无穷是返回1,当x是负无穷时返回-1。
第一层:熟练使用;
第二层:读懂代码;
第三层:通晓原理;
第四层:如何设计;
自己学到了什么,还留有什么问题;
关于分享
关于线下演讲分享和线上文章分享,我一直觉得技术领域要学东西的话线上文章分享是最好的形式,一是它传播广,触达用户多;二是耗时少,写一篇文章或看一篇文章都比听一个分享花的时间少很多;三是可沉淀,读者可以反复看细节,可以沉淀下来不断被人搜索到。 – Bang (JSPatch 作者)
YYModel 作者性能优化的几个 Tip:
缓存Model JSON 转换过程中需要很多类的元数据,如果数据足够小,则全部缓存到内存中。
查表当遇到多项选择的条件时,要尽量使用查表法实现,比如 switch/case,C Array,如果查表条件是对象,则可以用 NSDictionary 来实现。
避免 KVCKey-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 的属性个数都是已知的,这时选择数量较少的那一方进行遍历,会节省很多时间。
YYModel 源码学习篇
把不熟练的一些代码挑出来,弄清楚
NS_ASSUME_NONNULL_BEGIN / NS_ASSUME_NONNULL_END
为了防止写一大堆 nonnull(不可为nil),Foundation 还提供了一对儿宏,包在里面的对象默认加 nonnull 修饰符,只需要把 nullable 的指出来就行
1 NS_ASSUME_NONNULL_BEGIN
2 @interface Sark : NSObject@property (nonatomic, copy, nullable) NSString *workingCompany;
3 @property (nonatomic, copy) NSArray *friends;
4 - (nullable NSString *)gayFriend;
5 @end
6 NS_ASSUME_NONNULL_END
#define force_inline inline attribute((always_inline))
1 __attribute__((always_inline))的意思是强制内联,所有加了__attribute__((always_inline))的函数再被调用时不会被编译成函数调用而是直接扩展到调用函数体内,比如我定义了函数
2 __attribute__((always_inline)) void a()
3 void b(){a();}
4 b调用a函数的汇编代码不会是跳转到a执行,而是a函数的代码直接在b内成为b的一部分。
普通函数
调用时,会出现流程跳转来回
调用时,直接 将内联函数的代码全部复制到调用的地点;
内联函数的定义必须出现在调用之前;
牺牲内存空间,来提高函数执行速度;
内联函数的不足
通常,编译器比程序设计者更清楚对于一个特定的函数是否合适进行内联扩展;一些情况下,对于程序员指定的某些内联函数,编译器可能更倾向于不使用内联甚至根本无法完成内联。
代码比较长的,即使声明为inline,也不会最终内联
而有的一些比较短的小函数,即使没有声明inline,也会由c/c++编译器最终内联
而如果函数使用强制内联,那么最终就一定是内联
对于一些开发中的函数,它们可能从原来的不适合内联扩展变得适合或者倒过来。尽管内联函数或者非内联函数的转换易于宏的转换,但增加的维护开支还是使得它的优点显得更不突出了。
对于基于C的编译系统,内联函数的使用可能大大增加编译时间,因为每个调用该函数的地方都需要替换成函数体,代码量的增加也同时带来了潜在的编译时间的增加。
函数调用中堆栈的个人理解
NSCoder
iOS开发-数据存储NSCoder NSCoding是一个protocol. 如果实现了NSCoding,需要实现其中的两个方法:
1 - (void)encodeWithCoder:(NSCoder *)aCoder;
2 - (id)initWithCoder:(NSCoder *)aDecoder; // NS_DESIGNATED_INITIALIZER
方法中的主要的参数就是NSCoder,它是archivie字节流的抽象类.可以将数据写入一个coder,也可以从coder中读取我们写入的数据. NSCoder是一个抽象类,不能直接使用它来创建对象. 但是可以通过其子类NSKeyedUnarchiver从字节流中读取数据,NSKeyedArchiver将对象写入到字节流。
@package
@package变量,对于framework内部,相当于@protected, 对于framework外部,相当于@privat。
这个特性,很适合用于开发第三方的静态类库,因为多数人并不希望让别人知道自己属性的值。
.(点)和->(箭头)的区别
.(点语法)是访问类的属性,本质是调用set、get方法。
->是访问成员变量,但成员变量默认受保护,所以常常报错,手动设为public即可解决
Core Foundation框架和Cocoa Foundation框架区别
Core Foundation框架 (CoreFoundation.framework) 是一组C语言接口,它们为iOS应用程序提供基本数据管理和服务功能。下面列举该框架支持进行管理的数据以及可提供的服务:
群体数据类型 (数组、集合等)
程序包
字符串管理
日期和时间管理
原始数据块管理
偏好管理
URL及数据流操作
线程和RunLoop
端口和soket通讯Core Foundation框架和Foundation框架紧密相关,它们为相同功能提供接口,但Foundation框架提供Objective-C接口。如果您将Foundation对象和Core Foundation类型掺杂使用,则可利用两个框架之间的 “toll-free bridging”。所谓的Toll-free bridging是说您可以在某个框架的方法或函数同时使用Core Foundatio和Foundation 框架中的某些类型。很多数据类型支持这一特性,其中包括群体和字符串数据类型。每个框架的类和类型描述都会对某个对象是否为 toll-free bridged,应和什么对象桥接进行说明。
CoreFoundation 对于 ios开发的作用
对于corefoundation一直存在疑问,一直感觉他是作为ios开发的底层接口,可是为什么很多开发者习惯于实用corefoundation接口而不是用NS库,比如CFUUIDRef,NSUUID,都可以产生UUID,但是看到的大部分代码都是使用CFUUIDRef,这是为什么呢,直接使用corefoundation接口有什么好处呢?
核心是和其他加框架和架构方便“沟通”。The programming interfaces of Core Foundation objects have been designed for ease of use and reuse. At a general level, Core Foundation:
Enables sharing of code and data among various frameworks and libraries
Makes some degree of operating-system independence possible
Supports internationalization with Unicode strings
Provides common API and other useful capabilities, including a plug-in architecture, XML property lists, and preferences
Core Foundation makes it possible for the different frameworks and libraries on OS X to share code and data. Applications, libraries, and frameworks can define C routines that incorporate Core Foundation types in their external interfaces; they can thus communicate data—as Core Foundation objects—to each other through these interfaces.
__bridge 相关知识点
bridge,bridge_transfer和__bridge_retained详解
__bridge只做类型转换,但是不修改对象(内存)管理权;
__bridge_retained(也可以使用CFBridgingRetain)将Objective-C的对象转换为Core Foundation的对象,同时将对象(内存)的管理权交给我们,后续需要使用CFRelease或者相关方法来释放对象;
__bridge_transfer(也可以使用CFBridgingRelease)将Core Foundation的对象转换为Objective-C的对象,同时将对象(内存)的管理权交给ARC。
CFMutableDictionaryRef with ARC
如何创建并使用一个 CFMutableDictionaryRef
1 CFMutableDictionaryRef myDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
2 NSString *key = @"someKey";
3 NSNumber *value = [NSNumber numberWithInt: 1];
4 CFDictionarySetValue(myDict, (__bridge void *)key, (__bridge void *)value);
5 id dictValue = (__bridge id)CFDictionaryGetValue(myDict, (__bridge void *)key);
6 CFDictionaryRemoveValue(myDict, (__bridge void *)key);
如何根据 NSMutableDictionary 创建 CFMutableDictionaryRef
1 NSMutableDictionary *myDict = [NSMutableDictionary dictionary];
2 NSString *key = @"someKey";
3 NSNumber *value = [NSNumber numberWithInt: 1];
4 [myDict setObject:value forKey:key];
5 CFMutableDictionaryRef myCFDict = CFBridgingRetain(myDict);
6 // use myCFDict here
7 CFRelease(myCFDict);
CoreFoundation、CFDictionaryApplyFunction
Context
1 typedef struct {
2 void* object;
3 void* value;
4 }MyConext;
回调c函数
1 static void MyContextCllbackFunc(const void *_key, const void *_value, void *context) {
2 NSLog(@"%@", _key);
3 NSLog(@"%@", _value);
4 NSLog(@"%p", context);
}
示例
1 NSDictionary *dict = @{
2 @"key1" : @"value",
3 @"key2" : @"value",
4 @"key3" : @"value",
5 };
6 User *user = [User new];
7 MyConext context = {0};
8 context.object = (__bridge void *)(user);
9 context.value = @"value";
10 CFDictionaryApplyFunction((CFDictionaryRef)dict, MyContextCllbackFunc, &context);
输出
2016-07-13 16:07:29.556 LJFSourceCodeLearn[5521:241563] key1
2016-07-13 16:07:29.556 LJFSourceCodeLearn[5521:241563] value
2016-07-13 16:07:29.556 LJFSourceCodeLearn[5521:241563] 0x7fff51eb8b88
2016-07-13 16:07:29.557 LJFSourceCodeLearn[5521:241563] key3
2016-07-13 16:07:29.557 LJFSourceCodeLearn[5521:241563] value
2016-07-13 16:07:29.557 LJFSourceCodeLearn[5521:241563] 0x7fff51eb8b88
2016-07-13 16:07:29.557 LJFSourceCodeLearn[5521:241563] key2
2016-07-13 16:07:29.557 LJFSourceCodeLearn[5521:241563] value
2016-07-13 16:07:29.557 LJFSourceCodeLearn[5521:241563] 0x7fff51eb8b88
类似还有CFArrayApplyFunction使用
1 NSMutableArray *array = [NSMutableArray new];
2 for (int i = 0; i < 3; i++) {
3 RuntimeViewController *user = [RuntimeViewController new];
4 user.name = [NSString stringWithFormat:@"%d%d%d", i,i,i];
5 [array addObject:user];
6 }
7 //Context结构体实例
8 MyConext context = {0};
9 context.object = @"hahaha";
10 context.value = @"hello world...";
11 //遍历Array数组元素,每一次传入一个函数中进行处理
12 CFArrayApplyFunction((CFArrayRef)array,
13 CFRangeMake(0, CFArrayGetCount((CFArrayRef)array)),
14 MyContextCllbackFunc2,
15 &context);
C函数
1 static void MyContextCllbackFunc2(const void *user, void *context) {
2 RuntimeViewController *aUser = (__bridge RuntimeViewController *)(user);
3 MyConext *ctx = context;
4 NSLog(@"aUser.name = %@", aUser.name);
5 NSLog(@"ctx = %p", ctx);
6 }
输出
2016-07-13 16:07:29.557 LJFSourceCodeLearn[5521:241563] aUser.name = 000
2016-07-13 16:07:29.558 LJFSourceCodeLearn[5521:241563] ctx = 0x7fff51eb8bd8
2016-07-13 16:07:29.558 LJFSourceCodeLearn[5521:241563] aUser.name = 111
2016-07-13 16:07:29.558 LJFSourceCodeLearn[5521:241563] ctx = 0x7fff51eb8bd8
2016-07-13 16:07:29.558 LJFSourceCodeLearn[5521:241563] aUser.name = 222
2016-07-13 16:07:29.558 LJFSourceCodeLearn[5521:241563] ctx = 0x7fff51eb8bd8
__unsafe_unretained 修饰符
ARC 修饰符 相关知识点 而 unsafe_unretained是跟 weak类似的用法,但是__unsafe_unretained会更易造成野指针,
并且需要注意的是,尽管ARC式的内存管理是编译器的工作,但是附有_unsafe_unretained修饰符的变量不属于编译器的内存管理对象
kCFNull
Nil NSNull NULL kCFNull
kCFNull,其宏定义,其实就是NSNull的单例
1 const CFNullRef kCFNull; // the singleton null instance
测试下
1 NSNull *null1 = (id)kCFNull;
2 NSNull *null2 = [NSNull null];
输出信息
1 NSNull *) null1 = 0x0000000107b10af0
2 (NSNull *) null2 = 0x0000000107b10af0
其实 kCFNull这个宏 === [NSNull null]这一句代码
isnan 和 isinf 函数
math.h 文件中定义isnan(x) not a number 此宏返回一个非零值;isinf(x) 当x是正无穷是返回1,当x是负无穷时返回-1。