iOS数据结构之NSCache、NSMapTable、NSHashTable

3,532 阅读3分钟

1.NSCache

通常使用NSCache对象临时存储具有创建成本高昂的瞬时数据的对象。重用这些对象可以提高性能,因为它们的值不必重新计算。然而,这些对象对应用程序来说并不重要,如果内存不足,可以将其丢弃。如果丢弃,则在需要时必须重新计算它们的值。

NSCache是一个可变键值对集合,使用方式和NSMutableDictionary基本是相同。不过相对于NSMutableDictionary,NSCache有以下几个区别:
1.NSCache是线程安全的,NSMutableDictionary线程不安全。 2.当内存不足时,NSCache可能会自动删除键值对以释放内存,所以从缓存中取数据的时候总要判断是否为空。删除策略未知。
3.NSCache可以指定缓存限额,当缓存超出限额时自动释放内存。
4.NSCache的键只是对对象的强引用,对象不需要实现NSCopying协议。NSMutableDictionary的键会copy对象,对象必须实现NSCopying协议。

// UIView对象没有实现NSCopying协议
 UIView *view = [[UIView alloc] init];
 // 没有问题
[self.cache setObject:view forKey:view];
// 会崩溃,因为key没有实现NSCopying协议
[self.dic setObject:view forKey:view];

使用方式

1.设置缓存限额

// 缓存数量
@property NSUInteger countLimit;
// 缓存成本
@property NSUInteger totalCostLimit;

2.监听元素移除

@property (nullable, assign) id<NSCacheDelegate> delegate;
// 在移除元素之前,会回调下面的方法,返回要移除的元素。
- (void)cache:(NSCache *)cache willEvictObject:(id)obj;

代码演示:

#import "ViewController.h"

@interface ViewController () <NSCacheDelegate>

// 定义缓存池
@property (nonatomic, strong) NSCache *cache;

@end

@implementation ViewController
- (NSCache *)cache {
if (_cache == nil) {
    _cache = [[NSCache alloc] init];
    // 缓存中总共可以存储多少条
    _cache.countLimit = 5;
    // 缓存的数据总量为多少,5k
    _cache.totalCostLimit = 1024 * 5;
    // 设置代理
    _cache.delegate = self;
    }
    return _cache;
}

- (void)viewDidLoad {
  [super viewDidLoad];

  // 添加缓存数据
   for (int i = 0; i < 10; i++) {
    [self.cache setObject:[NSString stringWithFormat:@"hello %d",i] forKey:[NSString stringWithFormat:@"h%d",i]];
    NSLog(@"添加 %@",[NSString stringWithFormat:@"hello %d",i]);
   }

  // 输出缓存中的数据
   for (int i = 0; i < 10; i++) {
    NSLog(@"%@",[self.cache objectForKey:[NSString stringWithFormat:@"h%d",i]]);
   }
}

// 当缓存被移除的时候执行
- (void)cache:(NSCache *)cache willEvictObject:(id)obj{
    NSLog(@"缓存移除  %@",obj);
}

控制台输出结果为:

2.NSMapTable

NSMapTable是一个可变键值对集合,使用方式和NSMutableDictionary基本是相同。不过相对于NSMutableDictionary,NSMapTable有以下几个区别:
1.可以设置是否强引用键对象,NSMutableDictionary只能强引用键对象。如果键对象释放了,NSMapTable会自动移除键值对。 2.可以设置是否强引用值对象,NSMutableDictionary只能强引用值对象。如果值对象释放了,NSMapTable会自动移除键值对。 、、、 // 键值都是强引用,相当于NSMutableDictionary

  • (NSMapTable<KeyType, ObjectType> *)strongToStrongObjectsMapTable; // 键对象是弱引用,值对象是强引用。当键对象释放的时候,NSMapTable就会移除这对键值对。
  • (NSMapTable<KeyType, ObjectType> *)weakToStrongObjectsMapTable; // 键对象是强引用,值对象是弱引用。当值对象释放的时候,NSMapTable就会移除这对键值对。
  • (NSMapTable<KeyType, ObjectType> *)strongToWeakObjectsMapTable;
  • // 键值都是弱引用。当键对象或者值对象只要有一个释放,NSMapTable就会移除这对键值对。
  • (NSMapTable<KeyType, ObjectType> *)weakToWeakObjectsMapTable; 、、、

代码演示

- (void)viewDidLoad {
    NSMutableString *key = [[NSMutableString alloc] initWithString:@"key"];
    NSMutableString *value = [[NSMutableString alloc] initWithString:@"value"];
    NSMapTable *mapTable = [NSMapTable weakToStrongObjectsMapTable];
    [mapTable setObject:value forKey:key];
    NSLog(@"释放之前---%@", mapTable);
    key = nil;
    NSLog(@"释放之后---%@", mapTable);
}

控制台输出结果为:

释放之前---NSMapTable {
[5] key -> value
}

释放之后---NSMapTable {
}

3.NSHashTable

NSHashTable是一个可变集合,使用方式和NSMutableSet基本是相同。不过相对于NSMutableArray,NSMutableArray有以下几个区别:
1.可以设置集合内元素的内存管理方式,例如强引用,弱引用。当元素弱引用并释放的时候,NSHashTable会自动移除这个元素。

代码演示

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSMutableString *object = [[NSMutableString alloc] initWithString:@"object"];
    // 设置对集合内的元素为弱引用
    NSHashTable *hashTable = [NSHashTable weakObjectsHashTable];
    [hashTable addObject:object];
    NSLog(@"释放之前---%@", hashTable);
    object = nil;
    NSLog(@"释放之后---%@", hashTable);
}

控制台输出结果为:

释放之前---NSHashTable {
[1] object
}

释放之后---NSHashTable {
}