Object-C 聲明屬性爲什麼用下劃線,代碼規範和編程風格
#import <UIKit/UIKit.h>
@interface NewPlayerController : UIViewController{
NSString *test;
}
@property(nonatomic,retain) NSString *test;
@end
在*.m中
#import "NewPlayerController.h"
@implementation NewPlayerController
@synthesize test;
- (void)viewDidLoad
{
[super viewDidLoad];
test=[[NSString alloc] initWithFormat:@"test"];
}
@end
但是,發現很多別人寫的代碼是這樣子的:
#import <UIKit/UIKit.h>
@interface NewPlayerController : UIViewController{
NSString* _test;
}
@property(nonatomic,retain) NSString *test;
@end
在*.m中
#import "NewPlayerController.h"
@implementation NewPlayerController
@synthesize test=_test;
- (void)viewDidLoad
{
[super viewDidLoad];
_test=[[NSString alloc] initWithFormat:@"test"];
// 或者這樣
self.test=[[NSString alloc] initWithFormat:@"test"];
}
@end
這兩種方式到底有什麼區別?用那種好?
帶着這個疑問,我去網上搜索"代碼風格",“編碼規範”,“下劃線公約”。解釋太寬泛了,根本沒有一個定論。
於是只有理論+實踐自己在走一回吧。
開始把:
第一種方式,就是平常我們說的不帶下劃線的那種方式:
self.test=[[NSString alloc] initWithFormat:@"test"];
NSLog(@"self.test的應用計數:%d",[self.test retainCount]);
NSLog(@"test的應用計數:%d",[test retainCount]);
但是,我驚奇的發現,輸出竟然這樣:
2012-11-17 15:52:29.604 ArtTV[901:14303] self.test的應用計數:2
2012-11-17 15:52:33.264 ArtTV[901:14303] test的應用計數:2
於是,代碼變成這樣寫(僅僅是,self.test換成test):
test=[[NSString alloc] initWithFormat:@"test"];
NSLog(@"self.test的應用計數:%d",[self.test retainCount]);
NSLog(@"test的應用計數:%d",[test retainCount]);
輸出:
2012-11-17 15:59:58.274 ArtTV[954:14303] self.test的應用計數:1
2012-11-17 15:59:59.718 ArtTV[954:14303] test的應用計數:1
這纔是我們所期望的。應用計數應該爲1纔對,爲什麼會變成2呢?
恍然大悟了!原來原因出在這裏:我們申明test時,用的屬性修飾符retain。
當我們,使用self.test時,就使用了編譯器爲我們生成的setXXX方法。在該方法中retainCount被加1,所以,變爲2.
這樣的花,如果,我們在代碼中,直接用
self.test=[[NSString alloc] initWithFormat:@"test"];
這行代碼後,test變量的應用計數變成了2:
相當於:
首先,聲明一個字符串對象,這時候,引用計數爲1.
其次,再將test的值賦給self.test。(相當於,使用了setXXX方法,讓retainCount,加1),導致應用計數變成2.
所以,在類中,如果,僅僅是指針賦值,(將一個對象的指針賦給另一個指針)儘量避免使用self.test進行賦值。這樣會引起引用計數+1,容易引起釋放內存泄漏。而直接用test來賦值。
也就是,在方法中,使用self.xxxx,進行賦值,就會使用編譯器生成的setXXX方法,從而根據申明對象時的屬性(copy,retain,assign)進行調用setXXX方法。
如果,只用屬性之間來賦值(test= newValue;,就是不帶self),那麼,僅僅是指針之間的賦值。
第二種方式,就是帶下劃線的那種方式:
這樣,讓我們驗證:
代碼中,我們這麼寫:
test=[[NSString alloc] initWithFormat:@"test"];
我們會收到一個錯誤提示:
不管它,那就用它推薦的方法,繼續寫完:
self.test=[[NSString alloc] initWithFormat:@"test"];
NSLog(@"self.test的應用計數:%d",[self.test retainCount]);
NSLog(@"_test的應用計數:%d",[_test retainCount]);
運行代碼,輸出如下:
2012-11-17 16:30:39.525 ArtTV[1042:14303] self.test的應用計數:2
2012-11-17 16:30:41.553 ArtTV[1042:14303] _test的應用計數:2
運行輸出,很顯然,應用計數爲2,不是我們想要的結果。原因,跟我們討論的第一種情況,是一樣的。
這僅僅是一種調用的另一種方式:
默認情況,調用test,當我們申明test=_test;時候,那麼在類內部使用_test。
在使用_test,時候,也僅僅是指針的賦值。
使用self.test,就是要調用編譯器生成的響應的getXXX,setXXX方法了。
爲了證明我們的想法,我們將代碼修改成這樣:
_test=[[NSString alloc] initWithFormat:@"test"];
NSLog(@"self.test的應用計數:%d",[self.test retainCount]);
NSLog(@"_test的應用計數:%d",[_test retainCount]);
輸出如下:
2012-11-17 16:36:09.089 ArtTV[1074:14303] self.test的應用計數:1
2012-11-17 16:36:10.701 ArtTV[1074:14303] _test的應用計數:1
關於,使用方式,我們就將到這裏吧。親自動手,實驗一下。就明白其中的奧妙了!
但是,還沒有結束!
另一個疑惑,也隨之而來。
我們用那種方式好呢????
我看了一些帖子,說,蘋果不提倡在我們定義的類中使用“_xxxx”,這種形式的屬性聲明,因爲,在很多蘋果提供的框架中,大量使用“_xxxx”這種屬性聲明。
——————————————————————————————————————
避免使用下劃線作爲前綴,特別是在私有方法中。蘋果公司保留並使用本公約。使用第三方可能會導致命名空間衝突;自己不知不覺中,可能會覆蓋現有的私有方法,帶來災難性的後果。
——————————————————————————————————————打開一個看看:
在這種情況下,如果,我們,繼承了某個框架類,並且無意間聲明的“_xxxx”屬性跟父類的相同,那麼可能就會覆蓋掉,從而引發不可估計的後果!
但是,我看蘋果幫助文檔中寫的許多例子也在用“_xxxx”這種方式啊!所以,我覺的可以用,但要注意!
爲什麼是可以用呢?
因爲個人感覺:“_xxxx”確實很好用!
1.從風格上表明類的內部變量。
2.意在指明這個變量是內部變量(類外部不會使用。。。)
3.外部訪問用obj.xxxxx, 避免對類變量的直接訪問。
4.這樣的話,要是需要直接引用變量就用_xxxx,當需要用get,set方法時,就用self.xxx。
匯成一句話:
下劃線和非下劃線的使用,可以說是一種習慣問題吧。不用太過於糾結!
默認情況下,@synthesize name;編譯器爲我們生成的get,set方法中所使用的變量名稱,跟我們申明的變量名稱時一樣的(僅僅用self.name和name來區分確實不夠理想)。
但是,當我們用@synthesize name=_name;時,就爲屬性取了一個別名,那樣的話,指針變量,跟編譯器生成的get,set方法爲屬性賦值時就容易區分了!