博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Block内部访问实例变量会出现的问题
阅读量:6135 次
发布时间:2019-06-21

本文共 2556 字,大约阅读时间需要 8 分钟。

最近开发中正好遇到了一个问题: 首先这是一个会引起循环引用的 Block 属性, 然后需要在 Block 中访问实例变量。

ViewController

#import "ViewController.h"#import "TestView.h"@interface ViewController (){    int _a;}@end@implementation ViewController- (void)viewDidLoad {    [super viewDidLoad];    NSLog(@"self指向:%p - self本身:%p", self, &self);    __weak __typeof(self) weakSelf = self;    NSLog(@"weakself指向:%p - weakSelf本身:%p", weakSelf, &weakSelf);    TestView *test = [[TestView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];    test.backgroundColor = [UIColor cyanColor];    test.handler = ^(NSString *str) {        __strong __typeof(weakSelf) strongSelf = weakSelf;        NSLog(@"Block内部,weakSelf指向:%p - weakSelf本身:%p", weakSelf, &weakSelf);        NSLog(@"strongSelf指向:%p - strongSelf本身:%p", strongSelf, &strongSelf);        NSLog(@"%d", strongSelf->_a);    };    [self.view addSubview:test];    //想办法让ViewController被释放掉    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{        [UIApplication sharedApplication].keyWindow.rootViewController = [[UIViewController alloc] init];    });}- (void)dealloc {    NSLog(@"释放啦~");}复制代码

TestView

@interface TestView : UIView@property (nonatomic, copy) void(^handler)(NSString *str);@end@implementation TestView- (instancetype)initWithFrame:(CGRect)frame {    if (self = [super initWithFrame:frame]) {        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(7.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{            if (self.handler) {                self.handler(@"hahaha");            }        });    }    return self;}- (void)dealloc {    NSLog(@"testView - dealloc");}复制代码

这么做的流程就是,5秒之后,切换 keyWindow 的 RootVC ,此时 ViewController 被释放。然后再过两秒,调用 Block 。由于有 GCD ,所以 testView 会被延迟释放的。Block 执行完毕之后,testView 会被释放。当然,此时访问了 _a ,造成野指针访问,直接 Crash .

输出结果:

self指向:0x7fc8bb509f10 - self本身:0x7fff5a684bd8weakself指向:0x7fc8bb509f10 - weakSelf本身:0x7fff5a684bb8释放啦~Block内部,weakSelf指向:0x0 - weakSelf本身:0x60800005a840strongSelf指向:0x0 - strongSelf本身:0x7fff5a686070(lldb) //到这里已经Crash复制代码

可以看到,它们指向的地址是一样的,也就是当前 ViewController 。 另一方面,我们也验证了一个结论:

Block 会对内部访问的变量进行 copy 操作。在外面的 weakSelf 和

Block 内部的 weakSelf 不是同一个。看地址,外面的 weakSelf 是在栈区,而 Block 内部的 weakSelf 被拷贝到了堆区。

重点在这里:

此时,假若 ViewController 被释放了,然后这个 Block 执行了,此时 strongSelf 是空指针,指向 0x0 ,那么这个时候,你再用 “->” 运算符去取 _a ,肯定是取不到的,就会造成野指针错误。

PS:

“->” 叫做指向结构体成员运算符,用处是使用一个指向结构体或对象的指针访问其内成员。 我认为:self->_a 的含义就是,从 self 的首地址,偏移到 _a 。从而可以访问到 _a ,这与魔法数有些类似。 (理解有误的话欢迎指正~)

综上所述,在会引起 Retain cycle 的 Block 内部需要访问实例变量的时候,建议改写为属性。因为属性的 getter 方法是消息机制,向nil发送消息是不会有问题的。

转载地址:http://zoeua.baihongyu.com/

你可能感兴趣的文章
自己制作交叉编译工具链
查看>>
Qt Style Sheet实践(四):行文本编辑框QLineEdit及自动补全
查看>>
[物理学与PDEs]第3章习题1 只有一个非零分量的磁场
查看>>
深入浅出NodeJS——数据通信,NET模块运行机制
查看>>
onInterceptTouchEvent和onTouchEvent调用时序
查看>>
android防止内存溢出浅析
查看>>
4.3.3版本之引擎bug
查看>>
SQL Server表分区详解
查看>>
使用FMDB最新v2.3版本教程
查看>>
SSIS从理论到实战,再到应用(3)----SSIS包的变量,约束,常用容器
查看>>
STM32启动过程--启动文件--分析
查看>>
垂死挣扎还是涅槃重生 -- Delphi XE5 公布会归来感想
查看>>
淘宝的几个架构图
查看>>
Android扩展 - 拍照篇(Camera)
查看>>
JAVA数组的定义及用法
查看>>
充分利用HTML标签元素 – 简单的xtyle前端框架
查看>>
设计模式(十一):FACADE外观模式 -- 结构型模式
查看>>
iOS xcodebuile 自动编译打包ipa
查看>>
程序员眼中的 SQL Server-执行计划教会我如何创建索引?
查看>>
cmake总结
查看>>