nonatomic 带来的线程安全问题

一、结论

一个对象对外暴露的读写属性,如果这个属性在多个线程中访问,可能会出现crash。因此对外暴露的属性一定要考虑线程安全问题。

二、看下面的代码

#import "ViewController.h"
#import <malloc/malloc.h>
#import <objc/runtime.h>
@interface ViewController ()
@property (nonatomic,copy) NSString *name;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    dispatch_queue_t queue = dispatch_queue_create("abcde", DISPATCH_QUEUE_CONCURRENT);
    for (int i = 0 ; i < 10000; i ++) {
        dispatch_async(queue, ^{
            NSLog(@"%@",[NSThread currentThread]);
            self.name = [NSString stringWithFormat:@"我%d",i];
        });
    }

}


@end

 

分析:

属性的set方法,ARC环境下,如果有两个线程同时给属性设置值时,就会同时把属性给release释放两次。过渡释放造成崩溃

属性是atomic时,不会崩溃,里面加了同步锁,set,get方法只能串行执行。

三、现象

产生的现象是一个线程访问这个对象的时候,这个对象已经释放,可能是错误的内存、野指针等情况。

 

其他解决方式:

其一:改为串行队列

    dispatch_queue_t queue = dispatch_queue_create("abcde", DISPATCH_QUEUE_SERIAL);
    for (int i = 0 ; i < 10000; i ++) {
        dispatch_async(queue, ^{
            NSLog(@"%@",[NSThread currentThread]);
            self.name = [NSString stringWithFormat:@"我%d",i];
        });
    }

 

其二:改为同步执行

    dispatch_queue_t queue = dispatch_queue_create("abcde", DISPATCH_QUEUE_CONCURRENT);
    for (int i = 0 ; i < 10000; i ++) {
        dispatch_sync(queue, ^{
            NSLog(@"%@",[NSThread currentThread]);
            self.name = [NSString stringWithFormat:@"我%d",i];
        });
    }

 

其三:加锁

    NSLock *lock = [[NSLock alloc] init];
    dispatch_queue_t queue = dispatch_queue_create("abcde", DISPATCH_QUEUE_CONCURRENT);
    for (int i = 0 ; i < 10000; i ++) {
        [lock lock];
        dispatch_async(queue, ^{
            NSLog(@"%@",[NSThread currentThread]);
            self.name = [NSString stringWithFormat:@"我%d",i];
            [lock unlock];
        });
    }

 

其四:将字符串改为短的,变为标记指针 NSTaggedPointerString 类型,retain/release不起作用。如果字符串长度大于9或者如果有中文或其他特殊符号(可能是非 ASCII 字符)存在的话则会直接成为 __NSCFString 类型,__NSCFString与其他oc对象一样维护retainCount

    dispatch_queue_t queue = dispatch_queue_create("abcde", DISPATCH_QUEUE_CONCURRENT);
    for (int i = 0 ; i < 10000; i ++) {
        dispatch_async(queue, ^{
            NSLog(@"%@",[NSThread currentThread]);
            self.name = [NSString stringWithFormat:@"%d",i];
        });
    }

 

其五:将字符串改为 __NSCFConstantString 类型,retain/release不起作用(程序中内容相同的常量字符串只有一个)

    dispatch_queue_t queue = dispatch_queue_create("abcde", DISPATCH_QUEUE_CONCURRENT);
    for (int i = 0 ; i < 10000; i ++) {
        dispatch_async(queue, ^{
            self.name = @"哈哈哈哈哈哈哈哈哈哈哈哈";
        });
    }

 

posted @ 2020-11-05 17:34  黄增松  阅读(206)  评论(0编辑  收藏  举报