一、NSMutableArray底层原理
普通c数组,就是一段能被方便读写的连续内存控件。
使用一段线性内存空间的一个最明显的缺点是,在下标 0 处插入一个元素时,需要移动其它所有的元素,同样地,假如想要保持相同的内存指针作为首个元素的地址,移除第一个元素需要进行相同的动作
NSMutableArray本质是一个对象。它采用了环形缓冲区的结构。在两端插入和删除非常的快。插入头尾只是修改offset指针,如果插入数据到达阀值,一样需要扩容。
只有中间插入和删除时,才需要移动较少的内容
二、NSDictionary底层原理
NSDictionary其实就是一个散列表,
对key进行hash算法得到对应的index,在对index进行 & 运算得到具体的桶的位置。
所以对NSMutableDictionary扩容时,必须是2的倍数。例如原始长度是16,扩容后的长度是32,这样做是为了 & 运算的结果一致
static int indexFor(int h, int length) {
return h & (length-1);
}
var a = 21; // 10101
// 00111
var b = a & 7; //长度是8,这里要减1
print("b is \(b)")
// 扩容后,8变成16
b = a & 15; //01111
print("b is \(b)")
//打印
b is 5
b is 5
可以发现,&运算的结果一致。
但是扩容需要满足两个条件:
- 当前数据存储的数量(即size())大小必须大于等于阈值
- 当前加入的数据是否发生了hash冲突。
由于上面这两个条件,所以存在下面这些情况
-
就是hashmap在存值的时候(默认大小为16,负载因子0.75,阈值12),可能达到最后存满16个值的时候,再存入第17个值才会发生扩容现象,因为前16个值,每个值在底层数组中分别占据一个位置,并没有发生hash碰撞。
-
当然也有可能存储更多值(超多16个值,最多可以存26个值)都还没有扩容。原理:前11个值全部hash碰撞,存到数组的同一个位置(这时元素个数小于阈值12,不会扩容),后面所有存入的15个值全部分散到数组剩下的15个位置(这时元素个数大于等于阈值,但是每次存入的元素并没有发生hash碰撞,所以不会扩容),前面11+15=26,所以在存入第27个值的时候才同时满足上面两个条件,这时候才会发生扩容现象。
三、NSSet
NSSet和NSMutableSet是无序的,但是它保证数据的唯一性。当插入相同的数据时,不会有任何效果。从内部实现来说是hash表,所以可以常数时间内查找一个数据。
常用函数:
[NSSet setWithSet:(NSSet *)set];
用另外一个set对象构造
[NSSet setWithArray:(NSArray *)array];用数组构造
[NSSet setWithObjects:...]:创建集合对象,并且初始化集合中的数值,结尾必需使用nil标志。
[set count] ;得到这个结合对象的长度。
[set containsObject:...]:判断这个集合中是否存在传入的对象,返回Bool值。
[set objectEnumerator]:将集合放入迭代器。
[enumerator nextObject]:得到迭代器中的下一个节点数据,使用while遍历这个迭代器,方可遍历集合对象中的对象。
[set isEqualToSet:objset]:判断两个集合是否完全相等,返回Bool值。
[set isSubsetOfSet:objset]:判断集合中的所有数据是否都相等与objeset集合中,返回Bool值。
[set allObjects];
四、NSCache
NSCache 基本上就是一个会自动移除对象来释放内存的 NSMutableDictionary。无需响应内存警告或者使用计时器来清除缓存。唯一的不同之处是键对象不会像 NSMutableDictionary 中那样被复制
- (void)viewDidLoad {
[super viewDidLoad];
self.cache = [[NSCache alloc] init];
[self.cache setDelegate:self];
self.cache.countLimit = 40;
for (int i = 0; i < 50; i ++) {
[self.cache setObject:@(i) forKey:[NSString stringWithFormat:@"key_%d",i]];
}
NSLog(@"-|%@",[self.cache objectForKey:@"key_2"]);
NSLog(@"-|%@",[self.cache objectForKey:@"key_12"]);
}
- (void)cache:(NSCache *)cache willEvictObject:(id)obj {
NSLog(@"---%@",obj);
}
// ----------
2020-09-16 16:14:15.779289+0800 Test[40675:709451] ---0
2020-09-16 16:14:15.779461+0800 Test[40675:709451] ---1
2020-09-16 16:14:15.779563+0800 Test[40675:709451] ---2
2020-09-16 16:14:15.779648+0800 Test[40675:709451] ---3
2020-09-16 16:14:15.779727+0800 Test[40675:709451] ---4
2020-09-16 16:14:15.779803+0800 Test[40675:709451] ---5
2020-09-16 16:14:15.779873+0800 Test[40675:709451] ---6
2020-09-16 16:14:15.779935+0800 Test[40675:709451] ---7
2020-09-16 16:14:15.780014+0800 Test[40675:709451] ---8
2020-09-16 16:14:15.780199+0800 Test[40675:709451] ---9
2020-09-16 16:14:15.780408+0800 Test[40675:709451] -|(null)
2020-09-16 16:14:15.780667+0800 Test[40675:709451] -|12
NSCache提供了可设置缓存的数目与内存大小限制的方式。保证了处理的数据的线程安全性。缓存使用的key不需要是实现NSCopying的类。
当内存警告时内部自动清理部分缓存数据。
进入后台也会清理内存
五、copy/mutableCopy
这几个容器类的对象,不管是copy
还是mutableCopy
,都不会拷贝里面的对象
,如果想要拷贝里面的对象
,使用如下函数
- (instancetype)initWithDictionary:(NSDictionary<KeyType, ObjectType> *)otherDictionary copyItems:(BOOL)flag;
- (instancetype)initWithSet:(NSSet<ObjectType> *)set copyItems:(BOOL)flag;
- (instancetype)initWithArray:(NSArray<ObjectType> *)array copyItems:(BOOL)flag;