Why would one autorelease one's own property in Objective-C? - objective-c

Here's a code example to show what I mean:
- (void) setup {
[self setupObjectModel];
[self setupStoreCoordinator];
}
- (void) setupObjectModel {
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"Model" withExtension:#"momd"];
self.managedObjectModel = [[[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL] autorelease];
}

In case managedObjectModelis a strongproperty or defined with attribute retain, the setter will automatically retain the passed argument, thus autoreleaseing it will prevent a memory leak (if you don't do it, the retain count of the NSManagedObjectModel will be 2 although only managedObjectModelpoints to it.)
This is equivalent to
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
assuming the setter has the default behaviour

Related

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];

EXC_BAD_ACCESS when returning UIImage

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];

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:

AVAudioPlayerdelegate

I receive this error for this code-
warning: class 'BeatMaker' does not implement the 'AVAudioPlayerDelegate' protocol
-(IBAction)playBeat3 {
NSString *path = [[NSBundle mainBundle] pathForResource:#"beat3" ofType:#"mp3"];
AVAudioPlayer* theAudio=[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
theAudio.delegate = self;
[theAudio play];
}
Can anyone help me with this?
You've told your AVAudioPlayer instance that your BeatMaker class implements the AVAudioPlayerDelegate protocol with this line:
theAudio.delegate = self;
But apparently your BeatMaker class hasn't told the compiler that it is actually an AVAudioPlayerDelegate. You would do that in the header file:
#interface BeatMaker : NSObject <AVAudioPlayerDelegate> { ... }
Then you would have to make sure that you have implemented all the required functions of the AVAudioPlayerDelegate protocol in your BeatMaker class.
In this case, there are no required functions for the protocol, so if you are just copying code and you don't actually want to recieve messages from theAudio, you can just delete the line assigning self to the delegate property of theAudio.

initWithCoder not working as expected?

Does this seem right, the dataFilePath is on disk and contains the right data, but the MSMutable array does not contain any objects after the initWithCoder? I am probably just missing something, but I wanted to quickly check here before moving on.
-(id)initWithCoder:(NSCoder *)decoder {
self = [super init];
if(self) {
[self setReactorCore:[decoder decodeObjectForKey:#"CORE"]];
}
return self;
}
.
-(id)init {
self = [super init];
if(self) {
if([[NSFileManager defaultManager] fileExistsAtPath:[self dataFilePath]]) {
NSMutableData *data = [[NSMutableData alloc] initWithContentsOfFile:[self dataFilePath]];
NSKeyedUnarchiver *unArchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
NSMutableArray *newCore = [[NSMutableArray alloc] initWithCoder:unArchiver];
[self setReactorCore:newCore];
[newCore release];
[data release];
[unArchiver release];
} else {
NSMutableArray *newCore = [[NSMutableArray alloc] init];
[self setReactorCore:newCore];
[newCore release];
}
}
return self;
}
EDIT_001
I think I know where I am going wrong, I am archiving NSData and then trying to initialise my NSMutable array with it. I will rework the code and post back with an update.
gary
I am confused as to why you're doing things this way. You do not normally call initWithCoder: yourself. You ask the coder for its contents and it creates the objects for you. The whole decoding part of that method should be id archivedObject = [NSKeyedUnarchiver unarchiveObjectWithFile:[self dataFilePath]], where archivedObject is presumably the array you call newCore in your code (I don't know the contents of the file, so I'm just guessing from what you wrote). In that case, you'll want to mutableCopy it, since I don't think NS*Archiver preserves mutability.
I also hope you aren't expecting your initWithCoder: method that you wrote at the top of your post to be called when this NSArray is unarchived.
"I also hope you aren't expecting your initWithCoder: method that you wrote at the top of your post to be called when this NSArray is unarchived."
is not useful. Why can't you write why it's not called? Thanks
EDIT:
In fact, if the objects in your array implement NSCoding, initWithCoding is called on each of them when you restore your array (restore here means that you call [decoder decodeObjectForKey:#"yourArray"]). I'm actually doing this in my own code. So I think what you wrote is false!