*本文摘錄自[饒志臻的博客] 屬性聲明在@implementation里與extension里的區(qū)別
當你新建一個類的時候,Xcode會自動給你寫上以下代碼萍桌。
#import <Foundation/Foundation.h>
@interface Car : NSObject
@end
#import "Car.h"
@implementation Car
@end
Objective-C編譯器指令是以@打頭凌简,它通常用來描述文件中的內(nèi)容雏搂。.h文件中@interface指令用來標識文件的接口代碼的起始位置凸郑,而@end指令標示該段的結束位置。在.m文件中芙沥,@implementation指令用來標識實現(xiàn)的起始位置诲祸,@end標識結束位置
@interface用于定義類的公共接口,通常而昨,接口被稱為API(application programming interface)而真正使對象能夠運行的代碼救氯,位于@implementation中。
當我們要給一個Car類聲明一個發(fā)動機屬性的時候歌憨,如果對外公開着憨,則代碼為
#import <Foundation/Foundation.h>
@interface Car : NSObject
@property (nonatomic, strong) Engine *engine;
@end
如果不對外公開,則在.m里的代碼為
@interface Car ()
@property (nonatomic, strong) Engine *engine;
@end
@interface Car ()看起來和.h里的@interface Car : NSObject很像躺孝,其實@interface Car ()是一個特殊的匿名 Category享扔,即擴展(extension)。
類別(Category)是一種為現(xiàn)有的類添加新方法的方式植袍。
利用Objective-C的動態(tài)運行時分配機制惧眠,Category提供了一種比繼承(inheritance)更為簡潔的方法來對class進行擴展,無需創(chuàng)建對象類的子類就能為現(xiàn)有的類添加新方法氛魁,可以為任何已經(jīng)存在的class添加方法,包括那些沒有源代碼的類(如某些框架類)或链,申明的方法不需要在@implementation里實現(xiàn)祈纯。
但Category無法向類中添加新的實例變量,類別沒有空間容納實例變量簇爆。(也有一些技術可以克服類別無法增加新實例變量的局限。例如安寺,使用全局字典來存儲對象與你想要關聯(lián)的額外變量之間的映射软能。)
而extension可以添加新的實例變量
@property是以@開頭凳枝,所以它也是Objective—C編譯器指令,用于聲明屬性蹋订,并為它自動創(chuàng)建一個帶下劃線的實例變量捶箱,及實例變量的setter和getter方法荠锭。
而直接聲明實例變量的寫法,即
@interface Car () {
Engine *_engine;
}
@end
和
@implementation Car {
Engine *_engine;
}
@end
從語法上說它們等效。
如果只是聲明一個@implementation里需要用到的全局變量叫搁,自然是放在@implementation里聲明,但如果是聲明一個不對外公開的屬性呢,比如engine梨撞,既然是屬性,好像是需要在extension里聲明港粱,但如果我使用_engine來訪問成員變量宁炫,則并不會用到它的setter和getter方法遥昧。如果我使用點語法來訪問成員變量呢,點語法其實是調(diào)用了getter方法[Car engine],而這種默認的隱藏在代碼中多了落午,會影響代碼的閱讀和維護。
但engine明明是Car的一個屬性,卻聲明在@implementation里作為一個變量,其實實例變量也是這個對象的構成元素走哺,和屬性除了名字并沒有涵義上的區(qū)別。所以在@implementation里聲明的變量也是這個對象的屬性,只是為了區(qū)分兩種聲明方式的叫法不同而已敢朱。
另一個用@property和@implementation聲明屬性的區(qū)別就是旗们,@property可以給屬性添加屬性標識符岸梨,即assign,copy,weak纠永,strong尝江,nonatomic,但其實大部分的屬性標識符都有對應的所有權修飾符英上,assign對應__unsafe_unretained茂装,copy對應__strong修飾符(但copy賦值的是被復制的對象)善延,strong對應__strong豆茫,weak對應__weak。id和對象類型在沒有明確指定所有權修飾符時屋摇,默認為__strong修飾符火脉,而@property聲明屬性的默認屬性標識符為readwrite方援,assign, atomic先匪。atomic的確沒有對應的所有權修飾符哥桥,id和對象類型自然是沒有原子性的边涕,在iOS開發(fā),除非特殊需要褂微,我們都會給屬性標識符添加nonatomic功蜓,所以在這點上,@property和@implementation聲明屬性倒是沒什么區(qū)別宠蚂。
在@interface里使用@property聲明屬性的時候式撼,如果屬性類型為NSString,它的屬性標識符是需要添加copy的求厕,原因就在與著隆,設置方法的新值有可能指向一個NSMutableString類的實例,那么設置完屬性之后甘改,字符串的值就可能會在對象不知情的情況下遭人更改旅东,那在@implementation里聲明一個NSString會不會有這個顧慮呢?copy不是簡單的賦值十艾,對應的__strong并不會通過copyWithZone:方法復制賦值源所生產(chǎn)的對象抵代,所以@implementation里聲明的NSString沒有copy作用的修飾符,但在@implementation里聲明即這個屬性是不對外公開的忘嫉,即不會被其它對象直接修改這個屬性荤牍,那你既然聲明了一個NSString類型的屬性,自然用意就是使用一個不可變的字符串庆冕,自然自己不會去修改它康吵,如果你無意中修改了它,我只能說這是你的代碼寫錯了访递。所以不需要使用copy作用的修飾符晦嵌。同理,在extension里使用@property聲明NSString,也是不需要copy屬性標識符的惭载。所以NSString在@implementation里聲明并不會有所影響旱函。
博客:xuyafei.cn
簡書:jianshu.com/users/2555924d8c6e
微博:weibo.com/xuyafei86
Github:github.com/xiaofei86
總結
在@implementation里聲明并沒有缺點,但在extension里使用@property聲明屬性描滔,會有不帶來價值的隱藏代碼棒妨,以及_engine比self.engine更簡短易讀,最后還有可以避免在init和dealloc中會去調(diào)用self.engine含长。