iOS读写安全

所有Demo 都在文章末尾提供下载

锁的用法可以参照上篇文章 Objective-C 加锁:看我就够了!

1、atomic & noatomic

给属性添加atomic 可以保证属性的setter和getter原子性操作,也就是保证setter和getter内部是线程同步的

  • 源码 objc4 objc-accessors.mm文件
//setter方法
void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy)
{
    bool copy = (shouldCopy && shouldCopy != MUTABLE_COPY);
    bool mutableCopy = (shouldCopy == MUTABLE_COPY);
    reallySetProperty(self, _cmd, newValue, offset, atomic, copy, mutableCopy);
}

static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
{
...
    if (!atomic) { // 如果非原子性 直接赋值
        oldValue = *slot;
        *slot = newValue;
    } else { //如果是原子性,加自旋锁
        spinlock_t& slotlock = PropertyLocks[slot];
        slotlock.lock();
        oldValue = *slot;
        *slot = newValue;
        slotlock.unlock();
    }
...
}
// getter方法
  id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
   ...
    if (!atomic) return *slot; //如果非原子性 直接返回
        
    // Atomic retain release world
    spinlock_t& slotlock = PropertyLocks[slot]; //如果原子性 则加自旋锁
    slotlock.lock();
    id value = objc_retain(*slot);
    slotlock.unlock();
    ...
    // for performance, we (safely) issue the autorelease OUTSIDE of the spinlock.
    return objc_autoreleaseReturnValue(value);
}
复制代码
  • 从源码看 atomic是自旋锁,且只能保证在Setter和getter的时候安全,例如[NSMutableArray addobject]的时候 是不安全的

2、读写锁

为什么会有读写锁,用其他的的锁来实现不可以么?

普通锁可以达到同一时间段只有一个线程对本地文件进行读或者写。效率低。无法保证read的时候可以多线程read,而只是在write的时候 只有一个线程在write。

所以 pthread_rwlock解决这个问题。可以达到 当线程write的时候,只有一个线程写入。read的时候可以多线程read。

  • API:
#import <pthread.h>
pthread_rwlock_init(&_lock, NULL);
pthread_rwlock_rdlock(&_lock); //read的时候用的锁
pthread_rwlock_wrlock(&_lock);// write的时候用的锁
pthread_rwlock_unlock(&_lock);
pthread_rwlock_tryrdlock(pthread_rwlock_t *) //尝试read锁
pthread_rwlock_trywrlock(pthread_rwlock_t *) //尝试wirte锁
复制代码
  • 举个?
#import <pthread.h>

- (void)viewDidLoad {
    [super viewDidLoad];
    pthread_rwlock_init(&_lock, NULL);
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    dispatch_queue_t queue = dispatch_queue_create("rwqueue", DISPATCH_QUEUE_CONCURRENT);
    for (int i = 0; i < 10; i ++) {
        dispatch_async(queue, ^{
            for (int i = 0; i < 10; i ++) {
                [self read];
            }
        });
        
        dispatch_async(queue, ^{
            for (int i = 0; i < 10; i ++) {
                [self read];
            }
        });
        
        dispatch_async(queue, ^{
            [self write];
        });
    }
}

- (void) read {
    pthread_rwlock_rdlock(&_lock);
    sleep(1);
    NSLog(@"read");
    pthread_rwlock_unlock(&_lock);
}

- (void) write {
    pthread_rwlock_wrlock(&_lock);
    sleep(1);
    NSLog(@"write");
    pthread_rwlock_unlock(&_lock);
}
复制代码
  • 运行结果及分析:

3、dispatch_barrier_async

举个?

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    _queue = dispatch_queue_create("rwqueue", DISPATCH_QUEUE_CONCURRENT);
    
    for (int i = 0; i < 10; i ++) {
        [self read];
        [self write];
        [self write];
        [self read];
    }
    
}

- (void) read {
    dispatch_async(_queue, ^{
        sleep(1);
        NSLog(@"read");
    });
}

- (void) write {
    dispatch_barrier_async(_queue, ^{
        sleep(1);
        NSLog(@"write");
    });
}
复制代码
  • 运行结果分析
  • 注意点 必须传入dispatch_queue_create的并发队列 不能传入dispatch_get_global_queue队列 如果传入串行或者是global 就相当于dispatch_async ,没有栅栏(barrier)效果
  • 原理

本文分享自微信公众号 - 老沙课堂(gh_f73a6b772d4f),作者:沙睿

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-09-04

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 据结构与算法(八) 二叉树的练习

    •设定levelSize初始值为1(只有一个根节点)•当进行while循环的时候 levelsize-- 操作。因为levelSize和每层节点个数相等。所以当...

    老沙
  • 如何利用CocoaPods创建私有库

    老沙
  • 数据结构与算法(五) 队列

    •先进先出原则(First In First Out) FIFO•队尾(rear):只能进行入队操作(enQueue)->添加元素•队头(front):只能进行...

    老沙
  • Python 三种遍历目录的方法,轻松帮你找出隐藏文件

    无论在 Windows 系统中还是 Linux 系统中,都存在着隐藏文件以及隐藏文件夹。隐藏文件夹一般是系统关键性目录,例如 Windows 系统中的 C 盘中...

    猴哥yuri
  • GitHub实战系列~3.提交github的时候过滤某些文件 2015-12-10

    创建项目的时候在最下面,添加过滤器,选择vs 或者把 .gitignore 拷贝一份放git项目文件夹的根目录 ? 建完就有两个文件了,.gitignore 是...

    逸鹏
  • vue实战-实现换主题/皮肤功能

    接下来就是具体实现换皮肤功能了,换皮肤一般都是点击一个按钮弹出一些皮肤的选项,选中选项后皮肤生效。 我们将换皮肤功能抽成一个组件theme-switch。pc...

    我的小熊不见了丶
  • 数据库|Flask实现简单搜索功能

    用Flask实现简单搜索功能主要是通过form的方式传值,再到数据库中查询。下面是数据库的内容,主要是实现对content进行模糊匹配。

    算法与编程之美
  • 小结ES6基本知识点(三):类与继承

    ES6初学者,通过阅读本文可对ES6知识点有个大体的概念,强烈建议阅读阮一峰大大的ES6入门。

    前端林子
  • 如何设置Element表格显示或者隐藏列

    tianyawhl
  • 图像配准

    图像配准(Image registration)是将同一场景拍摄的不同图像进行对齐的技术,即找到图像之间的点对点映射关系,或者对某种感兴趣的特征建立关联。以同...

    瓜大三哥

扫码关注云+社区

领取腾讯云代金券