浅谈Objective-C和Cocoa编程最佳实践
浅谈Objective-C和Cocoa编程最佳实践是本文要介绍的内容,不多说,来看内容。在最新的3月TIOBE编程语言排行榜上,Objective-C份额仍在大幅增长,不断向最主流语言行列接近。
在国外知名技术问答网站StackOverflow上,Objective-C标签评分最高的问题是“What are best practices that you use when writing Objective-C and Cocoa?”盛大创新院高级研究员郝培强对其中有价值的回答进行了翻译(翻译原文),在得到译者授权后,CSDN移动频道转载全文如下:
郝培强认为这些建议条目不是金科玉律,因为Objective-C和Cocoa的发展很快,希望每条都可以引起大家的思考,在自行验证后再使用到自己的项目里面去。
有些初学时的做法,现在我认为是不标准的。
(1)有了property,在“私有”的成员变量前面不再使用"_"前缀。如果一个成员变量可以被其他的类访问,那就应该用property。我不喜欢“_”前缀,它会把代码弄得非常丑陋,现在终于可以不用它了。
(2)说起来私有,我倾向于把似有方法定义在.m文件里,放在一个私有的category内,如下:
#import "MyClass.h" @interface MyClass () - (void) someMethod - (void) someOtherMethod @end @implementation MyClass
Why clutter up the .h file with things outsiders should not care about(这句怎么翻译说不准)?.m文件里的空括号的作用是私有category,如果你不实现里面声明的方法,会引发编译警告。
(3)我把dealloc方法放在.m文件的顶部,紧挨着@synthesize语句。需要dealloc哪些东西,难道不应该是你考虑一个类的时候的首要问题么?尤其是在iPhone这种环境下。(译注:我也是这么做的,dealloc和init之类的放在一起,可以对照。默认的模版把dealloc放在最后面,不利于对照,也容易让人忽视dealloc)
在table cell里,为了性能令所有的元素不透明(包括cell本身)。也就是说,每个东西都设置恰当的背景色。
使用NSURLConnection时,你应该实现下面的delegate方法
- (NSCachedURLResponse *) connection:(NSURLConnection *) connection willCacheResponse:(NSCachedURLResponse *) cachedResponse { return nil; }
除非你希望缓存response,否则你大多数时候会觉得web调用都是反常的,尤其是web service调用。如上实现这个方法可以避免缓存任何的response。
(4)避免double。iPhone原生不支持任何的double精度运算。它们是使用库来模拟的。仅在你必须使用的时候使用double精度,例如CoreLocation。在数字常数后面使用f后缀,令编译器把它们当作float处理。
float val = someFloat * 2.2f;
更重要的是当someFloat是double的时候,你不需要混合精度计算,因为val已经把精度损失掉了。更新:在3GS上发生了些变化,更应该用float了:
即使在某些手机上看起来计算不同精度速度一样,但是float可以放更多数据在寄存器里,所以还是会快很多。(译注:程序不是计算为主的,就不必那么在乎了。)
(5)把property设置为nonatomic。atomic是默认值,是同步的,会自动加入同步代码避免在多线程时发生问题。但是99%的情况下,你的property不会在多线程环境下访问,用nonatomic可以避免代码臃肿提高效率。(译注:也就是说如果要在多线程环境下访问property,切记用atomic,或者自行加锁)
(6)SQLite保存大数据集非常非常快。例如地图程序应该把图块缓存在SQLite里。最昂贵的部分是硬盘I/O。要避免在大块之间产生大量的小的写入,就需要用BEGIN; 和COMMIT;。我们使用2秒钟的计时器去重置每次新的提交。一旦计时器国企,就发送COMMIT;,这样所有些操作就写到一个大块里面了。SQLLite把事务数据保存在磁盘上,使用Begin/End包装可以避免产生大量的事务文件,把所有的事务写到一个文件里。
当SQLite在主线程的时候会阻塞你的界面。如果你执行非常长的查询,一个好办法是把你的查询保存成静态对象,然后在另一个线程查询。把所有会修改数据库的查询用@synchronize() {}块包起来。
短查询就放在主线程,简单方便。(译注:我在多线程里面使用SQLite的方法更简单,用sqlite3_open_v2打开数据库,flag用SQLITE_OPEN_FULLMUTEX,这样SQLite的所有函数就是线程安全的了,不需要synchronize了,不过哪个效率更高,我不知道。)
更多SQLite优化技巧在此,虽然文章很老,但是观点貌似都还对:
http://web.utk.edu/~jplyon/sqlite/SQLite_optimization_FAQ.html
以前outlet的内存管理没什么好办法。现在的最佳实践是把outlet声明为property:
@interface MyClass :NSObject { NSTextField *textField; } @property (nonatomic, retain) IBOutlet NSTextField *textField; @end
使用property,内存管理语义更清晰;同时给其他的实例变量组织提供了一个统一的模式。
译注:这条值得商榷,一般来说不需要跨类存取的变量,是否是IBOutlet,我都认为尽量不要声明为property。这篇问答比较早,有很多信息也许不够新了。翻出来主要是给大家参考的。
使用LLVM/Clang静态分析器:
原文我不翻译了,最近的Xcode版本都已经内置包含了LLVM/Clang静态分析器,使用方法很简单:
Xcode3系列使用热键Shift+Command+A
Xcode4系列使用热键Shift+Command+B
为了保证效果,使用静态分析器前,最好Shift+Command+K清除一下Build。
静态分析器可以分析出很多滥用,不和规范的用法,特别是能够帮你找到潜在的内存泄漏。但是它不是万能的,如果你的代码逻辑很乱,尤其是内存管理大量都不符合规范,那么它也可能给出错误的建议。它的建议起作用的前提是,你大量的代码都符合Objective-C和Cocoa的规范,有少部分代码忘记了release之类的,那么它会给你很好的建议。
所以,永远都没有神仙皇帝,好好写代码是基础。
使用标准的Cocoa命名和格式规范以及术语。规范的好处是和别的Cocoa开发者合作的时候,更方便。
例如,应该做和不应该做的:
* 在对象的interface里不要声明id m_something,不要把它叫做成员变量或者字段;使用something或者_something(这个现在也不建议了)作为它的名字,叫它实例变量。
* getter不要叫-getSomething;应该为-something;。
* setter不要叫做-something;应该叫-setSomething:。
* 方法名由参数和冒号组成;形如-[NSObject performSelector:withObject:]而不是NSObject::performSelector。
* 方法名,参数,变量,类名使用驼峰式命名(由多个单词组成一个名称时,单词首字母大写,单词间无空格,无下划线)。
* 类名首字母大写,变量,方法名首字母小写。