Object-C语言中的block

Mac系统下在终端有一条命令(clang -rewrite-objc *.m),是可以将OC代码转换成C/C++代码的,其实就是运行时机制。OC就是在C语言的基础之上写成的,在预编译的时候还是转换成了C/C++语言。

今天查看了block的以下三种情况:

1.全局变量情况:

#import <Foundation/Foundation.h>
int age = 10;
int main(int argc, const char * argv[])
{

    @autoreleasepool { 
        void (^block)() = ^{
            NSLog(@"age = %d",age);
        };
        
        age = 20;
        
        block();
    }
    return 0;
}
这时候,你猜block中NSLog输出age会是多少呢?答案是20,你知道原因吗?不管你知不知道,我们都通过以下方式进行一个解析吧。

使用以上命令,clang -rewrite-objc main.m,就会生成一个main.cpp,打开main.cpp可以看到以下代码:

<span style="color:#cc0000;">int age = 10;</span>

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {

            NSLog((NSString *)&__NSConstantStringImpl__var_folders_2v_c9bzg71j6k1fsp8sq1ntg70m0000gn_T_main_278cb2_mi_0,age);
        }

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)};
<pre name="code" class="cpp">int main(int argc, const char * argv[])
{

    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 


        void (*block)() = (void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA);

        age = 20;

        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
    }
    return 0;
}

 哇,是不是要疯了,这是些什么东西啊,奔溃了!!!哎,其实我看到这些也是,不过不要着急,我们删掉main函数中的一些强制转换就可以比较清晰的分析了。 

int main(int argc, const char * argv[])
{

    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 


        void (*block)() = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA);

        age = 20;

        block->FuncPtr(block);
    }
    return 0;
}
分析下第一行代码:
void (*block)() = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA);
其实就是一个函数指针,函数指针懂吗?如果不懂,那你C语言基础还不够哦,赶紧去补一补吧!block这个函数指针就指向
__main_block_impl_0

这个函数指针了,__main_block_impl_0这个东西是这个结构体中的哦:

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
其实这就是一个类,在C++中,class和struct关键字的区别就是class的变量如果不加权限关键字(如:private)默认就是私有的,而struct则是默认是公有的。所以,这其实就是在创建一个类,所以这个结构体中的其他成员变量impl也是可以使用的。

好了,继续分析。__main_block_func_0这个参数传进去是传给结构体中的impl.FuncPtr。所以在以下代码中:

block->FuncPtr(block);
实际上就是调用传进来的__main_block_func_0这个函数。那我们来看看__main_block_func_0这个函数。

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {

            NSLog((NSString *)&__NSConstantStringImpl__var_folders_2v_c9bzg71j6k1fsp8sq1ntg70m0000gn_T_main_278cb2_mi_0,age);
        }
这其实就是在打印age。

关键点:你知道age打印的为什么是20而不是10吗?没错!就是因为age是全局变量,在打印之前,age已经被更改成20了。

其实以上部分,终点是在分析代码,而不是在分析为什么age不是10而是20。其实本文的重点,是在分析几种情况下age的数值是多少,以上做了许多铺垫。所以,以下情况将不会那么详细的讲解了。

2.普通情况下:

请看OC代码:

#import <Foundation/Foundation.h>
int main(int argc, const char * argv[])
{

    @autoreleasepool {
        <span style="color:#cc0000;">int age = 10;</span>
        void (^block)() = ^{
            NSLog(@"age = %d",age);
        };
        
        age = 20;
        
        block();
    }
    return 0;
}
这个时候,age是放在了自动释放池当中了。你猜猜age打印的是10还是20呢?经过测试,答案是10哦。

同上,我们再用clang -rewrite-objc main.m编译成C/C++代码。

代码如下:

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

            NSLog((NSString *)&__NSConstantStringImpl__var_folders_2v_c9bzg71j6k1fsp8sq1ntg70m0000gn_T_main_08415b_mi_0,age);
        }

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; 
        int age = 10;
        void (*block)() = (void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age);

        age = 20;

        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
    }
    return 0;
}
同样,你也可以将强制转换删除,将帮助你更好的理解代码。这时候,我们的关键点将看重__main_block_func_0函数,在这个函数中,我们可以看到他在里面是有一个局部变量,来保存age的,也就是说,在
void (*block)() = (void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age);
代码过后,其实是将age的值拷贝给了__main_block_func_0函数中的局部变量age了。所以外面的age随便怎么更改,里面的age还是一样的,所以打印出来的,还是原来被赋值进去的10。明白了吗?


3.加static关键字

我们来看下OC代码:

#import <Foundation/Foundation.h>
int main(int argc, const char * argv[])
{

    @autoreleasepool {
        <span style="color:#cc0000;">static</span> int age = 10;
        void (^block)() = ^{
            NSLog(@"age = %d",age);
        };
        
        age = 20;
        
        block();
    }
    return 0;
}
你猜,打印的结果是10,还是20呢?

我们还是一样,来看看预编译后的C/C++代码吧。

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

            NSLog((NSString *)&__NSConstantStringImpl__var_folders_2v_c9bzg71j6k1fsp8sq1ntg70m0000gn_T_main_9527a2_mi_0,(*age));
        }

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 int age = 10;
        void (*block)() = (void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &age);

        age = 20;

        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
    }
    return 0;
}
还是一样,关键点看__main_block_func_0函数,在这个函数里面,我们惊奇的发现,他定义的是一个指针,来指向外面的age,所以外面的age怎么变化,打印的age就是变化后的值。


4.加__block关键字

看OC代码:

#import <Foundation/Foundation.h>
int main(int argc, const char * argv[])
{

    @autoreleasepool {
        <span style="color:#cc0000;">__block</span> int age = 10;
        void (^block)() = ^{
            NSLog(@"age = %d",age);
        };
        
        age = 20;
        
        block();
    }
    return 0;
}
你猜猜结果是多少呢?同样,还是20哦。

那我们来看看预编译后的C/C++代码吧。

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

            NSLog((NSString *)&__NSConstantStringImpl__var_folders_2v_c9bzg71j6k1fsp8sq1ntg70m0000gn_T_main_3ab708_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, const 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};
        void (*block)() = (void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_age_0 *)&age, 570425344);

        (age.__forwarding->age) = 20;

        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
    }
    return 0;
}
我们发现有点复杂,还另外多了个age的struct(第一个struct),这样做是为什么?我不知道,但苹果肯定有自己的用处。我们还是关键看__main_block_func_0这个函数,可以看到,里面还是一个指针类型,指向外面的age,所以外面的age变化,打印的age也是会变化的。

我是不是很无聊?研究这个干啥,是啊,我也觉得有点无聊。玩了一天游戏了,研究下代码,放松下心情嘛!哈哈哈哈



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南窗客斯黄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值