NSMutableDictionary分析
我们都知道NSMutableDictionary/NSMutableArray,也会经常使用。大部分都知道NSMutableDictionary/NSMutableArray对里面的对象是强引用的。
我们先看官方文档怎么说?
- (void)setObject:(ObjectType)anObject forKey:(KeyType <NSCopying>)aKey;
官方文档的大概意思是我们在NSMutableDictionary的时候,key必须要遵循NSCopying协议。
因此NSMutableDictionary的key,实现NSCoping协议,相当于Key对象被copy一份后存入,那么对应的anObject则会被强引用。
我们看一下代码:
NSMutableDictionary *aDictionary = [[NSMutableDictionary alloc] initWithCapacity:0];
{
NSString *aKey = @"key";
NSObject *aObject = [[NSObject alloc] init];
[aDictionary setObject:aObject forKey:aKey];
}
NSLog(@"dictionary: %@", aDictionary);
控制台输出结果:
dictionary: {
key = "<NSObject: 0x600000b18fa0>";
}
如果不是强引用,那么作用域结束后,key变量指向NSString(也就是key)和aObject指向的NSObject应该被自动释放。但是aDictionary持有一份aObject对象的强引用,所以aDictionary对象不为空。
但是有时候我们希望不被强引用。那么我们的NSMapTable就出来了。
NSMapTable分析
NSMapTable继承自NSObject,自iOS6.0开始使用,NSMapTable是可变的。
API_AVAILABLE(macos(10.5), ios(6.0), watchos(2.0), tvos(9.0))
@interface NSMapTable<KeyType, ObjectType> : NSObject <NSCopying, NSSecureCoding, NSFastEnumeration>
初始化方法如下:
//指定初始化
- (instancetype)initWithKeyOptions:(NSPointerFunctionsOptions)keyOptions valueOptions:(NSPointerFunctionsOptions)valueOptions capacity:(NSUInteger)initialCapacity NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithKeyPointerFunctions:(NSPointerFunctions *)keyFunctions valuePointerFunctions:(NSPointerFunctions *)valueFunctions capacity:(NSUInteger)initialCapacity NS_DESIGNATED_INITIALIZER;
//便捷初始化
+ (NSMapTable<KeyType, ObjectType> *)mapTableWithKeyOptions:(NSPointerFunctionsOptions)keyOptions valueOptions:(NSPointerFunctionsOptions)valueOptions;
初始化方法方法中有两个参数keyOptions和valueOptions,都是NSPointerFunctionsOptions类型,NSPointerFunctionsOptions是一个枚举类型,
// Memory options are mutually exclusive
// default is strong
NSPointerFunctionsStrongMemory API_AVAILABLE(macos(10.5), ios(6.0), watchos(2.0), tvos(9.0)) = (0UL << 0), // use strong write-barrier to backing store; use GC memory on copyIn
NSPointerFunctionsZeroingWeakMemory API_DEPRECATED("GC no longer supported", macos(10.5, 10.8)) API_UNAVAILABLE(ios, watchos, tvos) = (1UL << 0), // deprecated; uses GC weak read and write barriers, and dangling pointer behavior otherwise
NSPointerFunctionsOpaqueMemory API_AVAILABLE(macos(10.5), ios(6.0), watchos(2.0), tvos(9.0)) = (2UL << 0),
NSPointerFunctionsMallocMemory API_AVAILABLE(macos(10.5), ios(6.0), watchos(2.0), tvos(9.0)) = (3UL << 0), // free() will be called on removal, calloc on copyIn
NSPointerFunctionsMachVirtualMemory API_AVAILABLE(macos(10.5), ios(6.0), watchos(2.0), tvos(9.0)) = (4UL << 0),
NSPointerFunctionsWeakMemory API_AVAILABLE(macos(10.8), ios(6.0), watchos(2.0), tvos(9.0)) = (5UL << 0), // uses weak read and write barriers appropriate for ARC
// Personalities are mutually exclusive
// default is object. As a special case, 'strong' memory used for Objects will do retain/release under non-GC
NSPointerFunctionsObjectPersonality API_AVAILABLE(macos(10.5), ios(6.0), watchos(2.0), tvos(9.0)) = (0UL << 8), // use -hash and -isEqual, object description
NSPointerFunctionsOpaquePersonality API_AVAILABLE(macos(10.5), ios(6.0), watchos(2.0), tvos(9.0)) = (1UL << 8), // use shifted pointer hash and direct equality
NSPointerFunctionsObjectPointerPersonality API_AVAILABLE(macos(10.5), ios(6.0), watchos(2.0), tvos(9.0)) = (2UL << 8), // use shifted pointer hash and direct equality, object description
NSPointerFunctionsCStringPersonality API_AVAILABLE(macos(10.5), ios(6.0), watchos(2.0), tvos(9.0)) = (3UL << 8), // use a string hash and strcmp, description assumes UTF-8 contents; recommended for UTF-8 (or ASCII, which is a subset) only cstrings
NSPointerFunctionsStructPersonality API_AVAILABLE(macos(10.5), ios(6.0), watchos(2.0), tvos(9.0)) = (4UL << 8), // use a memory hash and memcmp (using size function you must set)
NSPointerFunctionsIntegerPersonality API_AVAILABLE(macos(10.5), ios(6.0), watchos(2.0), tvos(9.0)) = (5UL << 8), // use unshifted value as hash & equality
NSPointerFunctionsCopyIn API_AVAILABLE(macos(10.5), ios(6.0), watchos(2.0), tvos(9.0)) = (1UL << 16),
常用的枚举值:
NSPointerFunctionsStrongMemory: 强引用存储对象
NSPointerFunctionsWeakMemory: 弱引用存储对象
NSPointerFunctionsCopyIn copy存储对象
那么NSMapTable的初始化
NSMapTable *aMapTable = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsCopyIn valueOptions:NSPointerFunctionsStrongMemory capacity:0];
就等同于
NSMutableDictionary *aDictionary = [[NSMutableDictionary alloc] initWithCapacity:0];
如果想要对应的值自动释放,我们可以这么做
NSMapTable *aMapTable = [NSMapTable mapTableWithKeyOptions:NSPointerFunctionsStrongMemory valueOptions:NSPointerFunctionsWeakMemory];
{
NSObject *keyObject = [[NSObject alloc] init];
NSObject *valueObject = [[NSObject alloc] init];
[aMapTable setObject:valueObject forKey:keyObject];
NSLog(@"NSMapTable:%@", aMapTable);
}
NSLog(@"NSMapTable:%@", aMapTable);
NSMapTable:NSMapTable {
[0] <NSObject: 0x600000b18f00> -> <NSObject: 0x600000b18f90>
}
NSMapTable:NSMapTable {
}
第一个NSLog打印出了key-value值,等到object对象指向的NSObject对象超出作用域,释放该对象,由于aMapTable弱引用object对象,aMapTable的中的key-value值会被安全的删除,第二个NSLog打印出的值为空。
NSMapTable与NSMutableDictionary比较
1、NSMutableDictionary是可变类型,NSMapTable没有可变类型,它本身就是可变的
2、NSMutableDictionary中对于key和value的内存管理方法唯一,即对key进行copy,对value进行强引用,而NSMapTable没有限制
3、NSMutableDictionary中对key值进行copy,不可改变,通常用字符串作为key值,只是key->object的映射,而NSMapTable的key是可变的对象,既可以实现key->object的映射,又可以实现object->object的映射