知识点总结iOS

iOS Block

2019-04-17  本文已影响1人  飞不越疯人院

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分为三种;
● _NSConcretStackBlock 栈区
● _NSConcretMallocBlock 堆区
● _NSConcretGlobalBlock 全局

Blockcopy操作
Block类别 copy结果
_NSConcretStackBlock 栈区 堆区
_NSConcretMallocBlock 堆区 增加引用计数
_NSConcretGlobalBlock 数据区 什么都不做

知识点:在MRC下如果对栈上的Block进行copy操作:会造成内存泄露;(因为copy操作会将Block在堆上复制一个副本,类似于alloc一个对象没有调用release一样;)


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)

上一篇下一篇

猜你喜欢

热点阅读