NSString crash on release - objective-c

Here is my ItemInfo class interface
#interface ItemInfo : NSObject {
NSString *item;
}
#property (nonatomic, copy) NSString *ipaddress;
... and the implementation
#synthesize item;
- (id) initWithItem:(NSString *)someItem {
self = [super init];
if(self) {
item = someItem; // Ideally these things should happen here.
// Since item is a NSString and not NSMutableString,
// it should be sent a retain, thus
// making its refcount = 1
// Is my understanding correct?
}
return self;
}
- (void) dealloc {
[item release]; // I own 'item', so I should release it when done
[super dealloc];
}
I am using this class from elsewhere like thus:
char *str = inet_ntoa(addy->sin_addr);
ItemInfo *h = [[ItemInfo alloc] initWithItem:[NSString stringWithFormat:#"%s", str]];
ContentBrowserViewController *d
= [[ContentBrowserViewController alloc] initWithItemInfo:h];
[self.navigationController pushViewController:d animated:YES];
[h release];
[d release];
The crash I am encountering is
*** -[CFString release]: message sent to deallocated instance 0x6225570. 0x6225570 is the address of h.item
Where am I going wrong?

You need to call your setter using self.item = someItem. You currently disregard the setter and therefore do not copy/own the String.

In your initWithItem:, you need to do item = [someItem copy]; You could also do item = [someItem retain];, but that would cause problems if your string was a NSMutableString.
The reason for the crash is that you pass an autoreleased string, and your initWithItem: doesn't say "I need this string to stay around" (retain) or "I need my personal version of this string" (copy). The string thus gets released too often as you do release it in your dealloc.
According to your source, I'm willing to bet that the crash does not happen in the code you've posted but actually when the NSAutoreleasePool is finally releasing the string.

Related

NSString property and custom init

I have 2 questions.
First - Are string declared as such in obj-c
#property (nonatomic, copy) NSString *name;
#property (nonatomic, copy) NSString *city;
Are those (nonatomic, copy) right or should I use (nonatomic, strong), or something else ?
Second - If I want to set custom initializer for above strings do I use
-(id)initWithName:(NSString *)n andCity:(NSString *)c
{
self = [super init];
if (self) {
self.name = n;
self.city = c;
}
}
or should I use:
-(id)initWithName:(NSString *)n andCity:(NSString *)c
{
self = [super init];
if (self) {
self.name = [n copy]
self.city = [c copy];
}
}
As I can see both ways seem to work for both questions but I'm sure one is more correct than the other, so I wanted to ask which should I use to write correct code in further projects.
Thanks.
You want to use copy for value-semantic-typed properties. (Of which NSString is always one.) Otherwise, I can pass you an instance of NSMutableString and then change it out from under you after the fact. Immutable objects will implement -copyWithZone: by doing return [self retain]; so as to not actually create a second copy when it's not needed. See also: NSString property: copy or retain?
In terms of your -init method, you want to avoid using the property setters like you have since they could be overridden in a subclass to do something unforeseeable by your class. So assuming default auto-ivar-synthesis naming pattern, you would want to do this:
-(id)initWithName:(NSString *)n andCity:(NSString *)c
{
if (self = [super init])
{
_name = [n copy];
_city = [c copy];
}
return self;
}
It's a subtle thing, and it won't often be a problem, but if you continue to use inherently-virtual property setters in -init and -dealloc methods, over a long enough timeline, you will get burned by this (can you tell I've been burned by this?)
As for leaked memory, if you're using ARC, doing something like self.foo = [bar copy]; when foo is a copy property will result in copy getting called twice, and potentially two copies being made, but ARC should take care of properly releasing the redundant/intermediate copy, and there should not be a memory leak.
First - it doesn't really matter if you use copy or strong. Personally i prefer to copy strings rather than retain them
Second - `
-(id)initWithName:(NSString *)n andCity:(NSString *)c
{
self = [super init];
if (self) {
self.name = n;
self.city = c;
}
}`
You don't need to use copy message once more cause your properties will copy n and c. If you apply your second piece of code you'll copy n and c twice and have a memory leak

a puzzle on Objective-C memory management

This is one segment of codes used in one of my project for managing one of my class instance:
#interface C: NSObject
{
NSMutableArray *myArr;
}
#property (nonatomic,retain) NSMutableArray *myArr;
//...
#end
#implementation C
#synthesize myArr;
//...
-(id)init
{
//...
myArr = [[NSMutableArray alloc] init];
//...
}
//version 1 of dealloc method
-(void)dealloc
{
//...
self.myArr = nil;
//...
}
//version 2 of dealloc method
-(void)dealloc
{
//...
[myArr release];
//...
}
here the version 1 dealloc method doesn't work and Xcode says something like "EXC_BAD_ACCESS..." and the app crashed.
if I modified the dealloc method as version 2, it works.
Does anybody have any idea why?
Thx in advance.
As Duncan said, the EXEC_BAD_ACCESS error means that the object doesn't exist anymore.
This is probably due to the fact that myArr is being released before the dealloc gets called.
To facilitate memory management and to keep track of reference counts, I like to make it clearer in the init methods, for example:
-(id)init
{
//...
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:0];
self.myArr = array;
[array release];
//...
}
By using the generated setter self.myArr = array; you are ensuring that the reference count is being delt with correctly, it will release the old value and retain the new one for you.
#MadhavanRP : if the property is a retained property:
#property(nonatomic, retain) NSMutableArray *myArr;
calling
self.myArr = nil
is exactely the same as calling
[myArr release];
myArr = nil;
Edit: #Sylvain beat me to it :)
This is OK even if it's bad idea/confusing to have same name for iVar and property. I removed the iVar declaration.
#interface C: NSObject
{}
#property (nonatomic,retain) NSMutableArray *myArr;
//...
#end
Generate your iVar using #synthetize.
#implementation C
#synthesize myArr = _myArr;
//...
Your init is all wrong. You are assigning the iVar without using the setter method.
-(id)init
{
//...
// You were doing
// _myArr = [[NSMutableArray alloc] init];
// You should do
NSMutableArray array = [[NSMutableArray alloc] init];
self.myArr = array;
[array release];
// You could avoid the temporary variable but this confuse XCode static analyzer
//...
}
This is OK. I guess that #synthetize generated another iVar to back your property.
This other iVar was not properly assign.
You would not notice this if you do not user self.myArr (or the equivalent [self myArr]).
This confusion is main reason why hardcore ObjC programmer do not like the new property thing. :) This confuse new programmers.
//version 1 of dealloc method
-(void)dealloc
{
//...
self.myArr = nil;
//...
}
This is not good as you bypass the setter, as in init method. This was working anyway.
//version 2 of dealloc method
-(void)dealloc
{
//...
[myArr release];
//...
}
It looks like your myArr object is being messaged when it should not be, in the first case you hide the problem by setting it to nil, in the second you don't. Either method should not crash. When do you call [super dealloc]; (you should call it at the end of the dealloc implementation).

NSMutableString returning (null)

What's wrong with this? The NSMutableString returns (null).
.h:
NSMutableString *aMutableString;
...
#property (assign) NSMutableString *aMutableString;
.m:
#synthesize aMutableString;
- (void)aMethod {
[self setAMutableString:[[NSMutableString alloc] initWithString:#"message: "]];
if (someCondition) {
[[self aMutableString] appendString:#"woohoo"];
}
}
- (void)anotherMethod {
NSLog(#"%#", [self aMutableString]);
[[self aMutableString] release];
}
First of all, your code has a couple of problems. First, you should define your aMutableString #property as retain, not assign. assign is generally for primitive, non-object types, like ints, etc., and for some special cases of objects. You appear to want to take ownership of aMutableString in such a way that it persists after the event loop returns. In your posted code, you end up accomplishing that because of how you incorrectly set the aMutableString in the following line:
[self setAMutableString:[[NSMutableString alloc] initWithString:#"message: "]];
By creating an NSMutableString with alloc/init, you're creating a potential memory leak situation, though in your situation, it actually makes up for your defining the property as assign rather than retain.
Your second -anotherMethod is also potentially dangerous in that:
1) it releases an instance variable you defined as assign
2) after releasing it, it doesn't set it to nil. If you try to access that instance variable elsewhere in that class at a later point in time, you will likely get a crash because the pointer is no longer valid, if the instance variable has been dealloced.
So, the code should most likely look something like this:
.h
NSMutableString *aMutableString;
...
#property (retain) NSMutableString *aMutableString;
.m:
#synthesize aMutableString;
- (void)dealloc {
[aMutableString release];
[super dealloc];
}
- (void)aMethod {
[self setAMutableString:[NSMutableString stringWithString:#"message: "]];
if (someCondition) {
[aMutableString appendString:#"woohoo"];
}
}
- (void)anotherMethod {
NSLog(#"%#", aMutableString);
// the following is potentially unsafe!
// [[self aMutableString] release];
// it should be one of the following:
[aMutableString release]; aMutableString = nil;
// or
// [self setAMutableString:nil];
}
That said, without more information, it's a little hard to say what the problem is. I assume you mean the NSLog() call is printing (null)? If so, that means that aMutableString is still nil. Are you calling -aMethod before calling -anotherMethod?
If you want to make sure that aMutableString is initialized to an empty string, you could override -init:
- (id)init {
if ((self = [super init])) {
aMutableString = [[NSMutableString alloc] init];
}
return self;
}
Make sure you have #synthesize aMutableString; in your .m file

Proper way to release objects in Objective-C

If I have an object Sale that contains another object Item. Let's say my Sale header looks like this:
#import <Foundation/Foundation.h>
#import "Item.h"
#interface Sale : NSObject {
Item *item;
}
#property (readonly) Item *item;
-(id) initWithItem:(Item*)i;
#end
And in my function I create a new Item and Sale object like so:
Item *i = [[Item alloc] init];
Sale *s = [[Sale alloc] initWithItem:i];
[i release];
If, when I'm done with the Sale object I simply do:
[s release];
is that enough to make sure that all of that memory allocation is cleaned up effectively?
Furthermore, will releasing *i cause any issues if I later try to access [s item]?
It depends on how you're storing i. If you copy or retain it in initWithItem:, you need to release it in Sale's dealloc method (see below). If you're storing a weak reference, this is fine.
- (id)initWithItem:(Item *)i {
if ((self = [super init])) {
// If you're retaining i here, you need to release it in dealloc.
item = [i retain];
}
return self;
}
- (void)dealloc {
[item release];
[super dealloc];
}
See Apple's Memory Management Programming Guide for more information.

Problem with releasing an object

I've got this code:
Entry.h
#import <Foundation/Foundation.h>
#interface Entry : NSObject {
id object;
SEL function;
}
#property (retain) id object;
#property (assign) SEL function;
-(Entry*) initWithObject:(id)object selector:(SEL)function;
#end
Entry.m
#import "Entry.h"
#implementation Entry
#synthesize object;
#synthesize function;
-(Entry*) initWithObject:(id)obj selector:(SEL)sel {
self = [super init];
[self setObject:obj];
[self setFunction:sel];
return self;
}
-(void) dealloc {
[super dealloc];
if ([self object] != nil)
[[self object] release];
}
#end
And when I do this:
Entry *hej = [Entry alloc];
[hej release];
I get:
objc[2504]: FREED(id): message object sent to freed object=0xf5ecd0
Program received signal: “EXC_BAD_INSTRUCTION”.
What am I doing wrong?
(And this insert code thing at stack overflow doesnt work, unless I'm doing something wrong and you're not supposed to click "code sample" and then paste.)
+alloc only allocates memory. You need -init to actually create the object in that memory space. Since you are only allocating memory and not creating an object there, calling -release on a chunk of memory is giving you an error. Further, you want your [super dealloc] call to appear at the end of you -dealloc method. Change those two things and the following should work:
Entry *hej = [[Entry alloc] init];
[hej release];
there are two problems here:
1) you need to check that self = [super init] does not return nil. Typical usage would be to follow wrap your initialization code with the conditional:
if ((self = [super init]) != nil) {
// initialize the object...
}
2) but where you are getting stuck is on instantiating your object: you should do it like this:
Entry *hej = [[Entry alloc] initWithObject:myObj selector:mySelector];
(assuming that you want to go through the custom initializer you just defined...
else just use the default init method.) but 'alloc' must be followed by an init.
Entry *hej = [[Entry alloc] init]; // will do the trick...
Firstly, you need an init to go with your alloc. Second, in dealloc, you send a message to self after calling [super dealloc]. You can't do that. The final deallocation should go at the end.
I would also recommend changing:
if ([self object] != nil)
[[self object] release];
to:
[self setObject:nil];
It's less code and functionally equivalent. =)
There are many things wrong with your code. I'll try to go through them.
First, its better to use a different ivar name to your property name so its clear where you are using each. Apple normally uses an underscore prefix, but any prefix will do.
#interface Entry : NSObject {
id _object;
SEL _function;
}
#property (retain) id object;
#property (assign) SEL function;
#synthesize object = _object;
#synthesize function = _function;
Next, you aren't using the standard init template (although this probably wont make any difference normally).
-(Entry*) initWithObject:(id)obj selector:(SEL)sel {
self = [super init];
if (self != nil) {
// initializations
}
return self;
}
Next, Apple (for good reasons) recommends against using getters/setters in your init/dealloc. So your init would be:
-(Entry*) initWithObject:(id)obj selector:(SEL)sel {
self = [super init];
if (self != nil) {
_object = [obj retain];
_object = sel;
}
return self;
}
Next, after [super dealloc] your object is destroyed, so you cannot reference self (and hence your ivars) after that, so your dealloc should look like:
-(void) dealloc {
// your deallocations
[super dealloc];
}
Further, as above, Apple recommends you should not use setters or getters in your dealloc routine, so your deallocation would initially look like:
if (_object != nil)
[_object release];
But further still, Objective C allows (and Cocoa encourages) that sending a method to nil does nothing. This is in stark contast to most other languages where messaging nil would cause a crash, but it is how Objective C/Cocoa work and you need to get used to it. So your deallocation is actually just:
[_object release];
And finally, alloc only allocates the memory for your object, you have to initialize it, so the initialization would be something like:
Entry *hej = [[Entry alloc] initWithObject:myobj selector:#selector(mymethod)];