3.1 手动挡的乐趣(与恐惧)
现在的孩子们(指 Swift 开发者)可能无法想象,在 ARC(Automatic Reference Counting)出现之前,我们是怎么活下来的。
那时候,我们是手动的引用计数(MRC, Manual Reference Counting)的奴隶。
每一个对象生下来(alloc),你都要负责对它说“我要你”(retain)。当你用完了,你必须记得说“我不要你了”(release)。
如果你忘了说 retain,对象就会在你转身的一瞬间消失,然后你的程序就会在一个莫名其妙的地方崩溃,报错信息只有冷冰冰的一行:EXC_BAD_ACCESS。这通常意味着你访问了一个还是温热的尸体(Zombie Object)。
如果你忘了说 release,恭喜你,你的 App 会像吃自助餐一样吞噬内存,直到被操作系统无情地杀掉。
那时候的 iOS 面试题,有一半都是关于内存管理的:
- “请问
retainCount是几?”(正确答案是:别问,问就是不准。) - “
autorelease什么时候释放?” - “为什么我在循环里创建临时对象会导致内存飙升?”
我们不仅是程序员,我们还是拆弹专家。
3.2 经典的崩溃:Unrecognized Selector
如果你写过 OC,你一定见过这个报错:
-[__NSCFString count]: unrecognized selector sent to instance 0x...
这通常发生在你把一个 NSArray 当成了 NSString 用,或者反过来。
因为 OC 是动态类型的,你把一个猫指给编译器看,说“这是狗”,编译器会说“好的,它是狗”。
但是当你试图命令这只“狗”去“汪汪叫”的时候,这只猫就会给你一巴掌(抛出异常)。
这种极其宽松的类型检查,既是噩梦,也是乐子。 它意味着你可以把任何东西传给任何方法。这也催生了一个著名的防崩溃库模式:即使你向我发送了我听不懂的消息,我也假装听懂了,或者把消息转发给那个真正懂的人。
3.3 那些年我们写的宏
为了少写点代码,OC 程序员个个都是宏定义(#define)大师。
#define WEAK_SELF __weak typeof(self) weakSelf = self;
#define STRONG_SELF __strong typeof(weakSelf) strongSelf = weakSelf;
#define RGB(r, g, b) [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:1]
每一个项目里都有一个 Prefix.pch,里面塞满了各种奇技淫巧。
有的宏甚至能把 OC 伪装成 JS 或者是 C#。
3.4 总结
那是一个充满了危险的年代。代码里到处是地雷。 但当我们成功地在一台 256MB 内存的 iPhone 3GS 上跑起一个复杂的 3D 游戏,且没有内存泄漏时,那种成就感是无与伦比的。 我们是走钢丝的人。