Here is the code below. It downloads a thumbnail image and then tries to create an image based on the thumbnail file path. But it gives me EXC_BAD_ACCESS error at method call "imageWithContentsOfFile". While EXC_BAD_ACCESS addresses the code trying to access an object that has been released most likely I don't know which object it could be. Any help'd be appreciated!
NSBlockOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^{
if([[NSFileManager defaultManager] fileExistsAtPath:operation.destinationPath ]){
NSString *key = [[MEURLCacheKeyRegister sharedRegister] cacheKeyForURL:operation.fileUrl];
UIImage *image = [UIImage imageWithContentsOfFile:operation.destinationPath];
}else{
DDLogDebug(#"Thumbnail file doesn't exist at %#", operation.destinationPath);
}
}
}];
AFDownloadRequestOperation *requestOperation = [FileServerDownloadUtils downloadOperationForURL:operation.fileUrl
destinationPath:operation.destinationPath
completion:completionOperation];
[self.fileSyncQueue addOperation:requestOperation];
EXC_BAD_ACCESS indicates that object has been released while its being accessed.
If I were you, I would try following things:
Save the file with .jpg and not .jpg.prv.jpg extension.
Try using initWithContentsOfFile instead of imageWithContentsOfFile as imageWithContentsOfFile autoreleases image which in rare edge cases creates crashes like this.
When passing code to a block, access object properties by making weak reference to self. Something like this: __weak MyController *weakSelf = self. Then use weakSelf to access properties inside the block.
These are just few clues which may help you digging it further. You may use NSZombie and other profiling tools to nail it down.
Related
Currently I am facing a memory issue problem in building iOS app. I checked for Memory leaks using Instruments. I found that there is one kind of leaks that keeps on showing up named swift_slowAlloc, which I don't have idea about. An snippet of the error is given below.
Another reason I think could happen is due to loading of several UIImages in my app. Just to provide a background, I take various portions of an original image in my app and do some processing on them. However, I don't need to keep the images for further calculations. I used autoreleasepool to release the UIImage; but I doubt that it is working. An example is given below:
#autoreleasepool {
UIImage *imageResized = MatToUIImage(resized28);
// MARK: Send resized28 to CNN and get the output. Fill the dict then
NSString *CNNScore;
CNNScore = [myclass CNNfloat:imageResized W1:W1 W2:W2 Wf1:Wf1 Wf2:Wf2 B1:B1 B2:B2 Bf1:Bf1 Bf2:Bf2];
imageResized = nil;
xtn = [NSNumber numberWithInteger:xt];
xbn = [NSNumber numberWithInteger:xb];
ytn = [NSNumber numberWithInteger:yt];
ybn = [NSNumber numberWithInteger:yb];
symbol = [NSString stringWithFormat:#"%#", CNNScore];
symtype = [NSString stringWithFormat:#"%#", [scoreDic objectForKey: symbol]];
numberInDict = [NSString stringWithFormat:#"%i", n];
inToMaroof = [NSArray arrayWithObjects: xtn, xbn, ytn, ybn, symbol,symtype, nil];
[toMaroof setObject: inToMaroof
forKey: numberInDict];
}
}
Can someone suggest anything on this issue?
These are some possible causes that may cause a memory leak in your program even if using ARC:
You set a strong reference to a parent in a child object. This causes a retain cycle.
You set a strong reference to a delegate in an interface.
You forgot to release an object when you do a toll-free bridging after transferring ownership.
You forgot to set a weak reference to objects you passed in a block.
I am relatively new to IOS programming.
I have a NSObject "Places" which is given below.
#interface Places : NSObject
#property(strong) NSString *placeName;
#property(strong) UIImage *placeImage;
#end
I am listing the array of Places objects in UITableView. On tableView:cellForRowAtIndexPath: images are fetching asynchronously from web urls. On tableView:didSelectRowAtIndexPath:, I pass the respective 'Places' object to another ViewController (detailViewController). In that detailViewController, I uses below code to display the image in a UIImageView.
self.imageView.Image = self.myPlaces.placeImage
My problem is when I pass that object, the image needn't be fetched to placeImage. Is there anyway to update the self.imageView on successive completion of image fetching to placeImage in mainViewController.
My code is given below. It is called in tableView:cellForRowAtIndexPath: of mainViewController.
NSURL *url = [NSURL URLWithString:#"http://imageurl_goes_here"];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
NSData *imageData = [NSData dataWithContentsOfURL:url];
dispatch_async(dispatch_get_main_queue(), ^{
if(imageData != nil)
{
place.placeImage = [UIImage imageWithData: imageData];
cell.cellImageView.image = place.placeImage;
}
});
});
Well, there are some techniques to archive that:
You can notify your second view controller every time you received image. E.g. in your inner dispatch_async. But this requires main controller to know much about second one.
You can use Key-Value Observing technique to observe every place's image updating. In this case you might also want to use some handy library that will do sanity for you (like unsubscribing when object is deallocated, otherwise you will get crash) like that one or any other.
This post of Matt Thompson may be helpful on other types of communications between instances.
i have iPhone application. In some cases, when the device is getting low on free memory, some actions (for example, opening the camera) might cause the application to crash.
My question is that:
I want to prevent these crashes, what is the common way applications
do such thing (blocking specific actions, notifying the user, other
ideas)? I ask because i didn't encountered such behaviour in iOS
applications i ran into.
Are there any ways of preventing such crashes and remain full app functionality, such as iOS system calls to free more memory and etc.? if anyone has best practice or good heuristic i would love to hear about it.
EDIT: I ask this question assuming i already implement the 'didReceiveMemoryWarning' function and freed all the memory i can.
EDIT 2: my app is about pictures. A lot like camera scanner apps, this app allows taking pictures, image processing and saving data about them in memory. my crashes usually happens when i scan a lot of pictures.
Some thumb rules i follow:
Using Arc
Use weak for iboutlets (except top level example: UIwindow) and for delegates
Use Strong for class properties and copy for NSString.
Dont access variables directly, use self....way.
Dont use autorelease way of creating new objects, example NSArray *array = [NSArray arrayWithObjects......., instead use NSArray *array = [NSArray alloc] initWit....
Same way for NSString class. try to use [NSString alloc] initWithFormat..... instead of [NSString stringWithFormat.
When ever you are adding NSNotification(addObserver...) centre must remove(removeObserver..) them in dealloc.
Implement didReceiveMemoryWarning(view controller level) or applicationDidReceiveMemoryWarning(application level and it is called first than view controller level) properly, how ever there are times when you only and only wish to save from crash.you can display an alert telling user less memory available, you can pop/present ..user to home screen.(Bad practice).
Dont perform any manipulation on main thread while being in background thread.Always use #autorelease block for background threads.
use GCD/NSOperation queue for long running processes.
Keep an sharp eye on image resources you are using, use image only of desired size not scale big image to small image size for your need.
USE autorelease pool for long running loops, which create a lot of autoreleased objects.
i have some code snippet for you which ypu can follow:
//way 1 all on main thread bad approach, basically we are just doing some image manipulation on main thread(should not do on main thread :))
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{
YourApplicationDelegate *appDelegate = (YourApplicationDelegate *)[[UIApplication sharedApplication]delegate];
[appDelegate showLandscapeLoading];//think it as progress view/loader
UIImage *pickedImage = [info objectForKey:UIImagePickerControllerOriginalImage];
NSData *imageData = UIImagePNGRepresentation(pickedImage);
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"category_imagename.jpeg"];
NSError * error = nil;
//from here
[imageData writeToFile:path options:NSDataWritingAtomic error:&error];
**//the important part for discussion UI manipulation on main thread bad bad bad**
CGSize size1;//A
size1.width = 400;
size1.height = 400;
UIGraphicsBeginImageContext(size1);
[pickedImage drawInRect:CGRectMake(0, 0, size1.width, size1.height)];
UIImage *bigImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSString *bigThumb = [documentsDirectory stringByAppendingPathComponent:#"category_thumb_imagename.jpeg"];
NSData *data1=UIImageJPEGRepresentation(bigImage, 0.5);
BOOL status1=[data1 writeToFile:bigThumb atomically:YES];
**//up to here should be in non ui thread/seperate thread**
**//below code should go in main thread**
NSLog(#"status1 -> %d",status1);
[self setCategoryImageName:bigImage];
[self.imgCategory setImage:pickedImage];
if (status1) {
isAddingCategoryImage = YES;
}
[appDelegate stopLandscapeLoading];
if (error != nil) {
NSLog(#"Error: %#", error);
return;
}
if ([self.popoverController isPopoverVisible]) {
[self.popoverController dismissPopoverAnimated:true];
}
[picker.view removeFromSuperview];
}
The correct way:
Using NSOperation:
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{
YourApplicationDelegate *appDelegate = (YourApplicationDelegate *)[[UIApplication sharedApplication]delegate];
[appDelegate showLandscapeLoading];
UIImage *pickedImage = [info objectForKey:UIImagePickerControllerOriginalImage];
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSError * error = nil;
NSOperationQueue *opQueue = [[NSOperationQueue alloc] init];
[opQueue addOperationWithBlock:^
{
// Create a graphics image context very slow stuff
CGSize newSize = CGSizeMake(400, 400);
UIGraphicsBeginImageContext(newSize);
// Tell the old image to draw in this new context, with the desired
// new size
[pickedImage drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
// Get the new image from the context
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
// End the context
UIGraphicsEndImageContext();
NSString *bigThumb = [documentsDirectory stringByAppendingPathComponent:#"category_thumb_imagename.jpeg"];
NSData *data1=UIImageJPEGRepresentation(newImage, 0.5);
BOOL status1=[data1 writeToFile:bigThumb atomically:YES];
// ok, now do UI stuff in the main queue
[[NSOperationQueue mainQueue] addOperationWithBlock:^
{
[self setCategoryImageName:bigThumb];
[self.imgCategory setImage:pickedImage];
if (status1) {
isAddingCategoryImage = YES;
}
[appDelegate stopLandscapeLoading];
if (error != nil) {
NSLog(#"Error: %#", error);
return;
}
if ([self.popoverController isPopoverVisible]) {
[self.popoverController dismissPopoverAnimated:true];
}
[picker.view removeFromSuperview];
}];
}];
}
thanks and regards,
ALOK
If you use non arc and You had allocated the many object and you did not release these object so it shows the memory problem. you relase all object in dealloc method.In goes upper product option and choose the Analyze. you will see where your application memory leak
If you have used old xcode and you have use new iphone simulator than it shows the memory leak
If you use arc than please comment the autorelease or [obj release] close.
Further than if you want to check their application than side corner button to hold and choose profile. it will show instruments tools. you can enable Nszombies. than you can check how to object values have take and you can see where the memory leak in your application.
No there are is no direct call to free RAM memory in iOS. If you use ARC in your project, define your properties as weak/strong etc correctly and have checked your application for memory leaks or zombie processes there will not be a RAM issue.
iOS frees up memory from other apps to allocate it it to the foreground app if needed and you should not try to deal with it. If you app crashes due to memory issues, you probably have a memory leak in your application. Use Instruments to profile your app.
The memory warning system had a lot of improvements since I started to develop for iOS, ARC also works great helping developers managing memory.
You should profile your app using leaks and allocations to see why your app is consuming so much memory.
Which kind of application are you developing? should be a high memory usage application such as games, or photos app?
A crash could be due to a not well managed answer to a memory warning or to a huge memory occupation that doesn't leave any last breath to your app.
The most common reason are pictures. These devices can't handle a lot of hires resources if you don't manage those situations in the right way, the memory footprint of your app grows until it can't free enough memory.
You need to give more details, though.
The code below works fine, I just don't know where the release should go, because I'm not sure what the rules are. I'm not using ARC.
- (void)myFunc {
// stuff happens
__block UIImage* photo = [UIImage imageWithCGImage:croppedCGImage];
[photo retain];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
(unsigned long)NULL), ^(void) {
[self doStuffToPhoto:photo];
// [photo release] causes EXC_BAD_ACCESS
});
// [photo release] causes EXC_BAD_ACCESS in doStuffToPhoto
}
- (void)doStuffToPhoto:(UIImage*)photo {
// do stuff
// [photo release] causes EXC_BAD_ACCESS
}
If I understand the docs right (look for The block Storage Type and Object and Block Variables here), there is no need to retain your photo variable: "__block variables live in storage that is shared between the lexical scope of the variable and all blocks and block copies declared or created within the variable’s lexical scope. Thus, the storage will survive the destruction of the stack frame if any copies of the blocks declared within the frame survive beyond the end of the frame".
But this no explanation why you get the EXC_BAD_ACCESS.
Hi I'm working through the Stanford iOS development class. I have a question regarding threading. I understand UIKit calls should be handled by the main thread. I was wondering if something like this is legal?
- (UIImage *)mapViewController:(MapViewController *)sender imageForAnnotation:(id<MKAnnotation>)annotation {
FlickrPhotoAnnotation *fpa = (FlickrPhotoAnnotation *) annotation;
NSURL *url = [FlickrFetcher urlForPhoto:fpa.photo format:FlickrPhotoFormatSquare];
__block UIImage *image;
dispatch_queue_t downloadQ = dispatch_queue_create("download queue", NULL);
dispatch_async(downloadQ, ^{
NSData *data = [NSData dataWithContentsOfURL:url];
if (data) {
dispatch_async(dispatch_get_main_queue(), ^{
image = [UIImage imageWithData:data];
});
}
});
dispatch_release(downloadQ);
return image;
}
or I should just return NSData and handle all the threading in the calling method?
Thanks
Your code won't do what you want.
You are putting an asynchronous block into a queue, and then immediately returning from the method. You are not guaranteed that the block will actually run before you return -- in fact, the odds are that it won't.
So you'll return the current value of image. Since you didn't initialize it, it's probably garbage. Whoever calls this method will try to use a garbage pointer to an image, and (if you're lucky) crash. If you had initialized it to nil:
__block UIImage *image = nil;
that would be a little more polite.
The problem here is: your method must return a UIImage, so you must wait for the time it takes to make a fully constructed UIImage before you return. There is zero benefit to doing this on some other thread -- you're still waiting the same amount of time, and switching threads just adds overhead.
In order to load the image in a usefully asynchronous way, you need some way to asynchronously tell the caller when the image is done loading, via a callback to a delegate method or block. For example, look at NSURLConnection in Foundation. It has an older method that calls back via a delegate, and a newer method (+sendAsynchronousRequest:queue:completionHandler:) that calls back via a block.
I concur with CodaFi's comment. Actually, images can be created off the main thread. UIView creation and manipulation must be done on the main thread, but UIImage is not a UIView though. Furthermore, the runtime is likely to give you a warning or error if you try to manipulate the displaying UI on another thread (it did for me when I accidentally updated a UITableView on another thread).