iOS Block基本使用(三)

問題:block內(nèi)部捕獲到的局部變量是局部變量的值,所以在block上修改不了局部變量抡驼。
但是局部的靜態(tài)變量捕獲是指針争占,所以block內(nèi)部可以修改值。全局變量和全局靜態(tài)變量沒有捕獲闷袒,可以直接獲取到(不存在作用域的問題)
怎么在block內(nèi)部修改局部變量坑律??囊骤?
方法一:改成靜態(tài)變量;卧瘛!也物!
方法二:__block修飾局部變量

void test(){
    __block int a = 10;
    MyBlock myBlock = ^{
        a = 11;
        NSLog(@"myBlock---------%d", a);//11
    };
    myBlock();
}

轉(zhuǎn)成底層代碼

typedef void(*MyBlock)(void);
//在block內(nèi)部捕獲的時(shí)候把&a傳進(jìn)來包裝成了__Block_byref_a_0對(duì)象
struct __Block_byref_a_0 {
  void *__isa;
__Block_byref_a_0 *__forwarding;
 int __flags;
 int __size;
 int a;
};

struct __test_block_impl_0 {
  struct __block_impl impl;
  struct __test_block_desc_0* Desc;
  __Block_byref_a_0 *a; // by ref
  __test_block_impl_0(void *fp, struct __test_block_desc_0 *desc, __Block_byref_a_0 *_a, int flags=0) : a(_a->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __test_block_func_0(struct __test_block_impl_0 *__cself) {
  __Block_byref_a_0 *a = __cself->a; // bound by ref

        (a->__forwarding->a) = 11;
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_r0_rjxg_z6540vblvmqqv4klgsm0000gn_T_main_cd5b88_mi_0, (a->__forwarding->a));
    }
static void __test_block_copy_0(struct __test_block_impl_0*dst, struct __test_block_impl_0*src) {_Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __test_block_dispose_0(struct __test_block_impl_0*src) {_Block_object_dispose((void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);}

static struct __test_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __test_block_impl_0*, struct __test_block_impl_0*);
  void (*dispose)(struct __test_block_impl_0*);
} __test_block_desc_0_DATA = { 0, sizeof(struct __test_block_impl_0), __test_block_copy_0, __test_block_dispose_0};
void test(){
    __attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 10};
    MyBlock myBlock = ((void (*)())&__test_block_impl_0((void *)__test_block_func_0, &__test_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344));
    ((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
}

可以看到__block修飾的局部變量a宫屠,在block內(nèi)部捕獲的時(shí)候 變成了一個(gè) __Block_byref_a_0 *a; // by ref 類型的對(duì)象 Blcok內(nèi)部有個(gè)指向這個(gè)對(duì)象的指針 而這個(gè)對(duì)象擁有a變量(而且這個(gè)對(duì)象擁有的內(nèi)存地址和我們聲明的a的內(nèi)存地址是一樣的) 被執(zhí)行Block的時(shí)候 通過被包裝對(duì)象的__forwarding對(duì)象 訪問到a 進(jìn)行修改


image.png

__block的內(nèi)存管理

當(dāng)block在棧上的時(shí)候 并不會(huì)對(duì)__block變量產(chǎn)生強(qiáng)引用(ARC)或者retain操作(MRC)

當(dāng)block被拷貝到堆上時(shí):
1.會(huì)調(diào)用Block內(nèi)部copy函數(shù) 這個(gè)函數(shù)內(nèi)部會(huì)調(diào)用_Block_object_assign函數(shù)
2._Block_object_assign函數(shù)會(huì)對(duì)__block變量形成強(qiáng)引用(retain) (__block修飾的變量也會(huì)被拷貝到堆上)
3.只要是被__block修飾的變量 copy后都是強(qiáng)引用 如果沒被__block修飾的對(duì)象 會(huì)根據(jù)對(duì)象的修飾符來確定是強(qiáng)引用還是弱引用

當(dāng)Block從堆中移除的時(shí)候:
1.會(huì)調(diào)用Block內(nèi)部的dispose函數(shù) 這個(gè)函數(shù)內(nèi)部會(huì)調(diào)用_Block_object_dispose函數(shù)
2._Block_object_dispose 這個(gè)函數(shù)會(huì)斷開對(duì)__block修飾的變量的強(qiáng)引用(realse)
block內(nèi)部使用了被__block修飾的對(duì)象的話 那么block內(nèi)部的 Desc 描述指針也會(huì)多兩個(gè)函數(shù) 這兩個(gè)函數(shù)就是用于內(nèi)存管理的copy函數(shù)和dispose函數(shù)

不使用__block修飾對(duì)象類型

void test(){
    NSObject *objc = [NSObject new];
    MyBlock myBlock = ^{
        NSLog(@"myBlock---------%@", objc);//11
    };
    myBlock();
}
typedef void(*MyBlock)(void);

struct __test_block_impl_0 {
  struct __block_impl impl;
  struct __test_block_desc_0* Desc;
  NSObject *objc;
  __test_block_impl_0(void *fp, struct __test_block_desc_0 *desc, NSObject *_objc, int flags=0) : objc(_objc) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __test_block_func_0(struct __test_block_impl_0 *__cself) {
  NSObject *objc = __cself->objc; // bound by copy

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_r0_rjxg_z6540vblvmqqv4klgsm0000gn_T_main_b0ec24_mi_0, objc);
    }
static void __test_block_copy_0(struct __test_block_impl_0*dst, struct __test_block_impl_0*src) {_Block_object_assign((void*)&dst->objc, (void*)src->objc, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static void __test_block_dispose_0(struct __test_block_impl_0*src) {_Block_object_dispose((void*)src->objc, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static struct __test_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __test_block_impl_0*, struct __test_block_impl_0*);
  void (*dispose)(struct __test_block_impl_0*);
} __test_block_desc_0_DATA = { 0, sizeof(struct __test_block_impl_0), __test_block_copy_0, __test_block_dispose_0};
void test(){
    NSObject *objc = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("new"));
    MyBlock myBlock = ((void (*)())&__test_block_impl_0((void *)__test_block_func_0, &__test_block_desc_0_DATA, objc, 570425344));
    ((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
}

__block修飾對(duì)象類型

void test(){
    __block NSObject *objc = [NSObject new];
    MyBlock myBlock = ^{
        NSLog(@"myBlock---------%@", objc);//11
    };
    myBlock();
}

轉(zhuǎn)成底層代碼

typedef void(*MyBlock)(void);
struct __Block_byref_objc_0 {
  void *__isa;
__Block_byref_objc_0 *__forwarding;
 int __flags;
 int __size;
 void (*__Block_byref_id_object_copy)(void*, void*);
 void (*__Block_byref_id_object_dispose)(void*);
 NSObject *objc;
};

struct __test_block_impl_0 {
  struct __block_impl impl;
  struct __test_block_desc_0* Desc;
  __Block_byref_objc_0 *objc; // by ref
  __test_block_impl_0(void *fp, struct __test_block_desc_0 *desc, __Block_byref_objc_0 *_objc, int flags=0) : objc(_objc->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __test_block_func_0(struct __test_block_impl_0 *__cself) {
  __Block_byref_objc_0 *objc = __cself->objc; // bound by ref

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_r0_rjxg_z6540vblvmqqv4klgsm0000gn_T_main_c53779_mi_0, (objc->__forwarding->objc));
    }
static void __test_block_copy_0(struct __test_block_impl_0*dst, struct __test_block_impl_0*src) {_Block_object_assign((void*)&dst->objc, (void*)src->objc, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __test_block_dispose_0(struct __test_block_impl_0*src) {_Block_object_dispose((void*)src->objc, 8/*BLOCK_FIELD_IS_BYREF*/);}

static struct __test_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __test_block_impl_0*, struct __test_block_impl_0*);
  void (*dispose)(struct __test_block_impl_0*);
} __test_block_desc_0_DATA = { 0, sizeof(struct __test_block_impl_0), __test_block_copy_0, __test_block_dispose_0};
void test(){
    __attribute__((__blocks__(byref))) __Block_byref_objc_0 objc = {(void*)0,(__Block_byref_objc_0 *)&objc, 33554432, sizeof(__Block_byref_objc_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("new"))};
    MyBlock myBlock = ((void (*)())&__test_block_impl_0((void *)__test_block_func_0, &__test_block_desc_0_DATA, (__Block_byref_objc_0 *)&objc, 570425344));
    ((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
}

可以看到和__block修飾的普通變量差不多 但是包裝成的對(duì)象多了兩個(gè)函數(shù) copy函數(shù)和dispose函數(shù) 這兩個(gè)和Block描述中的copy函數(shù)和dispose函數(shù)還不一樣,當(dāng)Block拷貝的到堆上的時(shí)候 被__block修飾的對(duì)象(objc)轉(zhuǎn)化的對(duì)象(__Block_byref_objc_0)也會(huì)被拷貝到堆上焦除,這個(gè)時(shí)候會(huì)觸發(fā)被包裝對(duì)象的copy函數(shù) 而這個(gè)函數(shù)會(huì)根據(jù)外部__block修飾對(duì)象(objc)的引用計(jì)數(shù)修飾符來決定對(duì)外部對(duì)象(objc)是強(qiáng)引用還是弱引用激况。當(dāng)Block從堆中移除的時(shí)候 會(huì)調(diào)用內(nèi)部的dispose函數(shù) 這個(gè)函數(shù)會(huì)斷開對(duì)轉(zhuǎn)化對(duì)象(__Block_byref_objc_0)的引用關(guān)系 如果計(jì)數(shù)為0 就直接釋放 釋放后會(huì)調(diào)用自己的dispose函數(shù) 對(duì)自己內(nèi)部對(duì)象(objc) 斷開引用關(guān)系 如果此時(shí)objc的引用計(jì)數(shù)為0 就會(huì)被釋放掉。(轉(zhuǎn)化對(duì)象持有的objc和我們外部聲明的objc對(duì)象有同一片內(nèi)存地址 是同一個(gè)對(duì)象)

注意:上面的分析 都是ARC的情況下膘魄。如果是MRC轉(zhuǎn)化對(duì)象(_Block_byref objc_0) 對(duì)外部對(duì)象(objc) 一直都是弱引用 不管是什么情況乌逐。這也是為什么在MRC下我們使用__block來解決循環(huán)引用的原因

Block與循環(huán)引用
1.在ARC的情況下 我們常常使用__weak __unsafe__unretained來解決

我們使用__weak 和 __unsafe__unretained 都有一個(gè)共同的作用 就是使Block持有的對(duì)象指向我們聲明的對(duì)象的指針式弱引用,那么我們外部指向?qū)嵗龑?duì)象的指針一旦被釋放 實(shí)例對(duì)象就會(huì)被釋放 那么Block就會(huì)被釋放 從而解決循環(huán)引用创葡。區(qū)別就是如果我們使用的是__weak 一旦我們的實(shí)例對(duì)象被釋放浙踢,block內(nèi)部持有的指向我們實(shí)例對(duì)象的指針會(huì)被指nil 不會(huì)產(chǎn)生野指針,但是__unsafe__unretained 不會(huì)有這一步操作 所有如果使用__unsafe__unretained block內(nèi)部持有的指向我們實(shí)例對(duì)象的指針會(huì)還會(huì)指向我們已經(jīng)釋放的實(shí)例對(duì)象的地址 產(chǎn)生野指針灿渴。這也是它不安全的原因洛波。所以我們一般使用__weak來解決循環(huán)引用

2.在MRC的情況下 我們使用__unsafe__unretained __block來解決 因?yàn)椴恢С謜eak指針

這兩個(gè)都可以使Block內(nèi)部的指向我們實(shí)例對(duì)象的指針進(jìn)行retain操作

1.Block的原理是怎樣的豌研?本質(zhì)是什么回怜?

Block是封裝了函數(shù)調(diào)用以及函數(shù)調(diào)用環(huán)境的OC對(duì)象 本質(zhì)上也是一個(gè)OC對(duì)象

Block內(nèi)部包含了一個(gè)__block_imp1 這個(gè)指針內(nèi)部包含了一個(gè)isa指針(也證明了本質(zhì)是個(gè)對(duì)象) 和 block內(nèi)部封裝的代碼塊的執(zhí)行地址 以及捕獲的變量的指針

和一個(gè)__main_block_desc_0 里面包含了block的大小等 如果內(nèi)部使用了實(shí)例對(duì)象或者_(dá)_block修飾的變量 會(huì)包含一個(gè)copy函數(shù)和dispose函數(shù).

2.__block的作用是什么彬碱?有什么使用注意點(diǎn)橄镜?

被__block修飾的變量 在block內(nèi)部可以被改變晌纫,因?yàn)樵赽lock內(nèi)部被__block修飾的變量會(huì)被包裝成一個(gè)__Block_byref_xxx_0的對(duì)象岗屏,這個(gè)對(duì)象內(nèi)部有一個(gè)指向我們聲明的變量的指針 指向的地址和我們聲明的變量是同一個(gè)地址禽捆,修改的時(shí)候也是通過這個(gè)對(duì)象拿到指針做修改藐石。block內(nèi)部對(duì)這個(gè)對(duì)象是強(qiáng)引用的误续。而如果被修飾的變量是實(shí)例對(duì)象 那么這個(gè)對(duì)象內(nèi)部會(huì)添加一個(gè)copy函數(shù)和dispose函數(shù)吨悍,在被copy到堆上時(shí),這個(gè)對(duì)象的copy函數(shù)會(huì)根據(jù)被修飾實(shí)例對(duì)象的關(guān)鍵字來確定對(duì)實(shí)例對(duì)象是強(qiáng)引用還是弱引用蹋嵌。再block釋放的時(shí)候育瓜,會(huì)調(diào)用dispose函數(shù) 來斷開對(duì)實(shí)例對(duì)象的引用關(guān)系。需要注意的是__block只能修飾實(shí)例對(duì)象 還有一些內(nèi)存管理問題 比如說修飾實(shí)例對(duì)象且該實(shí)例對(duì)象擁有該Block 會(huì)造成循環(huán)引用栽烂。而且在MRC下使用__block修飾的實(shí)例對(duì)象 不會(huì)被__Block_byref_xxx_0的對(duì)象retain操作躏仇。對(duì)象可能會(huì)被提前釋放恋脚。

3.Block的屬性修飾詞為什么是copy? 使用Block有哪些注意?

如果block不被copy 就是存儲(chǔ)在椄铺空間上慧起,我們就控制不了block的生命周期,可能我們使用的時(shí)候已經(jīng)被釋放了或者我們使用的時(shí)候 其內(nèi)部捕獲的變量已經(jīng)釋放了 導(dǎo)致程序錯(cuò)誤册倒。而拷貝到堆上蚓挤,我們可以方便的控制其生命周期。雖然增加了管理內(nèi)存的一些成本驻子。但是可以減少錯(cuò)誤灿意。在ARC的情況下 如果有一個(gè)強(qiáng)引用指向Block 內(nèi)部也會(huì)copy到堆上。使用strong也行崇呵。但是我們習(xí)慣使用copy缤剧。 需要注意的是不能引起循環(huán)引用。我們可以使用__weak來避免這個(gè)問題

4.Block修改NSMutableArray 需不需要添加__block域慷?

不需要荒辕,如果是對(duì)數(shù)組進(jìn)行增刪改查 我們不需要對(duì)其添加__block 因?yàn)椴皇歉淖償?shù)組的指針指向 只是操作數(shù)組。添加__block反而會(huì)增加開銷犹褒。

Block的copy

_NSConcreteStackBlock copy 變成 _NSConcreteMallocBlock
_NSConcreteGlobalBlock copy 還是全局block 什么也不做
_NSConcreteMallocBlock copy 引用計(jì)數(shù)加1

在ARC環(huán)境下 編譯器會(huì)根據(jù)情況自動(dòng)將棧上的Block復(fù)制到堆上 相當(dāng)于對(duì)棧上的Block進(jìn)行copy操作抵窒。
1.Blcok 作為函數(shù)返回值的時(shí)候 會(huì)自動(dòng)copy
2.將Block賦值給__strong強(qiáng)指針的時(shí)候 也會(huì)自動(dòng)做copy操作
3.Block作為GCD的參數(shù)時(shí) 也會(huì)被copy到堆上
4.Foundation框架下 block作為參數(shù)且方法名含有usingBlock時(shí) 會(huì)被自動(dòng)copy

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市叠骑,隨后出現(xiàn)的幾起案子李皇,更是在濱河造成了極大的恐慌,老刑警劉巖宙枷,帶你破解...
    沈念sama閱讀 211,948評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件掉房,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡慰丛,警方通過查閱死者的電腦和手機(jī)卓囚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來诅病,“玉大人哪亿,你說我怎么就攤上這事〔橇ィ” “怎么了?”我有些...
    開封第一講書人閱讀 157,490評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵页徐,是天一觀的道長(zhǎng)苏潜。 經(jīng)常有香客問我,道長(zhǎng)变勇,這世上最難降的妖魔是什么恤左? 我笑而不...
    開封第一講書人閱讀 56,521評(píng)論 1 284
  • 正文 為了忘掉前任贴唇,我火速辦了婚禮,結(jié)果婚禮上飞袋,老公的妹妹穿的比我還像新娘戳气。我一直安慰自己,他們只是感情好巧鸭,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,627評(píng)論 6 386
  • 文/花漫 我一把揭開白布瓶您。 她就那樣靜靜地躺著,像睡著了一般纲仍。 火紅的嫁衣襯著肌膚如雪呀袱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,842評(píng)論 1 290
  • 那天郑叠,我揣著相機(jī)與錄音夜赵,去河邊找鬼。 笑死乡革,一個(gè)胖子當(dāng)著我的面吹牛寇僧,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播沸版,決...
    沈念sama閱讀 38,997評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼嘁傀,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了推穷?” 一聲冷哼從身側(cè)響起心包,我...
    開封第一講書人閱讀 37,741評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎馒铃,沒想到半個(gè)月后蟹腾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,203評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡区宇,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,534評(píng)論 2 327
  • 正文 我和宋清朗相戀三年娃殖,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片议谷。...
    茶點(diǎn)故事閱讀 38,673評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡炉爆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出卧晓,到底是詐尸還是另有隱情芬首,我是刑警寧澤,帶...
    沈念sama閱讀 34,339評(píng)論 4 330
  • 正文 年R本政府宣布逼裆,位于F島的核電站郁稍,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏胜宇。R本人自食惡果不足惜耀怜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,955評(píng)論 3 313
  • 文/蒙蒙 一恢着、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧财破,春花似錦掰派、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至抖锥,卻和暖如春亿眠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背磅废。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工纳像, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人拯勉。 一個(gè)月前我還...
    沈念sama閱讀 46,394評(píng)論 2 360
  • 正文 我出身青樓竟趾,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親宫峦。 傳聞我的和親對(duì)象是個(gè)殘疾皇子岔帽,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,562評(píng)論 2 349

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