Received memory warning in dispatch_async

This is the code I am writing in cellForRowAtIndex for downloading the image :

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;
        }
    }
});  

But if there are more than 6 images, I am getting Memory exception with the following message on console :

Message from debugger: Terminated due to memory issue

Answers


There is nothing wrong in your downloading logic. The issue is that you are trying to save the images in your array, which end up increasing your application memory. As the images that you are holding are compressed pngs, they do not appear to hold much size, but it drastically increase the memory of your application and can crash your app.

To solve your issue, you need to save each and every file to document directory as soon as it is downloaded and then save the name of the images (or thumbnail if you need to display it somewhere) instead of the whole full resolution images.

Update

You can analyse memory allocation for your application using instruments tool in Xcode. It can give you hints for the object that are consuming the most memory.


As @Marek R said you have to use block with simple logic. First, create a method which only download the image async from 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()
})

}

Second, call imageFromUrl method and then continue your image modifications out of the block:

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;
    }

The code you use out of the block should be break in many methods, one of them should be called: 'resizeImageFrame()' another one should be 'loadModel()', to have an ordered code.

Good luck!


Need Your Help

SQL Server 2008 Witness Server error

sql-server database

Our SQL Server 2008 mirroring was setup and working a few weeks ago with a 3rd server as the witness. However, we started getting the following error message recently:

How to get previous page URL in javascript?

javascript cookies browser back-button

How can I design a function that will track the previous page a user has viewed. It will be should be designed to be called on each page.