Objective-C dynamic object creation memory issue - iphone

I've been putting on with a strange issue in my iPhone program for a while.
To make long story short: I have an XML which contains data about tree-like composition. I've wrote a parser which constructs a tree from the XML.
Somewhere in DataSource class I have following method:
- (id)loadAllData {
if (self.doc != nil) {
GDataXMLElement *root = [self.doc rootElement];
Component *component = [Component componentWithRoot:root];
NSLog(#"%#", component);
return component;
}
return nil;
}
Factory method looks like this:
+ (id)componentWithRoot:(GDataXMLNode*)element {
id<iComponent> component = nil;
NSString *clsName = [NSString stringWithFormat:#"%#Element", [element name]];
// Will return instance of a class or |nil| if class is not loaded.
Class cls = NSClassFromString(clsName);
if (cls != nil) {
component = [[cls alloc] init];
// dies on next line
NSLog(#"created %#", component);
// Seems that we have corresponding model class
if (component != nil) {
//... setting attrubutes for this model
//... trying to find child elements and add them to the component instance.
}
}
return component;
}
Also when I stop GDB on line where I call alloc and init methods and try to call them in debugger I see next error log:
(gdb) po component
Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_INVALID_ADDRESS at address: 0x828282df
0x01052a67 in objc_msgSend ()
The program being debugged was signaled while in a function called from GDB.
GDB has restored the context to what it was before the call.
To change this behavior use "set unwindonsignal off"
Evaluation of the expression containing the function (_NSPrintForDebugger) will be abandoned.
So the situation is: somehow my program starts up and displays values that I need correctly. But when I try to do something with my component, program just crashes. Any ideas? How can I find a error? I don't any reasons why NSLog(#"created %#", component); crashes application and so do other actions with component (actually I want to encode it to an archive).
Thanks in advance!
Update
This is piece of factory method code with comments
+ (id)componentWithRoot:(GDataXMLNode*)element {
id<iComponent> component = nil;
NSString *clsName = [NSString stringWithFormat:#"%#Element", [element name]];
NSLog(#"%#", clsName);
// Will return instance of a class or |nil| if class is not loaded.
Class cls = NSClassFromString(clsName);
if (cls != nil) {
component = [[[cls alloc] init] autorelease];
// Seems that we have corresponding model class
if (component != nil) {
[component setTitle:[element name]];
/*
when I run app I have first element named ConfigElement.
Code runes fine to this point and even properties are set correct.
But if I set breakpoint at this line and type in consle
po component
I get message which is posted below
*/
(gdb) po component
Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_INVALID_ADDRESS at address: 0x828282df
0x01052a67 in objc_msgSend ()
The program being debugged was signaled while in a function called from GDB.
GDB has restored the context to what it was before the call.
To change this behavior use "set unwindonsignal off"
Evaluation of the expression containing the function (_NSPrintForDebugger) will be abandoned.
Since this happens I want to make sure that all init methods are correct.
Here what I am doing:
first - check that name of class is ConfigElement - yes
second - look at ConfigElement init - everything ok:
- (id)init {
self = [super init];
return self;
}
third - look at super (Composite) initializer:
- (id)init {
if ((self = [super init])) {
children_ = [[NSMutableArray alloc] init];
}
return self;
}
then look for super initializer again (Component initializer now) - seems good again:
- (id)init {
if ((self=[super init])) {
title_ = nil;
name_ = nil;
icon_ = nil;
parent_ = nil;
children_ = nil;
}
return self;
}
Since Component is a subclass of NSObject I don't need to bother about super initializer again and could probably remove if statement but let it be. Also all these initilizers are designated and they are actually called at the moment of init.
So why I am getting that error msg in GDB?

The problem was in my buggy description method:
- (NSString*)description {
NSString *desc = [NSString stringWithFormat:#"Title: %#, Name: %#, Icon: %#, parent: %#, children: %#", self.title, self.name, self.icon, self.parent];
return desc;
}
Once this method was fixed, debugger started to print correct values.

Both componentWithRoot: and loadAllData should be returning an autoreleased object.
What toiletseat said are all good suggestions/questions. This sounds specifically like a problem internal to your Component class. Would have to see its innards to say more.

Related

memory management warning in Iphone

I have some doubts in objective-C programming. I have function like this.
+ (NSManagedObjectContext *) newContext {
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil)
{
NSManagedObjectContext* managedObjectContext = [[[NSManagedObjectContext alloc] init] autorelease];
[managedObjectContext setPersistentStoreCoordinator:coordinator];
return managedObjectContext;
}
return nil;
}
But when I run analyse on my project I am seeing something like this with warning
"Object with a +0 retain count returned to caller where a +1 (owning)
retain count is expected"
Can someone point me out so as to why Xcode is giving me a warning here. What is the correct way to return any variable?
The problem lies not within your code but within the name of your method. In Objective C, methods whose names start with init or new are assumed by the analyser to return an object with a retain count of 1. You are returning an object with a retain count of zero, therefore your method name should not contain new.

Allocating and initializing object in one function, sending it to other function and release it there

I Have this function that allocating and initializing object :
+(Item*)getItem:(NSString*)uid{
Item *file = [[Item alloc]init];
//do some stuff
return file;
}
and this is the call for this function:
Item *tmp = [LibraryScan getItem:itemid];
//do some stuff
[tmp release];
Now i want to release it after i get it,like i write above.
Have I done it right?
In my opinion, you should autorelease the object before returning it, letting the caller decide if he wants ownership on it :
+(Item*)getItem:(NSString*)uid{
Item *file = [[Item alloc]init];
//do some stuff
return [file autorelease];
}
So :
Item *tmp = [LibraryScan getItem:itemid];
//do some stuff
//[tmp release]; nothing to release
Yes your code is correct. But it also should meet the objective-c naming conventions that state that methods returning non-autoreleased objects (that is - the object that caller is responsible to release) should contain copy, alloc or new in method name.
So I would change your method name to newItem if you really want to return non-autoreleased object from it.
Its not wrong but its not a standard way.
Modify your implementation by this:
+(Item*)getItem:(NSString*)uid {
Item *file = [[[Item alloc]init]autorelease];
//do some stuff
return file;
}
and :
Item *tmp = [[LibraryScan getItem:itemid]retain];
//do some stuff
[tmp release];
It will help you to debug the memory leaks properly.

why is "error:&error" used here (objective-c)

why is "error:&error" used here (objective-c)
NSError *error = nil;
NSArray *array = [moc executeFetchRequest:request error:&error];
wouldn't an object in objective-c be effectively pass-by-reference anyway?
The argument type for error: is NSError** (i.e. a pointer to a pointer to an object). This permits the moc object to allocate and initialize a new NSError object as required. It is a common pattern, especially in Cocoa.
The NSError documentation gives some indication of the motivation for this approach:
Applications may choose to create subclasses of NSError to provide better localized error strings by overriding localizedDescription.
Passing in an NSError** argument allows that method to return any subclass of NSError that makes sense. If you passed in NSError*, you would have to supply an existing NSError object, and there would be no way for the method to return a different object from the one you passed in.
To be clear, the method could look something like this:
- (NSArray*)executeFetchRequest:(Request *)request error:(NSError**)error {
...
if ((error != NULL) && (some_error_condition)) {
*error = [[[SomeNSErrorSubclass alloc] init...] autorelease];
return nil;
}
}
Note that this also allows the calling code to ignore errors by simply passing in NULL for the error: parameter, as follows:
NSArray *array = [moc executeFetchRequest:request error:NULL];
Update: (in response to questions):
There are two reasons why the argument type has to be NSError** instead of NSError*: 1. variable scoping rules, and 2. NSError instances are imutable.
Reason #1: variable scoping rules
Let's assume that the function declaration were to look like this:
- (NSArray*)executeFetchRequest:(Request *)request error:(NSError*)error;
And we were to call the function like this:
NSError * error = nil;
[someArray executeFetchRequest:someRequest error:error];
if (error != nil) { /* handle error */ }
When you pass in a variable this way, the function body will not be able to modify the value of that variable (i.e. the function body will not be able to create a new variable to replace the existing one). For example, the following variable assignments will exist only in the local scope of the function. The calling code will still see error == nil.
- (NSArray*)executeFetchRequest:(Request *)request error:(NSError*)error {
...
error = [[[NSError alloc] init...] autorelease]; // local only
error = [[[SomeNSErrorSubclass alloc] init...] autorelease]; // local only
}
Reason #2: instances of NSError are immutable
Let's keep the same function declaration, but call the function like this:
NSError * error = [[[NSError alloc] init...] autorelease];
[someArray executeFetchRequest:someRequest error:error];
if (error != nil) { /* handle error */ }
First of all, the variable scoping rules guarantee that error can not be nil, so the if (error != nil) { ... condition will always be true, but even if you wanted to check for specific error information inside the if block, you would be out of luck because instances of NSError are immutable. This means that once they are created, you cannot modify their properties, so the function would not be able to change the domain or userInfo of that NSError instance that you created in the calling code.
- (NSArray*)executeFetchRequest:(Request *)request error:(NSError*)error {
...
error.domain = ... // not allowed!
error.userInfo = ... // not allowed!
}
It's effectively another return value. The error is not dominant by convention in Cocoa when there is a return value for the operation. When an error is encountered, it may be returned to you by this out parameter.
In the case of NSError, it works this way because NSError is not a mutable type - its fields are set at initialization and never mutated. Therefore, you cannot pass an NSError as usual and set the error code.

EXC_BAD_ACCESS iOS error

I know, I know. I've Google and searched here but can't figure out what's going on.
Anyway, i'm getting this error when using the following code, i'm sure it's something small. I know it's to do with memory management and down to how i'm handling my object in the main method.
Here's my main code:
Person *p1 = [[Person alloc] init];
[p1 initWithFirstname:#"John" lastname:#"Doe" andAge:23];
outputText.text = [p1 getDetails]; // App crashes inside getDetails
Then in the person class here's the two relevant methods:
-(Person *)initWithFirstname:(NSString *)_firstname lastname:(NSString *)_lastname andAge:
(int)_age {
self = [super init];
if(self) {
self.firstname = _firstname;
self.lastname = _lastname;
self.age = _age;
}
return self;
}
-(NSString *)getDetails {
return [NSString stringWithFormat:#"%# %#, %#", firstname, lastname, age];
}
Judging by your init method, age is an int which means the format specifier is wrong. Try this:
-(NSString *)getDetails
{
return [NSString stringWithFormat:#"%# %#, %d", firstname, lastname, age];
// ^^ format specifier for an int
}
You've written an initialization method, then called init twice. Just try:
Person *p1 = [[Person alloc] initWithFirstname:#"John" lastname:#"Doe" andAge:23];
outputText.text = [p1 getDetails];
You're first calling the default init of your newly allocated object. That init returns a pointer to your object which you store in p1. Then you call initWithFirstname:lastname:andAge: on p1, which is wrong as you've already called another initializer first. You're only supposed to run one initializer.
You need to do:
Person *p1 = [[Person alloc] initWithFirstname:#"John" lastname:#"Doe" andAge:23];
But your crash stems from the fact that you pass the number 23 to the format specifier %#. You need to use %d for the age instead. Also, you should retain the first and last name (and release them in your dealloc) to avoid any crashes later on when using non-const strings.
What happens is that because of %#, the NSLog tries to treat the 23 as a pointer to an object and wants to call the description method of the supposed object. But 23 is an invalid/unused address, and trying to access the memory at that "address" is what makes your app crash.

iPhone +NSCoding/NSKeyedArchiver

I Have a small problem with NSCoding and NSKeyedArchiver when persisting my App Settings class and was hoping someone could spot an issue if it's my code, I am relatively new to Obj-C but have plenty of coding experience in numerous languages so the code seems ok to me but...
I have an instance class to hold my app Settings, the class itself is a retained property of the main App Delegate, this is created via a
#interface AppDelegate : NSObject <UIApplicationDelegate> {
Settings *settings;
[...]
}
#property (nonatomic, retain) Settings *settings;
[...]
This is created in the applicationDidFinishLaunching event as:
settings = [Settings LoadSettings];
If I comment out the above line then the app works fine every time, however if I pull the oject back from persisted settings using NSCoder and NSKeyedUnarchiver, the SIGARBT error is thrown as a NSCFString selector is being sent for what is encoded as a boolean property? The Settings Class is defined as an NSObject which implements the protocol.
#interface Settings : NSObject <NSCoding>
As I say, creating an instance of the settings class is fine, there is no issue, saving it seems OK as well as checking the returning class from the LoadSettings method shows the right values, only after exiting the method does the expected bool value seem to be getting sent to the load method as an NSCFString
- (id)initWithCoder:(NSCoder *)decoder {
if (self = [super init]) {
[...]
self.animateMenus = [decoder decodeBoolForKey:#"animateMenus"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder {
[...]
[encoder encodeBool:animateMenus forKey:#"animateMenus"];
}
Once the settings have been loaded the property in question is used like this:
SettingsViewController *settingsView = [[SettingsViewController alloc] initWithNibName:#"SettingsView" bundle:nil];
[self presentModalViewController:settingsView animated:[AppDelegate instance].settings.animateMenus];
[settingsView release];
**The animateMenus member of the settings class will now throw the following:
-[NSCFString animateMenus]: unrecognized selector sent to instance 0xc712570 2010-10-15 11:12:51.828 App[900:207] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSCFString animateMenus]: unrecognized selector sent to instance 0xc712570'
Whereas, taking the 'settings = [Settings LoadSettings];' call out of the app start-up removes the issue (but then always uses the app defaults)?
Load and Save Methods:
+ (Settings*) LoadSettings {
Settings *s = nil;
#try {
NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:#"settings"];
if (data == nil) {
s = [[Settings alloc] init];
[s Initialise];
[s SaveSettings];
}
else
s = (Settings*)[NSKeyedUnarchiver unarchiveObjectWithData:data];
}
#catch (NSException * e) {
NSLog(#"Error Loading Settings\n%#", [e reason]);
}
#finally {
return s;
}
}
// Saves the settings dictionary to the user's device documents folder..
- (void) SaveSettings {
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:#"settings"];
}
Load is a static method all other members of Settings are instance.
You need to do:
settings = [[Settings LoadSettings] retain];
That's because in your LoadSettings, the result of NSKeyedUnarchiver is an autoreleased object. And it got released :-) At that location a new object was created, an NSString in this case.
Edit:
Well, I just noticed a major problem with LoadSettings that I missed at first: you mix memory release strategies: in one codepath you return the result of [[Settings alloc] init] which is not autoreleased, while in the other you retain the result of NSKeyedArchiver which is autoreleased. You need to make sure only one concept is used.
Since the method name LoadSettings does not contain alloc, copy or new in its name the convention is that it should return an autoreleased object. Thus, you should do:
if (data == nil) {
s = [[Settings alloc] init];
[s Initialise];
[s SaveSettings];
[s autorelease];
}
else
...