继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Block 的一些简单认识

青春有我
关注TA
已关注
手记 1206
粉丝 205
获赞 1008

Block 与局部变量

 int global = 100; void(^myBlock)(void) = ^{     NSLog(@"global = %d",global);
 };
 global = 101;
 myBlock();

block 可以捕获局部变量,在你声明 myBlock 后,因为需要在block 内使用了 global 变量,所以 block 为你捕获了这个变量。

局部变量的使用

1.如果使用 global 变量

在使用 clang -rewrite-objc main.m 后查看.cpp 文件可以看到

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int global; //block 捕获了这个变量
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _global, int flags=0) : global(_global) {
     impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

2.如果不使用 global 变量

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;
  }
};//没有捕获 global

局部变量的修改

webp

值类型的变量

直接修改报错,提示缺少了__block 的修饰符,那么加了__block 修饰符后有什么不一样呢。同样的我们还是查看.cpp 文件来比较

使用__block不使用
__Block_byref_global_0 *global; // by refint global;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_global_0 *_global, int flags=0) : global(_global->__forwarding)__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _global, int flags=0) : global(_global)

可以看到 global 由一个值类型变成了一个指针类型的变量,所以使用了__block 后可以修改 global 的值。

局部变量的修改结果

1.使用__block 修饰

 __block int global = 100; void(^myBlock)(void) = ^{      NSLog(@"global = %d",global);
  };
  global = 101;
  myBlock();  //运行结果
  global = 101

2.不使用__block 修饰

 int global = 100; void(^myBlock)(void) = ^{      NSLog(@"global = %d",global);
  };
  global = 101;
  myBlock();  //运行结果
  global = 100

修改结果总结

  1. 如果使用__block 修饰,并且在调用 block 之前修改 global ,那么block 内部的 global 也会跟着变。

  2. 如果不使用__block的话,并且在调用 block 之前修改 global,那么 block 内部的 global 不会发生改变。

原因

  1. 使用__block 修饰

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

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_bq_bv7y3wj94sg1_5ldvtywqy7c0000gn_T_main_b116b4_mi_0,(global->__forwarding->global));
 }

2.不使用__block修饰

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {    int global = __cself->global; // bound by copy
   NSLog((NSString *)&__NSConstantStringImpl__var_folders_bq_bv7y3wj94sg1_5ldvtywqy7c0000gn_T_main_62e250_mi_0,global);
}

使用__block 修饰:
__Block_byref_global_0 *global = __cself->global; // bound by ref
不使用__block 修饰:
int global = __cself->global; // bound by copy
划重点,因为一个是bound by ref 一个是bound by copy。一个是传递了指针,一个是对当前捕获的值的拷贝

Block 与 全局变量

在外层定义一个全部变量,执行下面的代码

   void(^myBlock)(void) = ^{        NSLog(@"global = %d",global);
    };
    global = 101;
    myBlock();     //运行结果
    global = 101

为什么不使用__block 也可以修改,我们打开.cpp 进行查看

int global = 100;//先定义了一个全局的变量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;
  }
};

在block 结构体的上方,已经定义了一个全局的变量,所以直接使用就可以了。不需要像局部变量一样,而且它并没有被 block 所捕获。

Block 与 静态变量

定义一个静态变量,执行下面的代码

   static int global = 100;    void(^myBlock)(void) = ^{        NSLog(@"global = %d",global);
    };
    global = 101;
    myBlock();     //运行结果
     global = 101

为什么不使用__block 也可以修改,我们打开.cpp 进行查看

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

在 global 进行赋值的时候是这样的

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {    int *global = __cself->global; // bound by copy

     NSLog((NSString *)&__NSConstantStringImpl__var_folders_bq_bv7y3wj94sg1_5ldvtywqy7c0000gn_T_main_d41b2e_mi_0,(*global));
 }

它和局部变量不使用__block 修饰看起来很像,所以做了下对比

静态全局变量:
int *global = __cself->global; // bound by copy
不使用__block 修饰的局部变量:
int global = __cself->global; // bound by copy
划重点,虽然都是 copy,但是静态全局变量 copy 了指针,而不使用__block修改的局部变量进行了值的 copy。

ARC 下的 Block

1.EXC_BAD_ACCESS (code=1, address=0x10)

我有一个 User类

user.h

typedef void(^nameBlock)(void);@interface User : NSObject@property (nonatomic,strong)NSString *name;@property (nonatomic,copy)nameBlock block;
-(instancetype)initWithName:(NSString *)name;@end

user.m

import "User.h"

@implementation User-(instancetype)initWithName:(NSString *)name{    self = [super init];    if (self) {
         _name = name;
    }    return self;
}@end

如果不赋值直接调用user.block();那么直接就会报错EXC_BAD_ACCESS (code=1, address=0x10),在使用时要注意


webp

不对 block 赋值,直接调用


所以在调用 block 之前需要先赋值然后调用,这样就能解决这个问题

2.循环引用

webp

循环引用

user有一个属性是一个 block,并且在内部又调用了 user,造成了循环引用。在编写代码的时候,编译器就给出了上面的黄色警告。

怎么知道发生了循环引用

xcode 9 的话,我们可以打断点看,我们在 autoreleasepool 结束的时候打断点


webp

debug memory


点击 Debug Memory 那个选项


webp

内存的图


本来这个 pool 结束的时候,user 应该被释放掉,但是因为在 block 内部强引用了 user,造成了循环引用。

如果我们去掉 block 内部的那些代码

webp

没有形成循环引用

如果没有了 block 内部对 user 的强引用,那么便不会发生这种情况

如果我们一定要在 block 内部调用 user

修改我们的代码

    User *user = [[User alloc]initWithName:@"Likee"];
    __weak typeof(user)weakUser = user;
    user.block = ^{
        weakUser.name = @"cat";        NSLog(@"name is %@",weakUser.name);
    };    NSLog(@"name is %@",user.name);
    user.block();

在 block的内部使用弱引用,这样的话,就能打破这个环。内存图就像下面这个样子


webp

解决循环引用



作者:LikeeCat
链接:https://www.jianshu.com/p/a8f7697228a5


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP