views:

450

answers:

5

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.)

+7  A: 

+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];
Martin Gordon
The last potential problem (which you'll rarely see in practice, but coding for it is "the right thing") is checking self for nil, as @Kent points out. Use this around your code: if ((self = [super init]) != nil) { ... }
Quinn Taylor
+1  A: 

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.

Chuck
what exactly does dealloc do? Why does it care if the object is initialized or not? Why not just free the memory that was allocated?
quano
It does free the memory. Dealloc destroys the object and frees the memory used to hold it. Thus you can't send the object any more messages since it no longer exists.
Chuck
Where can I find the source of the original dealloc? I wanna see what it does.
quano
Cocoa isn't open source, so you can't see Apple's implementation. You can read Apple's docs to find out what it does. If you want to see an example implementation, you could check out GNUstep Base.
Chuck
+3  A: 

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...
kent
chuck is also correct in pointing out that your dealloc method should call [super dealloc] as the very last thing before exiting... (so three problems in toto)
kent
+1  A: 

I would also recommend changing:

if ([self object] != nil)
    [[self object] release];

to:

[self setObject:nil];

It's less code and functionally equivalent. =)

Dave DeLong
But won't the object still exist if you do so?
quano
@quano: No, because it's going through your synthesized accessor. Since the property is declared retain, the accessor will release the old value when you set a new one. It's just the same as any time you call [self setObject:something], you don't need to explicitly release the old value.
Chuck
that makes sense. I also saw that you can just do [[self object] release], because it is okay to send messages to nil objects.
quano
Mmm, that's not a good idea. Apple strongly discourages using properties in init and dealloc methods. Manually releasing in dealloc is almost always the right thing to do. In this case you can just do [object release] — if object is nil, the message is ignored anyway, and no harm done.
Quinn Taylor
@Quinn - I call bull. With the iPhone SDK, you can define @properties without declaring a corresponding ivar. The compiler will notice this and add the ivar for you. However, you don't necessarily know what that ivar is called, and more importantly, it doesn't exist when you're typing code. This means the *only* way to release the object (if it's declared as retain or copy) is by setting it to nil. And the place to do that is in the dealloc method.
Dave DeLong
Fair enough, under Modern Runtime with synthesized ivars, you're correct. http://developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC/Articles/ocProperties.html#//apple_ref/doc/uid/TP30001163-CH17-SW17I was posing a general principle — using properties in -init... and -dealloc can cause unintended (and sometimes problematic) side effects. However, sometimes you can't get around it. My advice it to be careful, especially if you're implementing the property methods instead of synthesizing them. Know exactly what will happen when you call the property.
Quinn Taylor
@Quinn: There aren't generally any harmful effects to accessors that make them bad to use in init or dealloc. Your warnings must refer to some pretty far-flung edge case.
Chuck
@Chuck: He has a valid point only if the synthesized getter/setter has been overridden by a category method or a custom getter/setter. In that case, using them might have unexpected consequences.
Dave DeLong
Quoting Apple "Note: Typically in a dealloc method you should release object instance variables directly (rather than invoking a set accessor and passing nil as the parameter)" from <http://developer.apple.com/DOCUMENTATION/Cocoa/Conceptual/ObjectiveC/Articles/ocProperties.html>
Peter N Lewis
A: 

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)];
Peter N Lewis
Thanks peter. :)
quano