iOS Block底層解析二

一兵钮、__block 的解析

  • 接上一篇《iOS Block底層解析一》,在block中我們要修改局部變量(自動局部變量田柔,自動局部類對象)前面都加__block修飾 自動是auto的意思艾蓝,就是我們寫代碼蘋果自動給我們生成的 比如:int a = 10;其實是 auto int a = 10; 下面就直接說局部變量吧,這么麻煩的叫法膈應人

  • 為啥局部變量在block里面不能直接修改粪牲,從上一篇的分析我們知道我們傳進去的局部變量古瓤,相當于函數(shù)的參數(shù),你在函數(shù)里面改參數(shù)腺阳,外面的局部變量會變嗎落君?例如:

void test8(int a) {
    a++;
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int b = 10;
        test8(b);
        NSLog(@"%d",b);      
    }
    return 0;
}
打印結果: 
2020-04-13 21:12:24.922403+0800 blockTest[3434:163752] 10
  • 類對象的屬性修改要不要加__block呢? 其實不用的亭引,跟NSMutableArray等 增绎速、刪、改焙蚓、查是一樣的纹冤,我們修改的都是她里面的東西,其實是因為block最后拿到的還是person地址里面的name值 例如:
 // 對象類型
void test6() {
    Person *p = [Person new];
    p.name = @"hello block!";
    void(^block)(void) = ^{
        p.name = @"fuck block!";
        NSLog(@"--- %@",p.name);
    };
    block();
}
打印結果:
2020-04-13 21:18:06.198269+0800 blockTest[3486:167284] --- fuck block!

void test8() {
    NSMutableArray * arr = [NSMutableArray new];
    void(^block)(void) = ^{
        [arr addObject:@2];
        NSLog(@"--- %@",arr);
    };
    block();
}
打印結果:
2020-04-13 21:25:44.568467+0800 blockTest[3515:170553] --- (
    2
)
  • 但是我們改變他們本身就會報錯购公,例如:


    圖片.png

    圖片.png
  • 一定要知會的一點就是有__block修飾的捕獲變量和沒有__block修飾的捕獲變量要分開萌京,內(nèi)存管理上不要混淆,這樣思路才更清晰君丁。這也是跟上一篇分開解析的原因

  • 其實我們要加__block就兩種情況枫夺,局部變量和局部類對象,其他的都可以直接訪問在block中修改绘闷,廢話有點多 開搞 開搞

// __block作用
void test7() {
    
    __block int age = 10;
    __block NSObject *objc = [[NSObject alloc] init];
    void(^block)(void) = ^ {
        objc = nil;
        age = 20;
    };
    block();
}
  • 老套路clang一波
把轉換的去掉簡化代碼,擺好步驟:
// 第一步
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        test7();
    }
    return 0;
}

// 第二步
void test7() {
    // __block int age = 10;
    __Block_byref_age_0 age = {
        0,
        &age,
        0,
        sizeof(__Block_byref_age_0),
        10
        
    };
    //    __block NSObject *objc = [[NSObject alloc] init];
    __Block_byref_objc_1 objc = {
        0,
        &objc,
        33554432,
        sizeof(__Block_byref_objc_1),
        __Block_byref_id_object_copy_131,
        __Block_byref_id_object_dispose_131,
        objc_msgSend((id)(objc_msgSend(objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"))
     };
    
    // 調(diào)用
    void(*block)(void) = &__test7_block_impl_0(__test7_block_func_0, &__test7_block_desc_0_DATA, (__Block_byref_objc_1 *)&objc, (__Block_byref_age_0 *)&age, 570425344));
    block->FuncPtr(block);
}

// 第三步
struct __Block_byref_age_0 {
  void *__isa; // isa指針
__Block_byref_age_0 *__forwarding; // 指向自己的指針
 int __flags; // 不清楚是什么鬼 猜測是標識符什么的
 int __size; // 當前結構體的大小
 int age; // 捕獲值
};

// 第三步
struct __Block_byref_objc_1 {
  void *__isa; // isa指針
__Block_byref_objc_1 *__forwarding; // 指向自己的指針
 int __flags;
 int __size;
 void (*__Block_byref_id_object_copy)(void*, void*);  // 執(zhí)行copy操作
 void (*__Block_byref_id_object_dispose)(void*); // 執(zhí)行dispose操作
 NSObject *objc;// 捕獲 NSObject類型指針
};

// 第四步
static void __test7_block_func_0(struct __test7_block_impl_0 *__cself) {

    // 拿到 __Block_byref_objc_1里面 *objc指針
  __Block_byref_objc_1 *objc = __cself->objc; // bound by ref
    // 拿到__Block_byref_age_0里面 *age指針
  __Block_byref_age_0 *age = __cself->age; // bound by ref
        //objc->__forwarding指針指向 __Block_byref_objc_1 拿到里面的的objc 完成賦值
        (objc->__forwarding->objc) = __null;
        //age->__forwarding指針指向 __Block_byref_age_0 拿到里面的的age 完成賦值
        (age->__forwarding->age) = 20;
}

// 第五步
struct __test7_block_impl_0 {
  struct __block_impl impl;
  struct __test7_block_desc_0* Desc;
  __Block_byref_objc_1 *objc; //__Block_byref_objc_1 類型結果體
  __Block_byref_age_0 *age; //__Block_byref_age_0類型結果體
  __test7_block_impl_0(void *fp, struct __test7_block_desc_0 *desc, __Block_byref_objc_1 *_objc, __Block_byref_age_0 *_age, int flags=0) : objc(_objc->__forwarding), age(_age->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
  • 看得眼花较坛,那我們走一個印蔗,其實前面搞好了,這里就一目了然:

1.第一步一樣丑勤,第二步多了兩個東西华嘹,其實是一個鬼東西,差不多 __Block_byref_age_0和__Block_byref_objc_1兩個結構體
2.第三就是對這兩個結構體的解析法竞,是不是就是一個類耙厚,包裝成類,然后再保存在block結構體里面
3.第四步就是賦值個過程了__forwarding指針為啥要這樣搞岔霸,明明已經(jīng)拿到*objc的指針了薛躬,又(objc->__forwarding->objc) 這樣拿objc,這他么不是嚇搞嗎?我們想一下呆细,前面說的內(nèi)存管理型宝,很多時候block會copy到堆上的,然后蘋果是這樣設計的 如圖:


圖片.png

圖片.png
這樣搞的好處就是:
  • block在棧上的時候 block在棧上的 __forwarding指針指向自己
  • block在堆上的時候 block在棧上的 __forwarding指針指向堆內(nèi)存 block在堆上的_forwarding指針指向堆內(nèi)存
  • 所以你是指向棧還是堆 最后都能找到這個變量

二、__weak 修飾趴酣、 __strong修飾梨树、block的循環(huán)引用問題

  • 其實到上面block已經(jīng)搞完了,這個步驟都是根據(jù)之前的解析的都可以推導出來岖寞,還是要自己去多推導 還是說說吧
  • __weak 修飾的時候可以解除block的循環(huán)引用問題抡四,首先你要明白怎么樣才會造成循環(huán)引用問題:
  1. 兩個是否相互強引用 2.兩個以上是否形成強引用閉環(huán)
  • 那么我們可以得出結論 block只有在堆上才會形成強引用,就是執(zhí)行copy操作的時候 對就是結合上一篇文章說道的內(nèi)存管理總結:


    圖片.png

    圖片.png
  • 有些同學就問了 GCD里面的block調(diào)用self會嗎 UIview animation的block調(diào)用self會嗎 self要用__weak 修飾嗎仗谆?嗯 是不會的 因為 GCD 跟你的UIView 沒有強引用block 沒有形成相互強引用床嫌,還有很多第三方庫的block里面用self 也是一樣的,首先你要判斷的是 self有沒有強引用第三方的block 比如: Masonry AFN 第三方的block跟你的類不是強引用關系胸私,所以不會形成循環(huán)引用

  • 有同學會問 NSTimer的block里面會是強引用呢厌处? 因為timer是self的屬性strong的啊,然后你在人家的block里面調(diào)用肯定就形成相互強引用

  • 最后關于循環(huán)引用的問題就是前面說的兩個

  1. 兩個是否相互強引用 2.兩個以上是否形成強引用閉環(huán)
  • 還有種特殊的場景比較少見岁疼,用__weak 修飾的對象阔涉,什么情況下會在block里面再用__strong修飾,就是在block里面執(zhí)行多線程耗時操作的時候捷绒,為啥這么說瑰排,因為__weak 修飾的對象block結束的時候block里面的對象就釋放了,可是你后面的線程還要使用block里面的對象所以就會有空指針的問題(weak/__weak 修飾的對象釋放后為nil)

  • 關于block的底層解析就到這里了暖侨,也是對自己學習block的一個總結吧椭住,以后遇到block的難題基本都是這樣一套推倒,還有一些深入的東西和細節(jié)估計說得不到位字逗,希望各位大老爺指出

一個人的精力有限京郑,對你的思念無限
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市葫掉,隨后出現(xiàn)的幾起案子些举,更是在濱河造成了極大的恐慌,老刑警劉巖俭厚,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件户魏,死亡現(xiàn)場離奇詭異,居然都是意外死亡挪挤,警方通過查閱死者的電腦和手機叼丑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來扛门,“玉大人鸠信,你說我怎么就攤上這事〖夥桑” “怎么了症副?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵店雅,是天一觀的道長。 經(jīng)常有香客問我贞铣,道長闹啦,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任辕坝,我火速辦了婚禮窍奋,結果婚禮上,老公的妹妹穿的比我還像新娘酱畅。我一直安慰自己琳袄,他們只是感情好,可當我...
    茶點故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布纺酸。 她就那樣靜靜地躺著窖逗,像睡著了一般愤诱。 火紅的嫁衣襯著肌膚如雪雄嚣。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天琅绅,我揣著相機與錄音樊诺,去河邊找鬼仗考。 笑死,一個胖子當著我的面吹牛词爬,可吹牛的內(nèi)容都是我干的秃嗜。 我是一名探鬼主播,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼顿膨,長吁一口氣:“原來是場噩夢啊……” “哼锅锨!你這毒婦竟也來了?” 一聲冷哼從身側響起虽惭,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤橡类,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后芽唇,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡取劫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年匆笤,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谱邪。...
    茶點故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡炮捧,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出惦银,到底是詐尸還是另有隱情咆课,我是刑警寧澤末誓,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站书蚪,受9級特大地震影響喇澡,放射性物質發(fā)生泄漏。R本人自食惡果不足惜殊校,卻給世界環(huán)境...
    茶點故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一晴玖、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧为流,春花似錦呕屎、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至莲祸,卻和暖如春蹂安,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背虫给。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工藤抡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人抹估。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓缠黍,卻偏偏與公主長得像,于是被迫代替她去往敵國和親药蜻。 傳聞我的和親對象是個殘疾皇子瓷式,可洞房花燭夜當晚...
    茶點故事閱讀 45,077評論 2 355

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

  • 引導語: Block原理是怎樣的踱卵?本質是什么廊驼? _block的作用是什么?使用需要注意什么惋砂? block的屬性修飾...
    三月木頭閱讀 230評論 0 2
  • Block的本質<一> 1.對象類型的auto變量 在第一篇文章中我們講了在block中使用基本類型的自動變量的情...
    雪山飛狐_91ae閱讀 1,278評論 0 10
  • 1. Block是啥妒挎? 答:Block是將函數(shù)及其執(zhí)行上下文封裝起來的對象。 使用終端編譯.m內(nèi)容: 編譯之后會生...
    DoBetter1閱讀 278評論 0 1
  • Block在開發(fā)中常用的西饵,要想解決Block在開發(fā)中遇到的問題酝掩,我們需要了解Block的本質、截獲變量的特性眷柔、__...
    字節(jié)碼閱讀 532評論 0 2
  • 朋友在畢業(yè)之際選擇申請留學喳坠。開始忙碌,偏偏我這人宾尚,又屬于較真的人丙笋。 可能是比較介意他之前說的話沒有做到,于是就說話...
    小核桃吖閱讀 153評論 0 0