YYMemoryCache内存缓存

YYMemoryCache是用来存储占用内存小 速度较快的高速内存缓存, 存储key-value形式的键值对.

区别于NSDictionary, YYMemoryCache对key是retain而不是copy操做(YYMemoryCache的key能够是任何对象的缘由, 内部是使用CFMutableDictionaryRef实现的), 提供的API和NSCache相似, 而且YYMemoryCache的全部方法都是安全的.node

YYMemoryCache相比NSCache的特色:算法

  1. 使用LRU(least-recently-used)淘汰算法(NSCache是清除全部的缓存信息)
  2. 使用cost count age三个维度清除缓存(NSCache值提供了cost count两个维度)
  3. 自动清除缓存(当收到内存警告时 会在后台清除缓存)

YYMemoryCache中使用一个双向链表和字典进行增删改查操做的:数组

  1. 当须要增长新的缓存时, 会生成一个结点并设置为头结点
  2. 当须要获取一个缓存时, 从YYLinkedMap->_dic中经过key获取到对应的node结点, 并将node结点设置为头结点
  3. 当须要删除一个缓存时, 从YYLinkedMap->_dic中经过key删除到对应的node结点, 并将node结点从双向链表中删除
  4. 当须要按需清除缓存时, 从尾结点开始清除

一. YYLinkedMapNode

YYLinkedMapNode能够理解为链表中的结点. YYMemoryCache中是将传入的value封装成node存储到缓存中的.缓存

@interface _YYLinkedMapNode : NSObject {
    @package
    __unsafe_unretained _YYLinkedMapNode *_prev; // retained by dic
    __unsafe_unretained _YYLinkedMapNode *_next; // retained by dic
    id _key; // key
    id _value; // value
    NSUInteger _cost; // 结点的开销, 在内存中占用的字节数
    NSTimeInterval _time; // 操做结点的时间
}
@end

@implementation _YYLinkedMapNode

@end
复制代码

须要注意的是Node并不持有对_prev和_next的强引用, _prev和_next执行的内容是由_dic强引用安全

二. YYLinkedMap

YYLinkedMap能够理解为是一个双向链表, 方便操做头结点和尾结点.数据结构

@interface _YYLinkedMap : NSObject {
    @package
    CFMutableDictionaryRef _dic; // do not set object directly key: key, value: Node结点
    NSUInteger _totalCost; // 总开销
    NSUInteger _totalCount; // 缓存数量
    _YYLinkedMapNode *_head; // MRU, do not change it directly 头结点
    _YYLinkedMapNode *_tail; // LRU, do not change it directly 尾结点
    BOOL _releaseOnMainThread; // 是否在主线程release结点
    BOOL _releaseAsynchronously; // 是否异步release结点
}
复制代码

思考: 问什么YYLinkedMap中要使用一个字典呢?多线程

使用字典是为了使双向链表的查询某个结点的操做时间复杂度为O(1), 若是是双向链表获取某一个结点的话须要遍历双向链表, 时间复杂度为O(n)并发

YYLinkedMap中使用CFMutableDictionaryRef建立字典, 而不是使用NSDictionary建立字典的缘由:app

由于NSDictionary的key须要遵循NSCoding协议, 而CFMutableDictionaryRef则不须要, 另外CFMutableDictionaryRef更加靠近底层, 效率更高, 可是建立的_dic的内存须要咱们本身手动回收.异步

YYLinkedMap的接口:

// 在链表头部插入结点
    - (void)insertNodeAtHead:(_YYLinkedMapNode *)node;
    // 将结点移动到链表头部
    - (void)bringNodeToHead:(_YYLinkedMapNode *)node;
    // 移除结点
    - (void)removeNode:(_YYLinkedMapNode *)node;
    // 移除尾结点
    - (_YYLinkedMapNode *)removeTailNode;
    // 清空全部结点
    - (void)removeAll;
复制代码

YYLinkedMap的操做主要是对双向链表进行的插入 删除操做, 下图中虚线为弱引用, 实线为强引用

YYLinkedMap的接口实现: 添加结点:

- (void)insertNodeAtHead:(_YYLinkedMapNode *)node {
    // 将结点node保存在_dic中
    CFDictionarySetValue(_dic, (__bridge const void *)(node->_key), (__bridge const void *)(node));
    // 根据结点的cost开销修改map记录的总开销数
    _totalCost += node->_cost;
    // 记录缓存个数
    _totalCount++;
    if (_head) {
        // 将结点node设置为头结点
        node->_next = _head;
        _head->_prev = node;
        _head = node;
    } else {
        // 若是头结点为nil, 说明链表为空, 添加的结点node就是头结点 而且仍是尾结点
        _head = _tail = node;
    }
}
复制代码

移动结点:

- (void)bringNodeToHead:(_YYLinkedMapNode *)node {
    // 若是只有一个结点, 说明不须要移动
    if (_head == node) return;
    
    if (_tail == node) {
        // 若是node是尾结点, 从新设置尾结点
        _tail = node->_prev;
        _tail->_next = nil;
    } else {
        // node既不是头结点又不是尾结点, 至关于删除结点node
        node->_next->_prev = node->_prev;
        node->_prev->_next = node->_next;
    }
    // 将node设置为头结点
    node->_next = _head;
    node->_prev = nil;
    _head->_prev = node;
    _head = node;
}
复制代码

删除结点:

- (void)removeNode:(_YYLinkedMapNode *)node {
    // 将结点node从字典_dic中移除, 注意这是node的引用计数减1
    CFDictionaryRemoveValue(_dic, (__bridge const void *)(node->_key));
    // 修改总开销
    _totalCost -= node->_cost;
    // 缓存数量减1
    _totalCount--;
    // 修改node结点的下一个结点prev指向
    if (node->_next) node->_next->_prev = node->_prev;
    // 修改node结点的上一个结点的next指向
    if (node->_prev) node->_prev->_next = node->_next;
    // 若是node是头结点, 将头结点设置为node的下一个结点
    if (_head == node) _head = node->_next;
    // 若是node是尾结点, 将尾结点设置为node的上一个结点
    if (_tail == node) _tail = node->_prev;
}
复制代码

注意这里的结点操做执行的顺序, 考虑node是头结点或者尾结点 以及为中间结点的处理状况. 移除尾结点:

- (_YYLinkedMapNode *)removeTailNode {
    if (!_tail) return nil;
    // 获取尾结点
    _YYLinkedMapNode *tail = _tail;
    // 将尾结点从字典中移除, 由于字典有强引用
    CFDictionaryRemoveValue(_dic, (__bridge const void *)(_tail->_key));
    _totalCost -= _tail->_cost;
    _totalCount--;
    if (_head == _tail) {
        // 若是头结点=尾结点, 移除以后链表为空
        _head = _tail = nil;
    } else {
        // 否者重置尾结点
        _tail = _tail->_prev;
        _tail->_next = nil;
    }
    return tail;
}	
复制代码

清空数据:

- (void)removeAll {
    // 清空信息
    _totalCost = 0;
    _totalCount = 0;
    _head = nil;
    _tail = nil;
    if (CFDictionaryGetCount(_dic) > 0) {
        // 至关于对_dic进行了一次mutableCopy, 因为_dic是不可变, 因此holder和_dic执行了同一块内存空间(堆空间)
        CFMutableDictionaryRef holder = _dic;
        // 从新在堆空间申请内存, _dic执行新分配的内存(以前堆空间的内存地址保存在holder中)
        _dic = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
        // 若是须要异步执行, 不阻塞当前线程
        if (_releaseAsynchronously) {
            dispatch_queue_t queue = _releaseOnMainThread ? dispatch_get_main_queue() : YYMemoryCacheGetReleaseQueue();
            dispatch_async(queue, ^{
                CFRelease(holder); // hold and release in specified queue
            });
        } else if (_releaseOnMainThread && !pthread_main_np()) {
            // 主线程执行
            dispatch_async(dispatch_get_main_queue(), ^{
                CFRelease(holder); // hold and release in specified queue
            });
        } else {
            // 主线程执行
            CFRelease(holder);
        }
    }
}
复制代码

清空数据这里做者的操做很巧妙, 利用holder引用dic, 从新给dic申请堆空间, 在指定的队列中实现对holder的release操做.

三. YYMemoryCache

YYMemoryCache是对外提供的类, 提供了一些配置属性和外界访问的方法.

1. 属性和方法

@interface YYMemoryCache : NSObject
// 缓存名称
@property (nullable, copy) NSString *name;

// 缓存的总数量
@property (readonly) NSUInteger totalCount;

// 缓存的总开销
@property (readonly) NSUInteger totalCost;

// 缓存的数量限制, 默认值是NSUIntegerMax表示不作限制
@property NSUInteger countLimit;

// 缓存的开销限制, 默认值是NSUIntegerMax表示不作限制, 例如设置缓存总开销为200k, 那么当总开销大于200k时就会在后台自动清理
@property NSUInteger costLimit;

// 缓存的时间限制, 默认是DBL_MAX表示不限制
@property NSTimeInterval ageLimit;

// 自动清理时间设置, 默认值是5s, 也就是5秒钟会自动检查清理缓存
@property NSTimeInterval autoTrimInterval;

// 当收到内存警告时, 是否清除全部缓存, 默认是YES
@property BOOL shouldRemoveAllObjectsOnMemoryWarning;

// 当App进入后台时, 是否清除全部缓存, 默认是YES
@property BOOL shouldRemoveAllObjectsWhenEnteringBackground;

// 收到内存警告时执行的block
@property (nullable, copy) void(^didReceiveMemoryWarningBlock)(YYMemoryCache *cache);

// 进入后台时执行的block
@property (nullable, copy) void(^didEnterBackgroundBlock)(YYMemoryCache *cache);

// 当须要移除某个value时, release操做是否在主线程执行, 默认为NO
@property BOOL releaseOnMainThread;

// 当须要移除某个value时, release造做是否异步执行, 默认为YES
@property BOOL releaseAsynchronously;

// 缓存中是否存在key
- (BOOL)containsObjectForKey:(id)key;

// 从缓存中获取和key相关联的value
- (nullable id)objectForKey:(id)key;

// 缓存key-value, 若是value=nil, 表示移除key的缓存
- (void)setObject:(nullable id)object forKey:(id)key;

// 缓存key-value, 指定开销
- (void)setObject:(nullable id)object forKey:(id)key withCost:(NSUInteger)cost;

// 从缓存中移除key
- (void)removeObjectForKey:(id)key;

// 清空缓存
- (void)removeAllObjects;

// 将缓存修剪到指定的数量, 从链表的尾结点移除
- (void)trimToCount:(NSUInteger)count
    
// 将缓存修剪到指定的开销, 从链表的尾结点移除
- (void)trimToCost:(NSUInteger)cost;

// 将缓存修剪到指定的时间, 大于这个时间的数据都会被清除
- (void)trimToAge:(NSTimeInterval)age;
@end
复制代码

2. 初始化配置

在YYMemoryCahce的初始化方法中, 对一些属性进行了默认参数配置

@implementation YYMemoryCache {
    pthread_mutex_t _lock; // 互斥锁
    _YYLinkedMap *_lru; // least recent used
    dispatch_queue_t _queue; // 串行队列
}

- (instancetype)init {
    self = super.init;
    // 初始化锁
    pthread_mutex_init(&_lock, NULL);
    // 初始化双向链表
    _lru = [_YYLinkedMap new];
    // 串行队列, 用于执行修剪操做, 为何是串行队列?
    _queue = dispatch_queue_create("com.ibireme.cache.memory", DISPATCH_QUEUE_SERIAL);
    
    _countLimit = NSUIntegerMax;
    _costLimit = NSUIntegerMax;
    _ageLimit = DBL_MAX;
    _autoTrimInterval = 5.0;
    _shouldRemoveAllObjectsOnMemoryWarning = YES;
    _shouldRemoveAllObjectsWhenEnteringBackground = YES;
    
    // 注册App的通知
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_appDidReceiveMemoryWarningNotification) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_appDidEnterBackgroundNotification) name:UIApplicationDidEnterBackgroundNotification object:nil];
    
    // 自动清除功能
    [self _trimRecursively];
    return self;
}
复制代码

3. 自动清理缓存

在init方法中调用了_trimRecursively方法.

- (void)_trimRecursively {
    // 使用weakself, block不会对self进行强引用
    __weak typeof(self) _self = self;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_autoTrimInterval * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
        // __strong修饰, 防止self被提早销毁
        __strong typeof(_self) self = _self;
        if (!self) return;
        [self _trimInBackground];
        [self _trimRecursively];
    });
}

- (void)_trimInBackground {
    // 为何是串行队列? 串行队列中的任务会等待上个任务执行完成之后才执行, 是想按照cost->count->age的顺序去清除缓存
    dispatch_async(_queue, ^{
        [self _trimToCost:self->_costLimit];
        [self _trimToCount:self->_countLimit];
        [self _trimToAge:self->_ageLimit];
    });
}
复制代码

_trimRecursively方法中使用一个dispatch_after进行延时操做, 延时时间就是属性autoTrimInterval, 经过延时递归调用_trimRecursively方法实现自动清理缓存的功能.

_trimRecursively中为何使用__weak__strong去修饰self呢?

当不使用__weak__strong修饰self的时候, demo以下:

@interface MyObject : NSObject
@end
@implementation MyObject
- (void)_trimRecursively {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
        NSLog(@"1-----");
        if (!self) return; // block会捕获self, copy, self引用计数加一.
        [self _trimRecursively]; // 由于这里是异步调用, 不会阻塞当前线程(递归调用方法中又会重复对self进行 copy和release操做)
        NSLog(@"2-----"); // 执行完block之后, block释放对self的引用, release, self引用计数减一.
    });
}
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    MyObject *obj = [[MyObject alloc] init];
    [obj _trimRecursively];
}

@end
复制代码

viewDidLoad执行完成之后, obj就应该被释放, 可是在_trimRecursively的block中一直存在对obj的强引用, 因此就形成了obj永远不会被释放.

4. 手动清理缓存

_trimToCost _trimToCount _trimToAge三种清理缓存的方法处理是同样的, 只是清除缓存的依据不同, 因此这里只分析了一种状况.

// static修饰能够在多个文件中定义
// inline内联函数, 提升效率, 在调用的时候是在调用出直接替换, 而普通的函数须要开辟栈空间(push pop操做)
static inline dispatch_queue_t YYMemoryCacheGetReleaseQueue() {
    return dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
}
复制代码
- (void)_trimToCost:(NSUInteger)costLimit {
    BOOL finish = NO;
    // 避免多线程访问出现数据错乱, 因此选择加锁
    pthread_mutex_lock(&_lock);
    if (costLimit == 0) {
        // 若是内存开销设置为0, 表示清空缓存
        [_lru removeAll];
        finish = YES;
    } else if (_lru->_totalCost <= costLimit) {
        // 若是当前缓存的开销小于指定限制开销, 不须要清理
        finish = YES;
    }
    pthread_mutex_unlock(&_lock);
    if (finish) return;
    
    // 建立一个数组, 数组中存放的是结点, 为了在指定的线程 异步或者同步release
    NSMutableArray *holder = [NSMutableArray new];
    // 这里的时间复杂度是O(n)???
    while (!finish) {
        // 尝试加锁, 若是成功就为0
        if (pthread_mutex_trylock(&_lock) == 0) {
            // 将须要移除的结点添加到数组中
            if (_lru->_totalCost > costLimit) {
                // 这里node虽然从dic字典和双向链表中移除了, 可是又加入了数组中, 因此这里node并不会被销毁
                _YYLinkedMapNode *node = [_lru removeTailNode];
                if (node) [holder addObject:node];
            } else {
                finish = YES;
            }
            pthread_mutex_unlock(&_lock);
        } else {
            // 为了防止争夺资源, sleep10ms
            usleep(10 * 1000); //10 ms
        }
    }
    // holder中保存了须要release的node结点
    if (holder.count) {
        // 根据配置选择是否在主线程中release结点资源
        // YYMemoryCacheGetReleaseQueue()是内联函数, 建立的全局并发队列
        dispatch_queue_t queue = _lru->_releaseOnMainThread ? dispatch_get_main_queue() : YYMemoryCacheGetReleaseQueue();
        // 在队列中移除
        dispatch_async(queue, ^{
            // 这里个人理解是 block会对holder强引用, 当block执行完成之后holder的引用计数减1
            [holder count]; // release in queue
        });
    }
    // 由于block中对holder有强引用, 因此出了}之后, holder的引用计数减1, 不会被销毁, 而是在block中销毁(实如今指定的队列中销毁).
}
复制代码

5. 属性的setter方法

YYMemoryCache中提供了一些配置属性, 经过重写setter方法, 实现对YYLinkedMap的配置

- (NSUInteger)totalCount {
    pthread_mutex_lock(&_lock);
    NSUInteger count = _lru->_totalCount;
    pthread_mutex_unlock(&_lock);
    return count;
}

- (NSUInteger)totalCost {
    pthread_mutex_lock(&_lock);
    NSUInteger totalCost = _lru->_totalCost;
    pthread_mutex_unlock(&_lock);
    return totalCost;
}

- (BOOL)releaseOnMainThread {
    pthread_mutex_lock(&_lock);
    BOOL releaseOnMainThread = _lru->_releaseOnMainThread;
    pthread_mutex_unlock(&_lock);
    return releaseOnMainThread;
}

- (void)setReleaseOnMainThread:(BOOL)releaseOnMainThread {
    pthread_mutex_lock(&_lock);
    _lru->_releaseOnMainThread = releaseOnMainThread;
    pthread_mutex_unlock(&_lock);
}

- (BOOL)releaseAsynchronously {
    pthread_mutex_lock(&_lock);
    BOOL releaseAsynchronously = _lru->_releaseAsynchronously;
    pthread_mutex_unlock(&_lock);
    return releaseAsynchronously;
}
复制代码

6. 增删改查操做

查找:

- (BOOL)containsObjectForKey:(id)key {
    if (!key) return NO;
    // 由于须要操做map中的数据, 加锁
    pthread_mutex_lock(&_lock);
    BOOL contains = CFDictionaryContainsKey(_lru->_dic, (__bridge const void *)(key));
    pthread_mutex_unlock(&_lock);
    return contains;
}

- (id)objectForKey:(id)key {
    if (!key) return nil;
    pthread_mutex_lock(&_lock);
    // 先从字典中获取node
    _YYLinkedMapNode *node = CFDictionaryGetValue(_lru->_dic, (__bridge const void *)(key));
    if (node) {
        // 由于操做了node, 因此须要将node的时间修改为当前时间
        node->_time = CACurrentMediaTime();
        // 操做告终点, 将结点设置为头结点.
        [_lru bringNodeToHead:node];
    }
    pthread_mutex_unlock(&_lock);
    return node ? node->_value : nil;
}
复制代码

增长:

- (void)setObject:(id)object forKey:(id)key {
    [self setObject:object forKey:key withCost:0];
}

- (void)setObject:(id)object forKey:(id)key withCost:(NSUInteger)cost {
    if (!key) return;
    // 若是object为nil, 说明是移除key
    if (!object) {
        [self removeObjectForKey:key];
        return;
    }
    pthread_mutex_lock(&_lock);
    // 将node存储在字典中
    _YYLinkedMapNode *node = CFDictionaryGetValue(_lru->_dic, (__bridge const void *)(key));
    // 获取当前时间, 用于设置node的操做时间
    NSTimeInterval now = CACurrentMediaTime();
    if (node) {
        // 若是node已经存在缓存中, 须要将node的time修改, 而且将node添加到表头
        _lru->_totalCost -= node->_cost;
        _lru->_totalCost += cost;
        node->_cost = cost;
        node->_time = now;
        node->_value = object;
        [_lru bringNodeToHead:node];
    } else {
        // 若是node不存在, 须要建立node, 而且添加到表头
        node = [_YYLinkedMapNode new];
        node->_cost = cost;
        node->_time = now;
        node->_key = key;
        node->_value = object;
        [_lru insertNodeAtHead:node];
    }
    // 添加完成之后, 由于_totalCost增长, 须要检查总开销是否超过了设定的标准_costLimit
    if (_lru->_totalCost > _costLimit) {
        // 在串行队列中异步清除缓存
        dispatch_async(_queue, ^{
            [self trimToCost:_costLimit];
        });
    }
    // 添加完成之后, 由于_totalCount增长, 须要检查_totalCount是否大于_countLimit
    if (_lru->_totalCount > _countLimit) {
        // 由于每次只增长一个node, 因此只须要移除最后一个尾结点便可
        _YYLinkedMapNode *node = [_lru removeTailNode];
        if (_lru->_releaseAsynchronously) {
            dispatch_queue_t queue = _lru->_releaseOnMainThread ? dispatch_get_main_queue() : YYMemoryCacheGetReleaseQueue();
            dispatch_async(queue, ^{
                [node class]; //hold and release in queue
            });
        } else if (_lru->_releaseOnMainThread && !pthread_main_np()) {
            dispatch_async(dispatch_get_main_queue(), ^{
                [node class]; //hold and release in queue
            });
        }
    }
    pthread_mutex_unlock(&_lock);
}
复制代码

删除:

- (void)removeObjectForKey:(id)key {
    if (!key) return;
    pthread_mutex_lock(&_lock);
    // 获取须要移除的node
    _YYLinkedMapNode *node = CFDictionaryGetValue(_lru->_dic, (__bridge const void *)(key));
    if (node) {
        [_lru removeNode:node];
        if (_lru->_releaseAsynchronously) {
            dispatch_queue_t queue = _lru->_releaseOnMainThread ? dispatch_get_main_queue() : YYMemoryCacheGetReleaseQueue();
            dispatch_async(queue, ^{
                [node class]; //hold and release in queue
            });
        } else if (_lru->_releaseOnMainThread && !pthread_main_np()) {
            dispatch_async(dispatch_get_main_queue(), ^{
                [node class]; //hold and release in queue
            });
        }
    }
    pthread_mutex_unlock(&_lock);
}

- (void)removeAllObjects {
    pthread_mutex_lock(&_lock);
    [_lru removeAll];
    pthread_mutex_unlock(&_lock);
}
复制代码

7. 其余

- (NSString *)description {
    if (_name) return [NSString stringWithFormat:@"<%@: %p> (%@)", self.class, self, _name];
    else return [NSString stringWithFormat:@"<%@: %p>", self.class, self];
}
复制代码

重写description方法, 当log YYMemoryCache的对象时就会调用这个方法, log相关信息.

四. 思考

1. MRU LRU 淘汰算法?

LRU(least-recently-used): 最近最少使用, 是一种淘汰算法, 若是最近模块数据被访问, 那么未来这块数据被访问的几率也很大. 最久没有被访问的数据, 未来被访问的几率会很小. 将最久没有使用的淘汰掉, 须要使用一个time字段, 记录每次访问的时间, 当须要淘汰的时候, 就选择time最大的淘汰.

MRU(most-recently-used): 最近最常使用, 和LRU相反.

2. YYMemoryCache是如何保证线程安全的?

YYMemoryCache须要操做资源时加锁. Pthread_mutex锁.

YYMemoryCache中对清除缓存的操做 访问_YYLinkedMap中的数据 以及getter setter方法都加锁处理, 避免多线程访问的时候出现数据错乱.

3. YYMemoryCache是从哪几个方面提升性能的?

  • 选择合适的数据结构, 减小时间复杂度, YYMemoryCache中是使用双向链表+字典, 双向链表方便操做头结点和尾结点, 方便查找和修改中间结点的上一个和下一个节点, 操做的时间复杂度为O(1), 而双向链表的查找复杂度为O(n). 字典的查找复杂度为O(1), YYMemoryCache使用双向链表+字典的数据结构, 增删改查操做的时间复杂度都为O(1).
  • 清除缓存的操做异步执行, release对象异步执行

4. dispatch_semaphore和Pthread_mutex锁, 性能对比 如何选择?

// dispatch_semaphore是信号量, 当信号量为1时也能够当作锁来使用, 当信号量小于1时就会等待
dispatch_semaphore_t lock = dispatch_semaphore_create(1);
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
...
dispatch_semaphore_signal(lock);

pthread_mutex_t lock;
pthread_mutex_init(&lock, NULL);
pthread_mutex_lock(&lock);
...
pthread_mutex_unlock(&lock);
复制代码

当没有等待的状况下, dispatch_semaphore的性能要比pthread_mutext高, 可是一旦出现等待的状况, 性能就会降低不少.

相关文章
相关标签/搜索