My approach supports updating values. Since CFBinaryHeap does not support updating values, I put them on the invalid list, and as soon as it is checked out, the object is inserted again and a new checkout will be performed.
NS_ASSUME_NONNULL_BEGIN @interface BEPriorityQueue<ObjectType, ValueType> : NSObject - (void)dispose; @property (nonatomic, readonly) NSUInteger count; - (void)insert:(ObjectType)object value:(ValueType)value; - (void)update:(ObjectType)object value:(ValueType)value; - (ObjectType)extractMinimum; - (BOOL)containsObject:(ObjectType)object; - (id)valueForObject:(id)object; - (void)removeAllObjects; @end NS_ASSUME_NONNULL_END
With this implementation:
NS_ASSUME_NONNULL_BEGIN @interface BEPriorityQueue() - (CFComparisonResult)compareObject:(id)object1 with:(id)object2; @end static CFComparisonResult BEPriorityQueueCompareItems(const void *ptr1, const void *ptr2, void *info) { id object1 = (__bridge id)ptr1; id object2 = (__bridge id)ptr2; BEPriorityQueue* queue = (__bridge id)info; return [queue compareObject:object1 with:object2]; } static const void *BEPriorityQueueItemRetain(CFAllocatorRef allocator, const void *ptr) { return CFRetain(ptr); } static void BEPriorityQueueItemRelease(CFAllocatorRef allocator, const void *ptr) { CFRelease(ptr); } @implementation BEPriorityQueue { BOOL _disposed; CFBinaryHeapRef _binaryHeapRef; NSMapTable* _objectToValue; NSMutableSet* _invalidated; } - (instancetype)init { self = [super init]; if (self) { CFBinaryHeapCallBacks callbacks = (CFBinaryHeapCallBacks) { .version = 0, .retain = &BEPriorityQueueItemRetain, .release = &BEPriorityQueueItemRelease, .copyDescription = &CFCopyDescription, .compare = &BEPriorityQueueCompareItems }; CFBinaryHeapCompareContext compareContext = (CFBinaryHeapCompareContext) { .version = 0, .info = (__bridge void *)(self), .retain = NULL, .release = NULL, .copyDescription = NULL, }; _binaryHeapRef = CFBinaryHeapCreate(NULL, 0, &callbacks, &compareContext); _objectToValue = [NSMapTable strongToStrongObjectsMapTable]; _invalidated = [NSMutableSet set]; } return self; } - (void)dealloc { [self dispose]; if (_binaryHeapRef != NULL) { CFRelease(_binaryHeapRef); _binaryHeapRef = NULL; } } - (void)dispose { [self removeAllObjects]; _disposed = YES; } #pragma mark internal - (CFComparisonResult)compareObject:(id)object1 with:(id)object2 { id value1 = [_objectToValue objectForKey:object1]; id value2 = [_objectToValue objectForKey:object2]; return (CFComparisonResult)[value1 compare:value2]; } #pragma mark interface - (NSUInteger)count { BEEnsureFalse(_disposed); return (NSUInteger)CFBinaryHeapGetCount(_binaryHeapRef); } - (id)extractMinimum { BEEnsureFalse(_disposed); const void *ptr = NULL; if (!CFBinaryHeapGetMinimumIfPresent(_binaryHeapRef, &ptr)) return nil; id object = (__bridge id)ptr; id value = [_objectToValue objectForKey:object]; CFBinaryHeapRemoveMinimumValue(_binaryHeapRef); [_objectToValue removeObjectForKey:object]; // if the objects was invalidated, it may no longer be the minimum // therefore reinsert the object and extract again if ([_invalidated containsObject:object]) { [_invalidated removeObject:object]; [self insert:object value:value]; return [self extractMinimum]; } return object; } - (void)insert:(id)object value:(id)value { BEEnsureFalse(_disposed); BEEnsureIsNotNil(object); BEEnsureIsNotNil(value); BEEnsureTrue([value respondsToSelector:@selector(compare:)]); // <NSComparable> [_objectToValue setObject:value forKey:object]; // first to be available furing insertion compare CFBinaryHeapAddValue(_binaryHeapRef, (__bridge void *)object); } - (void)update:(id)object value:(id)value { BEEnsureFalse(_disposed); BEEnsureIsNotNil(object); BEEnsureTrue([value respondsToSelector:@selector(compare:)]); // <NSComparable> [_objectToValue setObject:value forKey:object]; // first to be available during insertion compare [_invalidated addObject:object]; } - (BOOL)containsObject:(id)object { BEEnsureFalse(_disposed); return CFBinaryHeapContainsValue(_binaryHeapRef, (__bridge void *)object); } - (id)valueForObject:(id)object { return [_objectToValue objectForKey:object]; } - (void)removeAllObjects { CFBinaryHeapRemoveAllValues(_binaryHeapRef); [_objectToValue removeAllObjects]; [_invalidated removeAllObjects]; } @end NS_ASSUME_NONNULL_END
source share