在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
原文地址:http://www.cocoachina.com/ios/20170522/19328.html
前言 最经买了本编写高质量代码 改善Objective-C程序的61个建议,拿到手看了下目录感觉内容比这本52个有效方法更深点,之前的这本也是浅浅的看过,具体讲什么也不是很记得了,所以打算先重新看下这本52个有效方法,然后再来拜读新入手的这本。 这里准备记录下Effective Objective-C 2.0 编写高质量iOS与OS X 代码的52个有效方法这本提到的知识点。 第 1 条:了解Objective-C 语言的起源 1.Objective-C 在C 语言基础上添加了面向对象特性。 关于面向过程、面向对象的区别大概是:面向对象是将事物高度抽象化, 面向过程是一种自顶向下的编程。
这个问题没有固定的答案,每个人回答的思路都是不一样的,这里可以看下逼乎上面的回答。(PS:是在下经验尚浅,不知如何回答) 2.使用消息结构的语言,其运行时所应执行的代码由运行环境来决定;而使用函数调用的语言,则由编译器决定。(Objective-C 利用运行时系统(Runtime )来做到消息传递,也叫做动态绑定 ) 3.Objective-C 的重要工作都由 “运行期组件”(runtime component)而非编译器来完成,运行期组件本质上就是一种与开发者所编代码相链接的 “动态库”(dynamic libary),其代码能把开发者编写的所有程序粘合起来。 关于静态库跟动态库的区别在于:静态库在编译的时候直接拷贝一份到应用程序的,会使得程序变大;动态库是在运行的时候加载到内存,程序会链接到动态库,不会使得程序变大,动态库相当于共享库,多个应用程序之间可以共享。 关于静态库、动态库的知识点以及制作:iOS 静态库和动态库的基本介绍和使用、iOS 静态库,动态库与 Framework 浅析、组件化-动态库实战 4.Objective-C 是C 的 “超集”,所以C 语言中的所有功能在编写Objective-C 代码时依然适用。 超集的意思大概就像爸爸跟儿子的区别:S1 就是 S2 的超集,S2 有的 S1 都有。
5.C 语言的内存模型(memory medel ),对象所占的内存总是分配在 “堆空间”(heap space)中,而绝不会分配在 “栈”(stack)上,不能在栈上面分配Objective-C 对象。 分配在堆中的内存必须直接管理,而分配在栈上用于保存变量的内存则会在其栈帧弹出时自动清理,Objective-C 将堆内存管理抽象出来了,不需要用malloc 及free 来分配或释放对象所占内存,Objective-C 运行期环境把这部分工作抽象成一套内存管理架构,叫 ”引用计数“ 。 6.对于创建结构体相比,创建对象需要额外的开销,例如分配及释放堆内存等操作,所以Objective-C 对于 ”非对象类型“ 通常都是适用结构体来存储,储存在栈空间。
第 2 条:在类的头文件中尽量少引用其他头文件 1.Objective-C 标准编写类方式也是头文件、实现文件组成 2.场景:A 类的头文件中有一个B 类型的属性 @property (nonatomic,strong) B *b;
要通过编译,处理方式有3种:使用#import #incudule @class关键字
3.两个类互相引用的问题: A 类中有B 类的属性,B 类中也有A 类的属性
4.所以应该将引入头文件的时机尽量延后,只有确有需要的时候才引用,这样子可以减少类的使用者所需引用的头文件数量。
第 3 条:多用字面量语法,少用与之等价的方法 1.使用字面量语法可以缩减源代码长度,使其更加易读,减少代码出错机率。字面量语法实际是一种 “语法糖”,也称 “糖衣语法”,是指计算机语言中与另外一套语法等效但是开发者用起来却更加方便的语法。 2.字面数值 NSNumber *someNumner = @1;
NSNumber *intNumner = @1;
NSNumber *floatNumner = @2.5f;
NSNumber *doubleNumner = @3.14159;
NSNumber *charNumner = @'s';
3.字面量数组 NSArray *array = @[@"a",@"b"@"c"];
NSString *string = array[0];
4.字面量字典 NSDictionary *dict = @{@"key":@"value"};
NSString *string = dict[@"key"];
5.可变数组与字典 NSMutableArray *mutable = [@[@"a",@"b"] mutableCopt];
6.局限性 字面量所创建的对象必须属于Foundation 框架,如果自定义这些类的子类,则无法用字面量语法创建其对象。 7.字符串字面量创建的是常量,对象不在持有了也不会立马被释放 ```objective-c 例子: strong NSObject *yourObject= [NSObject new]; weak NSObject myObject = yourObject; yourObject = nil; __unsafe_unretained NSObject theirObject = myObject; NSLog(@"%p %@", yourObject, yourObject); NSLog(@"%p %@", myObject, myObject); NSLog(@"%p %@", theirObject, theirObject); 2017-02-16 11:02:37.702543 TKApp[1767:599122] 0x0 (null) 2017-02-16 11:02:38.612380 TKApp[1767:599122] 0x0 (null) 2017-02-16 11:02:40.985613 TKApp[1767:599122] 0x0 (null) strong NSString *yourString = @"Your String"; weak NSString myString = yourString; yourString = nil; __unsafe_unretained NSString theirString = myString; NSLog(@"%p %@", yourString, yourString); NSLog(@"%p %@", myString, myString); NSLog(@"%p %@", theirString, theirString); 2017-02-16 11:00:42.407410 TKApp[1757:597837] 0x0 (null) 2017-02-16 11:00:44.340836 TKApp[1757:597837] 0x1013b9480 Your String 2017-02-16 11:00:45.392346 TKApp[1757:597837] 0x1013b9480 Your String 这里主要有2个知识点: 1.关于ARC中的引用计数问题 2.字符串常量和字符串字面量的区别是什么? Line By Line 第一种情况: __strong NSObject *yourObject = [NSObject new]; yourObject New了一个NSObject对象 并且持有 对象引用计数+1 __weak NSObject *myObject = yourObject; myObject 指向 yourObject指向的的对象地址 没有持有 对象引用计数 不变 yourObject = nil; yourObject 指向nil 不持有NSObject对象 对象不被持用 引用计数-1 这个时候这个对象自动释放 __unsafe_unretained NSObject *theirObject = myObject; 这个时候myObject已经被置为nil了 所以theirObject也为nil 第二种情况: 本来第二种情况也应该类似像第一种,这里就是关于字符串常量和字符串字面量的区别了。 What's the difference between a string constant and a string literal? 在这里为什么没有释放的情况跟字符串常量没什么联系,主要是这里是一个字符串字面量,字符串字面值创建了不会再修改了,一个对象持有这个字符串,当它不指向它了,也不会立马释放。 这里还有个点,Objective-C 会做字符串的编译单元,而且会合并相同字符串的编译单元,来减少额外的消耗去链接这些编译单元。 NSString string1 = @“pengxuyuan”; NSString string2 = @“pengxuyuan”; string1跟string2内存地址是一样的。
第 4 条:多用类型常量,少用#define 预处理指令 1. 在编码时候多次用到一个变量(数值,字符串等),我们会进行抽取以便修改一处所有用到的地方都会生效。 我们可能会使用#define 预处理指令`#define ANIMATION_DURATION 0.3 ` 编译的时候会将遇到的ANIMATION_DURATION 替换成0.3,这样子可以解决问题,但是会存在一些问题: * 预处理指令是没有包含类型的,有可能会将一些不需替换的也替换掉,导致异常 * 还有如果这个预处理被定义在头文件的话,引入了这个头文件的ANIMATION_DURATION 都会被替换,这是我们不希望看到的 这个时候我们定义一个常量的话,就可以包含类型信息`static const NSTimerInterval kAnimationDuration = 0.3;` 这样子在编译的过程中就可以清楚的知道要替换的类型,如果不一致会报警告,这样子也方便排查问题;常用的命名法是:若常量局限于 “编译单元”(translation-unit,也就是 “实现文件” 中),则在前面加字母k;若常量在类之外可见,则通常已类名作为前缀。 2. 定义常量的位置很重要。 如果将`#define ANIMATION_DURATION 0.3` `static const NSTimerInterval kAnimationDuration = 0.3;` 定义在头文件,引入了这个头文件都会有这个名字,而且 `static const NSTimerInterval kAnimationDuration = 0.3;` 定义在头文件的话,等于会声明一个全局变量,这样子所有类都可以使用了,这样子我们应该用类型作为前缀。 3. static 修饰符则意味该变量仅在此变量的编译单元可见。编译器每收到一个编译单元,就会输出一份 “目标文件”(object file)。在Objective-C 的语境下,”编译单元“ 通常指每个类的实现文件(.m 文件),如果声明此变量不加static,则编译器会为它创建一个 “外部符号”(external symbol),如果其他编译单元也声明同样的变量就会报错了。 4. 如果用static 和 const 声明一个变量,不会创建符号,而是会像#define 预处理指令一样,将遇到的变量全部替换,但是区别在这样子有变量类型。 5. 如果要定义一个外界可见的常量变量(constant variable),可以放在 “全局符号表”(global symbol table)中,来全局使用。 ```objective-c
//In the header file
extern NSString *const EOCStringConstant;
//In the implementtation file
NSString *const EOCStringConstant = @"VALUE"
编译器会在 “数据段”(data section)为字符串分配存储空间,这里在上面C 语言的内存模型有讲,数据段通常是指用来存放程序中已初始化的全局变量的一块内存区域。数据段属于静态内存分配。
第 5 条:用枚举表示状态、选项、状态码 1.C++ 11 标准扩充了枚举的特性,最新系统框架使用了 “强类型”(strong type)的枚举。 2.实现枚举所用的数据类型取决于编译器,不过其二进制位(bit)的个数必须能完全表示下枚举编号才行,一个字节含8个二进制位,所以至多能表示256中(2^8^个)枚举(编号为0~255)的枚举变量。 3.只要枚举定义得对,各选项之间就可通过 “按位或操作符”(bitwise OR operator)来组合。 4.用宏来定义枚举类型,这些宏具备向后兼容(backward compatibility)能力,如果目标平台编译器支持新标准,那就使用新式语法,否则改用旧式语法。 typedef NS_ENUM(NSUInterger,EOCConnectionState) {
EOCConnectionStateDisconnected,
EOCConnectionStateConnecting,
EOCConnectionStateConnected,
};
typedef NS_OPTINS (NSUInterger,EOCPermittedDirection) {
EOCPermittedDirectionUp = 1 << 0,
EOCPermittedDirectionDown = 1 << 1,
EOCPermittedDirectionLeft = 1 << 2,
EOCPermittedDirectionRight = 1 << 3,
}
5.在switch 语句中,最好不要有default 分支,这样子要做到处理所有样式,这样子在新家类型的时候,没有default 编译器会发出警告,让我们注意到。
|
请发表评论