最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解Objective C 中Block如何捕獲外部值

 更新時間:2022年09月05日 10:27:42   作者:小橘爺  
這篇文章主要為大家介紹了詳解Objective C 中Block如何捕獲外部值實現(xiàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

Block 本質(zhì)上也是一個 Objective-C 對象,它內(nèi)部也有個 isa指針。Block 是封裝了函數(shù)調(diào)用以及函數(shù)調(diào)用環(huán)境的 Objective-C 對象。Block 的底層結(jié)構(gòu)如下圖所示:

Block 對于不同類型的值會有不同的捕獲方式,本文將通過代碼展示其對于各種場景下的外部值是如何進(jìn)行捕獲的。

自動變量

首先展示源代碼:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSInteger value = 0;
        void(^block)(void) = ^{
            NSLog(@"%zd", value);
        };
        block();
    }
    return 0;
}

經(jīng)過 clang -rewrite-objc 之后,得到的代碼如下,可以看到,對于自動變量的捕獲,是會在 Block 結(jié)構(gòu)體中生成一個對應(yīng)類型的成員變量來實現(xiàn)捕獲的能力,這也解釋了為什么在 Block 中修改捕獲的值的內(nèi)容,無法對 Block 外的值產(chǎn)生影響。

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  NSInteger value; // 捕獲的 NSInteger value
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSInteger _value, int flags=0) : value(_value) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  NSInteger value = __cself->value; // bound by copy
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_zz_zyxvpxvq6csfxvn_n0000000000000_T_main_e3ca95_mi_0, value);
        }
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)};
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        NSInteger value = 0;
        void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, value));
        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
    }
    return 0;
}

靜態(tài)變量、靜態(tài)全局變量與全局變量

對于靜態(tài)變量、靜態(tài)全局變量與全局變量的捕獲,會稍有不同,其中:

  • 全局變量與靜態(tài)全局變量:直接使用,因為地址一直是可以直接獲取的。
  • 靜態(tài)變量:捕獲地址使用,因為 block 有可能會傳遞出創(chuàng)建時的作用域。
NSInteger globalValue = 1;
static NSInteger staticGlobalValue = 2;
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        static NSInteger staticValue = 3;
        void(^block)(void) = ^{
            globalValue += 1;
            staticGlobalValue += 2;
            staticValue += 3;
        };
        block();
    }
    return 0;
}
NSInteger globalValue = 1;
static NSInteger staticGlobalValue = 2;
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  NSInteger *staticValue;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSInteger *_staticValue, int flags=0) : staticValue(_staticValue) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  NSInteger *staticValue = __cself->staticValue; // bound by copy
            globalValue += 1;
            staticGlobalValue += 2;
            (*staticValue) += 3;
        }
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)};
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        static NSInteger staticValue = 3;
        void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &staticValue));
        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
    }
    return 0;
}

帶 __block 的自動變量

__block 修飾的自動變量,可以在 Block 內(nèi)部對其外部的值進(jìn)行修改:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        __block NSInteger value = 0;
        void(^block)(void) = ^{
            value = 10;
        };
        block();
        NSLog(@"%zd", value);
    }
    return 0;
}

這次生成的代碼復(fù)雜了一些,不過只關(guān)注 value 部分的話可以發(fā)現(xiàn),Block 為了捕獲 __block 類型的自動變量,會生成 __Block_byref_value_0 結(jié)構(gòu)體,并通過該結(jié)構(gòu)體來實現(xiàn)對外部 __block 自動變量的捕獲。

struct __Block_byref_value_0 { // 為捕獲 __block 的自動變量,生成的結(jié)構(gòu)體。為的是方便多個 Block 同時捕獲一個自動變量時使用。
  void *__isa; // isa 指針
__Block_byref_value_0 *__forwarding; // 在 Block 單純在棧上是,指向的是自己,拷貝到堆上后,指向的是在堆上的 Block。之所以需要這樣的指針是因為當(dāng) Block 拷貝到堆上時,調(diào)用方式是統(tǒng)一的。
 int __flags;
 int __size;
 NSInteger value; // 具體的值
};
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_value_0 *value; // 通過引用的方式捕獲 value,其中變量類型為 __Block_byref_value_0
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_value_0 *_value, int flags=0) : value(_value->__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_value_0 *value = __cself->value; // bound by ref
            (value->__forwarding->value) = 10; // 賦值代碼
        }
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->value, (void*)src->value, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->value, 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[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        __attribute__((__blocks__(byref))) __Block_byref_value_0 value = {(void*)0,(__Block_byref_value_0 *)&value, 0, sizeof(__Block_byref_value_0), 0};
        void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_value_0 *)&value, 570425344));
        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_zz_zyxvpxvq6csfxvn_n0000000000000_T_main_6bf1c6_mi_0, (value.__forwarding->value));
    }
    return 0;
}

__block 可以用于解決 block 內(nèi)部無法修改 auto 變量值的問題,__block 不能修飾全局變量、靜態(tài)變量(static),編譯器會將 __block 變量包裝成一個對象。

當(dāng) block 在棧上時,并不會對 __block 變量產(chǎn)生強引用。

當(dāng) blockcopy 到堆時,會調(diào)用 block 內(nèi)部的 copy 函數(shù),copy 函數(shù)內(nèi)部會調(diào)用 _Block_object_assign 函數(shù),_Block_object_assign 函數(shù)會對 __block 變量形成強引用(retain)。

當(dāng) block 從堆中移除時,會調(diào)用 block 內(nèi)部的 dispose 函數(shù),dispose 函數(shù)內(nèi)部會調(diào)用 _Block_object_dispose 函數(shù),_Block_object_dispose 函數(shù)會自動釋放引用的 __block 變量(release)。

捕獲對象

在探究完對標(biāo)量類型的捕獲之后,讓我們看一下對對象類型的捕獲:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSArray *array = [NSArray array];
        void(^block)(void) = ^{
            NSLog(@"%@", array);
        };
        block();
    }
    return 0;
}

通過轉(zhuǎn)譯的代碼可以看出,因為對象類型本身已經(jīng)是存儲在堆上的值了,所以直接獲取其地址即可,同時其新增了兩個函數(shù) __main_block_copy_0__main_block_dispose_0,這兩個函數(shù)是用來將對象拷貝到堆上和被從堆上移除時調(diào)用的,其內(nèi)部又分別調(diào)用了 _Block_object_assign_Block_object_dispose 用來對捕獲的對象進(jìn)行引用計數(shù)的增加和減少。

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  NSArray *array;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSArray *_array, int flags=0) : array(_array) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  NSArray *array = __cself->array; // bound by copy
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_zz_zyxvpxvq6csfxvn_n0000000000000_T_main_8ba4f7_mi_0, array);
        }
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->array, (void*)src->array, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->array, 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[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        NSArray *array = ((NSArray * _Nonnull (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSArray"), sel_registerName("array"));
        void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, array, 570425344));
        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
    }
    return 0;
}

Block 對象本身分為三種類型:

  • NSGlobalBlock:沒有訪問 auto 變量,調(diào)用 copy 方法之后不會發(fā)生變化。
  • NSStackBlock:訪問了 auto 變量,調(diào)用 copy 方法之后存儲位置從棧變?yōu)槎选?/li>
  • NSMallocBlock__NSStackBlock__ 調(diào)用了 copy 方法之后,引用計數(shù)增加。

ARC 環(huán)境下,編譯器會根據(jù)情況自動將棧上的 block 復(fù)制到堆上,比如以下情況:

  • Block 作為函數(shù)返回值時
  • Block 賦值給 __strong 指針時
  • Block 作為 Cocoa API 中方法名含有 usingBlock 的方法參數(shù)時
  • Block 作為 GCD API 的方法參數(shù)時

所以,當(dāng) Block 內(nèi)部訪問了對象類型的 auto 變量時。如果 Block 是在棧上,將不會對 auto 變量產(chǎn)生強引用。

如果 Block 被拷貝到堆上,會調(diào)用 Block 內(nèi)部的 copy 函數(shù),copy 函數(shù)內(nèi)部會調(diào)用 _Block_object_assign 函數(shù),_Block_object_assign 函數(shù)會根據(jù) auto 變量的修飾符(__strong、__weak__unsafe_unretained)做出相應(yīng)的操作,形成強引用或者弱引用。

如果 Block 從堆上移除,會調(diào)用 Block 內(nèi)部的 dispose 函數(shù),dispose 函數(shù)內(nèi)部會調(diào)用 _Block_object_dispose 函數(shù)。_Block_object_dispose 函數(shù)會自動釋放引用的 auto 變量(release)。

__block 對象類型的捕獲

如果想在 Block 中,對捕獲的對象的指針指向進(jìn)行修改,則需要添加 __block 關(guān)鍵字:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        __block NSArray *array = [NSArray array];
        void(^block)(void) = ^{
            array = [NSArray array];
            NSLog(@"%@", array);
        };
        block();
    }
    return 0;
}

通過轉(zhuǎn)譯我們可以看出,跟 __block 修飾的標(biāo)量類型相似,同樣會生成 __Block_byref_array_0 結(jié)構(gòu)體來捕獲對象類型。同時其內(nèi)部生成了 __Block_byref_id_object_copy__Block_byref_id_object_dispose 兩個函數(shù)指針,用于對被結(jié)構(gòu)體包裝的對象進(jìn)行內(nèi)存管理。

static void __Block_byref_id_object_copy_131(void *dst, void *src) {
 _Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
}
static void __Block_byref_id_object_dispose_131(void *src) {
 _Block_object_dispose(*(void * *) ((char*)src + 40), 131);
}
struct __Block_byref_array_0 {
  void *__isa;
__Block_byref_array_0 *__forwarding;
 int __flags;
 int __size;
 void (*__Block_byref_id_object_copy)(void*, void*);
 void (*__Block_byref_id_object_dispose)(void*);
 NSArray *array;
};
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_array_0 *array; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_array_0 *_array, int flags=0) : array(_array->__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_array_0 *array = __cself->array; // bound by ref
            (array->__forwarding->array) = ((NSArray * _Nonnull (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSArray"), sel_registerName("array"));
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_zz_zyxvpxvq6csfxvn_n0000000000000_T_main_3593f0_mi_0, (array->__forwarding->array));
        }
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->array, (void*)src->array, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->array, 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[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        __attribute__((__blocks__(byref))) __Block_byref_array_0 array = {(void*)0,(__Block_byref_array_0 *)&array, 33554432, sizeof(__Block_byref_array_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((NSArray * _Nonnull (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSArray"), sel_registerName("array"))};
        void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_array_0 *)&array, 570425344));
        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
    }
    return 0;
}

當(dāng) block 在棧上時,對它們都不會產(chǎn)生強引用。

當(dāng) block 拷貝到堆上時,都會通過 copy 函數(shù)來處理它們,__block 變量(假設(shè)變量名叫做 a):

_Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);

對象類型的 auto 變量(假設(shè)變量名叫做 p):

_Block_object_assign((void*)&dst->p, (void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);

當(dāng) block 從堆上移除時,都會通過 dispose 函數(shù)來釋放它們,__block 變量(假設(shè)變量名叫做 a):

_Block_object_dispose((void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);

對象類型的 auto 變量(假設(shè)變量名叫做 p):

_Block_object_dispose((void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);

以上就是詳解Objective C 中Block如何捕獲外部值的詳細(xì)內(nèi)容,更多關(guān)于Objective C Block捕獲外部值的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • IOS中UITableView滾動到指定位置

    IOS中UITableView滾動到指定位置

    這篇文章主要介紹了IOS中UITableView滾動到指定位置的實現(xiàn)方法,本文介紹的非常詳細(xì),具有參考借鑒價值,需要的朋友可以參考下
    2016-09-09
  • touchesBegan: withEvent: 不執(zhí)行解決

    touchesBegan: withEvent: 不執(zhí)行解決

    這篇文章主要介紹了touchesBegan: withEvent: 不執(zhí)行解決的相關(guān)資料,需要的朋友可以參考下
    2016-12-12
  • iOS開發(fā)中UIPopoverController的使用詳解

    iOS開發(fā)中UIPopoverController的使用詳解

    這篇文章主要介紹了iOS開發(fā)中UIPopoverController的使用,代碼基于傳統(tǒng)的Objective-C,需要的朋友可以參考下
    2015-11-11
  • iOS實現(xiàn)百度地圖拖拽后更新位置以及反編碼

    iOS實現(xiàn)百度地圖拖拽后更新位置以及反編碼

    百度地圖已經(jīng)開放了地圖API,大家可以很方便的調(diào)用地圖中的相應(yīng)數(shù)據(jù),并完成各項個性化的展示,下面這篇文章主要給大家介紹了關(guān)于iOS如何實現(xiàn)百度地圖拖拽后更新位置以及反編碼的相關(guān)資料,需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-12-12
  • IOS UITableViewCell詳解及按鈕點擊事件處理實例

    IOS UITableViewCell詳解及按鈕點擊事件處理實例

    這篇文章主要介紹了IOS UITableViewCell詳解及按鈕點擊事件處理實例的相關(guān)資料,這里附有示例代碼,大家可以看下如何實現(xiàn)按鍵點擊事件,需要的朋友可以參考下
    2016-12-12
  • 如何使用IOS自動化測試工具UIAutomation

    如何使用IOS自動化測試工具UIAutomation

    這篇文章主要介紹了UIAutomation使用實例、應(yīng)用技巧、基本知識點總結(jié)和需要注意事項,具有一定的參考價值
    2021-04-04
  • iOS學(xué)習(xí)筆記之遠(yuǎn)程推送、靜默推送與自定義消息推送

    iOS學(xué)習(xí)筆記之遠(yuǎn)程推送、靜默推送與自定義消息推送

    推送是各位iOS開發(fā)者們都會遇到的一個功能,下面這篇文章主要給大家介紹了關(guān)于iOS學(xué)習(xí)筆記之遠(yuǎn)程推送、靜默推送與自定義消息推送的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2018-08-08
  • iOS中設(shè)置view圓角化的四種方法示例

    iOS中設(shè)置view圓角化的四種方法示例

    最近因為工作的原因,遇到view圓角優(yōu)化的問題,所以將實現(xiàn)的幾種方法總結(jié)分享出來,下面這篇文章主要給大家介紹了關(guān)于iOS中設(shè)置view圓角化的四種方法,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-09-09
  • 深入詳解Objective-C中的@Synchronized關(guān)鍵字

    深入詳解Objective-C中的@Synchronized關(guān)鍵字

    這篇文章主要為大家介紹了深入詳解Objective-C中的@Synchronized關(guān)鍵字,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-03-03
  • iOS消息推送原理及具體實現(xiàn)代碼

    iOS消息推送原理及具體實現(xiàn)代碼

    這篇文章主要為大家詳細(xì)介紹了iOS 消息推送原理及具體實現(xiàn)代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-09-09

最新評論

沁源县| 民丰县| 北流市| 凌源市| 大名县| 准格尔旗| 望谟县| 广汉市| 黄冈市| 大田县| 绥化市| 全南县| 浙江省| 承德市| 小金县| 夏河县| 城口县| 玛曲县| 阜新市| 北安市| 同心县| 秭归县| 治县。| 宜良县| 太原市| 通城县| 秀山| 景洪市| 镇平县| 米泉市| 临漳县| 南昌县| 韩城市| 邹平县| 盐边县| 青铜峡市| 渭源县| 汉沽区| 远安县| 浦北县| 九龙城区|