Objective-C中的內(nèi)存管理

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. 如下圖

![內(nèi)存管理retain](http://img.blog.csdn.net/20150628091306890)

使用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ì)象.? 如下圖:

![內(nèi)存管理copy](http://img.blog.csdn.net/20150628091451099)

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

![內(nèi)存管理deepCopy](http://img.blog.csdn.net/20150628091542228)

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)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,702評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,143評(píng)論 3 415
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,553評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,620評(píng)論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,416評(píng)論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,940評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,024評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,170評(píng)論 0 287
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,709評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,597評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,784評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,291評(píng)論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,029評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,407評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,663評(píng)論 1 280
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,403評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,746評(píng)論 2 370

推薦閱讀更多精彩內(nèi)容

  • 內(nèi)存管理是程序在運(yùn)行時(shí)分配內(nèi)存、使用內(nèi)存,并在程序完成時(shí)釋放內(nèi)存的過程。在Objective-C中,也被看作是在眾...
    蹲瓜閱讀 3,135評(píng)論 1 8
  • 1.1 什么是自動(dòng)引用計(jì)數(shù) 概念:在 LLVM 編譯器中設(shè)置 ARC(Automaitc Reference Co...
    __silhouette閱讀 5,213評(píng)論 1 17
  • 自動(dòng)引用計(jì)數(shù) 自動(dòng)引用計(jì)數(shù):指內(nèi)存管理中對(duì)引用采取自動(dòng)計(jì)數(shù)的技術(shù)。 內(nèi)存管理/引用計(jì)數(shù) 持有對(duì)象引起引用計(jì)數(shù)加...
    南京小伙閱讀 1,325評(píng)論 2 3
  • 前言 從我開始學(xué)習(xí)iOS的時(shí)候,身邊的朋友、網(wǎng)上的博客都告訴我iOS的內(nèi)存管理是依靠引用計(jì)數(shù)的,然后說引用計(jì)數(shù)大于...
    蓋世英雄_ix4n04閱讀 564評(píng)論 0 1
  • 生命中的很多認(rèn)知都會(huì)隨著時(shí)間和經(jīng)歷慢慢改變。我對(duì)大學(xué)的向往從小學(xué)一直延續(xù)到高中,心...
    Mr果凍閱讀 656評(píng)論 2 4