I have a method that saves a bunch of UIImages as JPGs and my app is crashing, I believe due to memory not being released. I'm using UIImageJPEGRepresentation to save the images, and I'm wrapping it in an autorelease pool but it doesn't seem to be working.
for (Images *anImage in images) {
NSAutoreleasePool* p = [[NSAutoreleasePool alloc] init];
NSString *fileName = [NSString stringWithFormat:#"%#-%i-%i-%i.jpg", appDelegate.currentUserName, appDelegate.reportId, aDefect.defectId, i];
[UIImageJPEGRepresentation(anImage.image, 1) writeToFile:localImagePath atomically:NO];
[p drain];
i++;
}
When I run the code above, the analyser shows me that more and more memory is being used, and it eventually crashes. If I remove the line - [UIImageJPEGRepresentation(anImage.image, 1) writeToFile:localImagePath atomically:NO]; it works fine.
FYI the loop iterates through an array of NSManagedObjects.
Any help would be appreciated! Thanks
Here is the new code as per suggestion -
- (void) convertImages {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Report" inManagedObjectContext:context];
NSPredicate *predicate = [NSPredicate
predicateWithFormat:#"status != %#", #"Leads"];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:predicate];
[fetchRequest setIncludesPropertyValues:NO];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
for (Report *aReport in fetchedObjects) {
appDelegate.reportId = [aReport.reportId intValue];
NSArray *defects = [self getAllItemDefects];
for (ItemDefect *anItemDefect in defects) {
NSArray *defectImages = [self getImages:[anItemDefect.itemDefectId intValue] isMainImage:NO];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^(void) {
int i = 0;
for (Images *anImage in defectImages) {
NSString *fileName = [NSString stringWithFormat:#"%#-%i-%i-%i.jpg", appDelegate.currentUserName, [aReport.reportId intValue], [anItemDefect.itemDefectId intValue], i];
NSString *localImagePath = [documentsDirectory stringByAppendingPathComponent:fileName];
[UIImageJPEGRepresentation(anImage.image, 1) writeToFile:localImagePath atomically:NO];
i++;
}
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"FINISH: do eventual operations");
});
});
}
}
}
That gives me this error -
If I load the defectImages array within the dispatch block, it just does nothing. Thanks for your help.
Edit - As CoreData is not thread safe, I've declared new FetchRequests and ObjectContexts to fix that problem. I'm no longer getting the bad access error, but I'm back to running out of memory. I've simplified the code for you, but its producing a memory leak in this state -
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^(void) {
NSFetchRequest *backgroundFetchRequest = [[NSFetchRequest alloc] init];
NSManagedObjectContext *backgroundContext = [[[NSManagedObjectContext alloc] init] autorelease];
[backgroundContext setPersistentStoreCoordinator:persistentStoreCoordinator];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Images" inManagedObjectContext:backgroundContext];
NSPredicate *predicate = [NSPredicate
predicateWithFormat:#"itemDefectId=%i AND reportId=%i AND isMainImage=%i", itemDefectId, appDelegate.reportId, NO];
[backgroundFetchRequest setEntity:entity];
[backgroundFetchRequest setIncludesPropertyValues:NO];
[backgroundFetchRequest setPredicate:predicate];
NSArray *defectImages = [backgroundContext executeFetchRequest:backgroundFetchRequest error:&error];
NSMutableArray *images = [[NSMutableArray alloc] init];
for (Images *anImage in defectImages) {
[images addObject:anImage.image];
}
[images release];
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"FINISH: do eventual operations");
});
});
This isn't the good solution, use GCD:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
for (int i=0; i<[images count]; i++) {
dispatch_async(queue, ^(void) {
Images *anImage = (Images *)[images objectAtIndex:i];
NSString *fileName = [NSString stringWithFormat:#"%#-%i-%i-%i.jpg", appDelegate.currentUserName, appDelegate.reportId, aDefect.defectId, i];
[UIImageJPEGRepresentation(anImage.image, 1) writeToFile:localImagePath atomically:NO];
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"FINISH to write image %d", i);
});
});
}
Moreover, in your code, filename is ever the same, it doesn't change and isn't used. Your code is partial?
However, use dispatcher for async process.
Related
I am working on upload images application which seems working fine for me. I am uploading 100 images. and when i run this process 4-5 times application crashes out.
This seems to me memory usage issue.. How to reduce that memory. I don't want to my application get crashed.
btn_Cancel.userInteractionEnabled=NO;
btn_Share.userInteractionEnabled=NO;
btn_Back.userInteractionEnabled=NO;
btn_BackTransp.userInteractionEnabled=NO;
btn_BareCode.userInteractionEnabled=NO;
[self integratedLoaderView];
NSURL *url=[NSURL URLWithString:#"http:/apiurl /"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{
NSString *str_EstimateNumber=[NSString stringWithFormat:#"%#",txtFld_FolderName.text];
NSDateFormatter *formatter=[[NSDateFormatter alloc] init];
formatter.dateFormat=#"yyyy-MM-dd";
string=[formatter stringFromDate:[NSDate date]];
NSString *str_time;
formatter.dateFormat=#"hh.mm.ss a";
str_time=[formatter stringFromDate:[NSDate date]];
NSString *str_AppendString=[NSString stringWithFormat:#"%# at %#",string,str_time];
NSInteger str_Counter=[[NSString stringWithFormat:#"%lu",(unsigned long)[delegate.ary_ImageData count]] integerValue];
NSString *str_Value=[NSString stringWithFormat:#"%ld",(long)str_Counter];
NSString *str_Username=[NSString stringWithFormat:#"%#/Images",txtFld_SelectedFTP.text];
abc=0;
for (int i=0;i<[ary_Tosend count]; i++)
{
sleep(0.6);
abc++;
if ([str_ConnectionStatus isEqualToString:#"WiFi"])
{
}
if ([str_ConnectionStatus isEqualToString:#"Cellular"])
{
}
if ([str_ConnectionStatus isEqualToString:#"No Connection"])
{
btn_Cancel.userInteractionEnabled=YES;
btn_Share.userInteractionEnabled=YES;
btn_Back.userInteractionEnabled=YES;
btn_BackTransp.userInteractionEnabled=YES;
btn_BareCode.userInteractionEnabled=YES;
NSString *str_Total=[NSString stringWithFormat:#"%lu",(unsigned long)[ary_Tosend count]];
NSString *str_Saved=[NSString stringWithFormat:#"%lu",(unsigned long)[delegate.ary_ImageData count]];
NSInteger total_Count=[str_Total integerValue]-[str_Saved integerValue];
[self saveInDBWhenConnectionLost:total_Count];
[self ShowAlertForCheckInternetConnection];
return ;
}
if ([str_ConnectionStatus isEqualToString:#""])
{
btn_Cancel.userInteractionEnabled=YES;
btn_Share.userInteractionEnabled=YES;
btn_Back.userInteractionEnabled=YES;
btn_BackTransp.userInteractionEnabled=YES;
btn_BareCode.userInteractionEnabled=YES;
NSString *str_Total=[NSString stringWithFormat:#"%lu",(unsigned long)[ary_Tosend count]];
NSString *str_Saved=[NSString stringWithFormat:#"%lu",(unsigned long)[delegate.ary_ImageData count]];
NSInteger total_Count=[str_Total integerValue]-[str_Saved integerValue];
[self saveInDBWhenConnectionLost:total_Count];
[self ShowAlertForCheckInternetConnection];
return ;
}
NSString *str_fileName;
str_fileName=[NSString stringWithFormat:#"%#_%# %i.jpg",str_EstimateNumber,str_AppendString,abc];
NSDictionary *params=nil;
NSData *imageToUpload=UIImageJPEGRepresentation([ary_Tosend objectAtIndex:i],0.25);
NSString *encodedString=[NSString stringWithFormat:#"%#",[imageToUpload base64Encoding]];
params= #{
#"categoryName" :txtFld_Category.text,
#"folderName" :txtFld_FolderName.text,
#"fileName" :str_fileName,
#"userName" :str_Username,
#"postedFile" :encodedString,
#"accessToken" :#"Q)4%v59!#lyr"
};
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
httpClient.parameterEncoding = AFFormURLParameterEncoding;
[httpClient registerHTTPOperationClass:[AFJSONRequestOperation class]];
[httpClient setDefaultHeader:#"Accept" value:#"application/json"];
NSMutableURLRequest *request=[httpClient requestWithMethod:#"POST"
path:#"UploadImages/upload"
parameters:params];
NSURLResponse* response=nil;
NSData *data=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
if(data.length)
{
NSString *responseString=[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSError *error = nil;
NSDictionary *JSON = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
if(responseString && responseString.length)
{
[delegate.ary_GetUploadedCount addObject:JSON];
[self responseUpload:JSON];
dispatch_async(dispatch_get_main_queue(),
^{
if(i < delegate.ary_ImageData. count)
{
[delegate.ary_ImageData removeObjectAtIndex:i];
[delegate.ary_TotalImages removeObjectAtIndex:i];
[ary_DataToConvert removeObjectAtIndex:i];
[ary_DataToConvert1 removeObjectAtIndex:i];
NSUserDefaults *default_AllImg=[NSUserDefaults standardUserDefaults];
[default_AllImg setValue:ary_DataToConvert forKey:#"ImageDetail"];
[default_AllImg synchronize];
NSUserDefaults *default_AllImg1=[NSUserDefaults standardUserDefaults];
[default_AllImg1 setValue:ary_DataToConvert1 forKey:#"ImageData"];
[default_AllImg1 synchronize];
}
});
}
}
}
});
Please tell me.
Make sure you factor your code so that you load an image into memory, upload it, and then release it. Don't build an array of all the images in memory at once.
If you've done that you might still have an autorelease problem.
You might try adding an auto release pool inside your loop:
for (NSString *filename in filenames)
{
#autoreleasepool
{
//Your code to load an image and upload it to the server goes here
}
}
However, it's a bit hard to give more specific help until you post your code.
EDIT
You are using NSURLConnection. That class has been deprecated. Use NSURLSession instead. That's not the cause of your problem. The cause of your problem is that your are queuing up 100 connections at the same time, with all of their data in memory.
You should use NSURLSession and an upload task. With an upload task you just provide a URL to the file and the system takes care of uploading it for you (and manages the memory for you.)
I have been using NSURLConnection to perform some async downloads. Basically I retrieve a PFFile from Parse backend and display it in a web view. I offer an option to download to device. Everything is good to go and all tasks perform as required, however, lately i've noticed if I perform downloads at a high rate of speed some of them don't show up in the Downloads VC. I don't know how to test it, but my theory is it's getting cut off because the priority is set to High, EVEN THOUGH, users can't do anything until the download is complete. You have to exit out of the current UIWebView to select another document. This isn't a consist behavior which makes it harder for me to narrow down. It only happens when I download > exit > open > & download another document right away > exit > repeat until done and there is no specific document it does it with.
THE PROBLEM : downloads perform, and I get an NSLog of file created, however, when I go to the Downloads VC the document doesn't show up in the NSFRC, but still shows as a valid file in the directory.
THE PROCESS
How the download happens,
The UIWebView loads a PDF queried from Parse.com. No problem here.
A ProgressHUD displays
Once loaded the progressHUD goes away.
If they want to download the PDF they just click on the action sheet index for downloading and it starts the download process.
Another Progress HUD displays as you can see in the NSURL didReceiveData displays. This HUD does not allow user interaction until the download is complete. So you can't exit the View Controller to select another pdf until it's done completely done downloading. So NO i am not conducting numerous downloads simultaneously, it's one download at a time.
Then the user can exit after download complete, select another UITableViewCell which loads the respective PDF and repeats the process.
Loading the PDF :
PFQuery *EGQuery = [PFQuery queryWithClassName:#"Classname"];
[EGQuery whereKey:#"KeyName" equalTo:self.title];
[EGQuery getFirstObjectInBackgroundWithBlock:^(PFObject *object, NSError *error) {
if (!error) {
if ([object objectForKey:#"EGFile"]) {
PFFile *file = [object objectForKey:#"EGFile"];
self.pdfURL = [file url];
self.pdfName = [file name]; //Ends up as EG_2014_04 this is what I append to the filePath where it's stored so the filePath will be /PDFs/EG_2014_04
[self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:self.pdfURL]]];
} else {
}
Download method :
NSManagedObjectContext *context = [self managedObjectContext];
NSError *error = nil;
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Downloads"];
[request setPredicate:[NSPredicate predicateWithFormat:#"pubnumber = %#", self.title]];
[request setFetchLimit:1];
NSUInteger count = [context countForFetchRequest:request error:&error];
if (count == NSNotFound) {
} else if (count == 0) {
NSURL *url = [NSURL URLWithString:pdfURL]; //pdfURL is PFFile url
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
self.backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[connection start];
NSLog(#"Background time remaining = %.1f seconds", [UIApplication sharedApplication].backgroundTimeRemaining);
}];
NSData *pdfData = [[NSData alloc]
initWithContentsOfURL:[NSURL URLWithString:self.pdfURL]];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [[paths objectAtIndex:0] stringByAppendingPathComponent:#"PDFs"];
NSString *filePath = [path stringByAppendingPathComponent:self.pdfName];
NSFileManager *fm = [NSFileManager defaultManager];
[fm createFileAtPath:filePath contents:pdfData attributes:nil];
if ([fm createFileAtPath:filePath contents:pdfData attributes:nil])
{
dispatch_async(dispatch_get_main_queue(), ^{
NSManagedObject *newDWNLD = [NSEntityDescription insertNewObjectForEntityForName:#"Downloads" inManagedObjectContext:context];
[newDWNLD setValue:self.title forKey:#"pubnumber"];
[newDWNLD setValue:self.pubTitle forKey:#"pubtitle"];
[newDWNLD setValue:self.pdfName forKey:#"puburl"]; // this is what I use for the file path name in the PDF directory and this is how I call it in my NSFRC
});
NSLog(#"File was created");
} else {
NSLog(#"File not created");
}
});
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
expectedLength = MAX([response expectedContentLength], 1);
currentLength = 0;
HUD.dimBackground = YES;
HUD.mode = MBProgressHUDModeAnnularDeterminate;
HUD.labelText = #"Downloading...";
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
currentLength += [data length];
HUD.progress = currentLength / (float)expectedLength;
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
HUD.customView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"success.png"]];
HUD.mode = MBProgressHUDModeCustomView;
HUD.labelText = #"Success!";
HUD.detailsLabelText = #"Added to Downloads";
HUD.dimBackground = YES;
[HUD hide:YES afterDelay:1.6];
//Cancel Background task if completed
//[[UIApplication sharedApplication] endBackgroundTask:self.backgroundTask];
//self.backgroundTask = UIBackgroundTaskInvalid;
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(#"Error %#", error);
[HUD hide:YES];
}
DOWNLOADS VC
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Downloads" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:50];
NSSortDescriptor *azSort = [[NSSortDescriptor alloc] initWithKey:#"pubnumber" ascending:YES];
NSArray *azSortArray = #[azSort];
[fetchRequest setSortDescriptors:azSortArray];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"pubnumber" cacheName:nil];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return fetchedResultsController;
}
cellForRowAtIndexPath:
NSString *title = [NSString stringWithFormat:#"%#", [context valueForKey:#"pubnumber"]];
cell.textLabel.text = title;
etc etc
EDIT It seems to download all of them, and 'creates' a file, however it doesn't display all of them in the VC.
application DidFinishLaunchingWithOptions:
NSString *createPaths;
NSArray *documents = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
createPaths = [[documents objectAtIndex:0] stringByAppendingPathComponent:#"PDFs"];
NSLog(#"PDFs directory: %#", [[NSFileManager defaultManager] contentsOfDirectoryAtPath:createPaths error:&error]);
The above accrucately logs all the files that I downloaded, but they just aren't either getting recognized as fully downloaded from the UIWebView or creating the file path is going awry somehow during the download process so it's not displaying in the downloads view controller but the other documents do show up. It's consistently 1-2 missing from each session but like i stated before, its never the same document missing, it's whatever feels like missing is missing. Say I download 10 then close the app then re open it 9 will only be there in the Downloads VC sometimes 8, but it logs as being a valid filePath in the directory.
The problem appears to be your managed object context thread confinement, specifically you aren't confining. You get the context on the original thread, presumably the main thread, but then you capture it in the background block and access it directly.
You should take the result of saving your file and send it back to the main thread, then create and save the new managed object there.
I am using storyboard and and coredata in my app. In the home screen i want to list all my audio file details and in a button click i want to navigate to another screen and record a new file and save that file in database. When pressing back button i reached to home screen and i want to display the saved audio files in a tableview. Table row count is showing correctly. But the audio details are not correct. But when i stop and rebuild the app it is showing correctly. I wrote the code for reloading the tableview in viewWillApper.
- (void)viewWillAppear:(BOOL)animated
{
[tableview reloadData];
[self loadFiles];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"AudioDetails" inManagedObjectContext:managedObjectContext]];
[request setIncludesSubentities:NO]; NSError *err;
NSUInteger count = [managedObjectContext countForFetchRequest:request error:&err];
return count;
}
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"AudioCell";
AudioCell *cell = (AudioCell *)[tableViewdequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *nibArray = [[NSBundle mainBundle] loadNibNamed:CellIdentifier owner:self options:nil];
cell = (AudioCell *)[nibArray objectAtIndex:0];
[cell configurePlayerButton];
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"AudioDetails" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
for (NSManagedObject *info in fetchedObjects) {
if(audionames == nil)
audionames = [[NSMutableArray alloc] init];
[audionames addObject:[info valueForKey:#"audioName"]];
//NSManagedObject *details = [info valueForKey:#"details"];
//NSLog(#"Zip: %#", [details valueForKey:#"zip"]);
}
cell.titleLabel.text = [audionames objectAtIndex:indexPath.row];
return cell;
}
- (IBAction)SaveRecordingBtnClicked:(id)sender {
NSString *soundFilePath = [[self getDocumentDirectoryPath]
stringByAppendingPathComponent:OrginalAudioFileName];
NSURL *path=[NSURL URLWithString:soundFilePath];
NSError *error = nil;
AVAudioPlayer* avAudioPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:path error:&error];
NSManagedObjectContext *context = [self managedObjectContext];
AudioDetails *audioDetails = [NSEntityDescription
insertNewObjectForEntityForName:#"AudioDetails"
inManagedObjectContext:context];
NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:soundFilePath error:&error];
NSNumber *fileSizeNumber = [fileAttributes objectForKey:NSFileSize];
NSDate *fileCreationDate = [fileAttributes objectForKey:NSFileCreationDate];
int duration = avAudioPlayer.duration;
[audioDetails setValue:[NSString stringWithFormat:#"%#",OrginalAudioFileName] forKey:#"audioName"];
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
}
Hi good people I'm trying to prevent the freezing with
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{ CODE });
but I don't know how to use function.. I don't know where to put the managedObjectContext and how to use this dispatch_async my code is:
- (void)updateFacebookFriendsHighScore{
NSFetchRequest *requestche =[NSFetchRequest fetchRequestWithEntityName:#"Time"];
[requestche setReturnsObjectsAsFaults:NO];
NSPredicate *predicate=[NSPredicate predicateWithFormat:#"timeid==1"];
requestche.predicate=predicate;
NSArray *getIDTime = [self.managedObjectContext executeFetchRequest:requestche error:nil];
NSString *getTheTime = [[getIDTime valueForKey:#"time"] componentsJoinedByString:#""];
NSNumber *timeInInt = [NSNumber numberWithInteger: [getTheTime intValue]];
int timeFromDB = [timeInInt intValue];
timeFromDB = timeFromDB + 509;
int timeNow = [[NSDate date] timeIntervalSince1970];
if(timeNow > timeFromDB){
NSFetchRequest *updateHighScoreRequest = [NSFetchRequest fetchRequestWithEntityName:#"Friends"];
[updateHighScoreRequest setReturnsObjectsAsFaults:NO];
NSArray *friendsToUpdate = [self.managedObjectContext executeFetchRequest:updateHighScoreRequest error:nil];
for(NSArray *friendId in friendsToUpdate){
NSString *getFriendId = [friendId valueForKey:#"fbid"] ;
NSString *siteURL = [NSString stringWithFormat:#"http://www.example.com/example.php?fbid=%#", getFriendId];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:siteURL]];
[NSURLConnection sendAsynchronousRequest:urlRequest queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
NSString *resultsFromDB = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSNumber *theScoreForUpdate = [NSNumber numberWithInt:[resultsFromDB intValue]];
NSFetchRequest *updateTheHighScoreRequest = [NSFetchRequest fetchRequestWithEntityName:#"Friends"];
NSPredicate *updateTheHighScorePredicate = [NSPredicate predicateWithFormat:#"fbid==%#",getFriendId];
updateTheHighScoreRequest.predicate=updateTheHighScorePredicate;
Friends *setScore = [[self.managedObjectContext executeFetchRequest:updateTheHighScoreRequest error:nil] lastObject];
NSLog(#"%#", setScore);
[setScore setValue:theScoreForUpdate forKey:#"score"];
[self.managedObjectContext save:nil];
data = nil;
resultsFromDB = nil;
theScoreForUpdate = nil;
setScore = nil;
}];
updateHighScoreRequest = nil;
}
}
requestche = nil;
}
This code gets the time from database and update the highscore after 509 seconds from the CD result and when I run this request my app freeze ( DEADLOCK ).
I am from Bulgaria and I'm trying to learn Objective C. Here we don't have schools for this our country is very bad in all instance and Bulgaria is last in Europe Union...
Can some serious and good person help me with my code or explane how works everything in Objective C or only help me with this ?
Try this code. To keep the application as simple as possible, never take the Core data code out of the main thread i.e. any thing related to self.managedObjectContext such as save or executing fetch requests. It is because Core data is not thread safe and you will have to device a strategy to handle that. I am assuming that your application is straight forward and you don't need such a strategy. So, please try to keep it as simple as possible and always perform the core data operations (save, execute) on main thread. dispatch_async(dispatch_get_main_queue(), ^{ code }); will execute it on main thread.
-(void) updateFacebookFriendsHighScore
{
dispatch_async(dispatch_get_main_queue(), ^{
NSFetchRequest *requestche =[NSFetchRequest fetchRequestWithEntityName:#"Time"];
[requestche setReturnsObjectsAsFaults:NO];
NSPredicate *predicate=[NSPredicate predicateWithFormat:#"timeid==1"];
requestche.predicate=predicate;
NSArray *getIDTime = [self.managedObjectContext executeFetchRequest:requestche error:nil];
NSString *getTheTime = [[getIDTime valueForKey:#"time"] componentsJoinedByString:#""];
NSNumber *timeInInt = [NSNumber numberWithInteger: [getTheTime intValue]];
int timeFromDB = [timeInInt intValue];
timeFromDB = timeFromDB + 509;
int timeNow = [[NSDate date] timeIntervalSince1970];
if(timeNow > timeFromDB){
NSFetchRequest *updateHighScoreRequest = [NSFetchRequest fetchRequestWithEntityName:#"Friends"];
[updateHighScoreRequest setReturnsObjectsAsFaults:NO];
NSArray *friendsToUpdate = [self.managedObjectContext executeFetchRequest:updateHighScoreRequest error:nil];
for(NSArray *friendId in friendsToUpdate){
NSString *getFriendId = [friendId valueForKey:#"fbid"] ;
NSString *siteURL = [NSString stringWithFormat:#"http://www.example.com/example.php?fbid=%#", getFriendId];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:siteURL]];
[NSURLConnection sendAsynchronousRequest:urlRequest queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
NSString *resultsFromDB = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSNumber *theScoreForUpdate = [NSNumber numberWithInt:[resultsFromDB intValue]];
NSFetchRequest *updateTheHighScoreRequest = [NSFetchRequest fetchRequestWithEntityName:#"Friends"];
NSPredicate *updateTheHighScorePredicate = [NSPredicate predicateWithFormat:#"fbid==%#",getFriendId];
updateTheHighScoreRequest.predicate=updateTheHighScorePredicate;
Friends *setScore = [[self.managedObjectContext executeFetchRequest:updateTheHighScoreRequest error:nil] lastObject];
NSLog(#"%#", setScore);
[setScore setValue:theScoreForUpdate forKey:#"score"];
[self.managedObjectContext save:nil];
data = nil;
resultsFromDB = nil;
theScoreForUpdate = nil;
setScore = nil;
}];
updateHighScoreRequest = nil;
}
}
requestche = nil;
});
}
I'm doing a project using core data, and as far as I know anything done in the core data thread has to remain in said thread.
I do a call to an API to download some news items and then load them into the database:
[self.database.managedObjectContext performBlock:^{
for (NSDictionary *itemInfo in result) {
NSLog(#"%#", itemInfo);
[Item createItemWithInfo:itemInfo inManagedObjectContext:self.database.managedObjectContext];
}
[self.database.managedObjectContext save:nil];
}];
In my create method, besides setting up all the data in the objects I have an additional call to get an image related to the news item in question:
+ (Item *)createItemWithInfo:(NSDictionary *)info inManagedObjectContext:(NSManagedObjectContext *)context {
Item *item;
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Item"];
request.predicate = [NSPredicate predicateWithFormat:#"itemId = %#", [info valueForKeyPath:#"News.id_contennoticias"]];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"itemId" ascending:YES];
request.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSError *error = nil;
NSArray *matches = [context executeFetchRequest:request error:&error];
if (!matches || ([matches count] > 1)) {
// handle error
} else if ([matches count] == 0) {
item = [NSEntityDescription insertNewObjectForEntityForName:#"Item" inManagedObjectContext:context];
item.itemId = [NSNumber numberWithInteger:[[info valueForKeyPath:#"News.id_contennoticias"] integerValue] ];
item.title = [info valueForKeyPath:#"News.titulo_contennoticias"];
item.summary = [info valueForKeyPath:#"News.sumario_contennoticias"];
item.content = [info valueForKeyPath:#"News.texto_contennoticias"];
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
dateFormat.dateFormat = #"yyyy-MM-dd hh:mm:ss";
NSDate *creationDate = [dateFormat dateFromString:[info valueForKeyPath:#"News.fechacre_contennoticias"]];
item.creationDate = creationDate;
dispatch_queue_t imageDownloadQueue = dispatch_queue_create("image downloader", NULL);
dispatch_async(imageDownloadQueue, ^{
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"%#/files/imagenprincipal/%#", BASE_PATH, [info valueForKeyPath:#"News.imgprincipal"]]];
NSData *imageData = [NSData dataWithContentsOfURL:url];
dispatch_async(dispatch_get_current_queue(), ^{
item.image = imageData;
});
});
} else {
item = [matches lastObject];
}
return item;
}
On this part:
dispatch_async(dispatch_get_current_queue(), ^{
item.image = imageData;
});
I'm getting the error, my app just dies there. Also it says that dispatch_get_current_queue() is deprecated in iOS 6.
Since dispatch_get_current_queue() is called from within a block on the imageDownloadQueue, why aren't you using imageDownloadQueue directly? If you want to run it on the moc queue, then make sure not to use dispatch_get_current_queue().
In general be careful with using dispatch_get_current_queue(). Quoting from the Dispatch Queues page in the Apple Concurrency Programming guide:
Use the dispatch_get_current_queue function for debugging purposes or
to test the identity of the current queue.