编写高质量iOS和OS X代码的52个有效方法
前言
这本书和Objective-C高级编程-iOS和OS X多线程和内存管理实在是iOS开发人员必读书. 实在是太经典了. 相信懂的人自然懂~
而Objective-C高级编程的读书笔记我已经整理好发布了, 大家感兴趣的话可以去看看, 不感兴趣就直接略过吧~
Objective-C高级编程读书笔记之内存管理
Objective-C高级编程读书笔记之blocks
Objective-C高级编程读书笔记之GCD
这篇文章只是一个敲门砖, 我会把我觉得应该注意, 值得注意, 该掌握的东西都说一说, 大家不要指望看了这篇文章就不用去看书了, 那是远远不够的, 只是希望各位能借助我这篇文章, 先留个整体的印象, 然后再带着问题去研读这本书. 那才能达到最好的效果.
目录
第1章 : 熟悉Objective-C
第2章 : 对象, 消息, 运行时
第3章 : 接口与API设计
第4章 : 协议和分类
第5章 : 内存管理
第6章 : 块与大中枢派发(也就是Block与GCD)
第7章 : 系统框架
第1章 : 熟悉Objective-C
1. Objective-C是一门动态语言, 该语言使用的是”消息结构”而非”函数调用”.
- 消息结构 : 运行时所执行的代码由运行时环境决定
- 函数调用 : 运行时所执行的代码由编译器决定.
也就是说
1 |
[person run]; |
给person对象发送一条run消息 : 不到程序运行的时候你都不知道他究竟会执行什么代码. 而且, person这个对象究竟是Person类的对象, 还是其他类的对象, 也要到运行时才能确定, 这个过程叫动态绑定.
2. 堆空间
对象所占内存总是分配在堆空间中. 不能再栈中分配Objective-C对象.
- 栈空间 : 栈空间的内存不用程序员管理.
- 堆空间 : 堆空间的内存需要程序员管理.
1 2 |
NSString *anString = @"Jerry"; NSString *anotherString = anString; |
以上代码的意思是, 在堆空间中创建一个NSString实例对象, 然而栈空间中分配两个指针分别指向该实例. 如图,
在类的头文件中尽量少引入其他文件
在类的头文件中用到某个类, 如果没有涉及到其类的细节, 尽量用@class向前声明该类(等于告诉编译器这是一个类, 其他你先别管)而不导入该类的头文件以避免循环引用和减少编译时间.
多用字面量语法, 少用与之等价的方法
我们知道, 现在我们创建Foundation框架的类时有许多便捷的方法, 如
1 2 3 4 5 6 7 |
NSString *string = @"Jerry"; NSNumber *number = @10; NSArray *array = @[obj, obj1, obj2]; NSDictionary *dict = @{ @"key1" : obj1, @"key2" : obj2, @"key3" : obj3 }; |
我用们字面量语法替代传统的alloc-init来创建对象的好处 :
- 方便直观
- 更加安全
- 更利于debug
局限性 :
- 只有NSString, NSArray, NSDictionary, NSNumber支持字面量语法
- 若想用字面量语法创建出可变对象, 则需要在调用mutableCopy方法复制多一份(多调用了一个方法, 多创建了一个对象. 不必要)
关于字面量语法, 有位哥们写得挺清楚, 可以去看看浅谈OC字面量语法.
多用类型常量, 少用#define预处理指令
为什么少用#define预处理指令?
- 用预处理指令定义的常量不含类型信息
- 编译时只会进行简单查找与替代操作, 会分配多次内存
- 如果有人重新定义了常量值, 则会导致程序中常量值不一致
为什么多用类型常量?
- 在实现文件中使用static const定义只在该文件内可见的常量, 其他文件无法使用(无需给常量名称加前缀)
- 在头文件中使用extern来声明全局常量, 并在实现文件中定义其值, 可以供整个程序使用(需要给常量名称加前缀)
针对const和#define的优劣, 可参考我之前写过的一篇文章15分钟弄懂 const 和 #define
用枚举来表示状态, 选项, 状态码
相对于魔法数字(Magic Number), 使用枚举的好处不言而喻. 这里只说两个.
- 如果枚举类型的多个选项不需要组合使用, 则用NS_ENUM
1234567typedef NS_ENUM(NSInteger, UIViewAnimationTransition) {UIViewAnimationTransitionNone,UIViewAnimationTransitionFlipFromLeft,UIViewAnimationTransitionFlipFromRight,UIViewAnimationTransitionCurlUp,UIViewAnimationTransitionCurlDown,}; - 如果枚举类型的多个选项可能组合使用, 则用NS_OPTIONS
123456789typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {UIViewAutoresizingNone = 0,UIViewAutoresizingFlexibleLeftMargin = 1 << 0,UIViewAutoresizingFlexibleWidth = 1 << 1,UIViewAutoresizingFlexibleRightMargin = 1 << 2,UIViewAutoresizingFlexibleTopMargin = 1 << 3,UIViewAutoresizingFlexibleHeight = 1 << 4,UIViewAutoresizingFlexibleBottomMargin = 1 << 5};
以上代码为苹果源码.
使用NS_ENUM和NS_OPTIONS来替代C语言的enum的好处
- 可以自定义枚举的底层数据类型
- 在C中使用C的语法, 在OC中使用OC的语法, 保持语法的统一
另外, 在处理枚举的switch语句中, 不要使用default分支, 因为以后你加入新枚举之后, 编译器会提示开发者 : switch语句没有处理所有枚举(没使用default的情况下).
第2章 : 对象, 消息, 运行时
上一章我们说到, Objective-C是一门动态语言, 其动态性就由这一章来说明.
理解”属性”这一概念
1 2 3 4 5 6 7 |
@interface Person : NSObject { @public NSString *_firstName; NSString *_lastName; <a href='http://www.jobbole.com/members/kaishu6296'>@private</a> NSString *_address; } |
编写过Java或C++的人应该比较熟悉这种写法, 但是这种写法问题很大!!!
对象布局在编译器就已经固定了. 只要碰到访问_firstName变量的代码, 编译器就把其替换为”偏移量”, 这个偏移量是”硬编码”, 表示该变量距离存放对象的内存区域的起始地址有多远.
目前这样看没有问题, 但是只要在_firstName前面再加一个实例变量就能说明问题了.