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.
Related
I have these two buttons hooked up to these two methods (they're nearly identical)
-(void)moveOneImageNewer{
int num = [numberOfImage intValue];
num--;
numberOfImage = [[NSString stringWithFormat:#"%i",num] retain];
//Load the image
[self loadImage];
}
-(void)moveOneImageOlder{
int num = [numberOfImage intValue];
num++;
numberOfImage = [NSString stringWithFormat:#"%i",num];
//Load the image
[self loadImage];
}
If I hit either of them twice (or once each, basically if they get called a total of two times) I get an EXC_BAD_ACCESS. If I throw a retain on: numberOfImage = [[NSString stringWithFormat:#"%i",num]retain] it's fine though. Can someone explain why this is? I did an NSZombie on the instruments and traced it back to this stringWithFormat call. Thanks in advance!
+stringWithFormat: doesn't contain 'new', 'alloc', 'copy', or 'retain', so it should be expected that you have to retain the return value of it if you want the new NSString it creates to stick around.
Edited to include this handy link duskwuff kindly dug up: http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html#//apple_ref/doc/uid/20000994-BAJHFBGH
If numberOfImage is a properly declared property, e.g.
#property (copy) NSString *numberOfImage;
and it was properly synthesized (in the #implementation section for the class):
#synthesize numberOfImage;
then you can do:
- (void) moveOneImageNewer
{
self.numberOfImage = [NSString stringWithFormat: #"%i", [self.numberOfImage intValue] - 1];
// Load the image
[self loadImage];
}
The property setter will take care of retaining the string and, if necessary, releasing the previous string.
FWIW, why on earth is numberOfImage a string? Why not a simple int?
numberOfImage is an instance variable or property of your class, right?
You are setting it to a stringWithFormat (which returns an auto-released NSString) without claiming ownership of that object (by calling retain).
If you do not retain it, it will get auto-released before the method is called again (and then the first line will fail, as it tries to access the previously set, now auto-released value).
Consider using properties, they have auto-generated memory management code (including releasing the old NSString when you set the new one).
You haven't retained the string object in "moveOneImageOlder", so that object gets autoreleased at the end of the event cycle and points to nothing. That's why you get the EXC_BAD_ACCESS next time you try to use it.
Use a retain to claim ownership of the NSString. Remember to release when you're done though (you can use properties to help you with this)
-(void)moveOneImageNewer{
int num = [numberOfImage intValue];
num--;
[numberOfImage release];
numberOfImage = [[NSString stringWithFormat:#"%i",num] retain];
//Load the image
[self loadImage];
}
-(void)moveOneImageOlder{
int num = [numberOfImage intValue];
num++;
[numberOfImage release];
numberOfImage = [[NSString stringWithFormat:#"%i",num] retain];
//Load the image
[self loadImage];
}
Add this in dealloc:
- (void)dealloc {
[numberOfImage release];
[super dealloc];
}
Well, the NSString class method "stringWithFormat" returns an autorelease NSString object if I'm right.
So the second call to your method would have numberOfImage pointing to nothing, as the autorelease NSString object it used to be pointing to has already been released and deallocated since you didn't retain it.
The part that is directly causing the crash is [numberOfImage intValue] when you call the method a second time, as you sending a message to an object (pointed to by numberOfImage) that no longer exist.
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.
I'm having the code as below.
- (void)viewDidLoad
{
NSArray* myarr = [self createArray];
for (NSString* str in myarr)
{
NSLog(#"%#",str);
}
[myarr release];
}
-(NSArray*)createArray
{
NSArray* arr1 = [[NSArray alloc] initWithObjects:#"APPLE",#"MAC",#"IPHONE",nil];
return arr1;
}
When I "Build & Analyze", its showing two leaks. One at [myarr release] saying, incorrect decrement of the reference count of an object that is owned at this point. and Other at return arr1, saying, Potential leak of an object allocated on line 152 and stored into arr1.
From my above code, the method "createArray" is returning a pointer and I'm releasing it as well. Is my way of coding right or wrong?
From my above code, the method "createArray" is returning a pointer and I'm releasing it as well. Is my way of coding right or wrong?
that depends on how you look at it.
1) the ref counting looks ok
2) the static analyzer flags objc methods based on names, in some cases. so the issue will likely vanish if you rename createArray to newArray, or something named new*. so it expects a convention (the ones used by Apple) to be followed.
therefore, it's the message that's bit shallow, it doesn't really analyze the program, but bases its findings/results on convention -- and not an actual evident issue which a human can read.
If you're just using the array in your viewDidLoad method, then you don't need to alloc an array in there at all. You can just use an autoreleased array returned as 7KV7 suggested. You can return an autoreleased array in your -(void)createArray as well without alloc'ing an object. Here is an example.
- (void)viewDidLoad
{
NSArray* myarr = [self createArray];
for (NSString* str in myarr)
{
NSLog(#"%#",str);
}
}
-(NSArray*)createArray
{
return [NSArray arrayWithObjects:#"APPLE",#"MAC",#"IPHONE",nil];
}
If you don't have to alloc an object to use it, it makes for less, and cleaner code, IMO.
Try this
- (void)viewDidLoad
{
NSArray* myarr = [[NSArray alloc] initWithArray:[self createArray]];
for (NSString* str in myarr)
{
NSLog(#"%#",str);
}
[myarr release];
}
-(NSArray*)createArray
{
NSArray* arr1 = [[NSArray alloc] initWithObjects:#"APPLE",#"MAC",#"IPHONE",nil];
return [arr1 auotrelease];
}
The problem with your code is that
You do not allocate myarr using alloc or new so you do not take ownership of the object. Hence the issue in release.
You allocate arr1 so you take ownership of the object and you return arr1. Hence you do not release it. That is the reason for the leak.
I am receiving this error: '-[__NSCFArray insertObject:atIndex:]: mutating method sent to immutable object'
In the ViewDidLoad I initialized my array:
id object = [[NSUserDefaults standardUserDefaults] objectForKey:#"array"];
if (object)
{
locationArray = object;
NSLog(#"retrieved", object);
}
else
{
locationArray = [[NSMutableArray alloc] init];
NSLog(#"init");
}
Then, I am trying to add the data to locationArray:
ABMultiValueRef multi = ABRecordCopyValue(person, property);
NSUserDefaults *locatie = [NSUserDefaults standardUserDefaults];
// Set up an NSArray and copy the values in.
NSArray *theArray = [(id)ABMultiValueCopyArrayOfAllValues(multi) autorelease];
//everything goes fine the first time, but the second time i receive an error after at this code:
[locationArray addObject:theArray];
[locatie setObject:locationArray forKey:#"array"];
Every first time I select an address everything is fine. But every second time I am receiving that error.
What did I do wrong?
NSString *fname = (NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty);
NSMutableString *lname = (NSMutableString *)ABRecordCopyValue(person, kABPersonLastNameProperty);
What makes you think ABRecordCopyValue is going to return a mutable string here?
Just telling the compiler that it will return a mutable string (which is all “(NSMutableString *)” does) doesn't mean it will. If the Address Book documentation doesn't specify that this will return a mutable string, assume it won't and create a mutable string yourself.
Speaking of which…
NSMutableString *name = [[NSMutableString alloc] init];
Here's the string you should be appending to. You don't need lname to be mutable, because this string is mutable.
NSMutableString *space = #" ";
fname = [fname stringByAppendingFormat:space];
fname = [fname stringByAppendingFormat:lname];
By doing this, you waste the mutable string you created. You are creating two intermediate immutable strings here, not appending to the mutable string.
name = fname;
And here, you throw away the mutable string entirely (and thereby leak it since you never released it), replacing it with the immutable string you got from your series of stringByAppendingFormat: messages.
What you should do is send the name mutable string an appendFormat: message, passing both the first and last name strings. You don't need the space string; you can include that in the format string.
See also the NSMutableString docs.
[nameArray addObject:fname];
At no point prior to this statement have you created an NSMutableArray object and stored its pointer in the nameArray variable. Not in any code you've shown, anyway.
Until you do that, this variable holds nil, the pointer to no object. The addObject: message does nothing because that's what messages to nil do: Nothing. Logging the array you don't have produces “(null)” because that's the description of nil.
if (nameArray == nil) {
NSLog(#"NO DATA TO RETRIEVE FROM USERDEFAULTS");
You aren't showing any code that retrieves from user defaults. Even if you did, it would return an immutable array as Sven said; you would have to make a mutable copy.
This is the best I can do without a description of the problem. We may be able to provide more and better suggestions if you edit your question to tell us what happens when you run the above code, and not just what doesn't happen.
The problem occures when you get the locationArray from the NSUserDefaults and try to insert some object ([locationArray addObject:theArray];). The object you get from the NSUserDefaults is not mutable, so you have to create a mutable copy:
NSMutableArray* locationArray = [[NSUserDefaults standardUserDefaults] objectForKey:#"array"];
if (locationArray != nil)
locationArray = [[locationArray mutableCopy] autorelease];
else
locationArray = [NSMutableArray array];
And don't forget to check if fname is not nil before adding it to array
I'm looking for a good way to return a string constructed by a NSMutableString avoiding leaking :
eg:
+(NSString *)myMethod{
NSMutableString *numberToReturn = [[NSMutableString alloc] init];
[numberToReturn appendString:#"lorem ipsum"];
return numberToReturn;
}
The leak instrument said I had leak with this variable.
I tried autorelease but it crashes
I tried to return a copy or copying the mutablestring into a nsstring but leak still present.
Any idea or trick?
I'have a to call this method each time the user type a value into the textfield, so the application crashes due to bad memory management...
thank you
You should use -autorelease. Your method should be written as:
+ (NSString*)myMethod {
NSMutableString *stringToReturn = [[NSMutableString alloc] init];
[stringToReturn appendString:#"lorem ipsum"];
return [stringToReturn autorelease];
}
If there is a crash, the fault is elsewhere.
Of course, you can make use of factory methods that return an already-autoreleased instance, rewritting your method as
+ (NSString*)myMethod {
NSMutableString *result = [NSMutableString string];
[result appendString:#"lorem ipsum"];
return result;
}
or better yet for your example,
+ (NSString*)myMethod {
NSMutableString *result = [NSMutableString stringWithString:#"lorem ipsum"];
//...do something with result;
return result;
}
Obviously if you method's only purpose is just to return a new string with a string, you can avoid the whole method all together and use [NSMutableString stringWithString:#"lorem ipsum"] if you really need a mutable string.
You should autorelease the string before returning it. That's what the memory management rules say to do. If your app then crashes, that's evidence of a bug somewhere else in your code.