使用 __block 变量阻止泄漏

Block leak with __block variable

我有一个很大的内存泄漏,我已经确定发生 in/on requestContentEditingInputWithOptions: 方法。如果我理解正确的话,它会发生在 img 变量中。如果我把它设为 __block __weak,那么在我分配它 (img = [UIImage...]) 之后,图像就已经是 nil 了。我在某个地方很傻吗?或者我将如何避免这种内存泄漏?

- (UIImage*) getRightlySizedImgFromAsset:(PHAsset*)asset {

    __block UIImage *img;


    PHContentEditingInputRequestOptions *coptions = [PHContentEditingInputRequestOptions new];
    coptions.canHandleAdjustmentData = ^BOOL(PHAdjustmentData *adjustmentData) { return NO; };

    //semaphore used so the block runs synchronously and I can return img from this method at the end
    dispatch_semaphore_t sem = dispatch_semaphore_create(0);

    [asset requestContentEditingInputWithOptions:coptions completionHandler:^(PHContentEditingInput *contentEditingInput, NSDictionary *info) {
        NSURL* url = [contentEditingInput fullSizeImageURL];
        int orientation = [contentEditingInput fullSizeImageOrientation];

        CIImage* inputImage = [CIImage imageWithContentsOfURL:url options:nil];
        inputImage = [inputImage imageByApplyingOrientation:orientation];
        CIContext *context = [CIContext contextWithOptions:nil];

        img = [UIImage imageWithCGImage:[context createCGImage:inputImage fromRect:inputImage.extent]];

        dispatch_semaphore_signal(sem);
    }];
    dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);


    if (needToDoSomethingWithImg){
        [self doSomethingWithImage:img];
    }


    return img;
}

运行这段代码通过静态分析器(shift+command+B 或从 "Product" 菜单中选择 "Analyze"),它会指出 createCGImage 正在创建一个你永远不会释放的 CGImageRef

您可能想要执行以下操作:

CGImageRef imageRef = [context createCGImage:inputImage fromRect:inputImage.extent];
img = [UIImage imageWithCGImage:imageRef];
CFRelease(imageRef);

顺便说一句,您不应该同步执行此操作。你应该这样做:

- (void) getRightlySizedImgFromAsset:(PHAsset*)asset completionHandler:(void (^)(UIImage *))completionHandler {

    PHContentEditingInputRequestOptions *coptions = [PHContentEditingInputRequestOptions new];
    coptions.canHandleAdjustmentData = ^BOOL(PHAdjustmentData *adjustmentData) { return NO; };

    [asset requestContentEditingInputWithOptions:coptions completionHandler:^(PHContentEditingInput *contentEditingInput, NSDictionary *info) {
        NSURL* url = [contentEditingInput fullSizeImageURL];
        int orientation = [contentEditingInput fullSizeImageOrientation];

        CIImage* inputImage = [CIImage imageWithContentsOfURL:url options:nil];
        inputImage = [inputImage imageByApplyingOrientation:orientation];
        CIContext *context = [CIContext contextWithOptions:nil];

        CGImageRef imageRef = [context createCGImage:inputImage fromRect:inputImage.extent];
        UIImage *image = [UIImage imageWithCGImage:imageRef];
        CFRelease(imageRef);

        // if this stuff needs to happen on main thread, then dispatch it to the main thread

        if (needtodosomethingwithit)
            [self doSomethingWithImage:image];

        if (completionHandler) {
            completionHandler(image);
        }
    }];
}

Rob 说得对。图像可能很大,所以这就是你有大泄漏的原因。 Core Foundation 对象的经验法则是 "create rule." 在 "Create Rule" 上的 Xcode 中搜索并阅读文章。它的要点是这样的:

Core Foundation functions have names that indicate when you own a returned object:

Object-creation functions that have “Create” embedded in the name;

Object-duplication functions that have “Copy” embedded in the name. If you own an object, it is your responsibility to relinquish ownership (using CFRelease) when you have finished with it.