iOS開發(fā)中, 之前一直使用swift, 因此對(duì)于Objective-C的內(nèi)存管理機(jī)制長(zhǎng)期處于混亂的一知半解狀態(tài). 今天終于看到一篇講得透徹的博客[Objective-C內(nèi)存管理教程和原理剖析](http://blog.jobbole.com/66197/), 感謝作者.
本文只是個(gè)人的一個(gè)學(xué)習(xí)摘要, 更詳細(xì)內(nèi)容請(qǐng)參考原文.
ARC黃金法則:
>The basic rule to apply is:
***Everything that increases the reference counter with alloc, [mutable]copy[WithZone:] or retain is in charge of the corresponding [auto]release***.
## 基本的內(nèi)存分配
OC的對(duì)象是在堆里邊生成, 需要一個(gè)指針指向它.
```
ClassA *obj1 = [[ClassA alloc] init];
```
使用完之后不會(huì)自動(dòng)銷毀, 需執(zhí)行dealloc來銷毀, 否則會(huì)出現(xiàn)內(nèi)存泄露.
```
[obj1 dealloc];
```
那么看如下代碼:
```
ClassA *obj1 = [[ClassA alloc] init];
ClassA *obj2 = obj1;
[obj1 hello];
[obj1 dealloc];
// obj1, obj2都是指針, 指向同一對(duì)象. 而[obj1 dealloc]已經(jīng)銷毀了該對(duì)象. 因此下邊的代碼無法執(zhí)行, obj2是無效指針.
[obj2 hello];
[obj2 dealloc];
```
## 引用計(jì)數(shù)
為了避免出現(xiàn)上邊的無效指針,? OC采用引用計(jì)數(shù)的方式. 引用計(jì)數(shù)加1的有:alloc, retain, strong, 減1的有release. 當(dāng)對(duì)象的引用計(jì)數(shù)為0的時(shí)候, 會(huì)自動(dòng)調(diào)用dealloc.
```
ClassA *obj1 = [[ClassA alloc] init]; // 計(jì)數(shù)為1
[obj1 release]; // 計(jì)數(shù)為0, 自動(dòng)dealloc
ClassA *obj1 = [[ClassA alloc] init]; // 計(jì)數(shù)為1
ClassA *obj2 = obj1; // 計(jì)數(shù)為1, 指針賦值不會(huì)增加引用計(jì)數(shù)
[obj1 hello];
[obj1 release]; // 計(jì)數(shù)為0, 自動(dòng)dealloc
// 因?qū)ο笠驯籨ealloc, 故以下代碼不會(huì)執(zhí)行, obj2仍然是無效指針.
[obj2 hello];
[obj2 release];
```
那么如何解決obj2的無效指針問題呢? 通過retain使得引用計(jì)數(shù)加1.
```
ClassA *obj1 = [[ClassA alloc] init]; // 計(jì)數(shù)為1
ClassA *obj2 = obj1; // 計(jì)數(shù)為1
[obj2 retain]; // 計(jì)數(shù)為2
[obj1 hello];
[obj1 release]; // 計(jì)數(shù)為1
[obj2 hello];
[obj2 release]; // 計(jì)數(shù)為0, 自動(dòng)dealloc
```
所以, 引用計(jì)數(shù)的關(guān)鍵在于當(dāng)對(duì)象的引用計(jì)數(shù)為0時(shí), 會(huì)自動(dòng)調(diào)用dealloc銷毀該對(duì)象. 指針賦值時(shí), retain count不會(huì)自動(dòng)增加, 需要手動(dòng)retain.
另外, release一個(gè)對(duì)象之后, 應(yīng)立即將指針清空(release一個(gè)空指針是合法的). 如:
```
ClassA *obj1 = [[ClassA alloc] init];
[obj1 release];
obj1 = nil;
```
## Autorelease
自動(dòng)釋放池其實(shí)是NSAutoreleasePool, 可以自動(dòng)釋放對(duì)象.
```
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
```
NSAutoreleasePool內(nèi)部包含一個(gè)NSMutableArray, 用于保存聲明為autorelease的所有對(duì)象.
下邊看一下Apple的例子:
首先不用autorelease,
```
- (NSString *)fullName {
NSString *string = [[NSString alloc] initWithFormat:@"%@ %@", self.firstName, self.lastName];
return string;
}
```
則, 該string不能找到合適的時(shí)機(jī)釋放, 因此會(huì)出現(xiàn)內(nèi)存泄露.
那么, 采用autorelease之后,
```
- (NSString *)fullName {
NSString *string = [[[NSString alloc] initWithFormat:@"%@ %@", self.firstName, self.lastName] autorelease];
return string;
}
```
雖然, string不會(huì)立即釋放, 但autorelease會(huì)在其release的時(shí)機(jī)將其釋放.
或者采用更直接簡(jiǎn)便的方式, 這種方式?jīng)]有alloc, 所以也不要求release.
```
- (NSString *)fullName {
NSString *string = [NSString stringWithFormat:@"%@ %@", self.firstName, self.lastName];
return string;
}
```
其實(shí), 此方式也是通過alloc+autorelease的方式來實(shí)現(xiàn)的, 只是對(duì)使用者而言, 省掉了alloc+autorelease的步驟.
NSAutoreleasePool自身在銷毀的時(shí)候, 會(huì)遍歷并試圖release其中的所有autorelease對(duì)象. 如果該對(duì)象的引用計(jì)數(shù)為1, 則將其引用計(jì)數(shù)減1, 銷毀該對(duì)象; 如果該對(duì)象的引用計(jì)數(shù)大于1, 則autorelease之后其引用計(jì)數(shù)仍大于0, 對(duì)象未銷毀, 出現(xiàn)內(nèi)存泄露.
如在iOS工程的main.m文件中
```
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
```
其實(shí)可以自行使用NSAutoreleasePool,
```
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
for (int i=0;i<100;i++) {
for (int j=0;j<10000;j++) {
// 產(chǎn)生autorelease對(duì)象
[NSString stringWithFromat:@"1234567890"];
}
}
[pool release];
return 0;
```
可以看出, 所有autorelease對(duì)象都只能在NSAutoreleasePool執(zhí)行release的時(shí)候銷毀, 顯然不能很好地利用內(nèi)存. 那么可以使用內(nèi)嵌的NSAutoreleasePool, 代碼優(yōu)化如下:
```
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
for (int i=0;i<100;i++) {
NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];
for (int j=0;j<10000;j++) {
// 產(chǎn)生autorelease對(duì)象
[NSString stringWithFromat:@"1234567890"];
}
[loopPool release];
}
[pool release];
return 0;
```
這樣, 就可以做到及時(shí)地釋放內(nèi)存.
所以, 實(shí)例化一個(gè)對(duì)象可以有兩種方法:
1. 采用alloc + release的方法
```
Dog *dog = [[Dog alloc] init];
// 代碼
[dog release]; // alloc 和 release 必須一一對(duì)應(yīng).
```
2. 采用autorelease的方法
```
// 這種方式把dog對(duì)象加到autorelease pool中, 就不需要手動(dòng)release了.
+ (id)dog {
return [[[Dog alloc] init] autorelease];
}
```
所以, NSAutoreleasePool的關(guān)鍵在于, 放置到其中的autorelease對(duì)象, 會(huì)在該pool release的時(shí)候自動(dòng)銷毀(絕大多數(shù)情況下, 都是正常的引用計(jì)數(shù)為1的autorelease對(duì)象). 因此對(duì)象本身就沒必要調(diào)用release方法. 同時(shí)NSAutoreleasePool的release時(shí)機(jī)很重要. 因?yàn)閍utorelease pool不會(huì)立即釋放, 當(dāng)它到了最近的pool release的時(shí)候才會(huì)自動(dòng)檢測(cè)retainCount是否為0, 一旦為0則釋放pool. 當(dāng)pool本身銷毀的時(shí)候, 其中的對(duì)象才會(huì)執(zhí)行release操作, 否則將一直占用內(nèi)存空間.
所以, 不要濫用autorelease, 如對(duì)象的生命周期很清晰, 則結(jié)束使用后立即release. 過多等待autorelease對(duì)象浪費(fèi)內(nèi)存.
## property中的關(guān)鍵字
***@property中的關(guān)鍵字表示屬性如何存儲(chǔ), 其實(shí)全都是用于設(shè)置getter/setter的操作特性***, 如果我們將getter/setter展開來看的話就會(huì)很明白.
### assign
assign相當(dāng)于指針賦值, 不對(duì)引用計(jì)數(shù)進(jìn)行操作, 如原對(duì)象不用了, 一定要將其設(shè)置為nil. 一般基本變量用該屬性聲明, 如int, BOOL.
即: 調(diào)用setter方法時(shí)直接賦值, 不進(jìn)行retain操作. 不改變引用計(jì)數(shù).
***assign與weak的區(qū)別在于:***
>當(dāng)指針?biāo)赶驅(qū)ο髍elease之后, weak會(huì)賦nil給該指針, 而assign不會(huì)賦nil給指針. 所以使用assign的時(shí)候, 一旦指針?biāo)赶驅(qū)ο髍elease之后, 再給該對(duì)象發(fā)送消息(調(diào)用方法), 程序即會(huì)crash.
weak用于OC對(duì)象的弱引用, assign用于C原始類型.
### retain
retain其實(shí)是指針拷貝(地址相同), 會(huì)增加對(duì)象的引用計(jì)數(shù),而OC中的基本數(shù)據(jù)類型沒有引用計(jì)數(shù)。
如: 把對(duì)象添加到數(shù)組中[array addObject:obj];,該對(duì)象obj的引用計(jì)數(shù)retainCount將加1. 如下圖

使用retain, 調(diào)用setter方法時(shí), 先release舊值, 對(duì)賦予的新值進(jìn)行引用計(jì)數(shù)+1. 二者的內(nèi)存地址一樣.
看下邊的例子:
```
// Person.h
@interface Person: NSObject {
Dog *_dog;
}
@property (retain) Dog *dog;
@end
```
```
// Person.m
@implementation Person
@synthesize dog = _dog;
- (void) dealloc {
self.dog = nil; // 其實(shí)調(diào)用[self setDog:nil];方法
[super dealloc];
}
@end
```
參考之前的一篇文章[Objective-C中類的成員變量與屬性](http://blog.csdn.net/icetime17/article/details/45537975), 可知@property與@synthesize的關(guān)系, 總之, 這里編譯器會(huì)識(shí)別@synthesize, 并自動(dòng)為dog屬性添加getter/setter方法.
編譯器對(duì) @property (retain) Dog *dog; 展開為:
```
- (Dog *)dog;
- (void)setDog:(Dog *)aDog;
```
編譯器對(duì) @synthesize dog = _dog; 展開為:
```
- (Dog *)dog {
return _dog;
}
- (void)setDog:(Dog *)aDog {
if (_dog != aDog) {
[_dog release]; // 即使_dog的retainCount已為0, 對(duì)其做release也不會(huì)出錯(cuò).
_dog = [aDog retain];
}
}
```
getter/setter方法是被編譯器隱藏的, 如果不使用@synthesize dog = _dog;的話, 必須自己編寫這兩個(gè)方法.
### copy
不同于retain的指針拷貝, copy都是內(nèi)容拷貝. 即復(fù)制一個(gè)對(duì)象變成新的對(duì)象(新的內(nèi)存地址), 新對(duì)象的引用計(jì)數(shù)為1, 原對(duì)象的引用計(jì)數(shù)不變. 所以, copy得到的均為一個(gè)獨(dú)立的對(duì)象, 跟原對(duì)象沒有關(guān)系.
一般來說, ***block都是使用copy關(guān)鍵字***. 如下邊的SelectedCity的使用, 在這里只是簡(jiǎn)單地截取部分代碼, 不做太具體的解釋. 需要詳細(xì)了解block的同學(xué), 可以參考[轉(zhuǎn): 深入理解Objective-C的Block](http://blog.csdn.net/icetime17/article/details/46649423).
在CityListViewController中
```
// CityListViewController.h
typedef void(^SelectedCity)(NSString *);
@interface CityListViewController: UIViewController
@property (nonatomic, copy) SelectedCity selectedCity;
@end
// CityListViewController.m
if (_selectedCity) {
_selectedCity(cell.textLabel.text);
}
```
在MainViewController中:
```
// MainViewController.h
typedef void(^SelectedCity)(NSString *);
@interface MainViewController: UIVieController
@property (nonatomic, copy) SelectedCity selectedCity;
@end
// MainViewController.m
- (void)viewDidLoad {
__weak MainViewController *weakVC = self;
_selectedCity = ^(NSString *city) {
weakVC.cityLabel.text = city;
weakVC.vTracks.selectedCity = city; // vTracks中有selectedCity屬性
[weakVC.vTracks invokeRefresh];
};
}
- (IBAction)cityListClicked:(UIButton *sender) {
CityListViewController *cityListVC = [CityListViewController alloc] init];
cityListVC.selectedCity = _selectedCity;
[self.navigationController pushViewController:cityListVC animated:YES];
}
```
編譯器對(duì) @property (copy) NSString *str; 展開為
```
- (NSString *)str {
return _str;
}
- (void)setStr:(NSString *)newStr {
if (_str != newStr) {
[_str release];
_str = [newStr copy];
}
}
```
copy其實(shí)有兩種:
1. copy得到的是不可變類型, 如NSString, NSArray, NSDictionary, NSData, NSSet.
2. mutableCopy得到的是mutable類型. 如NSMutableString, NSMutableArray, NSMutableDictionary, NSMutableData, NSMutableSet.
***copy操作必須遵循NSCopying和NSMutableCopying協(xié)議, 實(shí)現(xiàn)其中的copyWithZone:和mutableCopyWithZone:方法, 即告訴編譯器如何做到copy操作***.
```
- (id)copyWithZone:(NSZone *)zone {
Dog *dog = [[[[self class] allocWithZone:zone] init] autorelease];
dog.name = self.name;
dog.year = self.year;
return dog;
}
```
### deep copy
上邊的copy默認(rèn)都是淺拷貝, 如對(duì)于NSArray而言, 淺拷貝只是copy一份NSArray, 該NSArray中的每一個(gè)元素仍然指向原NSArray中指向的對(duì)象.? 如下圖:

而deep copy則將NSArray中元素指向的對(duì)象也做一份拷貝, 一般用于NSArray, NSDictionary. 如下圖:

deep copy有兩種實(shí)現(xiàn)方式:
***1. 使用copyItems***
copyItems
```
- (id)initWithArray:(NSArray *)array copyItems:(BOOL)flag;
- (id)initWithDictionary:(NSDictionary *)otherDictionary copyItems:(BOOL)flag;
```
使用如下:
```
NSMutableArray *_carList = [[NSMutableArray alloc] init];
// 向_carList中填充內(nèi)容
NSMutableArray *carList1 = [[NSMutableArray alloc] initWithArray:_carList];
// 淺拷貝, 同樣會(huì)改變?cè)璤carList中的內(nèi)容
[[carList1 objectAtIndex:0] setName:@"淺拷貝內(nèi)容"];
NSMutableArray *carList2 = [[NSMutableArray alloc] initWithArray:_carList copyItems:YES];
// deep copy, 則不會(huì)改變?cè)璤carList中的內(nèi)容
[[carList2 objectAtIndex:0] setName:@"deep copy內(nèi)容"];
```
***2. 使用歸檔NSKeyedArchiever***
歸檔是將整個(gè)數(shù)組以二進(jìn)制形式存到NSData中, 需要的時(shí)候就將其讀取出來再轉(zhuǎn)換成原來數(shù)組的形式.
```
// 將NSArray的內(nèi)容保存到NSData中
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:_carList];
// 將二進(jìn)制對(duì)象data還原成NSArray
NSMutableArray *carList3 = NSKeyedArchiver unarchiveObjectWithData:data];
```
歸檔需要繼承NSCoding協(xié)議, 實(shí)現(xiàn)encodeWithCoder:和initWithCoder:兩個(gè)方法.
```
// 存檔方法
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:self.name forKey:@"name"];
[aCoder encodeObject:self.year forKey:@"year"];
}
// 解檔方法
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
if (self) {
self.name = [aDecoder decodeObjectForKey:@"name"];
self.year = [aDecoder decodeIntForKey:@"year"];
}
return self;
}
```
### strong
strong是強(qiáng)引用, 持有對(duì)象, 對(duì)象的引用計(jì)數(shù)+1, 如string1和string2都指向一個(gè)字符串, 則string1=nil, 而string2不變.
所有strong修飾符的變量在超出其變量作用域時(shí), 釋放其被賦予的對(duì)象. 即持有強(qiáng)引用的變量在超出其作用域時(shí)被放棄, 隨著強(qiáng)引用的失效, 引用的對(duì)象會(huì)隨之釋放.
strong變量執(zhí)行ARC計(jì)數(shù), 不會(huì)自動(dòng)釋放. 其死亡直接決定了所指向?qū)ο蟮乃劳?
為了確保使用中的實(shí)例不會(huì)被銷毀,ARC 會(huì)跟蹤和計(jì)算每一個(gè)實(shí)例正在被多少屬性,常量和變量所引用。哪怕實(shí)例的引用數(shù)為1,ARC都不會(huì)銷毀這個(gè)實(shí)例。
為了使之成為可能,無論你將實(shí)例賦值給屬性,常量或者是變量,屬性,常量或者變量,都會(huì)對(duì)此實(shí)例創(chuàng)建強(qiáng)引用。之所以稱之為強(qiáng)引用,是因?yàn)樗鼤?huì)將實(shí)例牢牢的保持住,只要強(qiáng)引用還在,實(shí)例是不允許被銷毀的。
實(shí)際上, strong和retain的意思相同, 推薦使用strong代替retain.
***id類型和對(duì)象類型的所有權(quán)修飾符默認(rèn)為strong, 會(huì)影響對(duì)象回收***
### weak
使用strong容易引起循環(huán)引用的問題. 為了防止循環(huán)強(qiáng)引用,可采用弱引用和無主引用。這兩種允許循環(huán)引用中的一個(gè)實(shí)例引用另外一個(gè)實(shí)例而不保持強(qiáng)引用,這樣實(shí)例能夠相互引用而不產(chǎn)生循環(huán)強(qiáng)引用。
弱引用? :對(duì)于生命周期中會(huì)變?yōu)閚il的實(shí)例采用。
無主引用:對(duì)于初始化賦值后再也不會(huì)變?yōu)閚il的實(shí)例采用。
weak是弱引用, 不持有對(duì)象, 在超出其變量作用域時(shí), 對(duì)象即被釋放. 在持有某對(duì)象的弱引用時(shí), 若該對(duì)象被釋放, 則此弱引用將自動(dòng)失效且等于nil.
對(duì)象的計(jì)數(shù)不變. 如string1和string2都指向一個(gè)字符串, 則string1=nil, 那么string2也會(huì)變?yōu)閚il.
因其沒有retain內(nèi)存地址, 若其指向的地址對(duì)象一旦釋放, 則該指針會(huì)指向nil.
ARC空閑時(shí)釋放, 對(duì)象釋放時(shí)自動(dòng)將指針置NULL. 不決定對(duì)象的存亡, 即使一個(gè)對(duì)象被持有無數(shù)個(gè)弱引用, 只要沒有強(qiáng)引用指向它, 則最后還是會(huì)清除
如兩個(gè)對(duì)象互相為對(duì)方的成員變量, 則一定不能同時(shí)retain, 否則dealloc函數(shù)形成死鎖, 兩個(gè)對(duì)象都無法釋放. weak可以用于防止野指針和死鎖, 避免循環(huán)引用.
ARC機(jī)制中,為什么UI對(duì)象用weak,代理用weak?比如我們往view里加一個(gè)button,相當(dāng)于在subviews這個(gè)數(shù)組里加一個(gè)button,這個(gè)數(shù)組空間里會(huì)有一塊內(nèi)存指向button,是強(qiáng)指針,只要控制器還在,button就在,所以當(dāng)創(chuàng)建button時(shí)用? weak,防止內(nèi)存泄漏。代理如果是強(qiáng)指針的話,它指向控制器,那么所有東西都不能relese了,前面道理同UI控件。
***weak不會(huì)影響對(duì)象回收, 一般UI對(duì)象和delegate用weak***
### __weak
__weak 聲明了可以自動(dòng)nil化的弱引用.
注意: block被copy時(shí), 會(huì)對(duì)block中用到的對(duì)象產(chǎn)生強(qiáng)引用(引用計(jì)數(shù)+1). 如果block中又引用了對(duì)象的其他成員變量, 就會(huì)對(duì)該變量本身產(chǎn)生強(qiáng)引用, 則變量本身和它自己的block屬性就形成了循環(huán)引用. 即使用self之類的有可能導(dǎo)致retain cycle, 所以一般用__weak的方式.
請(qǐng)看下邊的城市列表例子.
在CityListVC中, 通過block將city的值傳遞給MainVC.
在CityListVC.h中: 定義block塊對(duì)象
```
typedef void(^SelectedCity)(NSString *);
@property(copy,nonatomic) SelectedCity selectCity;
CityListVC.m中:
if (_selectCity) {
_selectCity(cell.textLabel.text); // 這里要通過block進(jìn)行View之間的數(shù)據(jù)傳遞.
}
```
MainVC.h中:
```
typedef void(^SelectedCity)(NSString *);
@property(copy,nonatomic) SelectedCity selectCity;
```
MainVC.m中:
```
self.vTracks.selectedCity = @"城市";
__weak MainViewController *weakVC = self;
_selectCity = ^(NSString *city){
weakVC.cityLabel.text = ([city isEqualToString:@"全國(guó)"]) ? @"城市" : city;
weakVC.vTracks.selectedCity = ([city isEqualToString:@"城市"] | [city isEqualToString:@"全國(guó)"]) ? nil : city;
[weakVC.vTracks invokeRefresh];
};
```
在block中, 使用__weak的方式聲明了一個(gè)weakVC, 即對(duì)自身對(duì)象的弱引用. 即block對(duì)象不對(duì)self對(duì)象進(jìn)行retain, 弱引用的方式可以避免出現(xiàn)循環(huán)引用. 如果是non-ARC, 則使用__block替換__weak, 即在block中引入一個(gè)新的結(jié)構(gòu)體成員變量指向這個(gè)__block變量, __block typeof(self) weakSelf = self.
### static
static, 全局變量.
1. 函數(shù)體內(nèi)static變量的作用范圍為該函數(shù)體, 該變量只分配一次, 其值在下次調(diào)用的時(shí)候仍維持上次的值.
2. 模塊內(nèi)的static可被模塊內(nèi)所有函數(shù)訪問, 但不能被模塊外其他函數(shù)訪問.
3. 類中的static成員變量可以視作類變量, 跟對(duì)象實(shí)例無關(guān), 屬于整個(gè)類所有, 對(duì)類的所有對(duì)象只有一份拷貝. 已經(jīng)實(shí)例化的各個(gè)對(duì)象之間的該static變量不會(huì)相互影響, 但對(duì)其的改變會(huì)影響到class本身. 若再次實(shí)例化新的對(duì)象, 則該新對(duì)象就擁有最新的static類變量.
4. 類中的static成員函數(shù)屬于整個(gè)類所有, 該函數(shù)不接收this指針, 只能訪問類的static成員變量.
### const
const, 常量不能修改. 超出作用域后會(huì)釋放. 即const對(duì)于對(duì)象生存期內(nèi)是常量, 對(duì)于整個(gè)類而言可變.
1. 可以指定指針, 指針?biāo)赶驍?shù)據(jù), 或二者都為const.
2. 函數(shù)中, const修飾形參, 則該輸入?yún)?shù)在函數(shù)內(nèi)部不能修改.
3. 類的成員函數(shù)若指定const, 則為常函數(shù), 不能修改類的成員變量.? (常見于返回一個(gè)const值的函數(shù))
### extern
extern: 該模塊中的變量和函數(shù)可在其他模塊中使用.
### 讀寫權(quán)限
readwrite, readonly: 控制成員變量的訪問權(quán)限, 對(duì)setter/getter的作用.
### 原子操作
nonatomic: 非原子性訪問, 不加同步, 多線程并發(fā)訪問.
atomic 線程保護(hù). 互斥鎖.
## 總結(jié)
assign: 一般用于int, BOOL等基本變量類型, 防止循環(huán)引用.
weak: 一般用于storyboard或xib中的UI對(duì)象和delegate對(duì)象.
delegate對(duì)象使用weak是為了防止循環(huán)引用。
assign與weak區(qū)別:當(dāng)對(duì)象被釋放后,weak會(huì)自動(dòng)將指針指向nil,而assign不會(huì)。
所以向nil發(fā)送消息導(dǎo)致野指針錯(cuò)誤unrecognized selector sent to instance。防止野指針
strong: 一般用于id類型(非delegate)和對(duì)象類型(NSString, UITableView等). 默認(rèn), 對(duì)應(yīng)于retain.
如果代碼寫的UI對(duì)象(使用alloc+init),要使用strong,否則可能在release的時(shí)候出如下警告:
Assigning retained object to weak variable; object will be released after assignment.
而如果是IB拉過來的UI對(duì)象,會(huì)自動(dòng)設(shè)置為weak。
_unsafe_unretained與weak功能一致, 區(qū)別在于當(dāng)指向的對(duì)象銷毀之后, weak將變量置為nil, 防止調(diào)用野指針.
block一般用copy。block一般使用copy關(guān)鍵之進(jìn)行修飾,block使用copy是從MRC遺留下來的“傳統(tǒng)”,在MRC中,方法內(nèi)容的block是在棧區(qū)的,使用copy可以把它放到堆區(qū)。但在ARC中寫不寫都行:編譯器自動(dòng)對(duì)block進(jìn)行了copy操作。
copy:用@property聲明 NSString、NSArray、NSDictionary 經(jīng)常使用copy關(guān)鍵字,是因?yàn)樗麄冇袑?duì)應(yīng)的可變類型:NSMutableString、NSMutableArray、NSMutableDictionary,他們之間可能進(jìn)行賦值操作,為確保對(duì)象中的字符串值不會(huì)無意間變動(dòng),應(yīng)該在設(shè)置新屬性值時(shí)拷貝一份。
如果我們使用是strong,那么這個(gè)屬性就有可能指向一個(gè)可變對(duì)象,如果這個(gè)可變對(duì)象在外部被修改了,那么會(huì)影響該屬性。
copy此特質(zhì)所表達(dá)的所屬關(guān)系與strong類似。然而設(shè)置方法并不保留新值,而是將其“拷貝” (copy)。 當(dāng)屬性類型為NSString時(shí),經(jīng)常用此特質(zhì)來保護(hù)其封裝性,因?yàn)閭鬟f給設(shè)置方法的新值有可能指向一個(gè)NSMutableString類的實(shí)例。這個(gè)類是NSString的子類,表示一種可修改其值的字符串,此時(shí)若是不拷貝字符串,那么設(shè)置完屬性之后,字符串的值就可能會(huì)在對(duì)象不知情的情況下遭人更改。所以,這時(shí)就要拷貝一份“不可變” (immutable)的字符串,確保對(duì)象中的字符串值不會(huì)無意間變動(dòng)。只要實(shí)現(xiàn)屬性所用的對(duì)象是“可變的” (mutable),就應(yīng)該在設(shè)置新屬性值時(shí)拷貝一份。
循環(huán)引用:
typeof (&*self) __weak weakSelf = self;
autorelease:在retainCount為1時(shí),不會(huì)繼續(xù)減一。而是標(biāo)記為需釋放,準(zhǔn)確的釋放時(shí)機(jī)暫不明確,可能與runloop有關(guān)。