atomic属性你真的会用了吗?

背景

最近遇到线上一个偶现的崩溃,简化一下问题的模型就是:

@protocol SceneDelegate <NSObject>

- (nullable NSData *)onSceneRequest;

@end

@interface MyScene : NSObject<SceneDelegate>

@end

@implementation MyScene

- (nullable NSData *)onSceneRequest {
    return [NSData new];
}

@end

@interface ViewController ()

@property(nonatomic, strong) id<SceneDelegate> scene;

@end

@implementation ViewController

- (void)setScene:(id<SceneDelegate>scene {
    self.scene = scene;
}

- (void)onRequest {
    if (self.scene) {
        NSData *data = [self.scene onSceneRequest];
        NSLog(@"%@", data);
    }
}

@end

崩溃的点在[self.scene onSceneRequest];
崩溃的类型是BAD_ACCESS(SIGBUS);

定位的过程

一开始并没有什么头绪,开始在网上扒SIGBUS崩溃的相关资料,找到了Apple的文章 Investigating Memory Access Crashes

其中有一段:

Consult the crashed thread’s backtrace for clues on where the memory access issue is occurring. Some types of memory access issues, such as dereferencing a NULL pointer, are easy to identify when looking at the backtrace and comparing it to the source code. Other memory access issues are identified by the stack frame at the top of the crashed thread’s backtrace:

  • If objc_msgSend, objc_retain, or objc_release is at the top of the backtrace, the crash is due to a zombie object. See Investigating Crashes for Zombie Objects.

对比一下崩溃的堆栈,这不正好就是objc_msgSend了么?
Apple如此笃定的认为,这就是一个僵死对象引起的?
然后,看代码并没有看出来哪里有什么僵死对象啊。搜遍代码就只有一个setScene修改对象的状态,即使是设置了nil,也不应该是僵死对象啊。
把焦点就围绕在setSceneonSceneRequest,代码深挖后,发现存在两个不同的线程分别调用这两个函数。似乎问题点就在这里了,Objective-C和Java不同,对象等号=赋值并不是一个原子操作。
如何验证这个猜测是否正确的呢?只需要弄多几个线程,同时执行者两个方法,密集的模拟一下了。
说干就干:

- (void)viewDidLoad {
    [super viewDidLoad];
    for (int i = 0; i < 2; i++) {
        [NSThread detachNewThreadWithBlock:^{
            while(true) {
                [self setScene:[MyScene new]];
                [NSThread sleepForTimeInterval:0.3];
            }
        }];
    }
    for (int i = 0; i < 10; i++) {
        [NSThread detachNewThreadWithBlock:^{
            while(true) {
                [self onRequest];
                [NSThread sleepForTimeInterval:0.5+(CGFloat)i/10.0];
            }
        }];
    }
}

创建两个线程,每隔0.3s就调用setScene,创建10个不同的线程,以不同的时间间隔不断的调用onRequest
好样的,跑起来,不到1分钟,就出现一个崩溃了。

好,就是这个原因了,修改方法就是atomic,对,就那么简单:)

@property(atomic, strong) id<SceneDelegate> scene;

背后的原因

为何对属性赋值的操作不是原子性的呢?

Objective-C的运行时的各个方法都是没有上锁的,大致理了一下多线程下是如何导致崩溃的:


image.png

所以,属性的访问修饰里才会有nonatomic和atomic的选择。
很多时候,我们都只是默默的写nonatomic,而没有思考什么时候才需要atomic。
注意:atomic并不能解决多线程的竞争,它只能解决这种指针错误的崩溃。
关于nonatomic和atomic的区别,可以参考这个文章
也可以阅读Apple的文章
SO上也有一些精彩的讨论

注意:上述的文章都有讲述到属性的原子性只是保证访问属性对象的时候是线程安全的,并没有说访问属性对象内部数据是线程安全的。
如果存在多线程访问和修改属性内部的情况,需要做额外的线程安全措施。

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

推荐阅读更多精彩内容