你要知道的@property都在這里
轉(zhuǎn)載請(qǐng)注明出處 http://www.reibang.com/p/646ae400fe7b
本文大綱
- Apple Adopting Modern Objective-C翻譯
- @property基本用法
- @property修飾符詳解
- @property進(jìn)階話題: 深入代碼理解
Apple在Adopting Modern Objective-C一文中介紹了現(xiàn)代化OC的寫法初婆,其中就介紹盡量使用@property定義類的屬性蓬坡,先來看看蘋果是怎么介紹property的。
Apple Official Property Introduction
Objective-C的屬性(property)是通過用@property
定義的公有或私有的方法磅叛。例如:
@property(readonly, getter=isBlue) BOOL blue;
屬性捕獲了對(duì)象的狀態(tài)屑咳。它們反映了對(duì)象的固有屬性(intrinsic attributes)以及對(duì)象與其他對(duì)象之間的關(guān)系。屬性(property)提供了一種安全弊琴、便捷的方式來與這些屬性(attribute)交互兆龙,而不需要手動(dòng)編寫一系列的訪問方法,如果需要的話可以自定義getter和setter方法來覆蓋編譯器自動(dòng)生成的相關(guān)方法敲董。
盡量多的使用屬性(property)而不是實(shí)例變量(attribute)因?yàn)閷傩裕╬roperty)相比實(shí)例變量有很多的好處:
- 自動(dòng)合成getter和setter方法紫皇。當(dāng)聲明一個(gè)屬性(property)的時(shí)候編譯器默認(rèn)情況下會(huì)自動(dòng)生成相關(guān)的getter和setter方法
- 更好的聲明一組方法。因?yàn)樵L問方法的命名約定腋寨,可以很清晰的看出getter和setter的用處聪铺。
- 屬性(property)關(guān)鍵詞能夠傳遞出相關(guān)行為的額外信息。屬性提供了一些可能會(huì)使用的特性來進(jìn)行聲明萄窜,包括
assign
(vscopy
),weak
,strong
,atomic
(vsnonatomic
),readwrite
,readonly
等铃剔。
屬性方法遵守一個(gè)簡(jiǎn)單的命名約定。getter的名字與屬性名相同(如:屬性名為date
則getter的名字也為date
)查刻,setter的名字則是屬性名字加上set
前綴并采用駝峰命名規(guī)則(如:屬性名為date
則setter的名字為setDate
)键兜。布爾類型的屬性還可以定義一個(gè)以is
開頭的getter方法,如:
@property (readonly, getter=isBlue) BOOL blue;
如果按照上面的方法聲明則以下所有訪問方式都正確:
if (color.blue) {}
if (color.isBlue) {}
if ([color isBlue]) {}
當(dāng)決定什么東西可以作為一個(gè)屬性的時(shí)候赖阻,需要注意以下這些不屬于屬性:
- init方法
- copy和mutableCopy方法
- 類工廠方法
- 開啟某項(xiàng)操作并返回一個(gè)BOOL結(jié)果的方法
- 明確的改變了一個(gè)getter的內(nèi)部狀態(tài)的副作用方法
除此之外蝶押,在你的代碼中使用屬性特性的時(shí)候請(qǐng)考慮以下規(guī)則:
- 一個(gè)可讀寫(read/write)的屬性有兩個(gè)訪問方法。setter方法是有一個(gè)參數(shù)的無返回值方法火欧,getter方法是沒有參數(shù)的且有一個(gè)返回值的方法棋电,返回值類型與屬性聲明的類型一致茎截。如果將這組方法轉(zhuǎn)換成一個(gè)屬性,就可以用readwrite關(guān)鍵字來標(biāo)記它(默認(rèn)即為
readwrite
可不寫)赶盔。 - 一個(gè)只讀(read-only)的屬性只有一個(gè)訪問方法企锌。即getter方法,它不接受任何參數(shù)于未,并且返回一個(gè)值撕攒。如果將這個(gè)方法轉(zhuǎn)換成一個(gè)屬性,就可以用readonly關(guān)鍵字標(biāo)記它烘浦。
- getter方法應(yīng)當(dāng)是冪等(idempotent)的(如果一個(gè)getter方法被調(diào)用兩次抖坪,那么第二次調(diào)用時(shí)返回的結(jié)果應(yīng)該和第一調(diào)用時(shí)返回的結(jié)果相同)。然而闷叉,如果一個(gè)getter方法每次調(diào)用時(shí)擦俐,是被用于計(jì)算結(jié)果,這是可以接受的握侧。
如何適配
識(shí)別出一組可以被轉(zhuǎn)換成一個(gè)屬性的方法蚯瞧,如這些方法:
- (NSColor *)backgroundColor;
- (void)setBackgroundColor:(NSColor *)color;
用@property語(yǔ)法和適當(dāng)?shù)年P(guān)鍵字將它們定義成一個(gè)屬性:
@property (copy) NSColor *backgroundColor;
有關(guān)屬性關(guān)鍵詞和其他注意事項(xiàng),可以閱讀Encapsulating Data品擎。
或者埋合,你也可以使用Xcode中的modern Objective-C轉(zhuǎn)換器來自動(dòng)轉(zhuǎn)換你的代碼。參考Refactoring Your Code Using Xcode萄传。
@property基本用法
手工創(chuàng)建getter與setter
@interface Person : NSObject
{
NSString *_name;
NSUInteger _age;
}
- (void)setName:(NSString*)name;
- (NSString*)name;
- (void)setAge:(NSUInteger)age;
- (NSUInteger)age;
@end
@implementation Person
- (void)setName:(NSString*)name {
_name = [name copy];
}
- (NSString*)name {
return _name;
}
- (void)setAge:(NSUInteger)age {
_age = age;
}
- (NSUInteger)age {
return _age;
}
@end
上述代碼就是手動(dòng)創(chuàng)建變量的getter
和setter
的實(shí)現(xiàn)甚颂,getter
和setter
本質(zhì)就是符合一定命名規(guī)范(前文Apple Official Property Introduction有講解)的實(shí)例方法。
具體使用方法如下
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *p = [[Person alloc] init];
//函數(shù)調(diào)用name的setter
[p setName:@"Jiaming Chen"];
//函數(shù)調(diào)用age的setter
[p setAge:22];
//函數(shù)調(diào)用name和age的getter盲再,輸出 Jiaming Chen 22
NSLog(@"%@ %ld", [p name], [p age]);
}
return 0;
}
通過調(diào)用方式可以看出西设,setter
和getter
本質(zhì)就是實(shí)例方法瓣铣,可以通過函數(shù)調(diào)用的方式來使用答朋。
為了方便使用,Objective-C允許使用點(diǎn)語(yǔ)法來訪問getter
和setter
棠笑。
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *p = [[Person alloc] init];
//使用點(diǎn)語(yǔ)法訪問name的setter
p.name = @"Jiaming Chen";
//使用點(diǎn)語(yǔ)法訪問age的setter
p.age = 22;
//使用點(diǎn)語(yǔ)法訪問name和age的getter梦碗,輸出 Jiaming Chen 22
NSLog(@"%@ %ld", p.name, p.age);
}
return 0;
}
使用點(diǎn)語(yǔ)法訪問的方式本質(zhì)還是調(diào)用了我們手動(dòng)創(chuàng)建的setter
和getter
。
當(dāng)有很多變量需要設(shè)置時(shí)蓖救,這樣手工創(chuàng)建setter
和getter
的方式難免很繁瑣洪规,因此合成存取方法就誕生了。
合成存取方法
@interface Person : NSObject
@property (nonatomic, copy) NSString* name;
@property (nonatomic, assign) NSUInteger age;
@end
@implementation Person
@synthesize name = _name;
@synthesize age = _age;
@end
在聲明一個(gè)屬性(property)的時(shí)候盡量使用Foundation
框架的數(shù)據(jù)類型循捺,如整形使用NSInteger
或NSUInteger
表示斩例,時(shí)間間隔的浮點(diǎn)類型使用NSTimeInterval
表示,這樣代碼數(shù)據(jù)類型更統(tǒng)一从橘。
上面的代碼使用@property
聲明兩個(gè)屬性name
和age
并為其設(shè)置了一些指示符(nonatomic
,copy
,assign
等念赶,下文會(huì)詳細(xì)介紹)础钠。
@synthesize
表示為這兩個(gè)屬性自動(dòng)生成名為_name
和_age
的底層實(shí)例變量,并自動(dòng)生成相關(guān)的getter
和setter
也可以不寫編譯器默認(rèn)會(huì)自動(dòng)生成'_屬性名'
的實(shí)例變量以及相關(guān)的getter
和setter
叉谜。
這里所說的編譯器自動(dòng)生成的實(shí)例變量就如同我們?cè)谏衔闹惺謩?dòng)創(chuàng)建setter
和getter
時(shí)聲明的變量_name
和_age
旗吁。也就是說編譯器會(huì)在編譯時(shí)會(huì)自動(dòng)生成并使用_name
和_age
這兩個(gè)變量來存儲(chǔ)這兩個(gè)屬性,跟name
和age
沒什么關(guān)系了停局,只是我們?cè)谏蠈邮褂眠@兩個(gè)屬性的時(shí)候可以用name
和age
的點(diǎn)語(yǔ)法來訪問getter
和setter
很钓。如果不想使用這兩個(gè)名字用于底層的存儲(chǔ)也可以任意命名,但最好按照官方的命名原則來命名董栽。
也可以自定義getter和setter方法來覆蓋編譯器默認(rèn)生成的方法码倦,就如同手動(dòng)創(chuàng)建getter
和setter
一樣。
@interface Person : NSObject
@property (nonatomic, copy) NSString* name;
@property (nonatomic, assign) NSUInteger age;
@end
@implementation Person
//編譯器會(huì)幫我們自動(dòng)生成_name和_age這兩個(gè)實(shí)例變量锭碳,下面代碼就可以正常使用這兩個(gè)變量了
@synthesize name = _name;
@synthesize age = _age;
- (void)setName:(NSString*)name {
//必須使用_name來賦值叹洲,使用self.name來設(shè)置值時(shí)編譯器會(huì)自動(dòng)轉(zhuǎn)為調(diào)用該函數(shù),會(huì)導(dǎo)致無限遞歸
//使用_name則是直接訪問底層的存儲(chǔ)屬性工禾,不會(huì)調(diào)用該方法來賦值
//這里使用copy是為了防止NSMutableString多態(tài)
_name = [name copy];
}
- (NSString*)name {
//必須使用_name來訪問屬性值运提,使用self.name來訪問值時(shí)編譯器會(huì)自動(dòng)轉(zhuǎn)為調(diào)用該函數(shù),會(huì)造成無限遞歸
return _name;
}
@end
使用自定義的getter和setter一般是用來實(shí)現(xiàn)懶加載(lazy load)闻葵,在很多情況下很常用民泵,比如:創(chuàng)建一個(gè)比較大的而又不一定會(huì)使用的對(duì)象,可以按照如下方法編寫槽畔。
@property (nonatomic, strong) CustomObject *customObject;
@synthesize customObject = _customObject;
- (CustomObject*) customObject {
if (_customObject == nil) {
//初始化操作栈妆,會(huì)調(diào)用setter方法
self.customObject = [[CustomObject alloc] init];
//如果按照如下方法編寫不會(huì)調(diào)用setter方法,如果自定義setter方法需要完成一些事情建議使用self.customObject的方式來設(shè)置
//_customObject = [[CustomObject alloc] init];
}
return _customObject;
}
@property指示符
在聲明屬性的時(shí)候一般會(huì)帶上幾個(gè)指示符厢钧,常用指示符有
atomic nonatomic
readwrite readonly
assign
strong
weak
copy
unsafe_unretained
retain
還可以設(shè)置getter
和setter
對(duì)其重命名鳞尔,這里不再贅述。
atomic/nonatomic
指定合成存取方法是否為原子操作早直,可以理解為是否線程安全寥假,但在iOS上即時(shí)使用atomic
也不一定是線程安全的,要保證線程安全需要使用鎖機(jī)制霞扬,超過本文的講解范圍糕韧,可以自行查閱。
可以發(fā)現(xiàn)幾乎所有代碼的屬性設(shè)置都會(huì)使用nonatomic
喻圃,這樣能夠提高訪問性能萤彩,在iOS中使用鎖機(jī)制的開銷較大,會(huì)損耗性能斧拍。
readwrite/readonly
readwrite
是編譯器的默認(rèn)選項(xiàng)雀扶,表示自動(dòng)生成getter
和setter
,如果需要getter
和setter
不寫即可肆汹。
readonly
表示只合成getter
而不合成setter
愚墓。
assign窍侧、weak、unsafe_unretained
assign
表示對(duì)屬性只進(jìn)行簡(jiǎn)單的賦值操作转绷,不更改所賦的新值的引用計(jì)數(shù)伟件,也不改變舊值的引用計(jì)數(shù),常用于標(biāo)量類型议经,如NSInteger
斧账,NSUInteger
,CGFloat
煞肾,NSTimeInterval
等咧织。
assign
也可以修飾對(duì)象如NSString
等類型對(duì)象,上面說過使用assign
修飾不會(huì)更改所賦的新值的引用計(jì)數(shù)籍救,也不改變舊值的引用計(jì)數(shù)习绢,如果當(dāng)所賦的新值引用計(jì)數(shù)為0對(duì)象被銷毀時(shí)屬性并不知道,編譯器不會(huì)將該屬性置為nil
蝙昙,指針仍舊指向之前被銷毀的內(nèi)存闪萄,這時(shí)訪問該屬性會(huì)產(chǎn)生野指針錯(cuò)誤并崩潰,因此使用assign
修飾的類型一定要為標(biāo)量類型奇颠。
@interface Person : NSObject
@property (nonatomic, assign) NSString* name;
@property (nonatomic, assign) NSUInteger age;
@end
@implementation Person
@synthesize name = _name;
@synthesize age = _age;
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *p = [[Person alloc] init];
//這里使用NSMutableString而不使用NSString是因?yàn)镹SString會(huì)緩存字符串败去,后面置空的時(shí)候?qū)嶋H沒有被銷毀
NSMutableString *s = [[NSMutableString alloc] initWithString:@"Jiaming Chen"];
//設(shè)置p.name不會(huì)增加s的引用計(jì)數(shù),只是單純將s指向的地址賦給p.name
p.name = s;
//輸出兩個(gè)變量的內(nèi)存地址烈拒,可以看出是一致的
NSLog(@"%p %p", p.name, s);
//這里可以正常訪問name
NSLog(@"%@ %ld", p.name, p.age);
//將上述字符串置空圆裕,引用計(jì)數(shù)為0,對(duì)象被銷毀
s = nil;
//查看其地址時(shí)仍然可以訪問到荆几,表示其仍然指向那一塊內(nèi)存
NSLog(@"%p", p.name);
//訪問內(nèi)容時(shí)發(fā)生野指針錯(cuò)誤吓妆,程序崩潰。因?yàn)閷?duì)象已經(jīng)被銷毀
NSLog(@"%@ %ld", p.name, p.age);
}
return 0;
}
使用weak
修飾的時(shí)候同樣不會(huì)增加所賦的新值的引用計(jì)數(shù)吨铸,也不減少舊值的引用計(jì)數(shù)行拢,但當(dāng)該值被銷毀時(shí),weak
修飾的屬性會(huì)被自動(dòng)賦值為nil
焊傅,這樣就可以避免野指針錯(cuò)誤剂陡。
使用unsafe_unretained
修飾時(shí)效果與assign
相同狈涮,不會(huì)增加引用計(jì)數(shù)狐胎,當(dāng)所賦的值被銷毀時(shí)不會(huì)被置為nil
可能會(huì)發(fā)生野指針錯(cuò)誤。unsafe_unretained
與assign
的區(qū)別在于歌馍,unsafe_unretained
只能修飾對(duì)象握巢,不能修飾標(biāo)量類型,而assign
兩者均可修飾松却。
為了防止多態(tài)的影響暴浦,對(duì)NSString
進(jìn)行修飾時(shí)一般使用copy
溅话。
下文會(huì)對(duì)weak
、unsafe_unretained
和copy
進(jìn)行詳細(xì)介紹歌焦。
strong飞几、weak
strong
表示屬性對(duì)所賦的值持有強(qiáng)引用表示一種“擁有關(guān)系”(owning relationship),會(huì)先保留新值即增加新值的引用計(jì)數(shù)独撇,然后再釋放舊值即減少舊值的引用計(jì)數(shù)屑墨。只能修飾對(duì)象。如果對(duì)一些對(duì)象需要保持強(qiáng)引用則使用strong
纷铣。
weak
表示對(duì)所賦的值對(duì)象持有弱引用表示一種“非擁有關(guān)系”(nonowning relationship)卵史,對(duì)新值不會(huì)增加引用計(jì)數(shù),也不會(huì)減少舊值的引用計(jì)數(shù)搜立。所賦的值在引用計(jì)數(shù)為0被銷毀后以躯,weak
修飾的屬性會(huì)被自動(dòng)置為nil
能夠有效防止野指針錯(cuò)誤。
weak
常用在修飾delegate等防止循環(huán)引用的場(chǎng)景啄踊。
copy
copy
修飾的屬性會(huì)在內(nèi)存里拷貝一份對(duì)象忧设,兩個(gè)指針指向不同的內(nèi)存地址。
一般用來修飾有對(duì)應(yīng)可變類型子類的對(duì)象颠通。
如:NSString/NSMutableString
,NSArray/NSMutableArray
,NSDictionary/NSMutableDictionary
等见转。
為確保這些不可變對(duì)象因?yàn)榭勺冏宇悓?duì)象影響,需要copy
一份備份蒜哀,如果不使用copy
修飾斩箫,使用strong
或assign
等修飾則會(huì)因?yàn)槎鄳B(tài)導(dǎo)致屬性值被修改。
這里的copy
還牽扯到NSCopying
和NSMutableCopying
協(xié)議撵儿,在下文會(huì)有簡(jiǎn)要介紹乘客。
@interface Person : NSObject
//使用strong修飾NSString
@property (nonatomic, strong) NSString* name;
@property (nonatomic, assign) NSUInteger age;
@end
@implementation Person
@synthesize name = _name;
@synthesize age = _age;
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *p = [[Person alloc] init];
NSMutableString *s = [[NSMutableString alloc] initWithString:@"Jiaming Chen"];
//將可變字符串賦值給p.name
p.name = s;
//輸出的地址和內(nèi)容均一致
NSLog(@"%p %p %@ %@", p.name, s, p.name, s);
//修改可變字符串s
[s appendString:@" is a good guy"];
//再次輸出p.name被影響
NSLog(@"%p %p %@ %@", p.name, s, p.name, s);
}
return 0;
}
copy
還被用來修飾block
,在ARC環(huán)境下編譯器默認(rèn)會(huì)用copy
修飾淀歇, 一般情況下在block
需要捕獲外界數(shù)據(jù)時(shí)該block
就會(huì)被分配在堆區(qū)易核,但在MRC環(huán)境下由于手動(dòng)管理引用計(jì)數(shù),block
一般被分配在棧區(qū)浪默,需要copy
到堆區(qū)來防止野指針錯(cuò)誤牡直。由于牽扯block
相關(guān)知識(shí),有興趣可以看博客另一篇文章iOS block探究(二): 深入理解
對(duì)于可變對(duì)象類型纳决,如NSMutableString
碰逸、NSMutableArray
等則不可以使用copy
修飾,因?yàn)?code>Foundation框架提供的這些類都實(shí)現(xiàn)了NSCopying
協(xié)議阔加,使用copy
方法返回的都是不可變對(duì)象饵史,如果使用copy
修飾符在對(duì)可變對(duì)象賦值時(shí)則會(huì)獲取一個(gè)不可變對(duì)象,接下來如果對(duì)這個(gè)對(duì)象進(jìn)行可變對(duì)象的操作則會(huì)產(chǎn)生異常,因?yàn)?code>OC沒有提供mutableCopy
修飾符胳喷,對(duì)于可變對(duì)象使用strong
修飾符即可湃番。具體栗子如下:
@interface Person : NSObject
//使用copy修飾NSMutableString
@property (nonatomic, copy) NSMutableString* name;
@property (nonatomic, assign) NSUInteger age;
@end
@implementation Person
@synthesize name = _name;
@synthesize age = _age;
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *p = [[Person alloc] init];
NSMutableString *s = [[NSMutableString alloc] initWithString:@"Jiaming Chen"];
//將可變字符串賦值給p.name
p.name = s;
//輸出的地址不一致,內(nèi)容一致
NSLog(@"%p %p %@ %@", p.name, s, p.name, s);
//修改p.name吭露,此時(shí)拋出異常
[p.name appendString:@" is a good guy."];
}
return 0;
}
上面的栗子使用copy
修飾可變對(duì)象吠撮,在進(jìn)行賦值的時(shí)候會(huì)通過copy
方法獲取一個(gè)不可變對(duì)象,因此p.name
的地址和s
的地址不同讲竿,而p.name
運(yùn)行時(shí)類型為NSString
纬向,調(diào)用appendString:
方法會(huì)拋出異常。
所以戴卜,針對(duì)不可變對(duì)象使用copy
修飾逾条,針對(duì)可變對(duì)象使用strong
修飾。
unsafe_unretained
使用unsafe_unretained
修飾時(shí)效果與assign
相同投剥,不會(huì)增加新值的引用計(jì)數(shù)师脂,也不會(huì)減少舊值的引用計(jì)數(shù)(unretained)當(dāng)所賦的值被銷毀時(shí)不會(huì)被置為nil
可能會(huì)發(fā)生野指針錯(cuò)誤(unsafe)。unsafe_unretained
與assign
的區(qū)別在于江锨,unsafe_unretained
只能修飾對(duì)象吃警,不能修飾標(biāo)量類型,而assign
兩者均可修飾啄育。
retain
在ARC環(huán)境下使用較少酌心,在MRC下使用效果與strong
一致。
copy的題外話
有時(shí)候我們需要copy
一個(gè)對(duì)象挑豌,或是mutableCopy
一個(gè)對(duì)象安券,這時(shí)需要遵守NSCopying
和NSMutableCopying
協(xié)議,來實(shí)現(xiàn)copyWithZone:
和mutableCopyWithZone:
兩個(gè)方法氓英,而不是重寫copy
和mutableCopy
兩個(gè)方法侯勉。
Foundation
框架中的很多數(shù)據(jù)類型已經(jīng)幫我們實(shí)現(xiàn)了上述兩個(gè)方法,因此我們可以使用copy
方法和mutableCopy
方法來復(fù)制一個(gè)對(duì)象铝阐,兩者的區(qū)別在于copy
的返回值仍未不可變對(duì)象址貌,mutableCopy
的返回值為可變對(duì)象。
type | copy | mutableCopy |
---|---|---|
NS* | 淺拷貝徘键,只拷貝指針练对,地址相同 | 單層深拷貝,拷貝內(nèi)容吹害,地址不同 |
NSMutable* | 單層深拷貝螟凭,拷貝內(nèi)容,地址不同 | 單層深拷貝赠制,拷貝內(nèi)容赂摆,地址不同 |
由上述表格可以看出挟憔,對(duì)于不可變類型钟些,使用copy
方法時(shí)是淺拷貝烟号,只拷貝指針,因?yàn)閮?nèi)容是不會(huì)變化的政恍。使用mutableCopy
時(shí)由于返回可變對(duì)象因此需要一份拷貝汪拥,供其他對(duì)象使用。對(duì)于可變類型篙耗,不管是copy
還是mutableCopy
均會(huì)進(jìn)行深拷貝迫筑,所指向指針不同。
前文介紹copy
修飾符的時(shí)候講過宗弯,在修飾NSString
這樣的不可變對(duì)象的時(shí)候使用copy
修飾脯燃,但其實(shí)當(dāng)給對(duì)象賦一個(gè)NSString
時(shí)仍舊只復(fù)制了指針而不是拷貝內(nèi)容,原因同上蒙保。
@interface Person : NSObject
//使用copy修飾
@property (nonatomic, copy) NSString* name;
@property (nonatomic, assign) NSUInteger age;
@end
@implementation Person
@synthesize name = _name;
@synthesize age = _age;
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *p = [[Person alloc] init];
NSString *s = @"Jiaming Chen";
p.name = s;
//p.name的地址與s地址相同辕棚,不可變對(duì)象copy是淺拷貝
NSLog(@"%p %p %@ %@", p.name, s, p.name, s);
}
return 0;
}
@property進(jìn)階:深入理解
由于篇幅有限,本文只用于介紹property基本用法邓厕,博客另一篇文章會(huì)深入講解property的實(shí)現(xiàn)機(jī)制逝嚎,有興趣可自行查閱iOS @property探究(二): 深入理解
備注
由于作者水平有限,難免出現(xiàn)紕漏详恼,如有問題還請(qǐng)不吝賜教补君。