block总结

1.block介绍

概念:block是将函数及其执行上下文封装起来的对象
下面将通过原码解析的方式理解概念
Objective-C代码如下:

-(void)method{
    int multiplier = 6;
    int(^Block)(int) = ^int(int num){
        return num * multiplier;
    };
    Block(2);
}

编译后的关键代码如下

 static void _I_MCBlock_method(MCBlock * self, SEL _cmd) {
    int multiplier = 6;
    /*
       int(^Block)(int) = ^int(int num){
        return num * multiplier;
      };
   */
     int(*Block)(int) = ((int (*) (int))&__MCBlock__method_block_impl_0((void *)__MCBlock__method_block_func_0, &__MCBlock__method_block_desc_0_DATA, multiplier));  

     __MCBlock__method_block_impl_0是一个结构体,传入三个参数,第一参数是函数指针__MCBlock__method_block_func_0,第二个参数是关于block相关描述的结构体,第三参数被block使用的局部变量。
从上面的代码可以看出block定义变量是一个函数指针。
    /*
       block(2)
    */
    ((int (*)(__block_impl *, int))((__block_impl *)Block)->FuncPtr)((__block_impl *)Block, 2);
}

__MCBlock__method_block_impl_0结构体的实现

struct __MCBlock__method_block_impl_0 {
  struct __block_impl impl;(结构体)
  struct __MCBlock__method_block_desc_0* Desc;(结构体)(关于block的相关描述)
  int multiplier;(block中使用的局部变量)
 /*c++当中对于结构体的构造函数的一个申明或者定义*/
  __MCBlock__method_block_impl_0(void *fp, struct __MCBlock__method_block_desc_0 *desc, int _multiplier, int flags=0) : multiplier(_multiplier) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;//标记位赋值
    impl.FuncPtr = fp;//函数指针赋值
    Desc = desc;//关于block描述的赋值
  }
};
 multiplier(_multiplier)将函数传递进来的_multiplier参数直接赋值给结构体中的multiplier变量

__block_impl 结构体的实现

struct __block_impl {
  void *isa;//isa指针,block是对象的标志
  int Flags;
  int Reserved;
  void *FuncPtr;//函数指针
};

关于__MCBlock__method_block_func_0

static int __MCBlock__method_block_func_0(struct __MCBlock__method_block_impl_0 *__cself, int num) {
  int multiplier = __cself->multiplier; // bound by copy
        return num * multiplier;
    }

2.block截获变量的特性

(1) block截获变量的特性需要分三种情况
1.局部变量

对于基本数据类型的局部变量截获其值,对于对象类型的局部 变量连同所有权修饰符一起截获。

2.局部静态变量

指针形式截获局部静态变量。

3.全局变量或者全局静态变量

不截获全局变量、静态全局变量。

(2)原码解析

Objective-C代码如下:

#import "MCBlock.h"

// 全局变量
int global_var = 4;
//静态全局变量
static int static_global_var = 5;

@implementation MCBlock

- (void)method
{
   //基本数据类型的局部变量
    int var = 1;
    //对象类型的局部变量
    __unsafe_unretained id unsafe_obj = nil;
    __strong id strong_obj = nil;
    
    //局部静态变量
    static int static_var = 3;
    void(^Block)(void) = ^{
        NSLog(@"局部变量<基本数据类型> var %d",var);
        NSLog(@"局部变量<unsafe_obj 对象类型> var %@",unsafe_obj);
        NSLog(@"局部变量<strong_obj 对象类型> var %@",strong_obj);
        NSLog(@"静态变量 %d",static_var);
        NSLog(@"全局变量 %d",global_var);
        NSLog(@"静态全局变量 %d",static_global_var);
    };
    Block(); 
}
@end

Objective-C编译后的主要代码如下:

int global_var = 4;
static int static_global_var = 5;
struct __MCBlock__method_block_impl_0 {
  struct __block_impl impl;
  struct __MCBlock__method_block_desc_0* Desc;
  // 截获局部变量的值
  int var;

 //  连同所有权修饰符一起截获
  __unsafe_unretained id unsafe_obj;
  __strong id strong_obj;
 //  以指针形式截获局部变量
  int *static_var;

 // 对全局变量、静态全局变量不截获

  __MCBlock__method_block_impl_0(void *fp, struct __MCBlock__method_block_desc_0 *desc, int _var, __unsafe_unretained id _unsafe_obj, __strong id _strong_obj, int *_static_var, int flags=0) : var(_var), unsafe_obj(_unsafe_obj), strong_obj(_strong_obj), static_var(_static_var) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

method对应的原码

static void _I_MCBlock_method(MCBlock * self, SEL _cmd) {
    int var = 1;
    __attribute__((objc_ownership(none))) id unsafe_obj = __null;
    __attribute__((objc_ownership(strong))) id strong_obj = __null;

    static int static_var = 3;

    void(*Block)(void) = ((void (*)())&__MCBlock__method_block_impl_0((void *)__MCBlock__method_block_func_0, &__MCBlock__method_block_desc_0_DATA, var, unsafe_obj, strong_obj, &static_var, 570425344));
    ((void (*)(__block_impl *))((__block_impl *)Block)->FuncPtr)((__block_impl *)Block);
}
(3)对应的题目
-(void)method{
    static int multiplier = 6;
    int (^Block)(int) = ^int (int num){
        return num * multiplier;
    };
    multiplier = 4;
    NSLog(@"result is %d",Block(2));
}
上述方法执行结果是 result is 8
-(void)method{
    int multiplier = 6;
    int (^Block)(int) = ^int (int num){
        return num * multiplier;
    };
    multiplier = 4;
    NSLog(@"result is %d",Block(2));
}
上述方法执行结果是 result is 12

3.__block修饰符

(1)使用说明及注意事项

使用说明:一般情况下,对被截获变量进行赋值操作需添加__block修饰符
对变量进行赋值需要分为两种情况:
1.基本数据类型和对象类型的局部变量需要__block修饰符;
2.静态局部变量、全局变量、静态全局变量不需要
注意:

截图.png

(1)使用误区

误区1(下面代码没有问题)

          NSMutableArray *array = [NSMutableArray array];
          void(^Block)(void) = ^{
             [array addObject:@123];
          };
          Block();
          NSLog(@"array=%@",array);
         以上代码是没有任何问题的

误区2

             NSMutableArray *array = nil;
              void(^Block)(void) = ^{
                array = [NSMutableArray array];
              };
            Block();
            NSLog(@"%@",array);
            编译会报错

误区2纠正写法

           __block    NSMutableArray *array = nil;
              void(^Block)(void) = ^{
                array = [NSMutableArray array];
              };
            Block();
            NSLog(@"%@",array);
(3)__block原理分析
    __block   int multiplier = 6;
    int (^Block)(int) = ^int (int num){
        return num * multiplier;
    };
    multiplier = 4;
    NSLog(@"result is %d",Block(2));
    上述方法执行结果是 result is 8

变量经过__block修饰后变成了对象,原码如下:

截图2.png

截图3.png

3.block的内存管理

(1)block分类

a._NSConcreteStackBlock 栈block
b._NSConcreteMallocBlock 堆block
c._NSConcreteGlobalBlock 全局block


截图4.png
(2)block的copy操作
截图5.png

截图7.png
(3)栈上block的销毁
截图6.png
(4)栈上__block变量的copy
截图8.png
(5)__forwarding总结

如果对__block没有进行copy操作,__forwarding操作的是栈上的__block变量,如果进行了copy操作,__forwarding操作的是堆上的__block变量。

 #import "ViewController.h"
 @interface ViewController ()

 @property (nonatomic,copy)int(^blk)(int);

 @end
 @implementation ViewController
 - (void)viewDidLoad {
     [super viewDidLoad];
   __block int multiplier = 10;
     _blk = ^int(int num){
        return num * multiplier;
     };
    multiplier = 6;
    [self executeBlock];
 }
 - (void)executeBlock{
    int result = _blk(4);
    NSLog(@"result is %d",result);
 }
打印结果: result is 24

5.block的循环引用

(1) 循环应用的例子
例子 a
     _array = [NSMutableArray arrayWithObject:@"block"];
     _stringBlock = ^NSString *(NSString *num){
         return [NSString stringWithFormat:@"helloc_%@",_array];
     };
    _stringBlock(@"hello");

截图9.png

解决方法:

    _array = [NSMutableArray arrayWithObject:@"block"];
    __weak NSArray *weakArray = _array;
    _stringBlock = ^NSString *(NSString *num){
        return [NSString stringWithFormat:@"helloc_%@",weakArray[0]];
    };
     _stringBlock(@"hello");
截图10.png
例子 b
    __block MCBlock *blockSelf = self;
    _blk = ^int(int num) {
        int result = num * blockSelf.var;
        return result;
    };
    _blk(3);

截图12.png

解决方法

    __block MCBlock *blockSelf = self;
    _blk = ^int(int num) {
        int result = num * blockSelf.var;
        blockSelf = nil
        return result;
    };
    _blk(3);
截图13.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • 1、通过CocoaPods安装项目名称项目信息 AFNetworking网络请求组件 FMDB本地数据库组件 SD...
    阳明先生x阅读 15,967评论 3 119
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,082评论 1 32
  • 在知乎上,学了“接地气的陈老师”写的《如何着手分析一个行业》,学习的心得是对于市场分析,有了一个大致的概念,但是具...
    丁叶_Seven阅读 369评论 0 1
  • 周末回家了,爷爷生日,给他买了件秋衣。 昨天给奶奶说起结婚的事,她就挺上心的给我算了算日子,虽然丛丛对日子不太满意...
    小么么哒阅读 255评论 2 1
  • 往事如烟,那么我们是生活在烟里还是烟外?历史是由无数的人、无数的往事构成,这丝丝缕缕的往事之烟汇聚在一起已经是烟雾...
    青梅醉雪阅读 273评论 3 8