美文网首页程序员技术栈
Block 的一些简单认识

Block 的一些简单认识

作者: 新南12138 | 来源:发表于2018-09-27 18:10 被阅读13次

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

局部变量的修改

值类型的变量

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

使用__block 不使用
__Block_byref_global_0 *global; // by ref int 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),在使用时要注意


不对 block 赋值,直接调用

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

2.循环引用

循环引用

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

怎么知道发生了循环引用

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


debug memory

点击 Debug Memory 那个选项


内存的图
本来这个 pool 结束的时候,user 应该被释放掉,但是因为在 block 内部强引用了 user,造成了循环引用。
如果我们去掉 block 内部的那些代码
没有形成循环引用

如果没有了 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的内部使用弱引用,这样的话,就能打破这个环。内存图就像下面这个样子


解决循环引用

参考内容

一篇文章看懂iOS代码块Block

相关文章

  • Block 的一些简单认识

    Block 与局部变量 block 可以捕获局部变量,在你声明 myBlock 后,因为需要在block 内使用了...

  • Block浅析

    参考了网上的一些Block的文档,加上自己对Block的使用、理解,对Block做一个简单的分析。 Block是C...

  • Block 初探

    在介绍Block之前通过一个简单的应用场景认识下Block 场景描述如下:TableView上面有多个Custom...

  • Block

    回顾一下在工作中使用block遇到一些问题和新的认识. block本质 block的本质就是C语言的函数指针,本身...

  • iOS Block - 深入学习篇

    前面写了一篇Block开发中的简单使用,这篇文章将深入的学习一下Block和开发中的一些使用。 目录 Block的...

  • Block的认识

    block的原理是怎样的?本质是什么?block是将函数及其执行上下文封装起来的对象或者说是结构体(3个关键字;继...

  • iOS:Block的本质

    我们项目中经常使用block来进行回调传值,之前我对block的认识也就仅仅的停留在基础的层面,包括简单的使用和一...

  • Block - block简单的使用

    参考文档 iOS Block详解 一、忘记block格式? 样例一.png 样例二.png 二、Block的定义 ...

  • iOS block 捕获外部变量以及注意点

    参考文章:深入研究Block捕获外部变量和__block实现原理做一些简单的总结说明: (1)对于四种非对象变量:...

  • 一些简单的Block回调

    ifndef smartSDKHeader_h define smartSDKHeader_h typedef v...

网友评论

    本文标题:Block 的一些简单认识

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