// // ASWeakMap.mm // Texture // // Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. // Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. // Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 // #import "ASWeakMap.h" @interface ASWeakMapEntry () @property (nonatomic, readonly) id key; @property id value; @end @implementation ASWeakMapEntry - (instancetype)initWithKey:(id)key value:(id)value { self = [super init]; if (self) { _key = key; _value = value; } return self; } @end @interface ASWeakMap () @property (nonatomic, readonly) NSMapTable *hashTable; @end /** * Implementation details: * * The retained size of our keys is potentially very large (for example, a UIImage is commonly part of a key). * Unfortunately, NSMapTable does not make guarantees about how quickly it will dispose of entries where * either the key or the value is weak and has been disposed. So, a NSMapTable with "strong key to weak value" is * unsuitable for our purpose because the strong keys are retained longer than the value and for an indefininte period of time. * More details here: http://cocoamine.net/blog/2013/12/13/nsmaptable-and-zeroing-weak-references/ * * Our NSMapTable is "weak key to weak value" where each key maps to an Entry. The Entry object is responsible * for retaining both the key and value. Our convention is that the caller must retain the Entry object * in order to keep the key and the value in the cache. */ @implementation ASWeakMap - (instancetype)init { self = [super init]; if (self) { _hashTable = [NSMapTable weakToWeakObjectsMapTable]; } return self; } - (ASWeakMapEntry *)entryForKey:(id)key { return [self.hashTable objectForKey:key]; } - (ASWeakMapEntry *)setObject:(id)value forKey:(id)key { ASWeakMapEntry *entry = [self.hashTable objectForKey:key]; if (entry != nil) { // Update the value in the existing entry. entry.value = value; } else { entry = [[ASWeakMapEntry alloc] initWithKey:key value:value]; [self.hashTable setObject:entry forKey:key]; } return entry; } @end