Reputation: 14123
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
Upvotes: 4
Views: 377
Reputation: 1112
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!
Upvotes: 1
Reputation: 882
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.
Upvotes: 4