Google的Objective-C編碼規(guī)范

Google的Objective-C編碼規(guī)范 總覽 背景知識(shí) ? ? ? ? Objective-C是一個(gè)C語言的擴(kuò)展語言颈娜,非常動(dòng)態(tài)季春,非常的“面向?qū)ο蟆蹦辉O(shè)計(jì)成既擁有復(fù)雜的面向?qū)ο笤O(shè)計(jì)理念又可以輕松使用與閱讀的語言,也是Mac OS X和iPhone開發(fā)的首選語言腹暖。? ? ? ? Cocoa是Mac OS X的主要應(yīng)用框架抢呆,提供迅速開發(fā)各種功能的Mac OS X應(yīng)用的Objective-C類集合。? ? ? ? Apple已經(jīng)有一個(gè)很好也被廣泛接受的Objective-C的編程規(guī)范,Google也有類似的C++編程規(guī)范碗暗,這份Objective-C編程規(guī)范很自然是Apple和Google的共同推薦的組合颈将。所以,在閱讀本規(guī)范前言疗,確保你已經(jīng)閱讀了:? ? ? ? Apple's Cocoa Coding Guidelines ? ? ? ? Google's Open Source C++ Style Guide ? ? ? ? 注意所有已在Google的C++編程規(guī)范里的禁用條款在Objective-C里也適用晴圾,除非本文檔明確指出反對(duì)意見。 ? ? ? ? 本文檔旨在描述可供可適用于所有Mac OS X代碼的Objective-C(包括Objective-C++)編碼規(guī)范和實(shí)踐噪奄。規(guī)范中的許多條款已經(jīng)改進(jìn)也不斷的被其他的項(xiàng)目和團(tuán)隊(duì)所證明其指導(dǎo)性死姚。Google的相關(guān)開源項(xiàng)目都遵守此規(guī)范。? ? ? ? Google已經(jīng)發(fā)布了一份作為Google Toolbox for Mac project (文檔中簡(jiǎn)稱為GTM)的組成部分的遵守本規(guī)范的開源代碼勤篮。這份開放代碼也是本文很好的例證(原文看不太懂--Code meant to be shared across different projects is a good candidate to be included in this repository. )? ? ? ? 注意本文不是Objective-C的教學(xué)指南都毒,我們假設(shè)讀者已經(jīng)了解語言。如果你是一個(gè)Objective-C的初學(xué)者或需要重溫叙谨,請(qǐng)閱讀The Objective-C Programming Language .示例 ? ? ? ? 人們說一個(gè)例子勝過千言萬語温鸽,所以就讓我們用例子來讓你感受以下編碼規(guī)范的風(fēng)格保屯,留間距手负,命名等等。? ? ? ? 下例是一份頭文件姑尺,展示對(duì)@interface 聲明正確的注釋和留間距//? GTMFoo.h //? FooProject // //? Created by Greg Miller on 6/13/08. //? Copyright 2008 Google, Inc. All rights reserved. // #import// A sample class demonstrating good Objective-C style. All interfaces, // categories, and protocols (read: all top-level declarations in a header) // MUST be commented. Comments must also be adjacent to the object they're // documenting. // // (no blank line between this comment and the interface) @interface GTMFoo : NSObject {? @private? NSString *foo_;? NSString *bar_; } // Returns an autoreleased instance of GMFoo. See -initWithString: for details // about the argument. + (id)fooWithString:(NSString *)string; // Designated initializer. |string| will be copied and assigned to |foo_|. - (id)initWithString:(NSString *)string; // Gets and sets the string for |foo_|. - (NSString *)foo; - (void)setFoo:(NSString *)newFoo; // Does some work on |blah| and returns YES if the work was completed // successfuly, and NO otherwise. - (BOOL)doWorkWithString:(NSString *)blah; @end? ? ? ? 下例是一份源文件竟终,展示對(duì)接口的@implementation 的實(shí)現(xiàn)的正確注釋和留間隔。它也包括了主要方法如getters,setters,init ,和dealloc 的相關(guān)實(shí)現(xiàn)切蟋。// //? GTMFoo.m //? FooProject // //? Created by Greg Miller on 6/13/08. //? Copyright 2008 Google, Inc. All rights reserved. // #import "GTMFoo.h" @implementation GTMFoo + (id)fooWithString:(NSString *)string {? return [[[self alloc] initWithString:string] autorelease]; } // Must always override super's designated initializer. - (id)init {? return [self initWithString:nil]; } - (id)initWithString:(NSString *)string {? if ((self = [super init])) {? ? foo_ = [string copy];? ? bar_ = [[NSString alloc] initWithFormat:@"hi %d", 3];? }? return self;? } - (void)dealloc {? [foo_ release];? [bar_ release];? [super dealloc]; } - (NSString *)foo {? return foo_; } - (void)setFoo:(NSString *)newFoo {? [foo_ autorelease];? foo_ = [newFoo copy];? } - (BOOL)doWorkWithString:(NSString *)blah {? // ...? return NO; } @end間隔與格式化 ? ? 空格對(duì)tab鍵 ? ? ? ? 僅使用空格统捶,縮進(jìn)兩個(gè)。? ? ? ? 我們使用空格用于縮進(jìn)柄粹,不要在編碼時(shí)使用tab鍵喘鸟,你應(yīng)該設(shè)置你的編輯器將tab鍵轉(zhuǎn)換成對(duì)應(yīng)的空格。? ? 行長(zhǎng)度 ? ? ? ? ? ? 代碼中的每行文本不要超過80個(gè)字符的長(zhǎng)度驻右。? ? ? ? 盡管Objective-C正變得比C++更加繁冗什黑,為了保持規(guī)范的互通性,我們還是決定保持80字符長(zhǎng)度的限制堪夭。這比你想象中的更容易做到愕把。? ? ? ? 我們知道本條款是有爭(zhēng)議的,但已有此多的代碼已經(jīng)遵從了本條款森爽,即使只是保持一致性也是一個(gè)充足的理由恨豁。? ? ? ? 你可以在Xcode里清楚地發(fā)現(xiàn)代碼中的違規(guī),設(shè)置? Xcode > Preferences > Text Editing > Show page guide. (之后就可以在代碼編輯區(qū)域里看到一條指定字符長(zhǎng)度的指示線了)? ? 方法聲明與定義 ? ? ? ? 留一個(gè)空格在-或+和返回類型之間爬迟,但參數(shù)列表里的參數(shù)之間不要留間隔橘蜜。? ? ? ? 方法應(yīng)該寫成這樣:- (void)doSomethingWithString:(NSString *)theString {? ...}? ? ? ? 星號(hào)前的空格是可選的,你可以根據(jù)原來的代碼風(fēng)格自行決定付呕。? ? ? ? 如果參數(shù)過多计福,推薦每個(gè)參數(shù)各占一行捧请。使用多行的情況下,在參數(shù)前加冒號(hào)用于對(duì)齊:? ? ? ? (很遺憾這里僅有Google Chrome瀏覽器能看出是冒號(hào)對(duì)齊的......)- (void)doSomethingWith:(GTMFoo *)theFoo? ? ? ? ? ? ? ? ? rect:(NSRect)theRect? ? ? ? ? ? ? interval:(float)theInterval {? ...}? ? ? ? 當(dāng)?shù)谝粋€(gè)關(guān)鍵字比其他的短時(shí)棒搜,后續(xù)行至少縮進(jìn)四個(gè)空格疹蛉。這樣你可以讓后續(xù)的關(guān)鍵字垂直對(duì)齊,而不是用冒號(hào)對(duì)齊:- (void)short:(GTMFoo *)theFoo? ? longKeyword:(NSRect)theRect? ? evenLongerKeyword:(float)theInterval {? ...}方法調(diào)用 ? ? ? ? 方法調(diào)用的格式和方法聲明時(shí)的格式時(shí)一致的力麸,如果格式風(fēng)格可選可款,遵從原有代碼的風(fēng)格。? ? ? ? 調(diào)用應(yīng)該將所有參數(shù)寫在一行:[myObject doFooWith:arg1 name:arg2 error:arg3];? ? ? ? 或者每個(gè)參數(shù)一行克蚂,用冒號(hào)對(duì)齊:? ? ? ? (對(duì)齊效果如前說明......)[myObject doFooWith:arg1? ? ? ? ? ? ? name:arg2? ? ? ? ? ? ? error:arg3];? ? ? ? 不要使用如下風(fēng)格的寫法[myObject doFooWith:arg1 name:arg2? // some lines with >1 arg? ? ? ? ? ? ? error:arg3];[myObject doFooWith:arg1? ? ? ? ? ? ? name:arg2 error:arg3];[myObject doFooWith:arg1? ? ? ? ? name:arg2? // aligning keywords instead of colons? ? ? ? ? error:arg3];? ? ? ? 在聲明和定義時(shí)闺鲸,如果因?yàn)殛P(guān)鍵字長(zhǎng)度使就算有四個(gè)空格在前仍然無法用冒號(hào)對(duì)齊,那么就讓后續(xù)行縮進(jìn)四個(gè)空格而不是冒號(hào)對(duì)齊:[myObj short:arg1? ? longKeyword:arg2? ? evenLongerKeyword:arg3];? ? @public 和 @private ? ? ? ? 權(quán)限控制符@public 和@private 縮進(jìn)一個(gè)空格.? ? ? ? 類似C++的public,protected,private:@interface MyClass : NSObject { @public? ... @private? ...}@end? ? 異常 ? ? ? ? 每個(gè)異常標(biāo)簽的@ 和開括號(hào)({ )寫在統(tǒng)一行埃叭,標(biāo)簽和開括號(hào)間隔一個(gè)空格摸恍,同樣適用于@catch 語句。? ? ? ? 如果你決定使用Objective-C的異常赤屋,那么就按如下格式立镶,不過你最好先看看Avoid Throwing Exceptions(見后)條款,了解為何你不應(yīng)使用異常类早。@try {? foo();}@catch (NSException *ex) {? bar(ex);}@finally {? baz();}命名 ? ? ? ? 命名規(guī)則對(duì)于維護(hù)代碼來說是非常重要的媚媒。Objective-C方法名往往很長(zhǎng),不過這也有好處涩僻,讀代碼就像讀散文(放屁),讓很多注釋變得毫無意義缭召。? ? ? ? 寫純Objective-C代碼時(shí),我們基本上遵守標(biāo)準(zhǔn)Objective-C naming rules 逆日,這些規(guī)則和C++的規(guī)則有很大的不同嵌巷。比如Google的C++代碼規(guī)范推薦變量名構(gòu)詞之間使用下劃線隔開,而本文檔推薦駝峰法室抽,也是Objective-C社區(qū)的標(biāo)準(zhǔn)搪哪。? ? ? ? 所有類,類別, 方法狠半,以及變量如包括縮寫噩死,則縮寫部分使用全大寫的縮寫(Initialisms )形式。這遵守Apple的標(biāo)準(zhǔn)神年,比如URL,TIFF以及EXIF已维。? ? ? 當(dāng)寫Objective-C++代碼時(shí),情況就不是那么單一了已日。許多項(xiàng)目需要實(shí)現(xiàn)帶一些Objective-C代碼的跨平臺(tái)的C++APIs或者連接后臺(tái)的C++代碼與前臺(tái)的原生Cocoa代碼.這會(huì)造成兩種規(guī)范直接沖突垛耳。? ? ? ? 我們的解決方法是根據(jù)方法/函數(shù)風(fēng)格來決定。如果在@implementation 塊,就使用Objective-C的命名規(guī)則堂鲜;如果在C++的方法實(shí)現(xiàn)塊栈雳,就使用C++的命名規(guī)則。避免了實(shí)體變量和本地變量在一個(gè)函數(shù)內(nèi)命名規(guī)則沖突的情況缔莲,而這種情況是對(duì)可讀性的極大損害哥纫。? ? 文件命名 ? ? ? ? 文件名反映了它所包含的實(shí)現(xiàn)類的名字,遵從你所在項(xiàng)目的習(xí)慣痴奏。? ? ? ? 文件擴(kuò)展名使用如下規(guī)則? ? ? ? ? ? 類別的文件名應(yīng)該包含擴(kuò)展類的名字蛀骇,比如GTMNSString+Utils.h or GTMNSTextView+Autocomplete.h ? Objective-C++ ? ? ? ? 在一份源文件里,Objective-C++代碼遵守當(dāng)前方法/函數(shù)的風(fēng)格? ? ? ? 為了盡量減少不同命名風(fēng)格間的沖突读拆,使用當(dāng)前方法的風(fēng)格擅憔。如果在@implementation塊,使用Objective-C命名規(guī)則檐晕,如果在C++類的函數(shù)實(shí)現(xiàn)塊暑诸,使用C++命名規(guī)則。// file: cross_platform_header.hclass CrossPlatformAPI { public:? ...? int DoSomethingPlatformSpecific();? // impl on each platform private:? int an_instance_var_;};// file: mac_implementation.mm#include "cross_platform_header.h"http:// A typical Objective-C class, using Objective-C naming.@interface MyDelegate : NSObject { @private? int instanceVar_;? CrossPlatformAPI* backEndObject_;}- (void)respondToSomething:(id)something;@end@implementation MyDelegate- (void)respondToSomething:(id)something {? // bridge from Cocoa through our C++ backend? instanceVar_ = backEndObject->DoSomethingPlatformSpecific();? NSString* tempString = [NSString stringWithInt:instanceVar_];? NSLog(@"%@", tempString);}@end// The platform-specific implementation of the C++ class, using// C++ naming.int CrossPlatformAPI::DoSomethingPlatformSpecific() {? NSString* temp_string = [NSString stringWithInt:an_instance_var_];? NSLog(@"%@", temp_string);? return [temp_string intValue];}? ? 類命名 ? ? ? ? 類名(不包括類別和協(xié)議名)應(yīng)該用大寫開頭的駝峰命名法辟灰。? ? ? ? 在應(yīng)用級(jí)別的代碼里个榕,盡量不要使用帶前綴的類名。每個(gè)類都有相同的前綴不能提高可讀性伞矩。不過如果是編寫多個(gè)應(yīng)用間的共享代碼笛洛,前綴就是可接受并推薦的做法了(型如 GTMSendMessage )夏志。? ? 類別命名 ? ? ? ? 類別命名應(yīng)該以兩三個(gè)字符的分類前綴作為一個(gè)項(xiàng)目或通用的公用部分乃坤。類別名應(yīng)該包含類的擴(kuò)展。? ? ? ? 舉個(gè)例子沟蔑,如果我們想要?jiǎng)?chuàng)建一個(gè)基于NSString 的類別用于解析湿诊,我們應(yīng)該把類別放到名字是GTMNSString+Parsing.h 的文件里,而類別本身的名字則是GTMStringParsingAdditions (是的瘦材,我們明白這個(gè)類別和其文件名字不匹配厅须,但這個(gè)文件可能還包括其他用于解析相關(guān)的類別)。類別的方法應(yīng)該都使用一個(gè)前綴(型如gtm_myCategoryMethodOnAString ),以防止Objective-C代碼在單名空間里沖突食棕。如果代碼本來就不考慮共享或在不同的地址空間(address-space)朗和,方法命名規(guī)則就沒必要恪守了。? ? Objective-C 方法命名 ? ? ? ? 方法使用小寫開頭的駝峰法命名簿晓,每個(gè)參數(shù)都應(yīng)該小寫開頭眶拉。? ? ? ? 方法名應(yīng)該盡可能讀起來像一句話,參數(shù)名就相對(duì)方法名的補(bǔ)充說明(比如convertPoing:fromRect: 或者 replaceCharactersInRange:withString: )憔儿,詳見Apple's Guide to Naming Methods ? ? ? ? 存取(Accessor)方法應(yīng)該一致的在"取(getting)"的時(shí)候直接用變量名而不是在簽名加"get",如下:- (id)getDelegate;? // AVOID- (id)delegate;? ? // GOOD? ? ? 不過這僅針對(duì)Objective-C代碼忆植,C++代碼仍然遵循自己的代碼規(guī)范。? ? 變量命名 ? ? ? ? 變量名使用小寫開頭的駝峰法,類成員變量名最后加一個(gè)下劃線朝刊,比如:myLovalVariable, myInstanceVariable_ . 下面看不懂耀里,原文Members used for KVO/KVC bindings may begin with a leading underscore iff use of Objective-C 2.0's @property isn't allowed. ? ? ? 一般變量命名 ? ? ? ? ? ? ? ? 不要使用匈牙利命名法去標(biāo)記語法,比如靜態(tài)類型或變量類型(int或pointer之類的)拾氓。使變量名盡量可以推測(cè)其用途屬性具有描述性冯挎。別一心想著少打幾個(gè)字母,讓你的代碼可以迅速被理解更加重要咙鞍。比如:// AVOIDint w;int nerr;int nCompConns;tix = [[NSMutableArray alloc] init];obj = [someObject object];p = [network port];// GOODint numErrors;int numCompletedConnections;tickets = [[NSMutableArray alloc] init];userInfo = [someObject object];port = [network port];? ? ? 實(shí)體變量 ? ? ? ? 實(shí)體變量用駝峰法命名并后綴下劃線织堂,就像usernameTextField_ . 然而我們?cè)试S一種例外就是用KVO/KVC去綁定一個(gè)實(shí)體變量而Objective-C 2.0 不能用(因?yàn)椴僮飨到y(tǒng)的限制)的情況,此時(shí)也可用前綴下劃線的方法給每個(gè)變量命名奶陈。如果可以使用Objective-C 2.0易阳,@property 和 @synthesize 提供了遵守命名規(guī)范的解決方法。? ? ? 常量 ? ? ? ? ? 常量(預(yù)定義吃粒,枚舉潦俺,局部常量等)使用小寫k開頭的駝峰法,比如kInvalidHandle , kWritePerm . 注釋 ? ? ? ? 盡管寫起來很痛苦徐勃,但注釋仍然是使代碼保持可讀性的極端重要的方式事示。下面的條款描述了你應(yīng)該注釋什么以及在哪里做注釋。但是記住:即使注釋是如此重要僻肖,最好的代碼還是自說明式的肖爵。起一個(gè)有意義的名字比起一個(gè)晦澀的名字然后在用注釋去解釋它好的多。? ? ? ? 當(dāng)你寫注釋的時(shí)候臀脏,記住注釋是寫給讀者劝堪,即下一個(gè)要理解你的代碼并繼續(xù)開發(fā)的人。"下一個(gè)"完全可能就是你自己揉稚。? ? ? 同樣秒啦,所有C++編程規(guī)范的條款仍然適用,只是增加了一些條款搀玖,如下.? ? 文件注釋 ? ? ? ? 每個(gè)文件的開頭都是版權(quán)聲明余境,接著是文件內(nèi)容的描述。? ? ? 法律聲明和作者欄 ? ? ? ? 每個(gè)文件都應(yīng)該包含如下信息:? ? ? ? ? ? 一份版權(quán)聲明(比如 Copyright 2008 Google Inc .)? ? ? ? ? ? 許可版本 為項(xiàng)目選擇合適的許可版本(比如Apache 2.0, BSD, LGPL, GPL)? ? ? ? 如果你把別人寫的文件做了相當(dāng)大的改動(dòng)灌诅,就把自己添加到作者欄去芳来。這樣別的開發(fā)者就方便聯(lián)系這個(gè)文件的實(shí)際開發(fā)人員了。? ? 聲明注釋 ? ? ? ? 每個(gè)接口猜拾,類別即舌,協(xié)議都必須伴隨描述它的用途以及如何整合的注釋。// A delegate for NSApplication to handle notifications about app// launch and shutdown. Owned by the main app controller.@interface MyAppDelegate : NSObject {? ...}@end? ? ? ? 如果已經(jīng)在文件的頂部寫了接口的詳細(xì)描述关带,你也可以簡(jiǎn)單的寫如"見文件頂部的完整描述"侥涵,當(dāng)然要有這些注釋的順序安排沼撕。? ? ? ? 此外public接口的每個(gè)方法都應(yīng)該添加關(guān)于函數(shù),參數(shù)芜飘,返回值以及副作用的注釋务豺。? ? ? ? 文檔默認(rèn)類都是同步的,如果類實(shí)例可以多線程訪問嗦明,必須要加上額外的說明笼沥。? ? 實(shí)現(xiàn)注釋 ? ? ? ? 使用豎線引用變量或符號(hào),而不是用引號(hào)娶牌。? ? ? ? 這可以減少歧義奔浅,特別是當(dāng)符號(hào)本身就是個(gè)常見的詞時(shí),可能使句子顯得支離破碎诗良,比如符號(hào)是"count":// Sometimes we need |count| to be less than zero.? ? ? ? 或者是對(duì)于那些已經(jīng)存在引號(hào)的情況// Remember to call |StringWithoutSpaces("foo bar baz")|? ? 對(duì)象所有權(quán) ? ? ? ? 使指針?biāo)袡?quán)的模型盡可能清晰汹桦,當(dāng)它屬于Objective-C的使用慣例時(shí)(不懂,原文是Make the pointer ownership model as explicit as possible when it falls outside the most common Objective-C usage idioms. )? ? ? ? 實(shí)例變量指向NSObject派生類的對(duì)象時(shí)都假定是retain的鉴裹,如果它們不是retain的則需要加上"weak"的文檔說明舞骆。對(duì)應(yīng)的,實(shí)體變量如果標(biāo)記上IBOutlets則是假定為非retain的径荔,若實(shí)際上用了retain督禽,就必須加上"strong"的說明。? ? ? ? 當(dāng)實(shí)例變量指向核心庫总处,C++或其他非Objective-C對(duì)象時(shí)狈惫,必須永遠(yuǎn)用注釋說明是strong還是weak的。必須注意為了支持Objective-C對(duì)象里的自動(dòng)化C++對(duì)象的封裝是默認(rèn)被關(guān)閉的的(這句話有歧義鹦马,原文是Be mindful that support for automatic C++ objects encapsulated in Objective-C objects is disabled by default)胧谈,這里 有說明。? ? ? ? strong和weak說明的文檔示例:@interface MyDelegate : NSObject { @private? IBOutlet NSButton* okButton_;? // normal NSControl? IBOutlet NSMenu* myContextMenu_;? // manually-loaded menu (strong)? AnObjcObject* doohickey_;? // my doohickey? MyController* controller_;? // so we can send msgs back (weak, owns me)? // non-NSObject pointers...? CWackyCPPClass* wacky_;? // some cross-platform object (strong)? CFDictionaryRef* dict_;? // (strong)}@end? ? ? ? strong? ? ? ? ? ? 對(duì)象會(huì)在類中retain ? ? ? ? weak? ? ? ? ? 對(duì)象不會(huì)在類中retain? (比如一個(gè)委托)Cocoa和Objective-C特性 ? ? 成員變量應(yīng)該定義為@private@interface MyClass : NSObject { @private? id myInstanceVariable_;}// public accessors, setter takes ownership- (id)myInstanceVariable;- (void)setMyInstanceVariable:(id)theVar;@end? ? 明確指定初始化 ? ? ? ? 注釋并說明指定的初始化菠红。? ? ? 明確指定初始化對(duì)想要子類化你的類的時(shí)候時(shí)很重要的第岖。那樣,子類化時(shí)只需要做一個(gè)或多個(gè)初始化去保證初值即可试溯。這也有助于在以后調(diào)試你的類時(shí)明了初始化流程。? ? 重寫指定初始化 ? ? ? ? 當(dāng)重寫一個(gè)子類并需要init... 方法郊酒,注意要重寫父類的指定初始化方法遇绞。? ? ? ? 如果你沒有正確重寫父類的指定初始化方法,你的初始化方法可能不會(huì)被調(diào)用燎窘,這會(huì)導(dǎo)致很多微妙而難以排除的錯(cuò)誤摹闽。? ? 初始化 ? ? ? ? 沒必要在初始化方法里把變量初始化為0 或者nil ,這是多余的。? ? ? ? 所有新分配內(nèi)存的對(duì)象內(nèi)容都初始化為0(除了 isa )褐健,所以不要在init 方法里做無謂的重初始化為0的操作付鹿。? ? 保持公有API簡(jiǎn)明 ? ? ? ? 保持你的類簡(jiǎn)單澜汤,避免"廚房水槽"似的APIs,如果一個(gè)方法沒必要公開就不要公開舵匾。使用私有類別保證公開頭文件的簡(jiǎn)潔俊抵。? ? ? ? 和C++不同,Objective-C無法區(qū)分公有私有方法坐梯,因?yàn)樗枪械幕栈濉R虼耍蔷褪菫榱俗層脩粽{(diào)用所設(shè)計(jì)吵血,不要把其他的方法放到公有API里谎替。這樣可以減少不期調(diào)用的可能性。這還包括重寫父類的方法蹋辅。對(duì)于那些內(nèi)部實(shí)現(xiàn)的方法钱贯,在實(shí)現(xiàn)文件里使用類別而不是將方法定義在公有頭文件里。// GTMFoo.m#import "GTMFoo.h"@interface GTMFoo (PrivateDelegateHandling)- (NSString *)doSomethingWithDelegate;? // Declare private method@end@implementation GTMFoo(PrivateDelegateHandling)...- (NSString *)doSomethingWithDelegate {? // Implement this method}...@end? ? ? ? 在Objective-C 2.0之前侦另,如果你在私有@interface 里聲明了一個(gè)方法喷舀,但忘記在主@implementation 文件里實(shí)現(xiàn)了,編譯器不會(huì)有什么反應(yīng)(這是因?yàn)槟銢]有在不同的類別里實(shí)現(xiàn)這些私有方法)淋肾。解決方案在是把函數(shù)寫到@implementation 里并指明類別硫麻。? ? ? ? 如果你用的是 Objective-C 2.0,你應(yīng)該使用類擴(kuò)展 而不是聲明私有類別樊卓,如下:@interface GMFoo () { ... }? ? ? 如此就可以保證函數(shù)做了聲明但沒有在@implememtation 里實(shí)現(xiàn)的時(shí)候編譯器會(huì)警報(bào)拿愧。? ? ? ? 再者,"private"方法并不是真正的private碌尔,你可能會(huì)無意間重寫了父類的一個(gè)"private"方法浇辜,這回導(dǎo)致bug的涌現(xiàn)⊥倨荩總的來說柳洋,私有方法應(yīng)該使用更特別的名字以阻止子類化時(shí)并不期望的重寫。? ? ? ? 最后叹坦,對(duì)于絕大多數(shù)類而言熊镣,Objective-C的類別是將@implelemtation做可理解的分塊,添加新的應(yīng)用級(jí)別的功能的最佳途徑募书。比如绪囱,與其在你的項(xiàng)目里隨便找個(gè)類來實(shí)現(xiàn)字符串的"中間截?cái)?功能,不如創(chuàng)建一個(gè)新的NSString 類別莹捡。? ? #import 和 #include ? ? ? ? 用#import 導(dǎo)入Objective-C或Objective-C++頭文件鬼吵,用#include 導(dǎo)入C或C++頭文件? ? ? ? 根據(jù)頭文件的語言去選擇合適的導(dǎo)入方式。? ? ? ? ? ? 當(dāng)導(dǎo)入的頭文件使用Objective-C或Objective-C++語言時(shí)篮赢,使用#import .? ? ? ? ? ? 當(dāng)導(dǎo)入標(biāo)準(zhǔn)C或 C++頭文件時(shí)齿椅,使用#include . 頭文件應(yīng)該使用自己的#define 重加載保護(hù)? ? ? ? 有些Objective-C頭文件沒有#define 重加載保護(hù)琉挖,所以只應(yīng)該用#import 導(dǎo)入。因此Objective-C頭文件只應(yīng)該被Objective-C源文件或其他的Objective-C頭文件所導(dǎo)入涣脚。這種情況下全部使用#import 是合適的示辈。? ? ? ? 標(biāo)準(zhǔn)C和C++頭文件不包含任何Objective-C元素都可以被一般的C或C++文件導(dǎo)入。因?yàn)闃?biāo)準(zhǔn)C和C++里根本沒有#import 涩澡,所以也只能用#include 導(dǎo)入顽耳。在Objective-C代碼中使用#include 一致的導(dǎo)入這些頭文件。? ? ? ? 本條款有助于跨平臺(tái)項(xiàng)目的無意錯(cuò)誤妙同。一位Mac開發(fā)者引入一份新C或C++頭文件時(shí)可能會(huì)忘記添加#define 重加載保護(hù)射富,因?yàn)樵贛ac上用#import 導(dǎo)入文件不會(huì)引發(fā)問題,但在別的使用#include 的平臺(tái)就可能出問題粥帚。在所有平臺(tái)一致的使用#include 意味著要么全部成功要么全部失敗胰耗,避免了那種另人沮喪的一些平臺(tái)上可以運(yùn)作而另一些不行的情況。#import#include#import "GTMFoo.h"#include "base/basictypes.h"? ? 使用框架根 ? ? ? ? 導(dǎo)入框架根的頭文件而不是分別導(dǎo)入框架頭文件? ? ? ? 看起來從Cocoa或Foundation這些框架里導(dǎo)入個(gè)別的文件很不錯(cuò)芒涡,但實(shí)際上你直接導(dǎo)入框架根頭文件效率更高柴灯。框架根已經(jīng)被預(yù)編譯故可更快的被加載费尽。還有赠群,記住用#import 指令而不是#include 導(dǎo)入Objective-C的框架。#import// good#import// avoid#import...

構(gòu)建時(shí)即設(shè)定autorelease ??????? 當(dāng)創(chuàng)建新的臨時(shí)對(duì)象時(shí)旱幼,在同一行代碼里就設(shè)定autorelease而不是寫到這個(gè)方法的后面幾行去??????? 即使這樣可能會(huì)造成一些輕微的延遲查描,但這樣避免了誰不小心把release 去掉,或在release 之前就return 而造成的內(nèi)存泄露柏卤,如下

// AVOID (unless you have a compelling performance reason)

MyController* controller = [[MyController alloc] init];

// ... code here that might return ...

[controller release];

// BETTER

MyController* controller = [[[MyController alloc] init] autorelease];

優(yōu)先autorelease而非retain ??????? 對(duì)象賦值時(shí)盡量采用autorelease 而不是retian 模式冬三。??????? 當(dāng)把一個(gè)新創(chuàng)建的對(duì)象賦予一個(gè)變量的時(shí)候,第一件要做的事情就是先釋放原來變量指向的對(duì)象以防止內(nèi)存泄露缘缚。這里也有很多"正確的"方法去做這件事勾笆。我們選擇autorelease時(shí)因?yàn)樗粌A向于出錯(cuò)。小心在密集的循環(huán)里可能會(huì)很快填滿autorelease池桥滨,而且它也確實(shí)會(huì)降低效率窝爪,但權(quán)衡下來還是可以接受的。

- (void)setFoo:(GMFoo *)aFoo {

[foo_ autorelease];? // Won't dealloc if |foo_| == |aFoo|

foo_ = [aFoo retain];

}

以聲明時(shí)的順序dealloc處理實(shí)例變量 ??????? dealloc 應(yīng)該用在@interface 聲明時(shí)同樣的順序處理實(shí)例變量该园,這也有助于評(píng)審者鑒別酸舍。??????? 代碼評(píng)審者檢查或修正dealloc 的實(shí)現(xiàn)要確保所有retain 的實(shí)例變量都獲得了釋放。?????? 為了簡(jiǎn)化評(píng)審dealloc 里初,將釋放retain 的實(shí)例變量代碼保持和@interface 里聲明的順序一致。如果dealloc 調(diào)用了其他方法去釋放實(shí)例變量忽舟,添加注釋說明那些實(shí)例變量被這些方法所處理了双妨。??? Setters copy NSStrings ??????? 在NSString 上調(diào)用Setters 方法時(shí)淮阐,永遠(yuǎn)使用copy 方式。(不太懂刁品,原文是Setters taking an NSString, should always copy? the string it accepts. )??????? 永遠(yuǎn)不要retain 一個(gè)字符串泣特,這可以防止調(diào)用者在你不知到的情況下修改了字符串。不要以為你可以改變NSString 的值挑随,只有NSMutableString 才能做到状您。

- (void)setFoo:(NSString *)aFoo {

[foo_ autorelease];

foo_ = [aFoo copy];

}

避免拋出異常 ??????? 不要 @throw Objective-C的異常,不過你還是要做好準(zhǔn)備捕獲第三方以及系統(tǒng)調(diào)用拋出的異常兜挨。??????? 我們的確在編譯時(shí)加入了-fobjc-exceptions 指令(主要是為了獲得@synchronized )膏孟,但我們并不@throw 。當(dāng)然在使用第三方庫的時(shí)候是允許使用@try拌汇,@catch, 以及@finally 的柒桑。如果你確實(shí)使用了,請(qǐng)務(wù)必明確到文檔中哪個(gè)方向你想拋出什么異常噪舀。??????? 除非你寫的代碼想要泡在MacOS 10.2或更之前魁淳,否則不要使用NS_DURING, NS_HANDLER, NS_ENDHANDLER, NS_VALUERETURN and NS_VOIDRETURN 這些宏。??????? 另外你要小心當(dāng)寫Objective-C++代碼的時(shí)候与倡,如果拋出Objective-C異常界逛,那些棧上的對(duì)象不會(huì)被清理。示例:

class exceptiontest {

public:

exceptiontest() { NSLog(@"Created"); }

~exceptiontest() { NSLog(@"Destroyed"); }

};

void foo() {

exceptiontest a;

NSException *exception = [NSException exceptionWithName:@"foo"

reason:@"bar"

userInfo:nil];

@throw exception;

}

int main(int argc, char *argv[]) {

GMAutoreleasePool pool;

@try {

foo();

}

@catch(NSException *ex) {

NSLog(@"exception raised");

}

return 0;

}

將會(huì)有如下輸出:

2006-09-28 12:34:29.244 exceptiontest[23661] Created

2006-09-28 12:34:29.244 exceptiontest[23661] exception raised

注意這里的析構(gòu)函數(shù)永遠(yuǎn)沒有機(jī)會(huì)被調(diào)用纺座。這是在你想用棧上的智能指針比如shared_ptr,linked_ptr ,還有STL對(duì)象的時(shí)候不得不關(guān)注的一個(gè)核心問題息拜。我們不得不痛心地說,如果你一定要在Objective-C++代碼里拋出異常比驻,那就請(qǐng)一定使用C++的異常该溯。永遠(yuǎn)不要重新拋出一個(gè)Objective-C的異常,也不允許在異常塊即@try,@catch,@finally 里生成棧上的 C++對(duì)象(比如std::string, std::vector 等).??? nil檢查 ?????? 僅在校驗(yàn)邏輯流程時(shí)做nil檢查别惦。??????? 使用nil檢查不是為了防止程序崩潰狈茉,而是校驗(yàn)邏輯流程。向一個(gè)空對(duì)象發(fā)送一條消息是由Objective-C運(yùn)行時(shí)處理的掸掸。方法沒有返回結(jié)果氯庆,你也可以安心走下去.然而這里也有一種,依執(zhí)行架構(gòu)不同而返回尺寸和OS X的版本(這段不懂)(見)Apple's documentation for specifics 扰付。??????? 注意這里和C/C++的空指針檢查是完全不同的堤撵,在那些環(huán)境里,并不處理空指針情況并可能導(dǎo)致你的應(yīng)用程序崩潰羽莺。不過你仍要自己確保提領(lǐng)的指針不為空实昨。??? BOOL類型陷阱 ??????? 整形的轉(zhuǎn)換為BOOL 型的時(shí)候要小心。不要直接和YES做比較盐固。??????? BOOL 在Objective-C里被定義為unsigned char荒给,這意味著它不僅僅只有YES (1)和NO (0)兩個(gè)值丈挟。不要直接把整形強(qiáng)制轉(zhuǎn)換為BOOL 型。常見的錯(cuò)誤發(fā)生在把數(shù)組大小志电,指針的值或者邏輯位運(yùn)算的結(jié)果賦值到BOOL型中曙咽,而這樣就導(dǎo)致BOOL 值的僅取決于之前整形值的最后一個(gè)字節(jié),有可能出現(xiàn)整形值不為0但被轉(zhuǎn)為NO的情況挑辆。應(yīng)此把整形轉(zhuǎn)為BOOL型的時(shí)候請(qǐng)使用ternery操作符例朱,保證返回YES 或NO 值。??????? 在BOOL鱼蝉,_BOOL 以及bool (見C++ Std 4.7.4, 4.12以及C99 Std 6.3.1.2)之間可以安全的交換值或轉(zhuǎn)型洒嗤。但BOOL 和Boolean 之間不可,所以對(duì)待Boolean 就像上面講的整形一樣就可以了蚀乔。在Objective-C函數(shù)簽名里僅使用BOOL 烁竭。??? 對(duì)BOOL值使用邏輯運(yùn)算(&&, ||, ! )都是有效的,返回值也可以安全的轉(zhuǎn)為BOOL型而不需要ternery操作符吉挣。

// AVOID

- (BOOL)isBold {

return [self fontTraits] & NSFontBoldTrait;

}

- (BOOL)isValid {

return [self stringValue];

}

// GOOD

- (BOOL)isBold {

return ([self fontTraits] & NSFontBoldTrait) ? YES : NO;

}

- (BOOL)isValid {

return [self stringValue] != nil;

}

- (BOOL)isEnabled {

return [self isValid] && [self isBold];

}

還有派撕,不要把BOOL 型變量直接與YES 比較。這樣不僅對(duì)于精通C的人很有難度睬魂,而且此條款的第一點(diǎn)也說明了這樣做未必能得到你想要的結(jié)果终吼。

// AVOID

BOOL great = [foo isGreat];

if (great == YES)

// ...be great!

// GOOD

BOOL great = [foo isGreat];

if (great)

// ...be great!

屬性 ??????? 屬性總的來說是遵循如下告誡的: 屬性是Objective-C 2.0的特性,所以只能跑在iPhone以及MacOS X 10.5(leopard)或更高的版本氯哮。點(diǎn)表示法訪問屬性是不被允許的际跪。????? 命名 ??????? 一個(gè)有屬性關(guān)聯(lián)實(shí)例變量都要在后面加下劃線,而該屬性的名稱就是實(shí)例變量不加尾部的下劃線的名字喉钢。??????? 使用@synthesize 標(biāo)識(shí)以正確的重命名屬性姆打。

@interface MyClass : NSObject {

@private

NSString *name_;

}

@property(copy, nonatomic) NSString *name;

@end

@implementation MyClass

@synthesize name = name_;

@end

位置 ??????? 在類接口聲明里,屬性的聲明必須緊挨著實(shí)例變量塊聲明之后肠虽。類定義里幔戏,屬性的定義必須緊挨著@implementation塊∷翱危縮進(jìn)和@interface或@implementation是一樣的闲延。

@interface MyClass : NSObject {

@private

NSString *name_;

}

@property(copy, nonatomic) NSString *name;

@end

@implementation MyClass

@synthesize name = name_;

- (id)init {

...

}

@end

對(duì)字符串使用拷貝特性 ?????? NSString 類型的屬性應(yīng)該永遠(yuǎn)聲明為帶有copy attribute ??????? 這點(diǎn)是因?yàn)镹SString 的setters 永遠(yuǎn)使用copy 而不是retain 。????? 永遠(yuǎn)不要對(duì)CFType屬性使用synthesize ??????? CFType類型應(yīng)該永遠(yuǎn)遵守@dynamic的實(shí)現(xiàn)規(guī)則韩玩。??????? 因?yàn)镃FType類型不能有reatin 屬性特性垒玲,開發(fā)者必須自己維護(hù)。(下面不懂找颓,原文In the rare case that you do actually want assignment it is better to make that completely clear by actually implementing the setter and getter and commenting why that is the case. )????? 列出所有的實(shí)現(xiàn)指令 ??????? 為所有屬性使用實(shí)現(xiàn)指令合愈,即使默認(rèn)是@dynamic??????? 盡管@dynamic是默認(rèn)的,顯式的列出所有屬性實(shí)現(xiàn)指令還是讓類中的每個(gè)屬性更加清楚。

// AVOID

@interface MyClass : NSObject

@property(readonly) NSString *name;

@end

@implementation MyClass

.

.

.

- (NSString*)name {

return @"foo";

}

@end

// GOOD

@interface MyClass : NSObject

@property(readonly) NSString *name;

@end

@implementation MyClass

@dynamic name;

.

.

.

- (NSString*)name {

return @"foo";

}

@end

原子性 ??????? 屬性使用過猶不及想暗。默認(rèn)情況妇汗,所有的synthesized setters 以及 getters 都是原子性的帘不,顯然所有的get说莫,set方法都過度使用了同步方法。所以除非你確實(shí)需要原子性寞焙,否則都將你的屬性聲明為nonatomic ????? 點(diǎn)表示法 ??????? 我們禁止使用點(diǎn)表示法是基于如下理由的:??????????? 1. 點(diǎn)表示法不過是標(biāo)準(zhǔn)函數(shù)調(diào)用的語法糖储狭,它的可讀性很有爭(zhēng)議。也不過就是換個(gè)形式調(diào)用函數(shù)而已捣郊。??????????? 2. 它使得提領(lǐng)操作含糊不請(qǐng).首先看: [foo setBar:1] 很清楚你就明白這是Objective-C對(duì)象上的操作辽狈。那么看 foo.bar = 1 就不太清楚到底是對(duì)Objective-C的對(duì)象還是結(jié)構(gòu)體/公用體/C++類進(jìn)行操作了。??????????? 3. 它使得函數(shù)調(diào)用看起來就像getters

NSString *upperCase = @"foo".uppercaseString;

不僅使人困惑呛牲,也很難在代碼審核的時(shí)候檢查出來刮萌。??????????? 4. 它隱藏了函數(shù)調(diào)用

bar.value += 10;

這實(shí)際上做了兩次函數(shù)調(diào)用(一次get一次set),而如果你的類很復(fù)雜的話娘扩,你發(fā)現(xiàn)一大堆事都在幕后做完了着茸。Cocoa模式 ??? 委托模式 ??????? 委托對(duì)象不應(yīng)該是retain ??????? 一個(gè)實(shí)現(xiàn)委托模式的類應(yīng)該:??????????? 1. 實(shí)例變量命名為delegate_ 以顯示這是個(gè)委托??????????? 2. 因此,訪存方法名就為delegate 和setDelegate ??????????? 3. delegate_ 變量不能設(shè)為retain ??? MVC模式 ??????? 將模型與視圖分離琐旁,將控制器從視圖和模型中分離涮阔,回調(diào)APIs使用@protocol ??????? 模型與視圖分離: 不要對(duì)模型或數(shù)據(jù)源的表現(xiàn)形式做任何假設(shè)。保持?jǐn)?shù)據(jù)與表現(xiàn)層的接口的抽象性灰殴。模型不對(duì)視圖有任何了解(一個(gè)好的實(shí)踐就是詢問你自己是否能對(duì)數(shù)據(jù)有多個(gè)不同形式的表現(xiàn)方法)??????? 控制器從視圖和模型中分離: 不要把"業(yè)務(wù)邏輯"放到視圖相關(guān)的類敬特;這會(huì)導(dǎo)致代碼可復(fù)用性下降。讓控制器的類承管代碼牺陶,但不要對(duì)表現(xiàn)形式做太多假設(shè)伟阔。??????? 用@protocol 定義回調(diào)APIs: 如果不是所有的方法都必須實(shí)現(xiàn)就使用@optional (例外:在Objecitve-C 1.0的時(shí)候,@optional 還不可用掰伸,所以用類別去定義一個(gè)"非標(biāo)準(zhǔn)協(xié)議(informal protocol)")

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末皱炉,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子碱工,更是在濱河造成了極大的恐慌娃承,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件怕篷,死亡現(xiàn)場(chǎng)離奇詭異历筝,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)廊谓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門梳猪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事春弥∏河矗” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵匿沛,是天一觀的道長(zhǎng)扫责。 經(jīng)常有香客問我,道長(zhǎng)逃呼,這世上最難降的妖魔是什么鳖孤? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮抡笼,結(jié)果婚禮上苏揣,老公的妹妹穿的比我還像新娘。我一直安慰自己推姻,他們只是感情好平匈,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著藏古,像睡著了一般增炭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上校翔,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天弟跑,我揣著相機(jī)與錄音,去河邊找鬼防症。 笑死孟辑,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蔫敲。 我是一名探鬼主播饲嗽,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼奈嘿!你這毒婦竟也來了貌虾?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤裙犹,失蹤者是張志新(化名)和其女友劉穎尽狠,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體叶圃,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡袄膏,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了掺冠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片沉馆。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出斥黑,到底是詐尸還是另有隱情揖盘,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布锌奴,位于F島的核電站兽狭,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏缨叫。R本人自食惡果不足惜椭符,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望耻姥。 院中可真熱鬧,春花似錦有咨、人聲如沸琐簇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽婉商。三九已至,卻和暖如春渣叛,著一層夾襖步出監(jiān)牢的瞬間丈秩,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工淳衙, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蘑秽,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓箫攀,卻偏偏與公主長(zhǎng)得像肠牲,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子靴跛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容