iOS进阶教程1-Block

版权声明:Coder Bruce https://blog.csdn.net/bruceyou1990/article/details/80164617

1 为什么加了__block就能在内部修改变量

__block前缀,把a变量的地址传递过去了 [参考文章]

我们看下实际发生了什么

我们声明一个block

实质是地址的copy

2 block存放在哪里

block存放在程序代码区,内存管理则分三种[
根据isa指针,block一共有3种类型的block
_NSConcreteGlobalBlock 全局静态 如果你block没有调用外部变量 : 比如你仅仅在block里面写一个打印语句
_NSConcreteStackBlock 保存在栈中,出函数作用域就销毁 : [声明时没有用copy修饰又引用外部变量]
_NSConcreteMallocBlock 保存在堆中,retainCount == 0销毁:[copy修饰] ]

程序分为 栈区、堆区、全局区、文字常量区、程序代码区

1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其
操作方式类似于数据结构中的栈。
2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回
收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的
全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另
一块区域。 - 程序结束后由系统释放。
4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放
5、程序代码区—存放函数体的二进制代码。

3 block为什么用copy修饰

我们将代码声明位copy,是因为block一开始是在栈上,我们要copy到堆上.

4 block不是线程安全

我们在调用block的时候,有没有另一个线程去造成block,如果你确定不会发生这种情况的话,属性选择nonatomic
不确定选择 atomic ,原子性不能保证线程安全,atomic只能保证本身原子性,不能保证线程安全
正确的书写方式:
ARC

MyBlock block =self.block
if(block){
block(123)
}

MRC

Myblock block =[self.block retatin];
if(block ){
block(123);
}
[block release]

而不是

if(self.block){
self.block(123);
//走到这里,可能已经被置空crash,因为不知道什么时候会被操作.
}

5 循环引用

循环应用是相互持有造成的
对一个block属性,在bloc里面引用别的属性,造成block持有self

[self.teacher requestData:^(NSData *data) {
    self.name = @"case";
}];

self -> teacher -> block -> self 造成循环引用

解决方法1 __weak typeof(self) weakSelf = self;

__weak typeof(self) weakSelf = self;
    [self.teacher requestData:^(NSData *data) {
        typeof(weakSelf) strongSelf = weakSelf;
       strongSelf.name = @"case";
    }];
  self -> teacher -> block -> weakSelf 弱引用不会造成循环

一般会在block回调里再强引用一下weakSelf(typeof(weakSelf) strongSelf = weakSelf;),因为__weak修饰的都是存在栈内,可能随时会被系统释放,造成后面调用weakSelf时weakSelf可能已经是nil了,后面用weakSelf调用任何代码都是无效的。strongSelf可以保证线程内都被持有不释放.线程外[block外]用完可以置为nil

//情况一

- (void)case1 {
    NSLog(@"case 1 Click");
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        self.name = @"case 1";
    });
}

//情况二

- (void)case2 {
    NSLog(@"case 2 Click");
    __weak typeof(self) weakSelf = self;
    [self.teacher requestData:^(NSData *data) {
        typeof(weakSelf) strongSelf = weakSelf;
       strongSelf.name = @"case 2";
    }];
}

//情况三

- (void)case3 {
    NSLog(@"case 3 Click");
    [self.teacher requestData:^(NSData *data) {
        self.name = @"case 3";
    }];
}

//情况四

- (void)case4 {
    NSLog(@"case 4 Click");
    [self.teacher requestData:^(NSData *data) {
        self.name = @"case 4";
        self.teacher = nil;
    }];
}

//情况五

- (void)case5 {
    NSLog(@"case 5 Click");
    Teacher *t = [[Teacher alloc] init];
    [t requestData:^(NSData *data) {
        self.name = @"case 5";
    }];
}

//情况六

- (void)case6 {
    NSLog(@"case 6 Click");
    [self.teacher callCase6BlackEvent];
    self.teacher.case6Block = ^(NSData *data) {
        self.name = @"case 6";
        //下面两句代码任选其一
        self.teacher = nil;
//        self.teacher.case6Block = nil;
    };
}

情况一:执行了dealloc,不泄露,此情况虽然是block,但未形成保留环block -> self
情况二:执行了dealloc,不泄露,此情况就是内存泄漏后的一般处理了 self ->teacher ->block ->strongSelf,后面那个strongSelf和原来的self并没有直接关系,因为strongSelf是通过weakSelf得来的,而weakSelf又没有强引用原来的self
情况三:未执行dealloc,内存泄漏,此情况就是最典型的循环引用了,形成保留环无法释放,self ->teacher ->block ->self
情况四:执行了dealloc,不泄露,虽然也是保留环,但通过最后一句,使self不再强引用teacher,打破了保留环
情况五:执行了dealloc,不泄露,未形成保留环 t ->block ->self
情况六:执行了dealloc,不泄露,最后两句代码任选其一即可防止内存泄漏,self.teacher 或者 case6Block 置为空都可以打破 retain cycle

PS: 虽然情况四、情况六的写法都可以防止内存泄漏,不过为了统一,个人建议最好还是按照普通写法即情况二的写法
参考demo [https://github.com/yuedong56/BlockRetainCycleDemo]

5.2 循环引用 -为什么一下情况不需要考虑循环引用

**GCD
self 没有强引用GCD** block 会在结束时释放引用对象
UIView 的animation
self 没有强引用 UIview 的animation
以上都是需要没有在当前控制器调用,自然不会存在强引用.
ANF
ANF调用不是在当前控制器

猜你喜欢

转载自blog.csdn.net/bruceyou1990/article/details/80164617
今日推荐