iOS Block总结

主要讲解 Block 的基础知识

Block部分一
Block部分二
Block部分三
Block知识点总结

1. 什么是Block?

Block是一个对象, 对象中封装了一个函数以及函数执行的上下文;Block的调用本质是函数的调用;


2. Block截获变量

请问以下代码执行的结果?

///局部变量:  基本数据类型
- (void)block1 {
    NSInteger count = 10;
    NSInteger (^Block)(NSInteger) = ^NSInteger(NSInteger num){
        return count * num;
    };
    count = 50;
    NSLog(@"局部变量:  基本数据类型  结果是: %ld", Block(2));
#局部变量:  基本数据类型  结果是: 20    
    __block NSInteger count1 = 10;
    NSInteger (^Block1)(NSInteger) = ^NSInteger(NSInteger num){
        return count1 * num;
    };
    count1 = 50;
    NSLog(@"用__block修饰的局部变量: 基本数据类型  结果是: %ld", Block1(2));
#用__block修饰的局部变量: 基本数据类型  结果是: 100
}
///静态局部变量
- (void)block3 {
    static NSInteger count = 9;
    NSInteger (^Block)(NSInteger) = ^NSInteger(NSInteger num){
        return count * num;
    };
    count = 10;
    NSLog(@"静态局部变量   结果是: %ld", Block(2));
}
#静态局部变量   结果是: 20
///全局变量
- (void)block4 {
    //NSInteger globalCount = 11;
    NSInteger (^Block)(NSInteger) = ^NSInteger(NSInteger num){
        return globalCount * num;
    };
    globalCount = 10;
    NSLog(@"全局变量   结果是: %ld", Block(2));
}
#全局变量   结果是: 20
///全局静态变量
- (void)block5 {
//    static const NSInteger staticGlobalCount = 22;
    NSInteger (^Block)(NSInteger) = ^NSInteger(NSInteger num){
        return staticGlobalCount * num;
    };
    staticGlobalCount = 33;
    NSLog(@"全局静态变量   结果是: %ld", Block(2));
}
#全局静态变量   结果是: 66
///局部变量: 对象
- (void)block2 {
    __block  Man *man = [[Man alloc] init];
    NSString* (^Block)(NSString*) = ^NSString* (NSString *name){
        man.name = name;
        return man.name;
    };
    NSLog(@"局部变量: 对象  结果是: %@", Block(@"Zhangsan"));
}

什么情况下要使用__block修饰符?
一般情况下要对被截获变量进行赋值(不是使用)操作时需要添加__block修饰符;

  1. 无论是对象还是基本数据类型如果是局部变量, 在block中修改,都需要用__block修饰;
  2. 静态局部变量, 静态全局变量, 全局变量不需要__block修饰就能在block更改;
///block中对对象的使用
- (void)testBlcok1 {
    NSMutableArray *arr = [NSMutableArray arrayWithCapacity:0];
    void(^Block)(void) = ^void(void){
        ///这里是对arr的使用, 而不是赋值, 所以不用__block修饰;
        [arr addObject:@"1"];
    };
    Block();
    NSLog(@"block中对对象的使用   数组中内容为:  %@", arr);
}
#block中对对象的使用   数组中内容为:  (1)

///block中对对象的赋值
NSMutableArray *arr1 = nil;
void(^Block1)(void) = ^void(void){
    ///这里是对arr的赋值, 要用__block修饰, 不然会报错;
     arr = [NSMutableArray arrayWithCapacity:0];
};
Block1();
结论:
  • 对于基本数据类型的局部变量时截获它的值;
  • 对于对象的局部变量或者成员变量连同所有权修饰符一起截获;
  • 指针形式截获局部静态变量;
  • 不截获全局变量和静态全局变量;

3. Block内存管理

Block分为三种;

  • NSStack`Block 栈区
  • NSMallocBlock 堆区
  • NSGlobalBlock 全局
Blockcopy操作
Block类别 copy结果
_NSStackBlock 栈区 堆区
_NSMallocBlock 堆区 增加引用计数
_NSGlobalBlock 数据区 什么都不做

4. Block的循环引用

4.1 自循环
///循环引用---自循环
- (void)circleReferenceSelf {
    _array = [NSMutableArray arrayWithCapacity:0];
    [_array addObject:@"Test"];
    _strBlock = ^NSString*(NSString *str) {
        NSString *resultStr = [NSString stringWithFormat:@"result is : %@", _array.firstObject];
        return resultStr;
    };
    NSLog(@"%@", _strBlock(@"HongKong"));
}

///不会产生自循环
- (void)notCircleReferenceSelf {
    _array = [NSMutableArray arrayWithCapacity:0];
    [_array addObject:@"Test"];
    __weak NSMutableArray *tempArr = _array;
    _strBlock = ^NSString*(NSString *str) {
        NSString *resultStr = [NSString stringWithFormat:@"result is : %@", tempArr.firstObject];
        return resultStr;
    };
    NSLog(@"%@", _strBlock(@"HongKong"));
}
#解除方案:使用__weak修饰临时变量指向_array; 原理是在下面的第二步中将stong(强引用)变为了weak(弱引用);

为什么会造成自循环?

  1. 当前类copy修饰_strBlock, 所以对其有强引用;
  2. 上面有提到对于对象的局部变量或者成员变量是连同所有权修饰符一起截获;因为_array使用strong修饰, 所以_strBlock中有一个strong类型的指针指向当前类;
  3. 造成自循环;
4.2 多循环
///循环引用----多循环引用(类似)
- (void)bigCircleReference {
    __block ViewController *weakSelf = self;
    _bigMan = [[Man alloc] init];
    _bigMan.intBlock = ^NSInteger(NSInteger num) {
        return weakSelf.index * num;
    };
    NSLog(@"会多循环引用:  %ld", _bigMan.intBlock(100));
}
#注意: 这段代码在MRC下不会有任何问题;  但是ARC下会循环引用;

//.不会产生多环引用
- (void)notBigCircleReference {
    __block ViewController *weakSelf = self;
    _bigMan = [[Man alloc] init];
    _bigMan.intBlock = ^NSInteger(NSInteger num) {
        NSInteger temp = weakSelf.index * num;
        weakSelf = nil;
        return temp;
    };
    NSLog(@"不会多循环引用:  %ld", _bigMan.intBlock(100));
}
#方案:通过断环方式或者开发中避免;

为什么会循环引用?

  1. 当前类用strong 修饰 _bigMan, 所以对其有强引用;
  2. Man类中用copy修饰_intBlock, 所以对其有强引用;
  3. 上篇文章中我们知道ARC下, __block修饰的对象会被强引用; 所以在_intBlock内部会有对当前类强引用;
  4. 造成多环循环引用;

文中示例代码


参考文章
iOS block-变量的捕获(capture)

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