ios -- block相關知識

所有的Block里面的self必須要weak一下?

很顯然答案不都是,有些情況下是可以直接使用self的,比如調用系統的方法:

[UIView?animateWithDuration:0.5?animations:^{?NSLog(@"%@",?self);

????}];

因為這個block存在于靜態方法中,雖然block對self強引用著,但是self卻不持有這個靜態方法,所以完全可以在block內部使用self。

另外,來看一個Masonry代碼布局的例子,這里面的self會不會造成循環引用呢?

[self.headView?mas_makeConstraints:^(MASConstraintMaker?*make)?{

????make.centerY.equalTo(self.otherView.mas_centerY);

}];

并不是 block 就一定會造成循環引用,是不是循環引用要看是不是相互持有強引用。block 里用到了 self,那 block 會保持一個 self 的引用,但是 self 并沒有直接或者間接持有 block,所以不會造成循環引用。可以看一下Masonry的源代碼:

View+MASAdditions.m

-?(NSArray?*)mas_makeConstraints:(void(^)(MASConstraintMaker?*))block?{?self.translatesAutoresizingMaskIntoConstraints?=?NO;

????MASConstraintMaker?*constraintMaker?=?[[MASConstraintMaker?alloc]?initWithView:self];

????block(constraintMaker);?return?[constraintMaker?install];

}

Block引起的循環引用

一般來說我們總會在設置Block之后,在合適的時間回調Block,而不希望回調Block的時候Block已經被釋放了,所以我們需要對Block進行copy,copy到堆中,以便后用。

Block可能會導致循環引用問題,因為block在拷貝到堆上的時候,會retain其引用的外部變量,那么如果block中如果引用了他的宿主對象,那很有可能引起循環引用

注意觀察,這個作為方法參數的Block體并沒有被任何方持有。因此,我們放心在Masonry中使用self.xxx 不會循環引用的。而且這個block里面用weakSelf還有可能會出問題,因為mas_qeual如果得到一個nil參數的話應該會導致程序崩潰。

因為UIView未強持有block,所以這個block只是個棧block,而且構不成循環引用的條件。棧block有個特性就是它執行完畢之后就出棧,出棧了就會被釋放掉。看mas_makexxx的方法實現會發現這個block很快就被調用了,完事兒就出棧銷毀,構不成循環引用,所以可以直接放心的使self。另外,這個與網絡請求里面使用self道理是一樣的。

Block與內存管理

根據Block在內存中的位置分為三種類型:

NSGlobalBlock是位于全局區的block,它是設置在程序的數據區域(.data區)中。

NSStackBlock是位于棧區,超出變量作用域,棧上的Block以及 __block變量都被銷毀。

NSMallocBlock是位于堆區,在變量作用域結束時不受影響。

注意:在 ARC 開啟的情況下,將只會有 NSConcreteGlobalBlock 和 NSConcreteMallocBlock 類型的 block。

位于堆內存:MallocBlock

堆中的block無法直接創建,其需要由_NSConcreteStackBlock類型的block拷貝而來(也就是說block需要執行copy之后才能存放到堆中)。由于block的拷貝最終都會調用_Block_copy_internal函數。

截獲自動變量值

Block表達式可截獲所使用的自動變量的值。截獲:保存自動變量的瞬間值。因為是“瞬間值”,所以聲明Block之后,即便在Block外修改自動變量的值,也不會對Block內截獲的自動變量值產生影響。

inti=10;

void(^blk)(void)=^{

NSLog(@"In block, i = %d",i)

};

i=20;//Block外修改變量i,也不影響Block內的自動變量blk();

//i修改為20后才執行,打印: In block, i = 10NSLog(@"i = %d",i);

//打印:i = 20

__block說明符號

自動變量截獲的值為Block聲明時刻的瞬間值,保存后就不能改寫該值,如需對自動變量進行重新賦值,需要在變量聲明前附加__block說明符,這時該變量稱為__block變量。

__blockinti=10;//i為__block變量,可在block中重新賦值

void(^blk)(void)=^{

NSLog(@"In block, i = %d",i);

};

i=20;

blk();//打印: In block, i = 20

NSLog(@"i = %d",i);//打印:i = 20

自動變量值為一個對象情況

當自動變量為一個類的對象,且沒有使用__block修飾時,雖然不可以在Block內對該變量進行重新賦值,但可以修改該對象的屬性。如果該對象是個Mutable的對象,例如NSMutableArray,則還可以在Block內對NSMutableArray進行元素的增刪:

NSMutableArray*array=[[NSMutableArrayalloc]initWithObjects:@"1",@"2",nil];

NSLog(@"Array Count:%ld",array.count);//打印Array Count:2

void(^blk)(void)=^{

[arrayremoveObjectAtIndex:0];//Ok

//array = [NSNSMutableArray new];//沒有__block修飾,編譯失敗!

};

blk();

NSLog(@"Array Count:%ld",array.count);//打印Array Count:1

Block也是Objective-C中的對象

根據Block對象創建時所處數據區不同而進行區別:

_NSConcreteStackBlock:在棧上創建的Block對象

_NSConcreteMallocBlock:在堆上創建的Block對象

_NSConcreteGlobalBlock:全局數據區的Block對象

使用__block發生了什么

Block捕獲的自動變量添加__block說明符,就可在Block內讀和寫該變量,也可以在原來的棧上讀寫該變量。自動變量的截獲保證了棧上的自動變量被銷毀后,Block內仍可使用該變量。__block保證了棧上和Block內(通常在堆上)可以訪問和修改“同一個變量”,__block是如何實現這一功能的?

__block發揮作用的原理:將棧上用__block修飾的自動變量封裝成一個結構體,讓其在堆上創建,以方便從棧上或堆上訪問和修改同一份數據。

Block循環引用原因:一個對象A有Block類型的屬性,從而持有這個Block,如果Block的代碼塊中使用到這個對象A,或者僅僅是用用到A對象的屬性,會使Block也持有A對象,導致兩者互相持有,不能在作用域結束后正常釋放。

全局塊存在于全局內存中, 相當于單例.

棧塊存在于棧內存中, 超出其作用域則馬上被銷毀

堆塊存在于堆內存中, 是一個帶引用計數的對象, 需要自行管理其內存

簡而言之,存儲在棧中的Block就是棧塊、存儲在堆中的就是堆塊、既不在棧中也不在堆中的塊就是全局塊。

Block訪問外界變量

MRC?環境下:訪問外界變量的 Block 默認存儲中。

ARC?環境下:訪問外界變量的 Block 默認存儲在中(實際是放在棧區,然后ARC情況下自動又拷貝到堆區),自動釋放。

Block的復制操作執行的是copy實例方法。不同類型的Block使用copy方法的效果如下表:


三、Block與外界變量

1、截獲自動變量(局部變量)值

(1)默認情況

對于 block 外的變量引用,block 默認是將其復制到其數據結構中來實現訪問的。也就是說block的自動變量截獲只針對block內部使用的自動變量, 不使用則不截獲, 因為截獲的自動變量會存儲于block的結構體內部, 會導致block體積變大。特別要注意的是默認情況下block只能訪問不能修改局部變量的值。

int age = 10;

myBlock block = ^{

? ? NSLog(@"age = %d", age);

};

age = 18;

block();

輸出結果

age = 10

?__block 修飾的外部變量

對于用 __block 修飾的外部變量引用,block 是復制其引用地址來實現訪問的。block可以修改__block 修飾的外部變量的值。

__block int age = 10;

myBlock block = ^{

? ? NSLog(@"age = %d", age);

};

age = 18;

block();

輸出為:

age = 18

為什么要用copy去修飾block呢

個人理解:默認情況下,block會存檔在棧中(棧是吃了吐),所以block會在函數調用結束被銷毀,在調用會報空指針異常,如果用copy修飾的話,可以使其保存在堆區(堆是吃了拉) ,它的生命周期會隨著對象的銷毀而結束的。只要對象不銷毀,我們就可以調用在堆中的block。

在了解block為什么要用copy之前,我們要先了解block的三種類型

一NSGlobalBlock:全局的靜態block 沒有訪問外部變量 你的block類型就是這種類型(也就是說你的block沒有調用其他外部變量)

二NSStackBlock:保存在棧中的block,沒有用copy去修飾并且訪問了外部變量,你的block類型就是這種類型,會在函數調用結束被銷毀 (需要在MRC)

三NSMallocBlock 保存在堆中的block 此類型blcok是用copy修飾出來的block 它會隨著對象的銷毀而銷毀,只要對象不銷毀,我們就可以調用的到在堆中的block。

信有很多面試者被問到這樣的問題:block使用什么修飾,往往能夠答出是copy,很多面試官就會問到:為什么要使用copy,這時候就懵了。

我親身體驗了一把,所以先總結一下。

block本身是像對象一樣可以retain,和release。但是,block在創建的時候,它的內存是分配在棧上的,而不是在堆上。他本身的作于域是屬于創建時候的作用域,一旦在創建時候的作用域外面調用block將導致程序崩潰。因為棧區的特點就是創建的對象隨時可能被銷毀,一旦被銷毀后續再次調用空對象就可能會造成程序崩潰,在對block進行copy后,block存放在堆區.

使用retain也可以,但是block的retain行為默認是用copy的行為實現的,

因為block變量默認是聲明為棧變量的,為了能夠在block的聲明域外使用,所以要把block拷貝(copy)到堆,所以說為了block屬性聲明和實際的操作一致,最好聲明為copy。

?block?會自動捕獲block?內部使用的變量,并對其強引用

block是通過添加引用來訪問實例變量的,所以self會被retain一次,block也是一個強引用,會引起循環引用。

3、關于 __strong

- (void)viewDidLoad {? ? [super viewDidLoad];? ??

MyOBJ *mm = [[MyOBJ alloc]init];? ? mm.name = @"Lilei";??

? __weak typeof(student) weakSelf = mm;? ? ??

? mm.doBlock = ^{? ? ? ? dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{? ? ? ??

? ? NSLog(@"my name is = %@",weakSelf.name);? ? ??

? });? ?

?};??

? mm.doBlock();}

//輸出:my name is = (null)

在dispatch_after這個函數里面。在doBlock()的block結束之后,mm被自動釋放了。 又由于dispatch_after里面捕獲的__weak的mm,在原對象釋放之后,__weak對象就會變成nil,防止野指針。

那么我們怎么才能在weakSelf之后,block里面還能繼續使用weakSelf之后的對象呢?

究其根本原因就是weakSelf之后,無法控制什么時候會被釋放,為了保證在block內不會被釋放,需要添加__strong。

weakSelf 是為了block不持有self,避免Retain Circle循環引用。

在 Block 內如果需要訪問 self 的方法、變量,建議使用 weakSelf。

strongSelf的目的是因為一旦進入block執行,假設不允許self在這個執行過程中釋放,就需要加入strongSelf。block執行完后這個strongSelf 會自動釋放,不會存在循環引用問題。

如果在 Block 內需要多次 訪問 self,則需要使用 strongSelf。

關于 多層嵌套的block

- (void)setUpModel{

? ? XXModel *model = [XXModel new];

__weak typeof(self) weakSelf = self;

? ? model.dodoBlock = ^(NSString *title) {

? ? ? ? __strong typeof(self) strongSelf = weakSelf;//第一層

? ? ? ? strongSelf.titleLabel.text = title;


? ? ? ? __weak typeof(self) weakSelf2 = strongSelf;

? ? ? ? strongSelf.model.dodoBlock = ^(NSString *title2) {

? ? ? ? ? ? __strong typeof(self) strongSelf2 = weakSelf2;//第二層

? ? ? ? ? ? strongSelf2.titleLabel.text = title2;

? ? ? ? };

? ? };

? ? self.model = model;

}

這樣就避免的引用循環,不管都多少個block嵌套,都可以按照這樣來做。

OS block嵌套block中weakify的使用

結論:嵌套中的block只需要寫strongify,不需要再寫一次weakify

只要持有block的變量和block中的變量不是同一個變量(可以指向同一個變量),就不會因此循環引用,導致memory leak。

所以,當block嵌套block的時候,內部的block不需要再次增加@weakify(self)。

https://juejin.im/post/5b9b24b05188255c372f437c

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,517評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,087評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,521評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,493評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,207評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,603評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,624評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,813評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,364評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,110評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,305評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,874評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,532評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,953評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,209評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,033評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,268評論 2 375

推薦閱讀更多精彩內容