一道网易面试题

一、题目描述

  题目来自网上一个博客,具体类似如下

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@interface ViewController ()
 
@property (nonatomic, strong) NSString *target;
 
@end
 
@implementation ViewController
 
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
 
    dispatch_queue_t queue = dispatch_queue_create("parallel", DISPATCH_QUEUE_CONCURRENT);
    for (int i = 0; i < 1000000000 ; i++) {
        dispatch_async(queue, ^{
            self.target = [NSString stringWithFormat:@"ksddkjalkd2018-11-09 12:04:09.750846+0800 ARCTest2[525:168910] 1111sdsdsjd%d",i];
            NSLog(@"%@", self.target);
        });
    }
}

  问代码执行之后会发生什么?

二、解析

  在设置target的setter中,是非线程安全的,未加锁;因此多线程访问这个属性setter方法的时候潜在crash的情况

  因为setter大概如下

1
2
3
4
5
6
7
8
- (void)setTarget:(NSString *)target
{
    if(_target != target)
    {
        [_target release];
        _target = [target retain];
    }
}

  对应runtime代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//objc_class.mm
void object_setIvar(id obj, Ivar ivar, id value)
{
    return _object_setIvar(obj, ivar, value, false /*not strong default*/);
}
 
 
static ALWAYS_INLINE
void _object_setIvar(id obj, Ivar ivar, id value, bool assumeStrong)
{
    //判断是否是TaggedPointer
    if (!obj  ||  !ivar  ||  obj->isTaggedPointer()) return;
 
    ptrdiff_t offset;
    objc_ivar_memory_management_t memoryManagement;
    //找对应的内存管理语义和属性偏移值
    _class_lookUpIvar(obj->ISA(), ivar, offset, memoryManagement);
 
    //如果找不到默认是否为Strong,不然为unsafe_unretained
    if (memoryManagement == objc_ivar_memoryUnknown) {
        if (assumeStrong) memoryManagement = objc_ivar_memoryStrong;
        else memoryManagement = objc_ivar_memoryUnretained;
    }
 
    //根据偏移值找到属性对应位置
    id *location = (id *)((char *)obj + offset);
     
    //判断不同的内存管理语义,调用方法
    switch (memoryManagement) {
    case objc_ivar_memoryWeak:       objc_storeWeak(location, value); break;
    case objc_ivar_memoryStrong:     objc_storeStrong(location, value); break;
    case objc_ivar_memoryUnretained: *location = value; break;
    case objc_ivar_memoryUnknown:    _objc_fatal("impossible");
    }
}

  在release的方法最后会调用obj_release

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//NSObject.mm
void
objc_storeStrong(id *location, id obj)
{  
    //如果新值指针和旧值一样,则不更新,直接return
    id prev = *location;
    if (obj == prev) {
        return;
    }
    //先对新值retain
    objc_retain(obj);
    //再赋值
    *location = obj;
    //最后对旧值release
    objc_release(prev);
}

  因为一个对象已经release了,但是这个指针指向的内存已经被回收,所以访问这个指针的内存会产生一个内存访问的错误

1
2018-11-09 15:22:04.860819+0800 ARCTest2[93017:2107037] *** -[CFString release]: message sent to deallocated instance 0x600000e70240

以上的代码用模拟器是比较容易出现的,因为GCD创建了64个线程,线程并发次数很多

如果使用iPhoneX的话,没有出现,(应该是比较难重现),但是存在crash的可能

可以看到GCD创建了6个线程,是6核的1倍

  

如果将target的修饰改为atomic,将不会crash,但是直接访问实例变量依旧会产生crash。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@interface ViewController ()
 
@property (atomic, strong) NSString *target;
 
@end
 
@implementation ViewController
 
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
 
    dispatch_queue_t queue = dispatch_queue_create("parallel", DISPATCH_QUEUE_CONCURRENT);
    for (int i = 0; i < 1000000000 ; i++) {
        dispatch_async(queue, ^{
            _target = [NSString stringWithFormat:@"ksddkjalkd2018-11-09 12:04:09.750846+0800 ARCTest2[525:168910] 1111sdsdsjd%d",i];
            NSLog(@"%@", self.target);
        });
    }
}

 

三、总结

  通过上面的例子,我们可以总结出来,对于一个变量,如果多线程访问之下,retain、release的顺序得不到保证的话,就会带来野指针的问题

  ARC只能保证在合适的地方插入retain、release;但是retain、release的顺序还需要业务来进行保证。

posted @   兜兜有糖的博客  阅读(499)  评论(0编辑  收藏  举报
编辑推荐:
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
点击右上角即可分享
微信分享提示