block使用小結(jié)、在arc中使用block、如何防止循環(huán)引用_第1頁
block使用小結(jié)、在arc中使用block、如何防止循環(huán)引用_第2頁
block使用小結(jié)、在arc中使用block、如何防止循環(huán)引用_第3頁
block使用小結(jié)、在arc中使用block、如何防止循環(huán)引用_第4頁
block使用小結(jié)、在arc中使用block、如何防止循環(huán)引用_第5頁
已閱讀5頁,還剩23頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

block使用小結(jié)、在arc中使用block、如何防止循環(huán)引用block使用小結(jié)、在arc中使用block、如何防止循環(huán)引用/NUMPAGES28block使用小結(jié)、在arc中使用block、如何防止循環(huán)引用block使用小結(jié)、在arc中使用block、如何防止循環(huán)引用引言使用block已經(jīng)有一段時(shí)間了,感覺自己了解的還行,但是幾天前看到CocoaChina上一個(gè)關(guān)于block的小測試主題:

【小測試】你真的知道blocks在Objective-C中是怎么工作的嗎?,發(fā)現(xiàn)竟然做錯了幾道,

才知道自己想當(dāng)然的理解是錯誤的,所以抽時(shí)間學(xué)習(xí)了下,并且通過一些測試代碼進(jìn)行測試,產(chǎn)生這篇博客。Block簡介(copy一段)Block作為C語言的擴(kuò)展,并不是高新技術(shù),和其他語言的閉包或lambda表達(dá)式是一回事。需要注意的是由于Objective-C在iOS中不支持GC機(jī)制,使用Block必須自己管理內(nèi)存,而內(nèi)存管理正是使用Block坑最多的地方,錯誤的內(nèi)存管理

要么導(dǎo)致returncycle內(nèi)存泄漏要么內(nèi)存被提前釋放導(dǎo)致crash。

Block的使用很像函數(shù)指針,不過與函數(shù)最大的不同是:Block可以訪問函數(shù)以外、詞法作用域以內(nèi)的外部變量的值。換句話說,Block不僅實(shí)現(xiàn)函數(shù)的功能,還能攜帶函數(shù)的執(zhí)行環(huán)境??梢赃@樣理解,Block其實(shí)包含兩個(gè)部分內(nèi)容Block執(zhí)行的代碼,這是在編譯的時(shí)候已經(jīng)生成好的;一個(gè)包含Block執(zhí)行時(shí)需要的所有外部變量值的數(shù)據(jù)結(jié)構(gòu)。

Block將使用到的、作用域附近到的變量的值建立一份快照拷貝到棧上。Block與函數(shù)另一個(gè)不同是,Block類似ObjC的對象,可以使用自動釋放池管理內(nèi)存(但Block并不完全等同于ObjC對象,后面將詳細(xì)說明)。Block基本語法基本語法在本文就不贅述了,同學(xué)們自學(xué)。Block的類型與內(nèi)存管理根據(jù)Block在內(nèi)存中的位置分為三種類型NSGlobalBlock,NSStackBlock,NSMallocBlock。NSGlobalBlock:類似函數(shù),位于text段;NSStackBlock:位于棧內(nèi)存,函數(shù)返回后Block將無效;NSMallocBlock:位于堆內(nèi)存。1、NSGlobalBlock如下,我們可以通過是否引用外部變量識別,未引用外部變量即為NSGlobalBlock,可以當(dāng)做函數(shù)使用。123456789{

//createaNSGlobalBlock

float

(^sum)(float,

float)=^(float

a,

float

b){

return

a+b;

};

NSLog(@"blockis%@",sum);

//blockis<__NSGlobalBlock__:0x47d0>}2、NSStackBlock如下:1234567891011121314151617181920{

NSArray

*testArr=@[@"1",

@"2"];

void

(^TestBlock)(void)=^{

NSLog(@"testArr:%@",testArr);

};

NSLog(@"blockis%@",^{

NSLog(@"testArr:%@",testArr);

});

//blockis<__NSStackBlock__:0xbfffdac0>

//打印可看出block是一個(gè)

NSStackBlock,

即在棧上,

當(dāng)函數(shù)返回時(shí)block將無效

NSLog(@"blockis%@",TestBlock);

//blockis<__NSMallocBlock__:0x75425a0>

//上面這句在非arc中打印是

NSStackBlock,

但是在arc中就是NSMallocBlock

//即在arc中默認(rèn)會將block從棧復(fù)制到堆上,而在非arc中,則需要手動copy.}3、NSMallocBlock只需要對NSStackBlock進(jìn)行copy操作就可以獲取,但是retain操作就不行,會在下面說明Block的copy、retain、release操作

(還是copy一段)不同于NSObjec的copy、retain、release操作:Block_copy與copy等效,Block_release與release等效;對Block不管是retain、copy、release都不會改變引用計(jì)數(shù)retainCount,retainCount始終是1;NSGlobalBlock:retain、copy、release操作都無效;NSStackBlock:retain、release操作無效,必須注意的是,NSStackBlock在函數(shù)返回后,Block內(nèi)存將被回收。即使retain也沒用。容易犯的錯誤是[[mutableAarryaddObject:stackBlock],(補(bǔ):在arc中不用擔(dān)心此問題,因?yàn)閍rc中會默認(rèn)將實(shí)例化的block拷貝到堆上)在函數(shù)出棧后,從mutableAarry中取到的stackBlock已經(jīng)被回收,變成了野指針。正確的做法是先將stackBlockcopy到堆上,然后加入數(shù)組:[mutableAarryaddObject:[[stackBlockcopy]autorelease]]。支持copy,copy之后生成新的NSMallocBlock類型對象。NSMallocBlock支持retain、release,雖然retainCount始終是1,但內(nèi)存管理器中仍然會增加、減少計(jì)數(shù)。copy之后不會生成新的對象,只是增加了一次引用,類似retain;盡量不要對Block使用retain操作。Block對外部變量的存取管理基本數(shù)據(jù)類型1、局部變量局部自動變量,在Block中只讀。Block定義時(shí)copy變量的值,在Block中作為常量使用,所以即使變量的值在Block外改變,也不影響他在Block中的值。1234567891011{

int

base=100;

long

(^sum)(int,

int)=^

long

(int

a,

int

b){

return

base+a+b;

};

base=0;

printf("%ld\n",sum(1,2));

//

這里輸出是103,而不是3,

因?yàn)閴K內(nèi)base為拷貝的常量

100}2、STATIC修飾符的全局變量因?yàn)槿肿兞炕蜢o態(tài)變量在內(nèi)存中的地址是固定的,Block在讀取該變量值的時(shí)候是直接從其所在內(nèi)存讀出,獲取到的是最新值,而不是在定義時(shí)copy的常量.12345678910111213{

static

int

base=100;

long

(^sum)(int,

int)=^

long

(int

a,

int

b){

base++;

return

base+a+b;

};

base=0;

printf("%ld\n",sum(1,2));

//

這里輸出是4,而不是103,

因?yàn)閎ase被設(shè)置為了0

printf("%d\n",base);

//

這里輸出1,

因?yàn)閟um中將base++了}3、__BLOCK修飾的變量Block變量,被__block修飾的變量稱作Block變量。

基本類型的Block變量等效于全局變量、或靜態(tài)變量。注:BLOCK被另一個(gè)BLOCK使用時(shí),另一個(gè)BLOCK被COPY到堆上時(shí),被使用的BLOCK也會被COPY。但作為參數(shù)的BLOCK是不會發(fā)生COPY的OBJC對象block對于objc對象的內(nèi)存管理較為復(fù)雜,這里要分staticgloballocalblock變量分析、還要分非arc和arc分析非ARC中的變量先看一段代碼(非arc)1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950@interface

MyClass:

NSObject

{

NSObject*_instanceObj;}@end

@implementation

MyClass

NSObject*__globalObj=

nil;

-(id)

init

{

if

(self

=[super

init]){

_instanceObj=[[NSObject

alloc]

init];

}

return

self;}

-(void)test{

static

NSObject*__staticObj=

nil;

__globalObj=[[NSObject

alloc]

init];

__staticObj=[[NSObject

alloc]

init];

NSObject*localObj=[[NSObject

alloc]

init];

__block

NSObject*blockObj=[[NSObject

alloc]

init];

typedef

void

(^MyBlock)(void);

MyBlockaBlock=^{

NSLog(@"%@",__globalObj);

NSLog(@"%@",__staticObj);

NSLog(@"%@",_instanceObj);

NSLog(@"%@",localObj);

NSLog(@"%@",blockObj);

};

aBlock=[[aBlock

copy]

autorelease];

aBlock();

NSLog(@"%d",[__globalObjretainCount]);

NSLog(@"%d",[__staticObjretainCount]);

NSLog(@"%d",[_instanceObjretainCount]);

NSLog(@"%d",[localObjretainCount]);

NSLog(@"%d",[blockObjretainCount]);}@end

int

main(int

argc,

char

*argv[]){

@autoreleasepool

{

MyClass*obj=[[[MyClass

alloc]

init]

autorelease];

[objtest];

return

0;

}}執(zhí)行結(jié)果為11121。__globalObj和__staticObj在內(nèi)存中的位置是確定的,所以Blockcopy時(shí)不會retain對象。_instanceObj在Blockcopy時(shí)也沒有直接retain_instanceObj對象本身,但會retainself。所以在Block中可以直接讀寫_instanceObj變量。localObj在Blockcopy時(shí),系統(tǒng)自動retain對象,增加其引用計(jì)數(shù)。blockObj在Blockcopy時(shí)也不會retain。ARC中的變量測試由于arc中沒有retain,retainCount的概念。只有強(qiáng)引用和弱引用的概念。當(dāng)一個(gè)變量沒有__strong的指針指向它時(shí),就會被系統(tǒng)釋放。因此我們可以通過下面的代碼來測試。代碼片段1(globalObject全局變量)12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788NSString

*__globalString=

nil;

-(void)testGlobalObj{

__globalString=

@"1";

void

(^TestBlock)(void)=^{

NSLog(@"stringis:%@",__globalString);

//stringis

/blog/wp-includes/images/smilies/icon_sad.gif"alt=":("class="wp-smiley">null)

};

__globalString=

nil;

TestBlock();}

-(void)testStaticObj{

static

NSString

*__staticString=

nil;

__staticString=

@"1";

printf("staticaddress:%p\n",&__staticString);

//staticaddress:0x6a8c

void

(^TestBlock)(void)=^{

printf("staticaddress:%p\n",&__staticString);

//staticaddress:0x6a8c

NSLog(@"stringis:%@",__staticString);

//stringis

/blog/wp-includes/images/smilies/icon_sad.gif"alt=":("class="wp-smiley">null)

};

__staticString=

nil;

TestBlock();}

-(void)testLocalObj{

NSString

*__localString=

nil;

__localString=

@"1";

printf("localaddress:%p\n",&__localString);

//localaddress:0xbfffd9c0

void

(^TestBlock)(void)=^{

printf("localaddress:%p\n",&__localString);

//localaddress:0x71723e4

NSLog(@"stringis:%@",__localString);

//stringis:1

};

__localString=

nil;

TestBlock();}

-(void)testBlockObj{

__block

NSString

*_blockString=

@"1";

void

(^TestBlock)(void)=^{

NSLog(@"stringis:%@",_blockString);

//stringis

/blog/wp-includes/images/smilies/icon_sad.gif"alt=":("class="wp-smiley">null)

};

_blockString=

nil;

TestBlock();}

-(void)testWeakObj{

NSString

*__localString=

@"1";

__weak

NSString

*weakString=__localString;

printf("weakaddress:%p\n",&weakString);

//weakaddress:0xbfffd9c4

printf("weakstraddress:%p\n",weakString);

//weakstraddress:0x684c

void

(^TestBlock)(void)=^{

printf("weakaddress:%p\n",&weakString);

//weakaddress:0x7144324

printf("weakstraddress:%p\n",weakString);

//weakstraddress:0x684c

NSLog(@"stringis:%@",weakString);

//stringis:1

};

__localString=

nil;

TestBlock();}由以上幾個(gè)測試我們可以得出:

1、只有在使用local變量時(shí),block會復(fù)制指針,且強(qiáng)引用指針指向的對象一次。其它如全局變量、static變量、block變量等,block不會拷貝指針,只會強(qiáng)引用指針指向的對象一次。

2、即時(shí)標(biāo)記了為__weak或__unsafe_unretained的local變量。block仍會強(qiáng)引用指針對象一次。(這個(gè)不太明白,因?yàn)檫@種寫法可在后面避免循環(huán)引用的問題)循環(huán)引用retaincycle循環(huán)引用指兩個(gè)對象相互強(qiáng)引用了對方,即retain了對方,從而導(dǎo)致誰也釋放不了誰的內(nèi)存泄露問題。如聲明一個(gè)delegate時(shí)一般用assign而不能用retain或strong,因?yàn)槟阋坏┠敲醋隽?,很大可能引起循環(huán)引用。在以往的項(xiàng)目中,我?guī)状斡脛討B(tài)內(nèi)存檢查發(fā)現(xiàn)了循環(huán)引用導(dǎo)致的內(nèi)存泄露。這里講的是block的循環(huán)引用問題,因?yàn)閎lock在拷貝到堆上的時(shí)候,會retain其引用的外部變量,那么如果block中如果引用了他的宿主對象,那很有可能引起循環(huán)引用,如:1234self.myblock=^{

[self

doSomething];

};為測試循環(huán)引用,寫了些測試代碼用于避免循環(huán)引用的方法,如下,(只有arc的,懶得做非arc測試了)1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465-(void)dealloc{

NSLog(@"nocycleretain");}

-(id)init{

self

=[super

init];

if

(self){

#ifTestCycleRetainCase1

//會循環(huán)引用

self.myblock=^{

[self

doSomething];

};#elifTestCycleRetainCase2

//會循環(huán)引用

__block

TestCycleRetain*weakSelf=

self;

self.myblock=^{

[weakSelfdoSomething];

};

#elifTestCycleRetainCase3

//不會循環(huán)引用

__weak

TestCycleRetain*weakSelf=

self;

self.myblock=^{

[weakSelfdoSomething];

};

#elifTestCycleRetainCase4

//不會循環(huán)引用

__unsafe_unretained

TestCycleRetain*weakSelf=

self;

self.myblock=^{

[weakSelfdoSomething];

};

#endif

NSLog(@"myblockis%@",

self.myblock);

}

return

self;}

-(void)doSomething{

N

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論