How to get array of images from network faster? (iOS) - objective-c

Basically, I have an array of urls as strings, and as I loop through this array, if the element is a url for an image, I want to turn that url into a UIImage object and add it to another array. This is very slow though since I have to request the data for each URL. I've tried using dispatch_async as I show below but it doesn't seem to make any difference at all.
The key is that when I add these objects to my other array, whether they are images or something else they have to stay in order. Can anyone offer any guidance?
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int i=0; i<[slides count]; i++){
__block NSString *mediaURLString = [primaryPhoto objectForKey:#"url"];
if ([self mediaIsVideo:mediaURLString]){
***some code***
}
else{ //if media is an image
dispatch_group_async(group, queue, ^{
mediaURLString = [mediaURLString stringByAppendingString:#"?w=1285&h=750&q=150"];
NSURL *url = [NSURL URLWithString:mediaURLString];
[mutableMedia addObject:url];
NSURL *url = ((NSURL *)self.mediaItem);
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLResponse *response;
NSError *error;
NSData *urlData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
UIImage *image = [[UIImage alloc] initWithData:urlData];
[mutableMedia replaceObjectAtIndex:i withObject:image];
});
}
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

try this:
[self performSelectorInBackground:#selector(WebServiceCallMethod) withObject:nil];
and create one method like this
-(void)WebServiceCallMethod
{
mediaURLString = [mediaURLString stringByAppendingString:#"?w=1285&h=750&q=150"];
NSURL *url = [NSURL URLWithString:mediaURLString];
[mutableMedia addObject:url];
NSURL *url = ((NSURL *)self.mediaItem);
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLResponse *response;
NSError *error;
NSData *urlData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
UIImage *image = [[UIImage alloc] initWithData:urlData];
[mutableMedia replaceObjectAtIndex:i withObject:image];
}
Hope it Helps!!

Do yourself a favor and don't use +sendSynchronousRequest:... Try something like this instead:
dispatch_group_t group = dispatch_group_create();
for (int i=0; i<[slides count]; i++)
{
__block NSString *mediaURLString = [primaryPhoto objectForKey:#"url"];
if ([self mediaIsVideo:mediaURLString]){
***some code***
}
else
{
//if media is an image
mediaURLString = [mediaURLString stringByAppendingString:#"?w=1285&h=750&q=150"];
NSURL *url = [NSURL URLWithString:mediaURLString];
[mutableMedia addObject:url];
NSURL *url = ((NSURL *)self.mediaItem);
NSURLRequest *request = [NSURLRequest requestWithURL:url];
dispatch_group_enter(group);
[NSURLConnection sendAsynchronousRequest: request queue: [NSOperationQueue mainQueue] completionHandler:
^(NSURLResponse *response, NSData *data, NSError *connectionError)
{
if (data.length && nil == connectionError)
{
UIImage *image = [[UIImage alloc] initWithData:data];
[mutableMedia replaceObjectAtIndex:i withObject:image];
}
dispatch_group_leave(group);
}];
}
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// Do stuff here that you want to have happen after all the images are loaded.
});
This will start asynchronous requests for all your URLs. When each request finishes, it will run its completion handler which will update your array, and when all requests have finished, the block in the dispatch_group_notify call will be executed.
This approach has the advantage that you can call it from the main thread, all individual completion blocks will be run on the main thread (thus ensuring thread-safety for the mutableMedia array (at least as far as this code goes)) and the final completion block will also be run on the main thread, so you can do whatever you need to update the UI directly.

There is a nifty solution using dispatch lib. The code below should stand for itself.
The basic idea is that an array contains "input" objects which each will be "transformed" via an asynchronous unary task - one after the other. The final result of the whole operation is an array of the transformed objects.
Everything here is asynchronous. Every eventual result will be passed in a completion handler which is a block where the result is passed as a parameter to the call-site:
typedef void (^completion_t)(id result);
The asynchronous transform function is a block which takes the input as a parameter and returns a new object - via a completion handler:
typedef void (^unary_async_t)(id input, completion_t completion);
Now, the function transformEach takes the input values as an NSArray parameter inArray, the transform block as parameter transform and the completion handler block as parameter completion:
static void transformEach(NSArray* inArray, unary_async_t transform, completion_t completion);
The implementation is a follows:
static void do_each(NSEnumerator* iter, unary_async_t transform,
NSMutableArray* outArray, completion_t completion)
{
id obj = [iter nextObject];
if (obj == nil) {
if (completion)
completion([outArray copy]);
return;
}
transform(obj, ^(id result){
[outArray addObject:result];
do_each(iter, transform, outArray, completion);
});
}
static void transformEach(NSArray* inArray, unary_async_t transform,
completion_t completion) {
NSMutableArray* outArray = [[NSMutableArray alloc] initWithCapacity:[inArray count]];
NSEnumerator* iter = [inArray objectEnumerator];
do_each(iter, transform, outArray, completion);
}
And build and run the following example
int main(int argc, const char * argv[])
{
#autoreleasepool {
// Example transform:
unary_async_t capitalize = ^(id input, completion_t completion) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
sleep(1);
if ([input respondsToSelector:#selector(capitalizedString)]) {
NSLog(#"processing: %#", input);
NSString* result = [input capitalizedString];
if (completion)
completion(result);
}
});
};
transformEach(#[#"a", #"b", #"c"], capitalize, ^(id result){
NSLog(#"Result: %#", result);
});
sleep(10);
}
return 0;
}
will print this to the console:
2013-07-31 15:52:49.786 Sample2[1651:1603] processing: a
2013-07-31 15:52:50.789 Sample2[1651:1603] processing: b
2013-07-31 15:52:51.792 Sample2[1651:1603] processing: c
2013-07-31 15:52:51.793 Sample2[1651:1603] Result: (
A,
B,
C
)
You can easily create a category for NSArray which implements, say a
-(void) asyncTransformEachWithTransform:(unary_async_t)transform
completion:(completion_t)completionHandler;
method.
Have fun! ;)
Edit:
IFF you ask how this applies to your problem:
The array of URLs is the input array. In order to create the transform block, simply wrap your asynchronous network request in an asynchronous method, say:
`-(void) fetchImageWithURL:(NSURL*)url completion:(completion_t)completionHandler;`
Then wrap method fetchImageWithURL:completion: into a appropriate transform block:
unary_async_t fetchImage = ^(id url, completion_t completion) {
[self fetchImageWithURL:url completion:^(id image){
if (completion)
completion(image); // return result of fetch request
}];
};
Then, somewhere in your code (possible a view controller) assuming you implemented the category for NSArray, and your array of urls is property urls:
// get the images
[self.urls asyncTransformEachWithTransform:fetchImage completion:^(id arrayOfImages) {
// do something with the array of images
}];

Related

which dispatch_async method is advisable to load the json data to table view controller?

I have json data and now I want to push into table view? But initially, I have to get the json data so which dispatch method or child thread is recommend so that i can load the data in background and then push it.
Best practice is load data in background thread using dispatch_async method and passing a queue other then main so it don't block UI, and when u have data ready just call reload table view on main thread...
Below is a class an example from real project...
Class level method load transcations
+ (void)getAllTransactionsWithHandler:(void(^)(NSArray *transactions, NSError *error))handler{
dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(q, ^{
NSURL *url = [[NSBundle mainBundle] URLForResource:trnasactionFileName withExtension:#"json"];//[NSData dataWithContentsOfFile:trnasactionFileName];
NSData *data = [NSData dataWithContentsOfURL:url];
if (!data) {
if (handler) {
handler(nil, [NSError errorWithDomain:#"bank" code:900 userInfo:nil]);
}
return ;
}
NSError *error = nil;
NSArray *jsonArray = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
if (error) {
if (handler) {
handler(nil, error);
}
}
else{
Transaction *transaction;
NSString *dateString;
NSMutableArray *objects = [NSMutableArray new];
for (NSDictionary *dic in jsonArray) {
transaction = [Transaction new];
dateString = dic[kOccured];
NSDateFormatter *dateFormater = [NSDateFormatter new];
[dateFormater setDateFormat:#"yyyy-MM-dd"];
transaction.occured = [dateFormater dateFromString:dateString];
transaction.proecessed = [dateFormater dateFromString:dic[kProcessed]];
transaction.desc = dic[kDescription];
transaction.amount = dic[kAmount];
[objects addObject:transaction];
}
if (handler) {
handler([NSArray arrayWithArray:objects], nil);
}
}
});
}
You can use this as
[Transaction getAllTransactionsWithHandler:^(NSArray *transactions, NSError *error) {
if (error) {
}else{
if ([transactions count] > 0) {
weakSelf.objects = transactions;
runOnMainThread(^{
[weakSelf.tableView reloadData];
});
}
}
}];
Where as runOnMainthread is a utility method which will run provided block of code on main thread
void runOnMainThread(void(^block)(void)){
if ([[NSThread currentThread] isMainThread])
block();
else{
dispatch_sync(dispatch_get_main_queue(), ^{
block();
});
}
}

Returning an object from inside block within category class method implementation

I have run into a certain problem with my implementation which I don't really know how to solve. Could You please advise.
I'm trying to implement an NSManagedObject category class Photo+Flickr.m with one class method +(void)photoWithFlickrData:inManagedObjectContext:
What I would like to do is download data from Flickr API using NSURLSessionDownloadTask and then create Photo object and insert this new created object into database (if it's not already there). This part works fine.
And at the end I would like to return new created (or object that was found in db) Photo object. And this is where I run into problem. Since I'm using category I can't use instance variables. I can't really find any good solution to get this Photo object from inside this completionHandler block.
My code:
#implementation Photo (Flickr)
+ (void)photoWithFlickrData:(NSDictionary *)photoDictionary
inManagedObjectContext:(NSManagedObjectContext *)context
{
NSString *placeId = [photoDictionary valueForKeyPath:FLICKR_PHOTO_PLACE_ID];
NSURL *urlInfoAboutPlace = [FlickrFetcher URLforInformationAboutPlace:placeId];
NSURLRequest *request = [NSURLRequest requestWithURL:urlInfoAboutPlace];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
NSURLSessionDownloadTask *task =
[session downloadTaskWithRequest:request
completionHandler:^(NSURL *localfile, NSURLResponse *response, NSError *error) {
if(!error) {
NSData *json = [NSData dataWithContentsOfURL:localfile];
NSDictionary *flickrPlaceDictionary = [NSJSONSerialization JSONObjectWithData:json
options:0
error:NULL];
dispatch_async(dispatch_get_main_queue(), ^{
Photo *photo = nil;
// flickr photo unique id
NSString *uniqueId = [photoDictionary valueForKeyPath:FLICKR_PHOTO_ID];
NSFetchRequest *dbRequest = [NSFetchRequest fetchRequestWithEntityName:#"Photo"];
dbRequest.predicate = [NSPredicate predicateWithFormat:#"uniqueId = %#", uniqueId];
NSError *error;
NSArray *reqResults = [context executeFetchRequest:dbRequest error:&error];
if (!reqResults || error || [reqResults count] > 1) {
//handle error
} else if ([reqResults count]) {
//object found in db
NSLog(#"object found!");
photo = [reqResults firstObject];
} else {
//no object in db so create a new one
NSLog(#"object not found, creating new one");
photo = [NSEntityDescription insertNewObjectForEntityForName:#"Photo"
inManagedObjectContext:context];
//set its properties
photo.uniqueId = uniqueId;
photo.title = [photoDictionary valueForKey:FLICKR_PHOTO_TITLE];
photo.region = [FlickrFetcher extractRegionNameFromPlaceInformation:flickrPlaceDictionary];
NSLog(#"title: %#", photo.title);
NSLog(#"ID: %#", photo.uniqueId);
NSLog(#"region: %#", photo.region);
}
});
}
}];
[task resume];
//how to get Photo *photo object???
//return photo;
}
I would really appreciate any suggestions on how to implement this.
Since you have async operations happening inside your blocks, you'll need to pass a completion handler (block) to your photoWithFlickrData:inManagedObjectContext: method and call it when you have valid photo data.
You'll need to add a new parameter to your method so you can pass in the completion handler. I'd do something like this:
+ (void)photoWithFlickrData:(NSDictionary *)photoDictionary
inManagedObjectContext:(NSManagedObjectContext *)context
withCompletionHandler:(void(^)(Photo *photo))completionHandler
Then, when you have a valid photo object, call completionHandler like so:
completionHandler(photo);
It looks like you'd want to put that at the very end of the block you're passing to dispatch_async:
/* ... */
dispatch_async(dispatch_get_main_queue(), ^{
Photo *photo = nil;
/* ... */
completionHandler(photo);
});
/* ... */
Then, you can call your method like so:
[Photo photoWithFlickrData:photoDictionary
inManagedObjectContext:context
withCompletionHandler:^(Photo* photo) {
/* use your valid photo object here */
}
];
Outside of your block before you call [session downloadTaskWithRequest:.....] define a variable like this
__block Photo *photoObject = nil;
Then inside the block after you finish setting its properties, set
photoObject = photo;
Now you can do whatever you want with the photoObject variable outside of the block.
Check out this Apple developer documentation on Blocks and Variables.

Big image download loop keeps leaking inside GCD queue

I'm downloading over 6000 high resolution (about 2.6GB), but can change depending on which customer logs in into our app. Because the sales agents need all the product images to be loaded when they are outside the country they need all to be available in offline-mode.
My code is pretty much ad-hoc and the problem of my code leaking is probably very obvious to the more experienced Objective-C programmers.
At the moment the status is that the code has downloaded 2041 of the 6253 and 580 seconds have passed. Yet the emulator's memory is already at 931MB.
Could I do anything to improve this code to get the memory back after each loop or X-amount of loop cycles?
int total_new_images = total_images_in_db - total_images_locally;
// Fetch the real pictures now
dispatch_queue_t imageQueue = dispatch_queue_create("Image Queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(imageQueue, ^{
NSString *path = [NSString new];
NSURL *url = [NSURL new];
NSData *data = [NSData new];
NSString *savePath = [NSString new];
NSArray *imgUrlArray = [NSArray new];
NSDate *starTime = [NSDate new];
#autoreleasepool {
int c = 0;
FMResultSet *rs = [self.efashionDB executeQuery:#"SELECT * FROM app_images"];
while ([rs next]) {
imgUrlArray = [[rs stringForColumn:#"full_url"] componentsSeparatedByString:#"/"];
savePath = [NSString stringWithFormat:#"%#/%#", imgFolder, [imgUrlArray objectAtIndex:[imgUrlArray count]-1]];
if ([self.fileMgr fileExistsAtPath:savePath]) {
//NSLog(#"'%#' is already saved locally.", [imgUrlArray objectAtIndex:[imgUrlArray count]-1]);
continue;
}
path = [NSString stringWithString:[rs stringForColumn:#"full_url"]];
url = [NSURL URLWithString:path];
data = [NSData dataWithContentsOfURL:url];
if (![data writeToFile:savePath atomically:YES]) {
NSLog(#"Saving of \"%#\" failed!", [imgUrlArray objectAtIndex:[imgUrlArray count]-1]);
} else {
//NSLog(#"Saving of \"%#\" succeeded!", [imgUrlArray objectAtIndex:[imgUrlArray count]-1]);
// Escape back to the main thread and give status update
dispatch_async(dispatch_get_main_queue(), ^{
NSString *progress = [[NSString alloc] initWithFormat:#"%d of the %d new images downloaded\nTime passed: %d seconds", c, total_new_images, (int)([starTime timeIntervalSinceNow] * -1)];
[_imageConsole setText:progress];
});
c++; // Only increment if it succeeded
}
if (c == 20) {
//break;
}
}
[rs close];
}
});
Try moving the #autoreleasepool inside the while loop. Otherwise your allocated objects won't be released until the while loop has finished.

IOS- Use to Try/Catch for load Json

I'm working load data from the server, but sometimes the server is not connected (error).
I wanna use to Try/Catch or something to avoid error app
1: try/cactch at load data
2: try/catch at image
I don't know how to use
I write code is:
#try
{
dispatch_async(htvque, ^{
NSData* data = [NSData dataWithContentsOfURL: [NSURL URLWithString:listChannel]];
NSError* error;
jsonTable = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
if (error) {
NSLog(#"%#", error);
}
else
{
NSMutableArray *arrImage = [jsonTable objectForKey:#"ListImage"];
for (int i =0; i<arrImage.count; i++) {
UIImage * result;
UIImageView *imgView = [[UIImageView alloc] init];
imgView.frame = CGRectMake(0, 30 * i , 20, 20);
imgView.image = result;
#try
{
NSData * data = [NSData dataWithContentsOfURL:[NSURL URLWithString: [arrImage objectAtIndex:i]]];
result = [UIImage imageWithData:data];
[self.view addSubview:imgView];
}
#catch
{}
#finally
{}
}
}
});
}
#catch(NSException * exp)
{
NSLOG(#"abc");
}
#finnaly
{
}
Don't use exceptions for this purpose for your ObjC programs.
In ObjC, just reserve use of exceptions for cases where:
you do not intend to recover
and when you do not intend to recover
-- if an exception is even appropriate there (I just don't use them, but that's a little too 'core for a lot of people).
Anyways - an exception in ObjC indicates a programmer error and is logically something you cannot expect to recover from (unlike other languages). Figure out what your error is instead of "try and swallow" error handling.
The remedy I suggest is to create a new question, which shows the appropriate code, and details the exception, how to reproduce it, etc.
Note: There is actually a handful of oddball Cocoa APIs which will throw in less than exceptional cases, when they should have just used another approach to error handling, such as NSError. The vast majority of Cocoa exceptions you will see in development are problems which you should and can correct (range error, does not respond to selector, reference counting).
I think this will be a reasonably robust way to do what you want. I wanted the example to show lots f error checking without relying on try/catch
dispatch_async(htvque, ^{
NSError* error = nil;
NSData* data = [NSData dataWithContentsOfURL:[NSURL URLWithString:listChannel] options:0 error:&error];
if (error) { NSLog(#"%#", error); return; }
NSDictionary *jsonTable = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
if (error) { NSLog(#"%#", error); return; }
if (![jsonTable isKindOfClass:[NSDictionary class]]) { NSLog(#"jsonTable is not an NSDictionary: %#", jsonTable); return; }
NSArray *images = [jsonTable objectForKey:#"ListImage"];
if (![images isKindOfClass:[NSArray class]]) { NSLog(#"images is not an NSArray: %#", images); return; }
NSMutableArray *dataForImages = [NSMutableArray arrayWithCapacity:images.count];
// Build up an array with all the image data. For simplicity sake, I'll just skip ones the fail to load.
for (NSString *URLString in images) {
if (![URLString isKindOfClass:[NSString class]]) { NSLog(#"URLString is not an NSString: %#", URLString); continue; }
NSData* data = [NSData dataWithContentsOfURL:[NSURL URLWithString:URLString] options:0 error:&error];
if (error) { NSLog(#"%#", error); continue; }
[dataForImages addObject:data];
}
// MUST SWITCH TO MAIN QUEUE BEFORE UPDATING UI!!!!!!!
dispatch_sync(dispatch_get_main_queue(), ^{
// This is just a different way of iterating the array.
[dataForImages enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
UIImage *image = [UIImage imageWithData:obj];
if (!image) { NSLog(#"Could not create image from data at index %d", idx); return; }
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
imageView.frame = CGRectMake(0, 30 * idx , 20, 20);
[self.view addSubview:imageView];
}];
});
});
This really shouldn't be a working solution, rather a rough outline.

Memory leak issue while downloading large number of images

I am trying to downloading more then 600 images in loop with a progress meter on the top of the screen to the user. I blocked my screen with a fade layer for showing activity and progress.
I am getting the memory warning message in between and app getting crashes.
My steps to reach the loop are :
On app delegate, I check first core data table for all rows which is having "0" value in isImageAvailable bool field.
If shows me some count (say 600), and I show and alert with YES and NO option.
On YES : [self performSelector:#selector(myDownload:) withObject:nil afterDelay:0.2];
in myDownload
NSOperationQueue *queue = [NSOperationQueue new];
// Create our NSInvocationOperation to call loadDataWithOperation, passing in nil
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self
selector:#selector(startUpdatingRecords:) object:nil];
// Add the operation to the queue
[queue addOperation:operation];
[operation release];
[queue release];
in startUpdatingRecords :
-(void)startUpdatingRecords:(id)sender
{
[self performSelectorInBackground:#selector(updateProgressMeter:) withObject: [NSString stringWithFormat:#"%d",self.loopStartIndex]];
// Variable declarations
CGSize newSizeLarge ;
NSPredicate *predicate;
NSMutableArray *MatchingID;
Image_DB *data;
// Cache Directory path
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSData *responseData; // = [[NSData alloc]init] ;
NSURL *url = [[[NSURL alloc]init] autorelease];
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc]init] autorelease];
UIImage *imgSelected_Large = [[[UIImage alloc]init] autorelease];
// Loop through all IDs
for (int i = 0; i < [self.arrayOfID count]; i++) //for (int i = loopStart; i < loopEnd; i++)
{
if (self.abortDownload)
{
break;
}
NSString *documentsDirectory = [[[NSString alloc] initWithFormat:#"%#",[paths objectAtIndex:0]] autorelease];
documentsDirectory = [paths objectAtIndex:0];
documentsDirectory = [documentsDirectory stringByAppendingFormat:#"/ImageFolder"]; // Image folder path
myClass *classObj = [self.arrayOfID objectAtIndex:i];
NSString *strURl = [[[NSString alloc] initWithFormat:#"%#%#", self.MyURL,recipeObj.recipeImageStr] autorelease];
//NSLog(#"URL = %#",strURl);
url = [NSURL URLWithString:strURl];
request = [NSMutableURLRequest requestWithURL:url];
responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:NULL error:NULL]; // Get Image Data into NSData
//imgSelected_Large = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:strURl]]];
NSLog(#"Download Count = %d",i+1);
if (responseData != nil)
{
imgSelected_Large = [UIImage imageWithData:responseData];
// Resizining image
newSizeLarge.width = 320;
newSizeLarge.height = 180;
imgSelected_Large = [self imageWithImage:imgSelected_Large scaledToSize:newSizeLarge]; // New sized image
NSData *dataPhoto; // no need to release it because UIImageJPEGRepresentation gives autoreleased NSData obj.
dataPhoto = UIImageJPEGRepresentation(imgSelected_Large, 0.6); // Set new image representation and its Compression Quality
documentsDirectory = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"Image_%d", classObj.nodeID]];
[dataPhoto writeToFile:documentsDirectory atomically:YES]; //Write file to local folder at default path
predicate = [NSPredicate predicateWithFormat: #"(image_ID = %d )",recipeObj.nodeID];
MatchingID = [CoreDataAPIMethods searchObjectsInContext:#"Image_DB" :predicate :#"image_ID" :YES :self.managedObjectContext];
// Setting flag variable for available image
for (int j = 0; j< [MatchingID count]; j++)
{
//Assign the Authors Records in Class Object and save to Database
data = (Image_DB*) [MatchingID objectAtIndex:j];
// data.image_large = dataPhoto; // Code for storing BLOB object to DB
data.extra_1 = #"1";
//NSLog(#"Flag updated");
}
}
// Exit out code
if ( i == [self.arrayOfID count] - 1 || i == [self.arrayOfID count]) // Its the last record to be stored
{
NSError *error;
if (![self.managedObjectContext save:&error])
{
// Handle the error...
NSLog(#"Error in updating %#",error);
}
self.isUpdateImageCalled = NO;
[self performSelectorOnMainThread:#selector(removeProgressMeter) withObject:nil waitUntilDone:NO];
}
// Update UI screen while in downloading process
[self performSelectorInBackground:#selector(updateProgressMeter:) withObject:[NSString stringWithFormat:#"%d",self.loopStartIndex+i+1]];
}
}
If I didn't release responseData then my app shows me memory warning and got crashed. If I released then, [NSConcreteMutableData release]: message sent to deallocated instance 0x1e931de0 error occures.
How to refine my code. Can any one suggest me on my code and rework and make a refined code.
Please please help me out.
Your responseData returned by sendSynchronousRequest is autoreleased thus you shouldn't release it yourself. For the first sight I don't see a memory leak in your code. It is possible that your application actually uses too much memory, without leaking it. Try to place an autorelease pool inside your for cycle:
for (...) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// your original code with a lot of autoreleased objects
[pool release];
}
If you wrap your code within an autorelease pool, all objects that are sent the autorelease message inside the wrap will be actually released when the pool itself is released: this way you purge the memory in every for cycle.
See also Using Autorelease Pools in the doc, it specifically mentions that you should use them in the case "if you write a loop that creates many temporary objects".