在dispatch_async中收到内存警告

4

这是我在cellForRowAtIndex中编写的下载图片的代码:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    @autoreleasepool {
        __block UIImage * img;
        __block NSData *data;
        if(![messageDocument.SmallImageURL isEqual:@""])
        {
            data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:messageDocument.SmallImageURL]];
            img = [UIImage imageWithData:data];
        }

        dispatch_async(dispatch_get_main_queue(), ^{
            imgUser.image = img;
            img = nil;

            imgUser.contentMode = UIViewContentModeScaleAspectFill;
            CGSize size;
            if(imgUser.image.size.width > imageFrame.size.width || imgUser.image.size.height > rectImage.size.height)
            {
                if(imgUser.image.size.width < imageFrame.size.width)
                {
                    rectImage.size.width = imgUser.image.size.width;
                }

                if(imgUser.image.size.height < rectImage.size.height)
                {
                    rectImage.size.height = imgUser.image.size.height;
                }

                size = CGSizeAspectFit(imgUser.image.size, rectImage.size);
                imgUser.frame = CGRectMake(imgUser.frame.origin.x, rectImage.origin.y, size.width, size.height);

                height = imgUser.frame.size.height;
            }
            else
            {
                imgUser.frame = CGRectMake(imageFrame.origin.x, imageFrame.origin.y, imgUser.image.size.width, imgUser.image.size.height);
                height = imgUser.image.size.height;

            }

            CGPoint contentOffset = tableMessageDetail.contentOffset;
            [tableMessageDetail beginUpdates];
            [tableMessageDetail endUpdates];
            [tableMessageDetail setContentOffset:contentOffset];
        });

            messageDocument.Pic = data;
            data = nil;
        if(messageDocument.Pic != nil)
        {
            Attachment *attachment = [Attachment new];
            attachment.DocId = messageDocument.DocId;
            attachment.DocURL = messageDocument.DocURL;
            attachment.ImageId = messageDocument.ImageId;
            attachment.MessageId = messageDocument.MessageId;
            attachment.SmallImageURL = messageDocument.SmallImageURL;
            attachment.OriginalFileName = messageDocument.OriginalFileName;
            if([messageDocument.DocURL isEqual:@""])
            {
                NSArray *attachmentArray = [messageDocument.SmallImageURL componentsSeparatedByString:@"/"];
                NSString *attachmentName = [attachmentArray objectAtIndex:attachmentArray.count - 1];
                attachment.AttachmentName = attachmentName;
            }
            else
            {
                NSArray *attachmentArray = [messageDocument.DocURL componentsSeparatedByString:@"/"];
                NSString *attachmentName = [attachmentArray objectAtIndex:attachmentArray.count - 1];
                attachment.AttachmentName = attachmentName;
            }

            attachment.Pic = messageDocument.Pic;
            [[CommonModel shared]CreateAttachment:attachment];
            [[CommonModel shared]UpdateMessageDocumentPic:messageDocument];

            attachment = nil;
        }
    }
});  

但是如果有超过6张图片,我会在控制台上收到以下消息的内存异常:

来自调试器的消息:由于内存问题终止


2
@autoreleasepool 需要在 for 循环内部。 - rmaddy
1
这部分代码的作用是什么: dispatch_async(dispatch_get_main_queue(), ^{ }); ? - Nicolas Miari
1
顺便提一下,请遵循标准命名约定。方法、变量和属性名称应以小写字母开头。类名应以大写字母开头。 - rmaddy
@NicolasMiari:这是未使用的代码。现在已将其删除。 - Nitish
@JeremyHuddlestonSequoia:收到内存警告后,过了一段时间我收到了消息:由于内存问题终止了调试器。 - Nitish
显示剩余4条评论
2个回答

4
你的下载逻辑没有问题,问题在于你试图将图片保存在数组中,这会增加应用程序的内存。由于你保存的图像是压缩的png格式,它们看起来并不占用太多空间,但它会显着增加应用程序的内存使用量,并可能导致应用程序崩溃。
为解决此问题,你需要在每个文件下载完成后立即将其保存到文档目录中,然后仅保存图像名称(或缩略图,如果需要在其他地方显示)。
更新:
你可以使用Xcode中的Instruments工具分析应用程序的内存分配情况。它可以为消耗最多内存的对象提供提示。

2
我不认同“没有问题”的说法。代码块应该始终具有简单的逻辑结构。块中存在复杂的代码,这种代码是无法进行测试或者维护的。其他笔记可以指出问题的根源,因此请投票赞同。 - Marek R

1

正如@Marek R所说,您需要使用简单逻辑的块。首先,创建一个仅从url异步下载图像的方法。

var cache = NSCache()

func imageForUrl(urlString: String, completionHandler:(image: UIImage?, url: String) -> ()) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), {()in
    var data: NSData? = self.cache.objectForKey(urlString) as? NSData

    if let goodData = data {
        let image = UIImage(data: goodData)
        dispatch_async(dispatch_get_main_queue(), {() in
            completionHandler(image: image, url: urlString)
        })
        return
    }

    var downloadTask: NSURLSessionDataTask = NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: urlString)!, completionHandler: {(data: NSData!, response: NSURLResponse!, error: NSError!) -> Void in
        if (error != nil) {
            completionHandler(image: nil, url: urlString)
            return
        }

        if data != nil {
            let image = UIImage(data: data)
            self.cache.setObject(data, forKey: urlString)
            dispatch_async(dispatch_get_main_queue(), {() in
                completionHandler(image: image, url: urlString)
            })
            return
        }

    })
    downloadTask.resume()
})

}

第二步,调用imageFromUrl方法,然后在块之外继续进行图像修改:
imageForUrl("http://useYourLinkHere/image.png", completionHandler:{(image: UIImage?, url: String) in
    self.imgUser.image = image!
})

        imgUser.contentMode = UIViewContentModeScaleAspectFill;
        CGSize size;
        if(imgUser.image.size.width > imageFrame.size.width || imgUser.image.size.height > rectImage.size.height)
        {
            if(imgUser.image.size.width < imageFrame.size.width)
            {
                rectImage.size.width = imgUser.image.size.width;
            }

            if(imgUser.image.size.height < rectImage.size.height)
            {
                rectImage.size.height = imgUser.image.size.height;
            }

            size = CGSizeAspectFit(imgUser.image.size, rectImage.size);
            imgUser.frame = CGRectMake(imgUser.frame.origin.x, rectImage.origin.y, size.width, size.height);

            height = imgUser.frame.size.height;
        }
        else
        {
            imgUser.frame = CGRectMake(imageFrame.origin.x, imageFrame.origin.y, imgUser.image.size.width, imgUser.image.size.height);
            height = imgUser.image.size.height;

        }

        CGPoint contentOffset = tableMessageDetail.contentOffset;
        [tableMessageDetail beginUpdates];
        [tableMessageDetail endUpdates];
        [tableMessageDetail setContentOffset:contentOffset];

        messageDocument.Pic = data;
        data = nil;
    if(messageDocument.Pic != nil)
    {
        Attachment *attachment = [Attachment new];
        attachment.DocId = messageDocument.DocId;
        attachment.DocURL = messageDocument.DocURL;
        attachment.ImageId = messageDocument.ImageId;
        attachment.MessageId = messageDocument.MessageId;
        attachment.SmallImageURL = messageDocument.SmallImageURL;
        attachment.OriginalFileName = messageDocument.OriginalFileName;
        if([messageDocument.DocURL isEqual:@""])
        {
            NSArray *attachmentArray = [messageDocument.SmallImageURL componentsSeparatedByString:@"/"];
            NSString *attachmentName = [attachmentArray objectAtIndex:attachmentArray.count - 1];
            attachment.AttachmentName = attachmentName;
        }
        else
        {
            NSArray *attachmentArray = [messageDocument.DocURL componentsSeparatedByString:@"/"];
            NSString *attachmentName = [attachmentArray objectAtIndex:attachmentArray.count - 1];
            attachment.AttachmentName = attachmentName;
        }

        attachment.Pic = messageDocument.Pic;
        [[CommonModel shared]CreateAttachment:attachment];
        [[CommonModel shared]UpdateMessageDocumentPic:messageDocument];

        attachment = nil;
    }

你在代码块外使用的代码应该分解成多个方法,其中之一应该被称为:'resizeImageFrame()',另一个应该是 'loadModel()',以便有序地编写代码。

祝好运!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接