面试题答案
一键面试1. NSHashTable 和 NSMapTable 内存管理机制
- NSHashTable:
- 存储方式:NSHashTable 以散列表的形式存储对象。它使用对象的哈希值来确定存储位置,以提高查找效率。
- 内存管理:
- 弱引用:当创建NSHashTable 并指定为弱引用类型时(
[NSHashTable weakObjectsHashTable]
),NSHashTable 不会对存储的对象保持强引用。这意味着当对象在其他地方释放时,NSHashTable 中的对应引用会自动被设置为nil
。 - 强引用:如果创建为强引用类型(
[NSHashTable strongObjectsHashTable]
),NSHashTable 会对存储的对象保持强引用,对象的生命周期会受NSHashTable 影响。
- 弱引用:当创建NSHashTable 并指定为弱引用类型时(
- NSMapTable:
- 存储方式:NSMapTable 存储键值对,类似于字典。它同样基于散列表结构,通过键的哈希值来确定存储位置。
- 内存管理:
- 键值引用类型:NSMapTable 可以对键和值分别设置不同的引用类型,如强引用、弱引用、复制等。例如,可以创建一个键为弱引用,值为强引用的 NSMapTable:
[NSMapTable mapTableWithKeyOptions:NSPointerFunctionsWeakMemory valueOptions:NSPointerFunctionsStrongMemory]
。这使得其内存管理更为灵活。
- 键值引用类型:NSMapTable 可以对键和值分别设置不同的引用类型,如强引用、弱引用、复制等。例如,可以创建一个键为弱引用,值为强引用的 NSMapTable:
2. 可能出现的问题
- 内存泄漏:
- 强引用情况:在NSHashTable(强引用)或NSMapTable(强引用键/值)中,如果对象在表外没有其他强引用,但表内对其保持强引用,而表本身没有被正确释放,这些对象将无法被释放,从而导致内存泄漏。
- 对象移除问题:如果没有正确从NSHashTable或NSMapTable中移除不再使用的对象,即使对象在其他地方已无强引用,也会导致内存泄漏。
- 过度占用内存:
- 大量对象存储:当存储大量对象时,NSHashTable和NSMapTable会占用较多内存。而且,由于散列表的特性,可能会存在一定的空间浪费(如哈希冲突导致的链增长)。
- 弱引用内存管理开销:在弱引用类型的NSHashTable或NSMapTable中,虽然不会对对象保持强引用,但系统需要额外的机制来管理弱引用,这也会带来一定的内存开销。
3. 性能优化方案
- 选择合适的存储策略:
- 引用类型选择:
- 如果对象在表外有足够的强引用,且希望表不影响对象生命周期,使用弱引用类型的NSHashTable或NSMapTable(如在视图控制器的视图已经被其他地方强引用,而只想在NSHashTable中跟踪时)。
- 如果对象的生命周期需要由表来控制,使用强引用类型。
- 容器选择:
- 如果只需要存储对象集合,NSHashTable 是合适的选择。
- 如果需要存储键值对,NSMapTable 更合适。而且,如果键是对象且可能重复使用,使用弱引用键的NSMapTable可以避免重复创建键对象导致的内存浪费。
- 引用类型选择:
- 优化遍历操作:
- 避免不必要遍历:尽量减少对NSHashTable或NSMapTable的遍历次数。例如,可以在添加/移除对象时更新相关统计信息,而不是每次都遍历整个表来获取统计数据。
- 快速遍历:使用快速枚举(
for (id obj in hashTable)
或for (id key in mapTable) { id value = [mapTable objectForKey:key]; }
),它比传统的NSEnumerator
遍历更高效。
- 处理对象生命周期与容器的关系:
- 对象移除:在对象不再需要时,及时从NSHashTable或NSMapTable中移除。例如,在对象的
dealloc
方法中(MRC环境)或在对象生命周期结束前(ARC环境),调用[hashTable removeObject:obj]
或[mapTable removeObjectForKey:key]
。 - 自动清理:对于弱引用类型的容器,可以利用对象释放后自动设置为
nil
的特性,在遍历或操作容器时跳过nil
值,实现自动清理。
- 对象移除:在对象不再需要时,及时从NSHashTable或NSMapTable中移除。例如,在对象的
4. ARC 和 MRC 环境下优化方案的不同
- ARC(自动引用计数):
- 内存管理简化:ARC 自动管理对象的引用计数,减少了手动管理引用计数的错误。在使用NSHashTable和NSMapTable时,无需手动调用
retain
、release
等方法。 - 对象移除:在ARC环境下,对象移除时不需要担心手动释放导致的悬垂指针问题,因为ARC会自动处理。但仍需及时移除不再使用的对象以避免内存泄漏。
- 内存管理简化:ARC 自动管理对象的引用计数,减少了手动管理引用计数的错误。在使用NSHashTable和NSMapTable时,无需手动调用
- MRC(手动引用计数):
- 手动引用管理:在MRC环境下,使用强引用类型的NSHashTable或NSMapTable时,需要手动调用
retain
和release
方法来管理对象的引用计数。例如,在向强引用的NSHashTable添加对象时,需要先retain
对象,从表中移除对象时,需要手动release
对象。 - 内存泄漏风险:由于手动管理引用计数,MRC环境下更容易出现内存泄漏问题,如忘记
release
对象或过度release
导致程序崩溃。因此,在MRC环境下更需要谨慎处理对象与容器之间的引用关系。
- 手动引用管理:在MRC环境下,使用强引用类型的NSHashTable或NSMapTable时,需要手动调用