EXC_BAD_ACCESS when returning UIImage - objective-c

I have a method which should return a UIImage created from contentsOfFile (to avoid caching), but when it returns, i receive EXC_BAD_ACCESS. Running through Instruments doesn't reveal any results, as it just runs, without stopping on a zombie.
The image is correctly copied in the Bundle Resources phase...
- (UIImage *)imageForName:(NSString *)name {
NSString *path = [[NSBundle mainBundle] pathForResource:name ofType:#"png"];
return [UIImage imageWithContentsOfFile:path];
}
This method was adapted from the PhotoScroller sample, which works correctly.
Thanks
EDIT:
This is the code that uses imageForName, and you can see i added the retain, as per Luke/T's suggestion, but the EXC_BAD_ACCESS is on the return, not my addObject: call:
NSMutableArray *images;
for (NSDictionary *dict in imageListForPage){
[images addObject:[[self imageForName:[dict valueForKey:#"name"]]retain]];
}

ImageWithContentsOfFile will return an auto-released object. If you are not retaining it (on return [edit]) then you will get a bad access.
Edit:
Check the pointer of the NSarray. You need to init the Array either alloc as normal or use the arraywith
e.g.
NSMutableArray *images = [NSMutableArray arrayWithCapacity:ARRAY_CAPACITY];//autoreleased
or
NSMutableArray *images = [[NSMutableArray alloc] init];//release later

Adding an object to an NSMutableArray will implicitly send it a retain, so that's not necessary in your code.
Can you confirm (using NSLog() or a breakpoint) that
[UIImage imageWithContentsOfFile:path]
returns an object in your imageForName: method?

Finally, this code should be:
NSMutableArray *images = [NSMutableArray new]; // new is same as [[alloc] init]
for (NSDictionary *dict in imageListForPage) {
[images addObject:[self imageForName:[dict valueForKey:#"name"]]];
}
// ... do something with images
[images release];

Related

Unable to store an UIImage in an NSMutableArray

I am trying to store a UIImage in an NSMutableArray but it doesn t work, here is the line I am using:
[images addObject:QRImage];
There are no compilation errors, but when I debug the app I see that the NSMutableArray has 0 objects. i.e. the image was not added.
I suspect your NSMutableArray is not properly initialized. Try this:
NSMutableArray *images = [[NSMutableArray alloc] init];
// ...
[images addObject:QRImage];

Objective C - NSString - Memory basics

I am trying to return an NSString that has been initialized from a plist.
If I comment out my release lines this code works. I would however like to release these objects from memory as I no longer need them.
I thought that 'initWithString' would copy the contents of the target string into my new string meaning I could safely release the NSMutableArray. But it isn't. Why not?
+ (NSString*) genImage {
NSString *path = [[NSBundle mainBundle] pathForResource:
#"Images" ofType:#"plist"];
NSMutableArray *arrayOfImages = [[NSMutableArray alloc] initWithContentsOfFile:path];
NSLog(#"%d", [arrayOfImages count]);
int indexToLoad = 0;
NSString *res = [[NSString alloc] initWithString:[arrayOfImages objectAtIndex:indexToLoad] ];
[arrayOfImages release];
[path release];
return res;
}
You do not retain the return value of -[NSBundle pathForResource:ofType:] (the path variable), so there is no need to release it (and doing so will cause a crash, most likely). However, you should autorelease res, as you do retain that. You can change your last line to
return [res autorelease];

NSMutableArray addObject in for loop - memory leak

i'm putting strings (which are filenames of files in a certain directory) into an NSMutableArray with a for loop:
h-file:
#import <Three20/Three20.h>
#interface AlbumController : TTThumbsViewController {
NSMutableArray *images;
}
#property (nonatomic, retain) NSMutableArray *images;
#end
m-file:
#import "AlbumController.h"
#import "PhotoSource.h"
#import "Photo.h"
#implementation AlbumController
#synthesize images;
-(void)createPhotos {
NSString *bundleRoot = [[NSBundle mainBundle] bundlePath];
NSArray *dirContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:bundleRoot error:nil];
NSArray *onlyJPGs = [dirContents filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"self ENDSWITH '.jpg'"]];
NSMutableArray *pics = [[onlyJPGs copy] autorelease];
if(!self.images) {
self.images = [[NSMutableArray alloc] init];
}
for(int i = 0; i < [onlyJPGs count]; i++)
{
//NSLog([pics objectAtIndex:i]);
NSString *ImgURL = [#"bundle://" stringByAppendingString:[pics objectAtIndex:i]];
Photo *photo = [[Photo alloc] initWithURL:ImgURL smallURL:ImgURL size:CGSizeMake(320, 212)];
[images addObject:photo];
[photo release];
}
}
-(void)viewDidLoad{
[self createPhotos]; // method to set up the photos array
self.photoSource = [[PhotoSource alloc]
initWithType:PhotoSourceNormal
title:#"Chili Pflanzen"
photos:images
photos2:nil
];
}
#end
i do not have any problem in the simulator but on my iPod...
Error message:
Data FOrmatters temporarily unavailable, will re-try after a 'continue'. (Unknown error loading shared library "/Developer/usr/lib/libXcodeDebuggerSupport.dylib")
thanks in advance
I think you should use mutableCopy and not copy on your pics array.
so instead of:
NSMutableArray *pics = [[onlyJPGs copy] autorelease];
you should use:
NSMutableArray *pics = [[onlyJPGs mutableCopy] autorelease];
More information about copy/mutablecopy in this question: Copy & mutableCopy?
Looks like the main issue is with
[images addObject:[[Photo alloc] initWithURL:ImgURL smallURL:ImgURL size:CGSizeMake(320, 212)]];
Here you are alloc'ing Photo but not releasing it. When you add an object to an array it increases the retain count for it.
Try changing it to
Photo *photo = [[Photo alloc] initWithURL:ImgURL smallURL:ImgURL size:CGSizeMake(320, 212)];
[images addObject:photo];
[photo release];
In addition ...
I'd change
self.images = [[[NSMutableArray alloc] init] autorelease];
to
if(!self.images) {
self.images = [[NSMutableArray alloc] init];
}
Otherwise there is the potential for a memory leak if it has already been initialized, as well as that you probably do not want it autoreleased;
Your NSMutableArray instance is autoreleased. You are assigning it to the images ivar. The fact that you have declared it as a retained property doesn't matter, because you aren't assigning it to the property. My guess is that you meant to assign to the property, and the crash is caused by the inadvertent deallocation.
Change:
images = [[[NSMutableArray alloc] init] autorelease];
...to:
self.images = [[[NSMutableArray alloc] init] autorelease];
...or:
images = [[NSMutableArray alloc] init];
Also note that your property is declared as NSArray when you are allocating an instance of NSMutableArray.
Also see the Memory Management Programming Guide.

Objective C - UITableView after calling reloadData my object properties are null/nil

I have a ViewController defined as follows:
#interface SectionController : UITableViewController {
NSMutableArray *sections;
}
- (void) LoadSections;
When LoadSection is call it makes a call to NSURLConnection to load a url which in turn calls
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
[connection release];
[responseData release];
NSDictionary *results = [responseString JSONValue];
NSMutableArray *jSections = [results objectForKey:#"Items"];
sections = [NSMutableArray array];
for (NSArray* jSection in jSections)
{
Section* section = [Section alloc];
section.Id = [jSection objectForKey:#"Id"];
section.Description = [jSection objectForKey:#"Description"];
section.Image = [jSection objectForKey:#"Image"];
section.Parent = [jSection objectForKey:#"Parent"];
section.ProductCount = [jSection objectForKey:#"ProductCount"];
[sections addObject:section];
[section release];
}
[jSections release];
[results release];
[delegate sectionsLoaded];
[self.view reloadData];
}
The data parses correctly and I now have sections filled with many items.
Calling [self.view reloadData] forces a callback to the delegate method cellForRowAtIndexPath which should then present the data into the cell however its at this point that sections is now nil again.
Can someone please point out my mistake? I must admit I am a newbie to objective c and it probably a pointer issue. What is need to do is retain the value of sections after calling reloadData.
Many thanks.
Seeing the new code the problem is obvious:
sections = [NSMutableArray array];
should become
[sections release];
sections = [[NSMutableArray alloc] init];
note that the array does not become again "nil", is instead deallocated and you get an invalid reference, which might (should) generate a crash on dereferencing.
I suggest you to read some articles on reference counted memory management as it might be not obvious if you are new to Objective-C, and often leads to mistake (i.e: autorelease is not magic at all)
best way to avoid all memory leaks here is just simply use #property (nonatomic, retain) NSMutableArray *sections; by using property you can be sure that all men management works will be correctly managed by system. Just don't forget that property retains value when you doing setSections:, so that you need to pass autoreleased object here.
self.sections = [NSMutableArray array];
...
[self.sections addObject:section];
Also to avoid all problem try to make all objects which should live only in this method autorelease. Like this:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSString *responseString = [[[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding] autorelease];
NSDictionary *results = [responseString JSONValue];
NSMutableArray *jSections = [results objectForKey:#"Items"];
self.sections = [NSMutableArray array];
for (NSArray* jSection in jSections) {
Section* section = [[[Section alloc] init] autorelease];
section.Id = [jSection objectForKey:#"Id"];
section.Description = [jSection objectForKey:#"Description"];
section.Image = [jSection objectForKey:#"Image"];
section.Parent = [jSection objectForKey:#"Parent"];
section.ProductCount = [jSection objectForKey:#"ProductCount"];
[self.sections addObject:section];
}
[delegate sectionsLoaded];
[self.view reloadData];
}
And also most of object you trying to release already autoreleased:
all params passed into your method shouldn't be released manually, check I think JSONValue also should returns autoreleased object and anything you getting by enumerating or by call objectForKey:

Loading images into NSArray using initWithObjects crashes but not with an NSMutableArray?

I'm doing some lazy loading of images into an array when the app has loaded. I have tried using an NSMutableArray and an NSArray (I don't need to alter the array once it's been created) but the latter crashes on me.
...
[self performSelectorInBackground:#selector(loadImageArrays) withObject:nil];
...
- (void)loadImageArrays {
NSAutoreleasePool *pool;
pool = [[NSAutoreleasePool alloc] init];
NSString *fileName;
imageArray = [[NSMutableArray alloc] init];
for(int i = 0; i <= x; i++) {
fileName = [NSString stringWithFormat:#"image_0000%d.png", i];
[imageArray addObject:[UIImage imageNamed:fileName]];
}
[pool drain];
}
vs
NSAutoreleasePool *pool;
pool = [[NSAutoreleasePool alloc] init];
imageArray = [[NSArray alloc] initWithObjects:
[UIImage imageNamed:#"image_00000.png"],
[UIImage imageNamed:#"image_00001.png"],
[UIImage imageNamed:#"image_0000X.png"],
nil];
[pool drain];
NSZombieEnabled = YES tells me that [UIImage retain] was sent to deallocated instance when using the latter code-snippet.
Both arrays have (nonatomic, retain) property in my h-file. Why are the images not being retained by the NSArray?
UIImage is part of UIKit, which is not thread safe. For example, the method imageNamed: could corrupt the global name-image dictionary that the UIImage class uses for caching.
You have to use Core Graphics if you want to load images on a background thread.
Edit to answer your comment:
You can load PNG files with:
CGDataProviderRef source = CGDataProviderCreateWithURL((CFURLRef)url);
CGImageRef image = CGImageCreateWithPNGDataProvider(source, NULL, NO, kCGRenderingIntentDefault);
CFRelease(source);
A CGImage is a core foundation object and can be stored in an NSArray. You can then make a UIImage from it (on the main thread, of course):
[[UIImage alloc] initWithCGImage:image]
Although I know there is a big difference in mutable and immutable arrays, I'm in doubt myself. Something tells me this isn't purely a mutable vs immutable issue. Looks like your pool is drained prematurely (sounds nasty). Not that it should make a difference, but could you try to spawn a new thread by doing;
[NSThread detachNewThreadSelector:#selector(loadImageArrays) toTarget:self withObject:nil];
I'm simply curious of the result.. Also try both snippets in a clean environment (i.e: with no other code around). If your second code snippet works there, you should be looking elsewhere for the solution.