kkbox-ios-dev筆記(三) - 內(nèi)存管理/代理

內(nèi)存管理(一)

  • 內(nèi)存泄漏:該釋放的對象, 沒有被釋放(已經(jīng)不再使用的對象, 沒有被釋放)
  • 無效內(nèi)存引用:內(nèi)存已經(jīng)被釋放了,我們還強(qiáng)行調(diào)用。會(huì)報(bào)EXC_BAD_ACCESS錯(cuò)誤。

基本原則

  • 如果是initnew、copy這些方法產(chǎn)生出來的對象,用完就該調(diào)用release.
  • 如果是其他一般方法產(chǎn)生出來的對象,就會(huì)回調(diào)auto-release對象、或是singleton對象(稍晚會(huì)解釋什么是singleton),就不需要另外調(diào)用release.

而調(diào)用retain與release的時(shí)機(jī)包括:

  • 如果是在一般代碼中用了某個(gè)對象,用完就要release或是auto-release
  • 如果是要將某個(gè)Objective-C對象,變成是另外一個(gè)對象的成員變量,就要將對象retain起來。但是delegate對象不該retain。
  • 在一個(gè)對象被釋放的時(shí)候,要同時(shí)釋放自己的成員變量,也就是要在實(shí)現(xiàn)delloc的時(shí)候,釋放自己的成員變量。
  • 要將某個(gè)對象設(shè)為另外一個(gè)對象的成員變量,需要寫一組getter/setter。

Getter/SetterProperty語法

  • 基本數(shù)據(jù)類型

@interface MyClass:NSObject
{
int number;
}

  • (int)number;
  • (void)setNumber:(int)inNumber;
    @end
> * 實(shí)現(xiàn)部分

> ```swift
- (int)number

{
return number;
}

  • (void)setNumber:(int)inNumber
    {
    number = inNumber;
    }

>* 如果是 OC 對象,我們則是要將原本成員變量已經(jīng)指向的內(nèi)存釋放,然后將傳入的對象`retain`起來。該寫法并不安全

>```swift
- (id)myVar {
return myVar;

}

  • (void)setMyVar:(id)inMyVar
    {
    [myVar release];
    myVar = [inMyVar retain];
    }
* 假如今天我們在開發(fā)中用到很多個(gè)線程,而在不同的線程中同時(shí)會(huì)用到`myVar`,在某某個(gè)線程中調(diào)用了`[myVar release]`之后,到`myVar`指定到`inMyVar`的位置之間,假如另外一個(gè)線程剛好用到了`myVar`,這時(shí)候`myVar`剛好指到了一個(gè)已経被釋放的內(nèi)存,這就造成了

EXC_BAD_ACCESS錯(cuò)誤.

  • 更安全的寫法:加鎖,讓程序在調(diào)用setMyVar:的時(shí)候,不讓其他線程調(diào)用myVar;另外一種簡單的方法如下:
  • (void)setMyVar:(id)inMyVar
    {
    id tmp = myVar;
    myVar = [inMyVar retain];
    [tmp release];
    }

* 上面的例子,用`property`語法可以寫成:

```swift
@interface MyClass:NSObject

{
id myVar;
int number;
}
@property (retain, nonatomic) id myVar;
@property (assign, nonatomic) int number;
@end
@implementation MyClass

  • (void)dealloc
    {
    [myVar release];
    [super dealloc];
    }
    @end
**`myVar = nil`與`self.myVar = nil`的區(qū)別?**

* 前者只是單純的將`myVar`的指針指向`nil`,但是并沒有釋放原本所指向的內(nèi)存位置,所以會(huì)造成內(nèi)存泄漏,但后者卻等同于調(diào)用`[self setMyVar:nil]`,會(huì)先釋放`myVar`原本指向的位置,然后將`myVar`設(shè)成`nil`。

內(nèi)存管理(二)

  • ARC 是通過靜態(tài)分析,在編譯時(shí)決定應(yīng)該要在代碼的哪個(gè)地方加入retainrelease。

循環(huán)retain

  • 錯(cuò)誤情況:
    1. 把代理設(shè)置為strong
    1. 某對象的某property是一個(gè)block,但是在這個(gè)block里面把對象自己給retain了一份。
    1. 使用timer的時(shí)候,到了dealloc的時(shí)候才停止timer
  • 假如我們在有一控制器,我們希望這個(gè)控制器可以定時(shí)更新,
    那么,我可能會(huì)使用+scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:方法建立timer對象,指定定時(shí)執(zhí)行某個(gè)selector.要特別注意,在建立這個(gè)timer的時(shí)候,我指定給timertarget,也曾被timer retain一份,因此,我們想要在控制器在dealloc的時(shí)候,才停止timer就曾有問題:因?yàn)榭刂破饕呀?jīng)被timer retain起來了,所以只要timer還在執(zhí)行,控制器就不
    可能走到dealloc的地方。

對象橋接

  • 什么是對象橋接:Foundation庫里面的每一個(gè)對象,都有對應(yīng)的 C 實(shí)現(xiàn),這 C 的實(shí)現(xiàn)叫作Core Foundation,當(dāng)我們在使用Core Foundation里面的 C 形態(tài)時(shí),像CFString、CFArray等,我們可以讓這些形態(tài)變成可以接受 ARC 的管理.這種讓 C 形態(tài)也可以被當(dāng)做 OC 對象,接受 ARC 管理的方式,就叫對象橋接。
  • 有三個(gè)關(guān)鍵字:__bridge、__bridge_retained__bridge_transfer
  • __bridge:會(huì)把Core Foundation的 C 資料形態(tài)轉(zhuǎn)換成 OC 對象,但是不會(huì)多做retainrelease。
  • __bridge_retained:會(huì)把Core Foundation的 C 資料形態(tài)轉(zhuǎn)換成 OC 對象,并且會(huì)做一次retain,但是之后必須由我們手動(dòng)調(diào)用CFRelease,釋放內(nèi)存。
  • __bridge_transfer:會(huì)把Core Foundation轉(zhuǎn)換成 OC 對象,并且會(huì)讓 ARC 主動(dòng)添加retainrelease
  • 但不一定每個(gè)Core Foundation型態(tài)都有辦法轉(zhuǎn)換成OC對象。詳見蘋果文檔詳細(xì)說明

內(nèi)存管理(三)

  • 略 (關(guān)于控制器的內(nèi)存管理)

代理

Delegate屬性應(yīng)該要用Weak,而非strong

  • 原因是:需要設(shè)置代理對象的這個(gè)對象,往往是其代理對象的成員變量,A 的實(shí)例是 A 對象,是 B 的成員變量,可能已經(jīng)被 B retain了一份,如果 A 又 retain了一次 B,就會(huì)出現(xiàn)循環(huán) retain的問題 -- 已經(jīng)被別人retain,又把別人retain一次。

命名規(guī)范

  • 至少傳入一個(gè)參數(shù),就是代理調(diào)用者本身;往往以傳入的類名開頭,讓我們可以辨別這是哪各類的代理方法。以代理UITableViewDelegate為例
  • - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

我們曾經(jīng)犯過的低級錯(cuò)誤

  • 源代碼
@class MyClass;

@protocol MyClassDelegate <NSObject>

  • (void)myClassWillBegin:(MyClass *)myClasss;
  • (void)myClassDidBegin:(MyClass *)myClasss;
  • (void)myClassWillStop:(MyClass *)myClasss;
  • (void)myClassDidStop:(MyClass *)myClasss;
    @end
    @interface MyClass : NSObject
    {
    id <MyClassDelegate> delegate;
    }
  • (void)begin;
  • (void)stop;
    @property (assign, nonatomic) id <MyClassDelegate> delegate;
    @end
    @implementation MyClass
  • (void)begin
    {
    [delegate myClassWillBegin:self];
    // Do something
    [delegate myClassDidBegin:self];
    }
  • (void)stop
    {
    [delegate myClassWillStop:self];
    // Do something
    [delegate myClassDidStop:self];
    }
    @synthesize delegate;
    @end
* 問題:在`myClassWillBegin:`里面想要做一些檢查,如果在某些條件下,這件事情不該跑起來,而應(yīng)該停止,所以在`myClassWillBegin:`里面調(diào)用了`stop`。但這么做,并不會(huì)讓這件事情結(jié)束,因?yàn)閌begin`這個(gè)方法在對代理調(diào)用完`myClassWillBegin:`之后,程序還是會(huì)繼續(xù)走下去,所以還是把`begin`整個(gè)做完了。

* 優(yōu)化后:

```swift
@class MyClass;
####Delegate屬性應(yīng)該要用Weak,而非strong
>* **原因是:**需要設(shè)置代理對象的這個(gè)對象,往往是其代理對象的成員變量,A 的實(shí)例是 A 對象,是 B 的成員變量,可能已經(jīng)被 B `retain`了一份,如果 A 又 `retain`了一次 B,就會(huì)出現(xiàn)循環(huán) `retain`的問題 -- 已經(jīng)被別人`retain`,又把別人`retain`一次。
>
>####命名規(guī)范
>* 至少傳入一個(gè)參數(shù),就是代理調(diào)用者本身;往往以傳入的類名開頭,讓我們可以辨別這是哪各類的代理方法。以代理`UITableViewDelegate`為例
>  * `- (void)tableView:(UITableView *)tableView
        didSelectRowAtIndexPath:(NSIndexPath *)indexPath`
>
>####我們曾經(jīng)犯過的低級錯(cuò)誤
>>* 源代碼
>>
>>```swift
>>@class MyClass;
@protocol MyClassDelegate <NSObject>
- (void)myClassWillBegin:(MyClass *)myClasss;
- (void)myClassDidBegin:(MyClass *)myClasss;
- (void)myClassWillStop:(MyClass *)myClasss;
- (void)myClassDidStop:(MyClass *)myClasss;
@end
@interface MyClass : NSObject
{
    id <MyClassDelegate> delegate;
}
- (void)begin;
- (void)stop;
@property (assign, nonatomic) id <MyClassDelegate> delegate;
@end
@implementation MyClass
- (void)begin
{
    [delegate myClassWillBegin:self];
    // Do something
    [delegate myClassDidBegin:self];
}
- (void)stop
{
    [delegate myClassWillStop:self];
    // Do something
    [delegate myClassDidStop:self];
}
@synthesize delegate;
@end
>>```
>>* 問題:在`myClassWillBegin:`里面想要做一些檢查,如果在某些條件下,這件事情不該跑起來,而應(yīng)該停止,所以在`myClassWillBegin:`里面調(diào)用了`stop`。但這么做,并不會(huì)讓這件事情結(jié)束,因?yàn)閌begin`這個(gè)方法在對代理調(diào)用完`myClassWillBegin:`之后,程序還是會(huì)繼續(xù)走下去,所以還是把`begin`整個(gè)做完了。
>>
>>* 優(yōu)化后:
>>
>>```swift
>>@class MyClass;
@protocol MyClassDelegate <NSObject>
  • (BOOL)myClassShouldBegin:(MyClass *)myClasss;
  • (void)myClassDidBegin:(MyClass *)myClasss;
  • (BOOL)myClassShouldStop:(MyClass *)myClasss;
  • (void)myClassDidStop:(MyClass *)myClasss;
    @end
    @interface MyClass : NSObject
    {
    id <MyClassDelegate> delegate;
    }
  • (void)begin;
  • (void)stop;
    @property (assign, nonatomic) id <MyClassDelegate> delegate;
    @end
    @implementation MyClass
  • (void)begin
    {
    if (![delegate myClassShouldBegin:self]) {
    return;
    }
    // Do something
    [delegate myClassDidBegin:self];
    }
  • (void)stop
    {
    if (![delegate myClassShouldStop:self]) {
    return;
    }
    // Do something
    [delegate myClassDidStop:self];
    }
    @synthesize delegate;
    @end
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 37.cocoa內(nèi)存管理規(guī)則 1)當(dāng)你使用new,alloc或copy方法創(chuàng)建一個(gè)對象時(shí),該對象的保留計(jì)數(shù)器值為1...
    如風(fēng)家的秘密閱讀 955評論 0 4
  • 1.Difference between shallow copy and deep copy? 淺復(fù)制和深復(fù)制的...
    用心在飛閱讀 1,070評論 0 9
  • 內(nèi)存管理 簡述OC中內(nèi)存管理機(jī)制。與retain配對使用的方法是dealloc還是release,為什么?需要與a...
    丶逐漸閱讀 2,081評論 1 16
  • *面試心聲:其實(shí)這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個(gè)offer,總結(jié)起來就是把...
    Dove_iOS閱讀 27,624評論 30 472
  • 給七七的一封信 七七、好久不見。那天看到你寫的這句話、忽然好難過、是真的好久不見了。 七七、不敢用親愛的再來稱呼你...
    毛蛋碎碎念閱讀 270評論 0 0

友情鏈接更多精彩內(nèi)容