根據(jù)蘋果官方cocoa的文檔總結(jié)的Objective-C軟件編碼規(guī)范。
8.1.成員變量應(yīng)該定義為@private.11
8.10.構(gòu)建時(shí)即設(shè)定autorelease.14
8.11.優(yōu)先autorelease而非retain.14
8.12.以聲明時(shí)的順序dealloc處理實(shí)例變量...14
統(tǒng)一編程風(fēng)格雌贱,提高的可讀性與編碼效率庄新,避免團(tuán)隊(duì)開發(fā)可能帶來的混亂。
本規(guī)范適用于公司項(xiàng)目產(chǎn)品運(yùn)用Objective-C作為開發(fā)語言的編碼活動(dòng)澄峰。
規(guī)則:編程時(shí)必須遵守的約定
建議:編程時(shí)需要考慮的約定
綠色代碼:對(duì)此規(guī)則或建議給出的正確例子
紅色代碼:對(duì)此規(guī)則或建議給出的反面例子
匈牙利命名法:是一種編程時(shí)的命名規(guī)范嫉沽。基本原則是:變量名=屬性+類型+對(duì)象描述俏竞,其中每一對(duì)象的名稱都要求有明確含義绸硕,可以取對(duì)象名字全稱或名字的一部分
駝峰命名法:就是當(dāng)變量名或函式名是由一個(gè)或多個(gè)單字連結(jié)在一起,而構(gòu)成的唯一識(shí)別字時(shí)魂毁,駝峰命名法第一個(gè)單字以小寫字母開始玻佩;第二個(gè)單字的首字母大寫或每一個(gè)單字的首字母都采用大寫字母
先看一個(gè)實(shí)例對(duì)代碼的基本格式有大致了解,可以看到基本的間距席楚,命名等等咬崔。
下例是一份頭文件,展示對(duì)@interface聲明正確的注釋和留間距烦秩。
//GTMFoo.h
//FooProject
//
//Created by Greg Miller on 6/13/08.
//Copyright 2008 Google, Inc. All rightsreserved.
//
#import
// A sample classdemonstrating good Objective-C style. All interfaces,
// categories, andprotocols (read: all top-level declarations in a header)
// MUST becommented. Comments must also be adjacent to the object they're
// documenting.
//
// (no blank linebetween this comment and the interface)
@interface GTMFoo: NSObject {
@private
NSString *foo_;
NSString *bar_;
}
// Returns anautoreleased instance of GMFoo. See -initWithString: for details
// about theargument.
+(id)fooWithString:(NSString *)string;
// Designatedinitializer. |string| will be copied and assigned to |foo_|.
-(id)initWithString:(NSString *)string;
// Gets and setsthe string for |foo_|.
- (NSString *)foo;
-(void)setFoo:(NSString *)newFoo;
// Does some workon |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 rightsreserved.
//
#import"GTMFoo.h"
@implementationGTMFoo
+(id)fooWithString:(NSString *)string {
return [[[selfalloc] initWithString:string] autorelease];
}
// Must alwaysoverride super's designated initializer.
- (id)init {
return [selfinitWithString:nil];
}
- (id)initWithString:(NSString*)string {
if ((self =[super init])) {
foo_ = [stringcopy];
bar_ =[[NSString alloc] initWithFormat:@"hi %d", 3];
}
returnself;
}
- (void)dealloc {
[foo_ release];
[bar_ release];
[super dealloc];
}
- (NSString *)foo{
return foo_;
}
-(void)setFoo:(NSString *)newFoo {
[foo_autorelease];
foo_ = [newFoocopy];
}
-(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)的空格。
代碼中的每行文本不要超過80個(gè)字符的長(zhǎng)度岭皂。
盡管Objective-C正變得比C++更加繁冗郊霎,為了保持規(guī)范的互通性,還是決定保持80字符長(zhǎng)度的限制爷绘。
你可以在Xcode里清楚地發(fā)現(xiàn)代碼中的違規(guī)书劝,設(shè)置Xcode>Preferences>TextEditing>Showpageguide进倍。(之后就可以在代碼編輯區(qū)域里看到一條指定字符長(zhǎng)度的指示線了)
留一個(gè)空格在-或+和返回類型之間,但參數(shù)列表里的參數(shù)之間不要留間隔购对。
參數(shù)對(duì)象的星號(hào)前需要加空格猾昆。
方法應(yīng)該寫成這樣:
- (void)doSomethingWithString:(NSString *)theString {
...
}
如果參數(shù)過多,推薦每個(gè)參數(shù)各占一行骡苞。使用多行的情況下垂蜗,在參數(shù)前加冒號(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)用的格式和方法聲明時(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)格的寫法:
[myObjectdoFooWith:arg1 name:arg2// some lineswith >1 arg
error:arg3];
[myObjectdoFooWith:arg1
name:arg2 error:arg3];
[myObjectdoFooWith: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];
5.5.@public和@private
權(quán)限控制符@public和@private縮進(jìn)一個(gè)空格:
@interface MyClass : NSObject {
@public
...
@private
...
}
@end
每個(gè)異常標(biāo)簽的@和開括號(hào)({)寫在統(tǒng)一行,標(biāo)簽和開括號(hào)間隔一個(gè)空格望浩,同樣適用于@catch語句辖所。
@interface MyClass : NSObject {
@public
...
@private
...
}
@end
5.7.Protocols
在類型定義和被中括號(hào)括起來的Protocols名稱之間不需要空格。
本規(guī)矩適用于類定義磨德,變量聲明和方法聲明時(shí)使用缘回。參考代碼:
@interface MyProtocoledClass :NSObject {
@private
id delegate_;
}
- (void)setDelegate:(id)aDelegate;
@end
所有類,類別剖张,方法,以及變量如包括縮寫揩环,則縮寫部分使用全大寫的縮寫(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ì)可讀性的極大損害。
6.1.文件命名
文件名反映了它所包含的實(shí)現(xiàn)類的名字涨享,遵從你所在項(xiàng)目的習(xí)慣筋搏。
文件擴(kuò)展名使用如下規(guī)則:
.h
C/C++/Objective-C header file
.m
Objective-C implementation file
.mm
Objective-C++ implementation file
.cc
Pure C++ implementation file
.c
C implementation file
類別的文件名應(yīng)該包含擴(kuò)展類的名字,比如GTMNSString+Utils.h or
GTMNSTextView+Autocomplete.h厕隧。
在一份源文件里奔脐,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.h
class CrossPlatformAPI {
public:
...
intDoSomethingPlatformSpecific();// implon each platform
private:
intan_instance_var_;
};
// file: mac_implementation.mm
#include "cross_platform_header.h"
// 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 fromCocoa 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ī)則就沒必要恪守了革砸。
6.5.Objective-C方法命名
方法使用小寫開頭的駝峰法命名,每個(gè)參數(shù)都應(yīng)該小寫開頭糯累。
方法名應(yīng)該盡可能讀起來像一句話算利,參數(shù)名就相對(duì)方法名的補(bǔ)充說明(比如convertPoing:fromRect:或者replaceCharactersInRange:withString:)。
存取(Accessor)方法應(yīng)該一致的在"取(getting)"的時(shí)候直接用變量名而不是在簽名加"get"泳姐,如下:
- (id)getDelegate;// AVOID
- (id)delegate;// GOOD
不過這僅針對(duì)Objective-C代碼效拭,C++代碼仍然遵循自己的代碼規(guī)范。
變量名使用小寫開頭的駝峰法,類成員變量名最后加一個(gè)下劃線允耿,比如:myLovalVariable借笙,myInstanceVariable_。
不要使用匈牙利命名法去標(biāo)記語法较锡,比如靜態(tài)類型或變量類型(int或pointer之類的)业稼。使變量名盡量可以推測(cè)其用途屬性具有描述性。別一心想著少打幾個(gè)字母蚂蕴,讓你的代碼可以迅速被理解更加重要低散。比如:
int w;
int nerr;
int nCompConns;
tix = [[NSMutableArray alloc] init];
obj = [someObject object];
p = [network port];
int numErrors;
int numCompletedConnections;
tickets = [[NSMutableArray alloc] init];
userInfo = [someObject object];
port = [network port];
實(shí)體變量用駝峰法命名并后綴下劃線,就像usernameTextField_骡楼。允許一種例外就是用KVO/KVC去綁定一個(gè)實(shí)體變量而Objective-C2.0不能用(因?yàn)椴僮飨到y(tǒng)的限制)的情況熔号,此時(shí)也可用前綴下劃線的方法給每個(gè)變量命名。如果可以使用Objective-C2.0鸟整,@property和@synthesize提供了遵守命名規(guī)范的解決方法引镊。
常量(預(yù)定義,枚舉篮条,局部常量等)使用小寫k開頭的駝峰法弟头,比如kInvalidHandle,kWritePerm涉茧。
注釋是使代碼保持可讀性的極端重要的方式赴恨。下面的條款描述了你應(yīng)該注釋什么以及在哪里做注釋。但是記住:即使注釋是如此重要伴栓,最好的代碼還是自說明式的伦连。起一個(gè)有意義的名字比起一個(gè)晦澀的名字然后在用注釋去解釋它好的多。
寫注釋的時(shí)候钳垮,記住注釋是寫給讀者惑淳,即下一個(gè)要理解你的代碼并繼續(xù)開發(fā)的人。"下一個(gè)"完全可能就是你自己饺窿。
同樣歧焦,所有C++編程規(guī)范的條款仍然適用,只是增加了一些條款短荐,如下:
文件注釋:
每個(gè)文件的開頭都是版權(quán)聲明倚舀,接著是文件內(nèi)容的描述叹哭。
法律聲明和作者欄:
每個(gè)文件都必須包含如下信息:
本文件內(nèi)容描述和作者欄
如果你把別人寫的文件做了相當(dāng)大的改動(dòng)忍宋,就把自己添加到作者欄去。這樣別的開發(fā)者就方便聯(lián)系這個(gè)文件的實(shí)際開發(fā)人員了风罩。
每個(gè)接口糠排,類別,協(xié)議都必須伴隨描述它的用途以及如何整合的注釋超升。
// A delegate for NSApplication to handle notificationsabout 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í)例可以多線程訪問轿钠,必須要加上額外的說明。
使用豎線引用變量或符號(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 barbaz")|
8.Cocoa和Objective-C特性
8.1.成員變量應(yīng)該定義為@private
參考代碼:
@interface MyClass : NSObject {
@private
idmyInstanceVariable_;
}
// 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ò)誤。
強(qiáng)烈建議在@implementation之后就立即重寫NSObject的方法蔑鹦。建議重寫init...夺克,copyWithZone:和dealloc方法。init...相關(guān)的方法寫在一起嚎朽,接下來是copyWithZone:铺纽,最后是dealloc。
不要調(diào)用NSObject的new方法哟忍,也不要在子類中重寫它狡门,而是應(yīng)該使用alloc和init方法來初始化retained的對(duì)象。
Objective-C代碼顯式調(diào)用alloc和init方法來創(chuàng)建和retain一個(gè)對(duì)象锅很。new的方法可能會(huì)帶來內(nèi)存上調(diào)試的麻煩其馏。
沒必要在初始化方法里把變量初始化為0或者nil,這是多余的爆安。
所有新分配內(nèi)存的對(duì)象內(nèi)容都初始化為0(除了isa)叛复,所以不要在init方法里做無謂的重初始化為0的操作。
8.7.保持公有API簡(jiǎn)明
保持你的類簡(jiǎn)單,如果一個(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 thismethod
}
...
@end
8.8.#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
...
8.10.構(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 performancereason)
MyController* controller = [[MyController alloc] init];
// ... code here that might return ...
[controller release];
// BETTER
MyController* controller = [[[MyController alloc] init]autorelease];
8.11.優(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_ = [aFooretain];
}
8.12.以聲明時(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í)例變量被這些方法所處理了咳胃。
NSStrings
在NSString上調(diào)用Setters方法時(shí)植康,永遠(yuǎn)使用copy方式。永遠(yuǎn)不要retain一個(gè)字符串展懈,這可以防止調(diào)用者在你不知到的情況下修改了字符串销睁。不要以為你可以改變NSString的值,只有NSMutableString才能做到存崖。
- (void)setFoo:(NSString *)aFoo {
[foo_autorelease];
foo_ = [aFoocopy];
}
不要@throwObjective-C的異常榄攀,不過你還是要做好準(zhǔn)備捕獲第三方以及系統(tǒng)調(diào)用拋出的異常。
我們的確在編譯時(shí)加入了-fobjc-exceptions指令(主要是為了獲得@synchronized)金句,但我們并不@throw檩赢。當(dāng)然在使用第三方庫(kù)的時(shí)候是允許使用@try,@catch违寞,以及@finally的贞瞒。如果你確實(shí)使用了,請(qǐng)務(wù)必明確到文檔中哪個(gè)方向你想拋出什么異常趁曼。
除非你寫的代碼想要泡在MacOS10.2或更之前军浆,否則不要使用NS_DURING,NS_HANDLER挡闰,NS_ENDHANDLER乒融,NS_VALUERETURNandNS_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[]) {
GMAutoreleasePoolpool;
@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] exceptionraised
注意這里的析構(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等)廉涕。
8.15.nil檢查
僅在校驗(yàn)邏輯流程時(shí)做nil檢查命迈。
使用nil檢查不是為了防止程序崩潰,而是校驗(yàn)邏輯流程火的。向一個(gè)空對(duì)象發(fā)送一條消息是由Objective-C運(yùn)行時(shí)處理的壶愤。方法沒有返回結(jié)果,你也可以安心走下去馏鹤。
注意這里和C/C++的空指針檢查是完全不同的征椒,在那些環(huán)境里,并不處理空指針情況并可能導(dǎo)致你的應(yīng)用程序崩潰湃累。不過你仍要自己確保提領(lǐng)的指針不為空勃救。
8.16.BOOL類型陷阱
整形的轉(zhuǎn)換為BOOL型的時(shí)候要小心。不要直接和YES做比較治力。
BOOL在Objective-C里被定義為unsignedchar蒙秒,這意味著它不僅僅只有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++Std4.7.4,4.12以及C99Std6.3.1.2)之間可以安全的交換值或轉(zhuǎn)型涤伐。但BOOL和Boolean之間不可馒胆,所以對(duì)待Boolean就像上面講的整形一樣就可以了缨称。在Objective-C函數(shù)簽名里僅使用BOOL。
對(duì)BOOL值使用邏輯運(yùn)算(&&祝迂,||睦尽,!)都是有效的,返回值也可以安全的轉(zhuǎn)為BOOL型而不需要ternery操作符液兽。
- (BOOL)isBold {
return [selffontTraits] & NSFontBoldTrait;
}
- (BOOL)isValid {
return [selfstringValue];
}
- (BOOL)isBold {
return ([selffontTraits] & NSFontBoldTrait) ? YES : NO;
}
- (BOOL)isValid {
return [selfstringValue] != nil;
}
- (BOOL)isEnabled {
return [selfisValid] && [self isBold];
}
還有,不要把BOOL型變量直接與YES比較掌动。這樣不僅對(duì)于精通C的人很有難度四啰,而且此條款的第一點(diǎn)也說明了這樣做未必能得到你想要的結(jié)果。
BOOL great = [foo isGreat];
if (great == YES)
// ...be great!
BOOL great = [foo isGreat];
if (great)
// ...be great!
屬性遵循如下規(guī)則:屬性是Objective-C2.0的特性粗恢,所以只能跑在iPhone以及MacOSX10.5(leopard)或更高的版本柑晒。
一個(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
屬性的聲明必須緊接變量申明的括號(hào)后。屬性的定義應(yīng)該緊接@implementation模塊后面妖碉。它和@interface或者@implementation的縮進(jìn)是相同的涌庭。
@interface MyClass : NSObject {
@private
NSString *name_;
}
@property(copy, nonatomic) NSString *name;
@end
@implementation MyClass
@synthesize name = name_;
- (id)init {
...
}
@end
8.18.為NSString使用Copy屬性
NSString的屬性定義為copy。
如果自己實(shí)現(xiàn)setters方法欧宜,也請(qǐng)使用copy而不是retain坐榆。