Repository: SDWebImage/SDWebImage
File: SDWebImage/SDWebImageDownloaderOperation.m (Objective-C)

Code before:
        _unownedSession = session;
        _callbacksLock = dispatch_semaphore_create(1);
        _coderQueue = dispatch_queue_create("com.hackemist.SDWebImageDownloaderOperationCoderQueue", DISPATCH_QUEUE_SERIAL);
    }
    return self;
}

- (nullable id)addHandlersForProgress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                            completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock {
    SDCallbacksDictionary *callbacks = [NSMutableDictionary new];
    if (progressBlock) callbacks[kProgressCallbackKey] = [progressBlock copy];
    if (completedBlock) callbacks[kCompletedCallbackKey] = [completedBlock copy];
    LOCK(self.callbacksLock);
    [self.callbackBlocks addObject:callbacks];
    UNLOCK(self.callbacksLock);
    return callbacks;
}

- (nullable NSArray<id> *)callbacksForKey:(NSString *)key {
    LOCK(self.callbacksLock);
    NSArray<SDCallbacksDictionary *> *callbackBlocks = [self.callbackBlocks copy];
    UNLOCK(self.callbacksLock);
    NSMutableArray<id> *callbacks = [NSMutableArray arrayWithCapacity:callbackBlocks.count];
    for (SDCallbacksDictionary *callbacksDic in callbackBlocks) {
        id callback = callbacksDic[key];
        if (callback) {
            [callbacks addObject:callback];
        }
    }
    return [callbacks copy]; // strip mutability here
}

- (BOOL)cancel:(nullable id)token {
    BOOL shouldCancel = NO;
    LOCK(self.callbacksLock);
    [self.callbackBlocks removeObjectIdenticalTo:token];
    if (self.callbackBlocks.count == 0) {
        shouldCancel = YES;
    }
    UNLOCK(self.callbacksLock);
    if (shouldCancel) {
        [self cancel];
    }
    return shouldCancel;
}

- (void)start {
    @synchronized (self) {
        if (self.isCancelled) {
            self.finished = YES;
            [self reset];

Review comment (suggestion):
Actually....I don't know why previous writter use a `NSDictionary` to store just two type of completion block. Why not create a simple Object instead ? Without that tricky code anymore.

```objectivec
@interface SDWebImageDownloaderOperationCallback : NSObject

@property (nonatomic, copy, nullable) SDWebImageDownloaderProgressBlock progressBlock;
@property (nonatomic, copy, nullable) SDWebImageDownloaderCompletedBlock completionBlock;

@end

// Then just upgradte all internal APIs.

@property (strong, nonatomic, nonnull) NSMutableArray<SDWebImageDownloaderOperationCallback> *callbackBlocks;

// - (nullable NSArray<id> *)callbacksForKey:(NSString *)key -> Use this instead. Simpler
- (NSArray<SDWebImageDownloaderProgressBlock> *)progressBlocks {
    LOCK(self.callbacksLock);
    NSMutableArray<SDWebImageDownloaderOperationCallback *> *callbackBlocks = [self.callbackBlocks copy];
    UNLOCK(self.callbacksLock);
    NSMutableArray<SDWebImageDownloaderProgressBlock> *mutableProgressBlocks = [NSMutableArray arrayWithCapacity:callbackBlocks.count];
    for (SDWebImageDownloaderOperationCallback callbackBlock in callbackBlocks) {
        [mutableProgressBlocks addObject:callbackBlock];
    }
    return [mutableProgressBlocks copy];
}
```


Code after:
        _finished = NO;
        _expectedSize = 0;
        _unownedSession = session;
        _callbacksLock = dispatch_semaphore_create(1);
        _coderQueue = dispatch_queue_create("com.hackemist.SDWebImageDownloaderOperationCoderQueue", DISPATCH_QUEUE_SERIAL);
    }
    return self;
}

- (nullable id)addHandlersForProgress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                            completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock {
    SDCallbacksDictionary *callbacks = [NSMutableDictionary new];
    if (progressBlock) callbacks[kProgressCallbackKey] = [progressBlock copy];
    if (completedBlock) callbacks[kCompletedCallbackKey] = [completedBlock copy];
    LOCK(self.callbacksLock);
    [self.callbackBlocks addObject:callbacks];
    UNLOCK(self.callbacksLock);
    return callbacks;
}

- (nullable NSArray<id> *)callbacksForKey:(NSString *)key {
    LOCK(self.callbacksLock);
    NSMutableArray<id> *callbacks = [[self.callbackBlocks valueForKey:key] mutableCopy];
    UNLOCK(self.callbacksLock);
    // We need to remove [NSNull null] because there might not always be a progress block for each callback
    [callbacks removeObjectIdenticalTo:[NSNull null]];
    return [callbacks copy]; // strip mutability here
}

- (BOOL)cancel:(nullable id)token {
    BOOL shouldCancel = NO;
    LOCK(self.callbacksLock);
    [self.callbackBlocks removeObjectIdenticalTo:token];
    if (self.callbackBlocks.count == 0) {
        shouldCancel = YES;
    }
    UNLOCK(self.callbacksLock);
    if (shouldCancel) {
        [self cancel];
    }
    return shouldCancel;
}

- (void)start {
    @synchronized (self) {
        if (self.isCancelled) {
            self.finished = YES;
            [self reset];
            return;
        }