『ios』-增強block理解

有些東西我覺得還是寫出來比較好鞍爱,你知道是你知道的,但是寫出來總結(jié)出來又是不一樣的一種收獲。
先看這斷代碼吧蠕趁!

-(void)test{
testPerson*person = [[testPerson alloc]init];
    __weak testPerson * weakPerson = person;
    person.mitBlock = ^{
        __strong testPerson * strongPerson = weakPerson;
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [strongPerson test];
        });
    };
    person.mitBlock();
}
首先person是一個局部變量娜搂,所以在test方法執(zhí)行完person對象就會被銷毀迁霎。(如果沒有寫_strong來修飾的話。)
用了strong來修飾百宇,則會在gcd里面形成一個強引用指針考廉。當mitblock執(zhí)行完,gcd外面的strongPerson釋放掉携御,這時候gcd里面的由于copy出來一份在堆上昌粤,所以還會存在不會釋放掉,當gcd執(zhí)行結(jié)束啄刹,strongperson釋放掉涮坐。沒有循環(huán)引用問題。

Block的分類

下面這三個我相信看字面就知道什么意思

_NSConcreteStackBlock   棧鸵膏,_NSConcreteMallocBlock 堆膊升,_NSConcreteGlobalBlock 全局 

那么什么時候是stack 什么時候是malloc 什么時候是global?

首先先來認識下c語言中的這幾種變量

自動變量
函數(shù)參數(shù)
靜態(tài)變量
靜態(tài)全局變量
全局變量
int global_i = 1;//全局變量

static int static_global_j = 2;//靜態(tài)全局變量

int main(int argc, const char * argv[]) {
   
    static int static_k = 3;//靜態(tài)變量
    int val = 4;   //自動變量
    
    void (^myBlock)(void) = ^{
        global_i ++;
        static_global_j ++;
        static_k ++;
    };

第一種stack
只用到外部局部變量谭企、成員屬性變量廓译,且沒有強指針引用的block都是StackBlock。
StackBlock的生命周期由系統(tǒng)控制的债查,一旦返回之后非区,就被系統(tǒng)銷毀了

在mrc下
__block int i = 0;  
    NSLog(@"%p",&i);
    void (^myBlock)(void) = ^{
        i ++;
        NSLog(@"Block 里面的%p",&i);
    };
    NSLog(@"%@",myBlock);
    myBlock();

第二種 malloc
有強指針引用或copy修飾的成員屬性引用的block會被復(fù)制一份到堆中成為MallocBlock,沒有強指針引用即銷毀盹廷,生命周期由程序員控制,一般在arc下征绸,不管你加不加_block ,都會是堆。

arc下
__block int i = 0;  
    NSLog(@"%p",&i);
    void (^myBlock)(void) = ^{
        i ++;
        NSLog(@"Block 里面的%p",&i);
    };
    NSLog(@"%@",myBlock);
    myBlock();

第三種 global
沒有用到外界變量或只用到全局變量、靜態(tài)變量的block為_NSConcreteGlobalBlock管怠,生命周期從創(chuàng)建到應(yīng)用程序結(jié)束淆衷。

int global_i = 1;
static int static_global_j = 2;
int main(int argc, const char * argv[]) {
    static int static_k = 3;
    void (^myBlock)(void) = ^{
            NSLog(@"Block中 變量 = %d %d %d",static_global_j ,static_k, global_i);
        };
    NSLog(@"%@",myBlock)
    myBlock();
    return 0;
}

分析block函數(shù)

int global_i = 1;
static int static_global_j = 2;
int main(int argc, const char * argv[]) {
    static int static_k = 3;
    int val = 4;
    void (^myBlock)(void) = ^{
        global_i ++;
        static_global_j ++;
        static_k ++;
        NSLog(@"Block中 global_i = %d,static_global_j = %d,static_k = %d,val = %d",global_i,static_global_j,static_k,val);
    };

下面看一下上面這段代碼源碼渤弛。

int global_i = 1;

static int static_global_j = 2;

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int *static_k;
  int val;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_static_k, int _val, int flags=0) : static_k(_static_k), val(_val) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) { //這里是block
  int *static_k = __cself->static_k; // bound by copy        這里靜態(tài)變量傳進來的是一個指針
  int val = __cself->val; // bound by copy        這里自動變量傳進來的是值祝拯。   所以靜態(tài)變量可以改變值,而自動變量是不能變的她肯。
        global_i ++;
        static_global_j ++;
        (*static_k) ++;
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_45_k1d9q7c52vz50wz1683_hk9r0000gn_T_main_6fe658_mi_0,global_i,static_global_j,(*static_k),val);
    }
static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};

上面代碼中我已經(jīng)把原因?qū)懬宄思淹贰O旅婵聪录觃block的實現(xiàn)。

int main(int argc, const char * argv[]) {
    __block int i = 0;
    void (^myBlock)(void) = ^{
        i ++;
        NSLog(@"%d",i);
    };
    myBlock();
    return 0;
}

源碼如下

struct __Block_byref_i_0 {
  void *__isa;
__Block_byref_i_0 *__forwarding;
 int __flags;
 int __size;
 int I;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_i_0 *i; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_i_0 *_i, int flags=0) : i(_i->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_i_0 *i = __cself->i; // bound by ref   主要代碼是這兩行  
        (i->__forwarding->i) ++;    
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_45_k1d9q7c52vz50wz1683_hk9r0000gn_T_main_3b0837_mi_0,(i->__forwarding->i));
    }
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->i, (void*)src->i, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->i, 8/*BLOCK_FIELD_IS_BYREF*/);}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main(int argc, const char * argv[]) {
    __attribute__((__blocks__(byref))) __Block_byref_i_0 i = {(void*)0,(__Block_byref_i_0 *)&i, 0, sizeof(__Block_byref_i_0), 0};

    void (*myBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_i_0 *)&i, 570425344));

    ((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);

    return 0;
}

相信你注意到了__Block_byref_i_0這個結(jié)構(gòu)體晴氨,__Block_byref_i_0,這個結(jié)構(gòu)體有5個成員變量康嘉。第一個是isa指針,第二個是指向自身類型的__forwarding指針籽前,第三個是一個標記flag亭珍,第四個是它的大小,第五個是變量值聚假,名字和變量名同名

說下我的理解块蚌,加了_block之后,在arc下會把block copy到堆上膘格,則原來指向棧上的forwarding轉(zhuǎn)而指向堆上面的forwarding,而forwarding又會指向自身财松,(i->__forwarding->i)瘪贱。
下面盜下霜神博客里的圖。這樣理解的更深刻一些辆毡。


1194012-5f5f486bab68191f.jpg

MRC環(huán)境下菜秦,只有copy,__block才會被復(fù)制到堆上舶掖,否則球昨,__block一直都在棧上,block也只是__NSStackBlock眨攘,這個時候__forwarding指針就只指向自己了主慰。

下面看下帶對象的block

__block id block_obj = [[NSObject alloc]init];
    id obj = [[NSObject alloc]init];

    NSLog(@"block_obj = [%@ , %p] , obj = [%@ , %p]",block_obj , &block_obj , obj , &obj);
    
    void (^myBlock)(void) = ^{
        NSLog(@"***Block中****block_obj = [%@ , %p] , obj = [%@ , %p]",block_obj , &block_obj , obj , &obj);
    };
    myBlock();
struct __Block_byref_block_obj_0 {
  void *__isa;
__Block_byref_block_obj_0 *__forwarding;
 int __flags;
 int __size;
 void (*__Block_byref_id_object_copy)(void*, void*);
 void (*__Block_byref_id_object_dispose)(void*);
 id block_obj;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  id obj;
  __Block_byref_block_obj_0 *block_obj; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, id _obj, __Block_byref_block_obj_0 *_block_obj, int flags=0) : obj(_obj), block_obj(_block_obj->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_block_obj_0 *block_obj = __cself->block_obj; // bound by ref
  id obj = __cself->obj; // bound by copy

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_45_k1d9q7c52vz50wz1683_hk9r0000gn_T_main_e64910_mi_1,(block_obj->__forwarding->block_obj) , &(block_obj->__forwarding->block_obj) , obj , &obj);
    }
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->block_obj, (void*)src->block_obj, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_assign((void*)&dst->obj, (void*)src->obj, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->block_obj, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_dispose((void*)src->obj, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};

這個結(jié)構(gòu)體__Block_byref_block_obj_0,跟自動變量的結(jié)構(gòu)體差不多鲫售。原理也差不多共螺。

疑惑解決,為何appending不需要加_block也可以修改值情竹。

看下面的代碼和源碼

NSMutableString * str = [[NSMutableString alloc]initWithString:@"Hello,"];
        void (^myBlock)(void) = ^{
            [str appendString:@"World!"];
            NSLog(@"Block中 str = %@",str);
        }; 
    NSLog(@"Block外 str = %@",str);
       myBlock();

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  NSMutableString *str;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSMutableString *_str, int flags=0) : str(_str) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  NSMutableString *str = __cself->str; // bound by copy   主要看這里藐不,block里傳進來的是指針所以可以修改變量的值。

            ((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)str, sel_registerName("appendString:"), (NSString *)&__NSConstantStringImpl__var_folders_45_k1d9q7c52vz50wz1683_hk9r0000gn_T_main_33ff12_mi_1);
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_45_k1d9q7c52vz50wz1683_hk9r0000gn_T_main_33ff12_mi_2,str);
        }
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->str, (void*)src->str, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->str, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};

int main(int argc, const char * argv[]) {
    NSMutableString * str = ((NSMutableString *(*)(id, SEL, NSString *))(void *)objc_msgSend)((id)((NSMutableString *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSMutableString"), sel_registerName("alloc")), sel_registerName("initWithString:"), (NSString *)&__NSConstantStringImpl__var_folders_45_k1d9q7c52vz50wz1683_hk9r0000gn_T_main_33ff12_mi_0);

        void (*myBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, str, 570425344));

    NSLog((NSString *)&__NSConstantStringImpl__var_folders_45_k1d9q7c52vz50wz1683_hk9r0000gn_T_main_33ff12_mi_3,str);

    ((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);

    return 0;
}

最后總結(jié)下。
首先arc默認進行了copy操作雏蛮,而mrc必須手動進行copy涎嚼。
靜態(tài)全局變量,全局變量挑秉,函數(shù)參數(shù)法梯,也是可以在直接在Block中改變變量值的,但是他們并沒有變成Block結(jié)構(gòu)體__main_block_impl_0的成員變量衷模,因為他們的作用域大鹊汛,所以可以直接更改他們的值.靜態(tài)全局變量,全局變量阱冶,函數(shù)參數(shù)他們并不會被Block持有刁憋,也就是說不會增加retainCount值。

對非對象變量來說木蹬,自動變量的值至耻,被copy進了Block,不帶__block的自動變量只能在里面被訪問镊叁,并不能改變值尘颓。帶__block的自動變量 和 靜態(tài)變量 就是直接地址訪問。所以在Block里面可以直接改變變量的值晦譬。

對對象變量來說
在MRC環(huán)境下疤苹,__block根本不會對指針所指向的對象執(zhí)行copy操作,而只是把指針進行的復(fù)制敛腌。
而在ARC環(huán)境下卧土,對于聲明為__block的外部對象,在block內(nèi)部會進行retain像樊,以至于在block環(huán)境內(nèi)能安全的引用外部對象尤莺。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市生棍,隨后出現(xiàn)的幾起案子颤霎,更是在濱河造成了極大的恐慌,老刑警劉巖涂滴,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件友酱,死亡現(xiàn)場離奇詭異,居然都是意外死亡氢妈,警方通過查閱死者的電腦和手機粹污,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來首量,“玉大人壮吩,你說我怎么就攤上這事进苍。” “怎么了鸭叙?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵觉啊,是天一觀的道長。 經(jīng)常有香客問我沈贝,道長杠人,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任宋下,我火速辦了婚禮嗡善,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘学歧。我一直安慰自己罩引,他們只是感情好,可當我...
    茶點故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布枝笨。 她就那樣靜靜地躺著袁铐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪横浑。 梳的紋絲不亂的頭發(fā)上剔桨,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天,我揣著相機與錄音徙融,去河邊找鬼洒缀。 笑死,一個胖子當著我的面吹牛欺冀,可吹牛的內(nèi)容都是我干的帝洪。 我是一名探鬼主播,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼脚猾,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了砚哗?” 一聲冷哼從身側(cè)響起龙助,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蛛芥,沒想到半個月后提鸟,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡仅淑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年称勋,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片涯竟。...
    茶點故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡赡鲜,死狀恐怖空厌,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情银酬,我是刑警寧澤嘲更,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站揩瞪,受9級特大地震影響赋朦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜李破,卻給世界環(huán)境...
    茶點故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一宠哄、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧嗤攻,春花似錦毛嫉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至恶耽,卻和暖如春密任,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背偷俭。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工浪讳, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人涌萤。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓淹遵,卻偏偏與公主長得像,于是被迫代替她去往敵國和親负溪。 傳聞我的和親對象是個殘疾皇子透揣,可洞房花燭夜當晚...
    茶點故事閱讀 43,627評論 2 350

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