【iOS】筆記·OC高級編程·iOS與OS X 多線程與內(nèi)存管理

  1. 自動引用計(jì)數(shù) (ARC,Automatic Reference Counting)

1.1 什么是自動引用計(jì)數(shù)?

ARC是指內(nèi)存管理中對引用采用自動計(jì)數(shù)的技術(shù)。在LLVM編譯器中設(shè)置ARC為有效狀態(tài),就無需再次鍵入retain或者是release代碼。

1.2 內(nèi)存管理/引用計(jì)數(shù)

圖1·照明設(shè)備動作對應(yīng)于OC對象動作
圖2·引用計(jì)數(shù)的內(nèi)存管理

1.2.2 內(nèi)存管理的思考方式

從上面的內(nèi)容,我們可以得出這么一個(gè)結(jié)論:
1. 自己生成的對象,自己所持有。
2. 非自己生成的對象,自己也能持有。
3. 不再需要自己持有的對象時(shí)釋放。
4. 非自己持有的對象無法釋放。

除了以上四條提到的三個(gè)詞:“生成”、“持有”、“釋放”,還有另外一個(gè)詞:“廢棄”或者叫“銷毀”。

圖3·對象操作與OC方法的對應(yīng)

這些有關(guān)OC內(nèi)存管理的方法,實(shí)際上不包括在該語言中,而是包含在Cocoa框架中用于OS X、iOS 應(yīng)用開發(fā)。Cocoa框架中 Foundation 框架類庫的 NSObject 類擔(dān)負(fù)內(nèi)存管理的職責(zé)。OC內(nèi)存管理中的alloc、retain、release、dealloc方法分別指代 NSObject 類的alloc類方法、retain實(shí)例方法、release實(shí)例方法和dealloc實(shí)例方法。

圖4·Cocoa框架、Foundation框架和NSObject類的關(guān)系

自己生成的對象,自己所持有

使用以下名稱開頭的方法名意味著自己生成的對象只有自己持有:

  • alloc
  • new
  • copy
  • mutableCopy

copy -- 基于NSCopying協(xié)議實(shí)現(xiàn)協(xié)議的copyWithZone:方法生成并持有對象的副本。

mutableCopy -- 基于NSMutableCopying協(xié)議實(shí)現(xiàn)協(xié)議的mutableCopyWithZone:方法生成并持有對象的副本。

“自己”指的是“對象的使用環(huán)境”,但將之理解為編程人員“自身”也是沒錯的。

// [NSObject new]與[[NSObject alloc] init]是完全一致的。
id obj1 = [[NSObject alloc] init];
// or
id obj2 = [NSObject new];

非自己生成的對象,自己也能持有

本小節(jié)略有疑問,閱讀原書查看更多內(nèi)容。

不再需要自己持有的對象時(shí)釋放

id obj = [[NSObject alloc] init];
[obj release];
// 指向?qū)ο蟮闹羔橂m仍然保留在變量obj中,貌似能夠訪問,但對象已經(jīng)被釋放了,是決不可訪問的,這時(shí)候的obj已經(jīng)變成了*野指針*,為避免訪問報(bào)錯,可以將obj設(shè)置為空指針。
obj = nil;
// 因?yàn)榻o空指針發(fā)消息是不會報(bào)錯的。

用alloc/new/copy/mutableCopy方法生成并持有的對象,或者用retain方法持有的對象,一旦不再需要,務(wù)必要用release方法進(jìn)行釋放。

圖5·release和autorelease的區(qū)別

無法釋放非自己持有的對象
即只有當(dāng)你持有該對象時(shí),你才有權(quán)利釋放它,否則不要去執(zhí)行釋放操作;釋放過一遍了,不要嘗試再次釋放。

1.2.3 alloc/retain/release/dealloc 實(shí)現(xiàn)

  • GNUstep的實(shí)現(xiàn)


    圖6·GNUstep中的實(shí)現(xiàn)機(jī)制
  • Apple的實(shí)現(xiàn)


    圖7·通過引用計(jì)數(shù)表追溯對象

1.2.5 autorelease

顧名思義,autorelease就是自動釋放,這看上去很像ARC,但實(shí)際上它更類似于C語言中的自動變量(局部變量)的特性。自動變量的概念是程序執(zhí)行時(shí),若某自動變量超出其作用域,該自動變量將被自動廢棄。autorelease會像C語言的自動變量那樣來對待對象實(shí)例。當(dāng)超出其作用域(相當(dāng)于變量作用域)時(shí),對象實(shí)例的release實(shí)例方法被調(diào)用。另外,同C語言的自動變量不同的是,編程人員可以設(shè)定變量的作用域。

autorelease的具體使用方法如下:

  • 生成并持有NSAutoreleasePool對象
  • 調(diào)用已分配對象的autorelease實(shí)例方法
  • 廢棄NSAutoreleasePool對象
圖8·NSAutoreleasePool對象的生命周期

NSAutoreleasePool對象的生存周期相當(dāng)于C語言變量的作用域。對于所有調(diào)用過autorelease實(shí)例方法的對象,在廢棄NSAutoreleasePool對象時(shí),都將調(diào)用release實(shí)例方法。

值得注意的是,在Cocoa框架中,相當(dāng)于程序主循環(huán)的NSRunLoop或者在其他程序可運(yùn)行的地方,對NSAutoreleasePool對象進(jìn)行生成、持有和廢棄處理。因此,開發(fā)者不一定非得使用NSAutoreleasePool對象來進(jìn)行開發(fā)工作。

圖9·NSRunLoop每次循環(huán)過程中 NSAutoreleasePool對象被生成或廢棄

盡管如此,不是代表你就可以不用手動管理autorelease的調(diào)用了,在大量產(chǎn)生autorelease的對象時(shí),只要不廢棄NSAutoreleasePool對象,那么生成的對象就不能被釋放,因此有時(shí)會產(chǎn)生內(nèi)存不足的現(xiàn)象。典型的例子就是讀入大量圖像的同時(shí),改變其尺寸等等一些列的操作,不停的消耗內(nèi)存,因此這時(shí)就需要我們手動創(chuàng)建NSAutoreleasePool對象,并適時(shí)廢棄,來優(yōu)化內(nèi)存。

圖10·釋放地釋放autorelease對象

另外,Cocoa框架中也有很多類方法用于返回autorelease的對象,比如NSMutableArray類的arrayWithCapacity類方法。

id array = [NSMutableArray arrayWithCapacity: 1];
// 等同于以下
id array = [[[NSMutableArray alloc] initWithCapacity: 1] autorelease];

1.2.6 autorelease 實(shí)現(xiàn)

圖11·autorelease的GNUstep實(shí)現(xiàn)源代碼
圖12·提高調(diào)用OC方法的速度

更多內(nèi)容查看原書。

1.3 ARC 規(guī)則

1.3.1 概要

實(shí)際上“引用計(jì)數(shù)式內(nèi)存管理”的本質(zhì)部分在ARC中并沒有改變。就像“自動引用計(jì)數(shù)”這個(gè)名詞表示的那樣,ARC只是自動地幫助我們處理“引用計(jì)數(shù)”的相關(guān)部分。

在編譯單位上,可設(shè)置ARC有效或無效,這一點(diǎn)便能佐證上述結(jié)論。比如對每一個(gè)文件可選擇使用或不使用ARC。

圖13·同一程序中按文件單位可以選擇ARC有效/無效

設(shè)置ARC有效的編譯方法如下:

  • 使用clang(LLVM編譯器)3.0或以上版本。
  • 指定編譯器屬性為“-fobjc-arc”

Xcode4.2默認(rèn)設(shè)定為對所有的文件ARC有效。

1.3.2 內(nèi)存管理的思考方式

1. 自己生成的對象,自己所持有。
2. 非自己生成的對象,自己也能持有。
3. 不再需要自己持有的對象時(shí)釋放。
4. 非自己持有的對象無法釋放。

思考方式與非ARC模式下的是一樣的,只是在源代碼的記述方法上稍有不同。首先要理解ARC中追加的所有權(quán)聲明。

1.3.3 所有權(quán)修飾符

OC編程中為了處理對象,可將變量類型定義為id類型或各種對象類型。

所謂對象類型就是指向NSObject這樣的OC類的指針,例如“NSObject *”。id類型用于隱藏對象類型的類名部分,相當(dāng)于C語言中常用的“void *”。

ARC有效時(shí),id類型和對象類型同C語言其他類型不同,其類型上必須附加所有權(quán)修飾符。所有權(quán)修飾符一共有四種。

  • __strong 修飾符
  • __weak 修飾符
  • __unsafe_unretained 修飾符
  • __autoreleasing 修飾符

__strong 修飾符
__strong修飾符是id類型和對象類型默認(rèn)的所有權(quán)修飾符。也就是說,以下源代碼中的id類型的變量obj,實(shí)際上被附加了所有權(quán)修飾符。

id obj = [[NSObject alloc] init];
// 上面的代碼與下面是相同的
id __strong obj = [[NSObject alloc] init];

如同“strong”這個(gè)名詞所示,__strong 修飾符表示對對象的“強(qiáng)引用”。持有強(qiáng)引用的變量在超出其作用域時(shí)被廢棄,隨著強(qiáng)引用的失效,引用的對象會隨之釋放。

{
    // 自己生成并持有對象
    id __strong obj = [[NSObject alloc] init];
    // 因?yàn)樽兞縪bj為強(qiáng)引用,所以自己持有對象
}
// 此時(shí)變量obj超出其作用域,強(qiáng)引用失效,所以自動的釋放了自己持有的對象。
// 對象的所有者不存在了,因此廢棄該對象。

附有__strong修飾符的變量之間可以相互賦值。

id __strong obj0 = [[NSObject alloc] init];
id __strong obj1 = [[NSObject alloc] init];
id __strong obj2 = nil;

obj0 = obj1;
obj2 = obj0;

obj1 = nil;
obj0 = nil;
obj2 = nil;

上面的代碼,三個(gè)變量所指向的2個(gè)對象都能夠被正確的釋放(詳細(xì)解釋參考原書)??梢钥闯觯琠_strong修飾符的變量,不僅只在變量作用域中,在賦值上也能夠正確地管理其對象的所有者。

也可以在方法參數(shù)上,使用附有__strong修飾符的變量。

Test.h

@interface Test : NSObject {
    id __strong obj_;
}
-(void)setObject:(id __strong)obj;
@end

Test.m

@implementation Test

- (instancetype)init
{
    self = [super init];
    return self;
}
- (void)setObject:(id)obj {
    obj_ = obj;
}

main.m

{
    id __strong test = [[Test alloc] init];
    [test setObject: [[NSObject alloc] init]];
}

下面來解釋一下上面的代碼執(zhí)行結(jié)果。

  1. test變量,持有Test對象的強(qiáng)引用。
  2. Test對象的obj_成員,持有NSObject對象的強(qiáng)引用。
  3. 因?yàn)閠est變量超出作用域,強(qiáng)引用失效,所以自動釋放Test對象。
  4. Test對象的所有者不存在,因此廢棄該對象。
  5. 廢棄Test對象的同時(shí),Test對象的obj_成員也被廢棄,NSObject對象的強(qiáng)引用失效,自動釋放NSObject對象。
  6. NSObject對象的所有者不存在,因此廢棄該對象。

因此,__strong修飾符,使得無需額外的工作便可使用于類成員變量和方法參數(shù)中。
另外,__strong修飾符同后面將要講到的__weak修飾符和__autoreleasing修飾符一起,可以保證將附有這些修飾符的自動變量初始化為nil。

id __strong obj0;
id __weak obj1;
id __autoreleasing obj2;
// 等同于如下:
id __strong obj0 = nil;
id __weak obj1 = nil;
id __autoreleasing obj2 = nil;

總結(jié):


圖14·對__strong修飾符的總結(jié)

__weak 修飾符

__weak修飾符的引入是為了解決引用計(jì)數(shù)式內(nèi)存管理中必然會發(fā)生的“循環(huán)引用”的問題。

圖15·循環(huán)引用

我們來看一個(gè)循環(huán)引用的例子:

{
    id test0 = [[Test alloc] init];
    id test1 = [[Test alloc] init];
    [test0 setObject: test1];
    [test1 setObject: test0];
}
圖16·類成員變量的循環(huán)引用

循環(huán)引用容易發(fā)生內(nèi)存泄漏。所謂內(nèi)存泄漏就是應(yīng)當(dāng)廢棄的對象在超出其生存周期后繼續(xù)存在。

一個(gè)對象持有其自身時(shí),也會發(fā)生循環(huán)引用。

圖17·自引用

這時(shí)候就該__weak修飾符上場了,__weak修飾符與__strong修飾符相反,提供弱引用。弱引用不能持有對象實(shí)例。

看看這個(gè)修改后的實(shí)例:

圖18·__weak修飾符避免循環(huán)引用

__weak修飾符還有另一優(yōu)點(diǎn)。在持有某對象的弱引用時(shí),若該對象被廢棄,則此弱引用將自動失效且處于nil被賦值的狀態(tài)(空弱引用)。

id __weak obj1 = nil;
{
    id __strong obj0 = [[NSObject alloc] init];
    obj1 = obj0;
    NSLog(@"A: %@", obj1);
}
NSLog(@"B: %@", obj1);

// 執(zhí)行結(jié)果如下:
A: <NSObject: 0x753e180>
B: (null)

__weak修飾符只能用于iOS5以上及OS X Lion以上版本,在iOS4及OS X Snow Leopard中,可以使用__unsafe_unretained修飾符來代替。

更多詳細(xì)內(nèi)容查看原書。

__unsafe_unretained 修飾符

__unsafe_unretained修飾符正如其名unsafe所示,是不安全的所有權(quán)修飾符。盡管ARC式的內(nèi)存管理是編譯器的工作,但附有__unsafe_unretained修飾符的變量不屬于編譯器的內(nèi)存管理對象。這一點(diǎn)在使用時(shí)要注意。

id __unsafe_unretained obj = [[NSObject alloc] init];

編譯器會給出警告,提示和使用__weak修飾符時(shí)類似。警告,自己生成并持有的對象不能繼續(xù)為自己所有,所以生成的對象會立即被釋放。

id __unsafe_unretained obj1 = nil;
{
    id __strong obj0 = [[NSObject alloc] init];
    obj1 = obj0;
    NSLog(@"A: %@", obj1);
}
NSLog(@"B: %@", obj1);

// 執(zhí)行結(jié)果如下:
A: <NSObject: 0x753e180>
B: <NSObject: 0x753e180>

運(yùn)行到NSLog(@"B: %@", obj1);時(shí),其實(shí)obj1所指向的對象已經(jīng)被釋放了,最后一行NSLog只是碰巧正常運(yùn)行而已。雖然訪問了已經(jīng)被廢棄的對象,但應(yīng)用程序在個(gè)別運(yùn)行狀況下才會崩潰。所以,有可能程序在執(zhí)行到那一行時(shí),發(fā)生野指針錯誤,引發(fā)崩潰。野指針:變量地址指向了一塊已經(jīng)被廢棄了的內(nèi)存塊,試圖訪問方法、變量,從而引發(fā)崩潰。

__unsafe_unretined的存在是為了解決在iOS4及OS X Snow Leopard中的應(yīng)用程序下使用,避免循環(huán)引用的修飾符,當(dāng)然,使用時(shí)要注意,在訪問__unsafe_unretained修飾符變量時(shí)要確保對象存在,不然程序會崩潰。

小結(jié):__weak和__unsafe_unretained的區(qū)別就在于,__weak屬于ARC范疇,ARC自動管理由__weak修飾符修飾的變量,在指向的對象被廢棄時(shí),自動將變量置為nil,而__unsafe_unretained不屬于ARC管理范疇,不會自動將變量置為nil,需要使用者自己判斷。

__autoreleasing 修飾符

ARC有效時(shí)autorelease會如何?實(shí)際上,后面講到的原則也會說明(參考1.3.3節(jié)),不能使用autorelease方法。另外,也不能使用NSAutoreleasePool類。這樣一來,雖然autorelease無法直接使用,但實(shí)際上,ARC有效時(shí)autorelease功能是起作用的。

ARC無效時(shí)的寫法

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
id obj = [[[NSObject alloc] init] autorelease];
[pool drain];

ARC有效時(shí)的寫法

@autoreleasepool {
    id __autoreleasing obj = [[NSObject alloc] init];
}

在ARC中,通過“@autoreleasepool塊”來替代非ARC中的“NSAutoreleasePool類對象生成、持有以及廢棄”這一范圍。

可以這么理解,在ARC有效時(shí),用@autoreleasepool塊替代NSAutoreleasePool類,用附有__autoreleasing修飾符的變量替代autorelease方法。

圖19·@autoreleasepool 和 附有 __autoreleasing 修飾符的變量

但是要知道,顯式地附加 __autoreleasing 修飾符同顯式地附加 __strong 修飾符一樣罕見。

我們通過實(shí)例來看看為什么非顯式地使用 __autoreleasing 修飾符也可以。

取得非自己生成并持有的對象時(shí),如同一下源代碼,雖然可以使用alloc/new/copy/mutableCopy以外的方法來取得對象,但該對象已被注冊到了 autoreleasepool。這同在ARC無效時(shí)取得調(diào)用了 autorelease 方法的對象是一樣的。這是由于編譯器會檢查方法名是否以alloc/new/copy/mutableCopy開始,如果不是則自動將返回值的對象注冊到 autoreleasepool。

另外,根據(jù)后面要講到的遵守內(nèi)存管理方法命名規(guī)則(參考1.3.4節(jié)),init方法返回值的對象不注冊到 autoreleasepool。

@autoreleaspool {
    id __strong obj = [[NSMutableArray array];
}

來看看到底發(fā)生了什么:


圖20·非自己生成并持有的對象在自動釋放池中的生命周期

再來看看源代碼的調(diào)用過程:

圖21·NSMutableArray類方法array的內(nèi)部實(shí)現(xiàn)和原理

雖然__weak修飾符是為了避免循環(huán)引用而使用的,但在訪問附有__weak修飾符的變量時(shí),實(shí)際上必定要訪問注冊到autoreleasepool的對象。

看下面的例子:

id __strong obj0 = [[NSObject alloc] init];
id __weak obj1 = obj0;
NSLog(@"class=%@", [obj1 class]);

以下源代碼與此相同:

id __strong obj0 = [[NSObject alloc] init];
id __weak obj1 = obj0;
id __autoreleasing tmp = obj1;
NSLog(@"class=%@", [tmp class]);

為什么在訪問附有__weak修飾符的變量時(shí)必須訪問注冊到autoreleasepool的對象呢?這是因?yàn)開_weak修飾符只持有對象的弱引用,而在訪問引用對象的過程中,該對象有可能被廢棄。如果把要訪問的對象注冊到autoreleasepool中,那么在@autoreleasepool塊結(jié)束之前都能確保該對象存在。

id的指針或?qū)ο蟮闹羔樤跊]有顯式指定時(shí)會被附加上__autoreleasing修飾符,這部分重點(diǎn)內(nèi)容較多,也較難以理解,查看原書。

注意,可借助強(qiáng)大的非公開函數(shù)_objc_autoreleasePoolPrint()方法來查看自動釋放池的狀態(tài)

圖22·借助_objc_autoreleasePoolPrint()方法查看自動釋放池狀態(tài)

專欄:__strong修飾符/__weak修飾符


圖23·__strong修飾符/__weak修飾符

1.3.4 規(guī)則

在ARC有效的情況下編譯源代碼,必須遵守一定的規(guī)則。下面就是具體的ARC的規(guī)則。

  • 不能使用retain/release/retainCount/autorelease
  • 不能使用NSAllocateObject/NSDeallocateObject
  • 必須遵守內(nèi)存管理的方法命名規(guī)則
  • 不能顯式調(diào)用dealloc
  • 使用@autoreleasepool塊替代NSAutoreleasePool
  • 不能使用區(qū)域(NSZone)
  • 對象型變量不能作為C語言結(jié)構(gòu)體(struct/union)的成員
  • 顯式轉(zhuǎn)換“id”和“void *”

下面詳細(xì)介紹各項(xiàng)。

不能使用retain/release/retainCount/autorelease
內(nèi)存管理是編譯器的工作,因此沒有必要再使用內(nèi)存管理的方法。這段話摘自蘋果的官方說明:“設(shè)置ARC有效時(shí),無需再次輸入retain或者是release代碼?!?/strong>實(shí)際上,如果在ARC有效時(shí),調(diào)用這些方法,編譯器是會報(bào)錯的,因此可以把這句話更準(zhǔn)確的描述為:“設(shè)置ARC有效時(shí),禁止再次鍵入retain或者是release代碼?!?/strong>

總之,只能在ARC無效且手動進(jìn)行內(nèi)存管理時(shí)使用retain/release/retainCount/autorelease方法。

不能使用NSAllocateObject/NSDeallocateObject

NSAllocateObject函數(shù)用于ARC無效時(shí)的NSObject類的alloc類方法,用于生成并持有對象的。同樣的,也禁止使用用于釋放對象的NSDeallocateObject函數(shù)。

須遵守內(nèi)存管理的方法命名規(guī)則

如1.2.2節(jié)所示,在ARC無效時(shí),用于對象生成/持有的方法必須遵守以下命名規(guī)則。

  • alloc
  • new
  • copy
  • mutableCopy
    以上述名稱開始的方法在返回對象時(shí),必須返回給調(diào)用方所應(yīng)當(dāng)持有的對象。這在ARC有效時(shí)也一樣,返回的對象完全沒有改變。只是在ARC有效時(shí)要追加一條命名規(guī)則。
  • init
    init開始的方法的規(guī)則要比 alloc/new/copy/mutableCopy 更嚴(yán)格。該方法必須是實(shí)例方法,并且必須要返回對象。返回的對象應(yīng)為 id類型或該方法聲明類的對象類型,抑或是該類的超類型或子類型。該返回對象并不注冊到autoreleasepool上?;旧现皇菍?code>alloc方法返回值的對象進(jìn)行初始化處理并返回該對象。

其他拓展自定義的命名規(guī)則舉例如下:

-(id)initWithObject:(id) obj;
-(id)initWithName:(NSString *)name age:(int)age;

不要顯式調(diào)用dealloc
無論ARC是否有效,只要對象的所有者都不持有該對象,該對象就被廢棄。對象被廢棄時(shí),不管ARC是否有效,都會調(diào)用對象的dealloc方法。

- (void) dealloc {
    /* 此處運(yùn)行該對象被廢棄時(shí)必須實(shí)現(xiàn)的代碼 */
    ...
    [super dealloc];// 必須放在最后調(diào)用(ARC無效時(shí)才手動調(diào)用,ARC有效時(shí)不允許調(diào)用)
}

比如使用C語言庫時(shí),在該庫內(nèi)部分配緩存時(shí),如以下所示,dealloc方法需要通過free來釋放留出的內(nèi)存。

- (void) dealloc {
    free(buffer_);
}

dealloc方法在大多數(shù)情況下還適用于刪除已注冊的代碼或者觀察者對象。

- (void) dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver: self];
}

另外一定注意一點(diǎn),在ARC無效時(shí),重寫了dealloc方法后,[super dealloc];必須放在最后調(diào)用。

使用@autoreleasepool塊替代NSAutoreleasePool:參考1.3.3節(jié)

不能使用區(qū)域(NSZone)
雖說ARC有效時(shí),不能使用區(qū)域(NSZone)。正如前所述(參考1.2.3節(jié)),不管ARC是否有效,區(qū)域在現(xiàn)在的運(yùn)行時(shí)系統(tǒng)(編譯器宏OBJC2被設(shè)定的環(huán)境)中已單純地被忽略。

對象型變量不能作為C語言結(jié)構(gòu)體的成員

下面的代碼會引發(fā)編譯錯誤:

struct Data {
    NSMutableArray *array;
};
編譯錯誤:error: ARC forbids Objective-C objcs in structs or unions
      NSMutableArray *array;

要把對象型變量加入到結(jié)構(gòu)體成員中時(shí),可強(qiáng)制轉(zhuǎn)換為 void * 或是附加前面所述的 __unsafe_unretained 修飾符。

struct Data {
    NSMutableArray __unsafe_unretained *array;
};

如前所述,附有 __unsafe_unretained 修飾符的變量不屬于編譯器的內(nèi)存管理對象。如果管理時(shí)不注意賦值對象的所有者,便有可能遭遇內(nèi)存泄漏或程序崩潰,在使用時(shí)要多加注意。

顯式轉(zhuǎn)換 idvoid*
在ARC無效時(shí),像以下代碼這樣將id變量強(qiáng)制轉(zhuǎn)換void *變量并不會出問題。

/* ARC無效 */
id obj = [[NSObject alloc] init];
void *p = obj;
id o = p;
[o release];

但是在ARC有效時(shí)這便會引起編譯錯誤,這種操作不被允許。

id型或?qū)ο笮妥兞抠x值給void *或者逆向賦值時(shí)都需要進(jìn)行特定的轉(zhuǎn)換。如果只想單純地賦值,則可以使用“__bridge 轉(zhuǎn)換”。

id obj = [[NSObject alloc] init];
void *p = (__bridge void *)obj;
id o = (__bridge id)p;

像這樣,通過“__bridge 轉(zhuǎn)換”,id和void *就能通過互相轉(zhuǎn)換。
但是轉(zhuǎn)換為 void * 的__bridge 轉(zhuǎn)換,其安全性與賦值給 __unsafe_unretained 修飾符相近,甚至?xí)?。如果管理時(shí)不注意賦值對象的所有者,就會出現(xiàn)懸垂指針(野指針)而導(dǎo)致程序崩潰。

__bridge轉(zhuǎn)換中還有另外兩種轉(zhuǎn)換,分別是“__bridge_retained 轉(zhuǎn)換”和“__bridge_transfer 轉(zhuǎn)換”。

id obj = [[NSObject alloc] init];
void *p = (__bridge_retained void *)obj;

__bridge_retained 轉(zhuǎn)換可使要轉(zhuǎn)換賦值的變量也持有所賦值的對象。
__bridge_transfer 轉(zhuǎn)換提供與此相反的動作,被轉(zhuǎn)換的變量所持有的對象在該變量被賦值給轉(zhuǎn)換目標(biāo)變量后隨之釋放。相當(dāng)于是做了“轉(zhuǎn)移”(乾坤大挪移 ̄□ ̄||)。

同__bridge_retained轉(zhuǎn)換與retain類似,__bridge_transfer轉(zhuǎn)換與release相似。在給id obj賦值時(shí)retain即相當(dāng)于 __strong修飾符的變量。

如果使用以上兩種轉(zhuǎn)換,那么不使用id型或?qū)ο笮妥兞恳部梢陨?、持有以及釋放對象。雖然可以這么做,但在ARC中并不推薦這種方法。

void *p = (__bridge_retained void *)[[Person alloc] initWithName:@"CJL" age:29];
NSLog(@"class=%@", [(__bridge id)p class]);
(void)(__bridge_transfer id)p;
// 打印Person的dealloc方法,發(fā)現(xiàn)方法被調(diào)用了

這些轉(zhuǎn)換多數(shù)使用在OC對象與CoreFoundation對象之間的相關(guān)變換中。

專欄:OC對象與CoreFoundation對象


圖24·OC對象與CoreFoundation對象

更多內(nèi)容查看原書。

1.3.5 屬性
當(dāng)ARC有效時(shí),OC類的屬性也會發(fā)生變化。

@property(nonatomic, strong) NSString *name;

當(dāng)ARC有效時(shí),以下可作為這種屬性聲明中使用的屬性來用。

圖25·屬性聲明的屬性與所有權(quán)修飾符的對應(yīng)關(guān)系

1.3.6 數(shù)組
...

最后編輯于
?著作權(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)容

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