美文网首页
iOS Block(3)-__block修饰变量

iOS Block(3)-__block修饰变量

作者: switer_iOS | 来源:发表于2021-07-02 10:42 被阅读0次

在block函数体里面修改变量在日常开发中常见,我们可以轻松在block体内部修改static变量或全局变量,但是却无法修改auto变量。尝试在block中修改auto变量,编译器错误如下:

image

有三种解决方案:
①. 将需要修改的变量设置为全局的;
②. 将需要修改的变量设置为static类型;
③. 在需要修改的变量前加上“__block”修饰符。
针对方案①,全局变量定义太多影响代码阅读,全局变量生命周期长,占内存;
针对方案②,static类型变量的生命周期同APP一样长,一直存在于内存中,所以舍弃。
方案③是完美方案.

来看一个简单的demo:

image

OC转换为C++代码:

void(*block)(void);

struct __Block_byref_age_0 {
  void *__isa;
__Block_byref_age_0 *__forwarding;
 int __flags;
 int __size;
 int age;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_age_0 *age; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_age_0 *_age, int flags=0) : age(_age->__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_age_0 *age = __cself->age; // bound by ref

            (age->__forwarding->age) = 20;
            NSLog((NSString *)&__NSConstantStringImpl__var_folders__6_s9x0n6313d99yqk5pltzp6ym0000gn_T_main_3fd458_mi_0,(age->__forwarding->age));
        }
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->age, (void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->age, 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, char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 

        __attribute__((__blocks__(byref))) __Block_byref_age_0 age = {(void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 10};

        block = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_age_0 *)&age, 570425344));
        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
        return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
    }
}

__Block_byref_age_0结构体,根据其内部定义:

struct __Block_byref_age_0 {
  void *__isa;
__Block_byref_age_0 *__forwarding;
 int __flags;
 int __size;
 int age;
};

内部包含isa指针,毫无疑问是一个OC对象;
成员__forwarding指向结构体本身;
成员__size保存了结构体__Block_byref_age_0本身的内存大小;
居然有个成员int age,并且由之前老巢__main_block_impl_0迁移到此;
再看看__main_block_impl_0结构体:

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_age_0 *age; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_age_0 *_age, int flags=0) : age(_age->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

内部也有一个age成员,不过是结构体__Block_byref_age_0类型指针,所以block内部访问的auto变量就是该结构体age指针指向的内容。经过“__block”修饰之后,基本类型的“age”变量被包装成“__Block_byref_age_0”结构体对象。
在main()函数中,我们看到"__block int age = 10;"被定义成:

 __attribute__((__blocks__(byref))) __Block_byref_age_0 age = {(void*)0,
            (__Block_byref_age_0 *)&age,
            0,
            sizeof(__Block_byref_age_0),
            10};

同时block被定义成:

 block = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_age_0 *)&age, 570425344))

也就是将"__Block_byref_age_0"类型的“age”传入到block的构造函数__main_block_impl_0中,因此__main_block_impl_0中的age被赋值这样的结构体:

 __attribute__((__blocks__(byref))) __Block_byref_age_0 age = {(void*)0,
            (__Block_byref_age_0 *)&age,
            0,
            sizeof(__Block_byref_age_0),
            10};

即:__Block_byref_age_0中最后一个成员“int age;”被赋值10,__forwarding被赋予__Block_byref_age_0age的地址,这里就是结构体本身,与前面讲述的一致。

再看看block内部的代码块:

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_age_0 *age = __cself->age; // bound by ref

            (age->__forwarding->age) = 20;
            NSLog((NSString *)&__NSConstantStringImpl__var_folders__6_s9x0n6313d99yqk5pltzp6ym0000gn_T_main_3fd458_mi_0,(age->__forwarding->age));
        }

OC代码age = 20;对应的C++代码(age->__forwarding->age) = 20;,C++代码中有两个不同类型的age变量,前一个age是__Block_byref_age_0类型的结构体指针,后一个age是__Block_byref_age_0结构体中的int型age变量。中间的__forwarding指向的还是__Block_byref_age_0类型的结构体指针age,即第一个age,具体为什么这么设计,后文会讲到。
兜了这么一圈,终于明白了,原来"__block"将局部变量包装成一个__Block_byref_age_0结构体对象,结构体中有与局部变量同名同类型的变量。在block体内修改"__block"变量,通过一系列指针指向关系,最终指向了__Block_byref_age_0结构体内与局部变量同名同类型的那个成员,并成功修改变量值

相关文章

网友评论

      本文标题:iOS Block(3)-__block修饰变量

      本文链接:https://www.haomeiwen.com/subject/fnxbcltx.html