集合类内存管理特点
- NSArray:不可变数组,一旦创建,其内容和长度不可改变。内存管理遵循ARC(自动引用计数)规则,当对象不再被任何强引用指向时,ARC会自动释放其占用的内存。
- NSMutableArray:可变数组,可以动态添加、删除和修改元素。同样遵循ARC规则,但由于其可变性,在操作元素时可能会涉及到更多的内存分配和释放操作。
- NSSet:不可变集合,集合中的元素无序且唯一。内存管理基于ARC,与NSArray类似。
- NSMutableSet:可变集合,可动态添加和删除元素。内存管理遵循ARC,操作元素时会有额外的内存管理开销。
- NSDictionary:不可变字典,以键值对的形式存储数据,一旦创建不可改变。内存管理遵循ARC。
- NSMutableDictionary:可变字典,可动态添加、删除和修改键值对。内存管理遵循ARC,由于可变性会有更多内存操作。
多线程环境下安全问题
- 数据竞争:多个线程同时对集合类进行读写操作时,可能会导致数据不一致。例如,一个线程正在读取数组中的元素,而另一个线程同时删除了该元素,就会导致读取到无效数据。
- 内存损坏:在多线程环境下,集合类的内部结构可能会被多个线程同时修改,导致内存损坏。比如,多个线程同时向可变数组中添加元素,可能会破坏数组的内部存储结构。
- 死锁:如果在多线程中使用锁来保护集合类的访问,可能会因为锁的嵌套使用或获取锁的顺序不当而导致死锁。
确保数据一致性和完整性及高性能的措施
- 使用线程安全的集合类:iOS提供了一些线程安全的集合类,如
NSMutableArray
的线程安全替代品NSMutableArray(Concurrency)
,NSMutableDictionary
的线程安全替代品NSMutableDictionary(Concurrency)
等。这些类内部已经实现了线程同步机制,可以直接在多线程环境下安全使用。
- 锁机制:
- 互斥锁(Mutex):使用
pthread_mutex_t
或dispatch_semaphore_t
来保护对集合类的访问。在进行读写操作前,先获取锁,操作完成后释放锁。例如:
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
// 读操作
pthread_mutex_lock(&mutex);
NSArray *array = self.mutableArray;
pthread_mutex_unlock(&mutex);
// 写操作
pthread_mutex_lock(&mutex);
[self.mutableArray addObject:@"new object"];
pthread_mutex_unlock(&mutex);
- **读写锁(Read - Write Lock)**:如果读操作远多于写操作,可以使用读写锁。读操作时允许多个线程同时进行,写操作时则独占锁。iOS中可以使用`pthread_rwlock_t`实现读写锁。
pthread_rwlock_t rwLock;
pthread_rwlock_init(&rwLock, NULL);
// 读操作
pthread_rwlock_rdlock(&rwLock);
NSArray *array = self.mutableArray;
pthread_rwlock_unlock(&rwLock);
// 写操作
pthread_rwlock_wrlock(&rwLock);
[self.mutableArray addObject:@"new object"];
pthread_rwlock_unlock(&rwLock);
- GCD(Grand Central Dispatch):
- 串行队列:使用
dispatch_queue_t
创建一个串行队列,将对集合类的所有操作都提交到该队列中执行,这样可以保证操作的顺序性,避免数据竞争。
dispatch_queue_t queue = dispatch_queue_create("com.example.collectionQueue", DISPATCH_QUEUE_SERIAL);
// 读操作
dispatch_sync(queue, ^{
NSArray *array = self.mutableArray;
});
// 写操作
dispatch_sync(queue, ^{
[self.mutableArray addObject:@"new object"];
});
- **并发队列结合栅栏函数**:如果需要在并发队列中进行读写操作,可以使用栅栏函数(`dispatch_barrier_async`)。写操作使用栅栏函数提交到并发队列,读操作则正常提交。这样可以保证写操作完成后再进行读操作,避免数据竞争。
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.example.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
// 读操作
dispatch_async(concurrentQueue, ^{
NSArray *array = self.mutableArray;
});
// 写操作
dispatch_barrier_async(concurrentQueue, ^{
[self.mutableArray addObject:@"new object"];
});
- 使用副本:在进行读操作时,先获取集合类的不可变副本,然后在副本上进行操作。这样即使其他线程修改了原集合,也不会影响读操作。
// 读操作
NSArray *copyArray = [self.mutableArray copy];
// 在copyArray上进行操作