- (void)setSomeInstance:(SomeClass *)aSomeInstanceValue
{
if (someInstance == aSomeInstanceValue)
{
return;
}
SomeClass *oldValue = someInstance;
someInstance = [aSomeInstanceValue retain];
[oldValue release];
}
ok, so setter should look like. I understand first 3 lines - prevent before situation when new object is the same as the old one.
But what about this line:
SomeClass *oldValue = someInstance;
Why system have to keep address of old object. Why can't it be simply
[someinstance release];
someinstance = [aSomeInstanceValue retain];
Actually - no reason.
It's usually just a choice.
There are three idioms for writing accessors.
Autorelease:
- (void)setFoo:(id)newFoo {
[foo autorelease];
foo = [newFoo retain];
}
Less code to write, but I think autorelease in this case is being lazy.
Retain then release
- (void)setFoo:(id)newFoo {
[newFoo retain];
[foo release];
foo = newFoo;
}
Check first
- (void)setFoo:(id)newFoo {
if ([foo isEqual:newFoo]) {
return;
}
[foo release];
foo = [newFoo retain];
}
The only difference between the last two is that the second checks to see if the new value is different to the current value before trying to set the property. At the cost of an extra if statement. So - if the new value is likely to be the same as the old value, using this construction gives better performance.
Generally, and if you're not using properties for some strange reason, use retain then release, and then if profiling shows that there's a bottleneck - use the check first method.
I would suggest the default retain setter works something like this:
- (void) setFoo:(id) foo {
if ( foo == _foo) return;
[_foo release];
_foo = [foo retain];
}
if you don't check if the old and the new foo are the same, you might end up with a reference to a deallocated object if you for some reason write something like this:
myObject.foo = myObject.foo;
Because the same object would first be released, and then retained. If the myObject is the sole owner, the object would be deallocated after the first release, leaving you with a dangling pointer.
The default retain setter works like that :
- (void)setFoo:(Foo *)aFood
{
if (_foo != nil)
[_foo release];
if (aFood != nil)
_foo = [aFood retain];
}
Related
I'm trying to learn Objective C. I came across the following code which the compiler generates behind the scenes for #property(nonatomic, retain) NSString* myField
-(NSString*) myField
{
return myField_; //assuming myField_ is the name of the field.
}
-(void) setMyField:(NSString*) newValue
{
if(newValue != myField_)
{
[myField_ release];
myField_ = [newValue retain];
}
}
Now my question is; Why to call retain on newValue? Instead the following syntax should be used:
myField_ = newValue;
[myField_ retain];
Please advise why the above syntax is not used because as per my understanding, we want to retain the object pointed to by myField_ ?
They're the same (both are correct). You don't copy the object - retain returns the same pointer that was retained, so it's shorter and cleaner to write
ivar = [newObj retain];
than separately assigning and retaining the object.
Both syntaxes are correct. In the first case we also retain the object pointed by myField since we assign [newValue retain] to it.
I have a setter like this:
- (UIImagePickerController *) foto {
if (_foto == nil) {
_foto = [[UIImagePickerController alloc] init];
_foto.delegate = self;
}
return _foto;
}
it is declared like
#property (nonatomic, retain) UIImagePickerController *foto;
with
#synthesize foto = _foto;
on my dealloc I have
[_foto release];
At some point in my code I want to do this
self.foto = nil;
but something in my soul says the object assigned to self.foto previously will leak, because it was alloc on the setter... how do I make it right?
thanks.
Edit: No, that should be fine. As long as you don't assign something else to _foto before you release, it should work.
Yup. You create an object, then loose the pointer to it. If you throw an autorelease on the init line, that will fix it. You could also use ARC.
The init line doesn't actually do anything... You assign the pointer to an object you create, then assign it to something else.
I don't think there is a leak there. When you assign to self.foto like this:self.foto = nil;, it will release the former one automatically. If you assign it by this way: _foto = nil;, you need to release it manually before the assignment.
Yes that works, and will not leak. When you set the value of _foto, its retain count is 1 (because you called alloc). As long as you release it (which you've said you do) in dealloc, you should be fine, as the retain count will go back to 0. UNLESS your setter is ALSO written by you, and written improperly. It needs to explicitly release the old value, if it's not nil. Something like this:
- (void)setFoto:(UIImagePickerController*)foto {
if (_foto) {
[_foto release];
_foto = nil;
}
if (foto)
_foto = [foto retain];
}
I heard from people that you should use self in viewDidUnload. For instance, this is good:
- (void)viewDidUnload
{
self.object = nil;
self.object2 = nil
}
- (void)viewDidUnload
{
object = nil;
object2 = nil;
}
Is there a difference between the 2? And what is it?
There is a difference. The code that is generated by #synthesize will call release on the references to the objects you have before setting the new value. A call to self.object = nil will effectively both release the reference and set it to nil. Without the self it will just set the reference to nil.
What is almost the same as your second example is this:
- (void)viewDidUnload
{
[object release]; object = nil;
[object2 release]; object2 = nil;
}
Note they are not quite the same - if you had defined a custom getter/setter, or had KVC observers set up around one of those properties the self.object = nil would trigger them, whereas the straight [object release] above would not.
First of all, using self eliminates ambiguity.
- (void)viewDidUnload
{
id object = #"whatever";
object = nil; // This refers to the local variable above
self.object = nil; // This refers to the setter of the ivar belonging to the class
}
Another thing to watch out for is if you have the variables synthesized (meaning if you tell the compiler to generate getter & setter automatically), self.object will invoke the getter/setter, while simply object refers to the actual ivar. To prevent this ambiguity when accessing ivar vs calling the setter, you can write something like this:
self->object = nil; // This refers to the ivar object, not the getter/setter
// Or
#synthesize object = _object; // With this, you refer to the ivar as _object
// Then somewhere else
_object = nil; // This refers to the ivar
object = nil; // Compile error, undeclared identifier
self.object = nil; // This refers to the setter
According to Cocoa coding guidelines, we should avoid using underscores in naming variables though. So personally, if I really need to access the ivar directly (for example, when you are overriding the setter), I prefer using ->.
I have an object that I alloc/init like normal just to get a instance. Later in my application I want to load state from disk for that object. I figure I could unarchive my class (which conforms to NSCoding) and just swap where my instance points to. To this end I use this code...
NSString* pathForDataFile = [self pathForDataFile];
if([[NSFileManager defaultManager] fileExistsAtPath:pathForDataFile] == YES)
{
NSLog(#"Save file exists");
NSData *data = [[NSMutableData alloc] initWithContentsOfFile:pathForDataFile];
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
[data release];
Person *tempPerson = [unarchiver decodeObjectForKey:#"Person"];
[unarchiver finishDecoding];
[unarchiver release];
if (tempPerson)
{
[self release];
self = [tempPerson retain];
}
}
Now when I sprinkled some NSLogs throughout my application I noticed
self.person: <Person: 0x3d01a10> (After I create the object with alloc/init)
self: <Person: 0x3d01a10> (At the start of this method)
tempPerson: <Person: 0x3b1b880> (When I create the tempPerson)
self: <Person: 0x3b1b880> (after i point self to the location of the tempPerson)
self.person: <Person: 0x3d01a10> (After the method back in the main program)
What am I missing?
Don't do this. Besides that it breaks identity rules, you can't change the pointer values other parts of a program hold.
A better approach would be to use the PIMPL idiom: your class holds a pointer to an implementation object and you only swap that.
E.g. something along the lines of this:
#class FooImpl;
#interface Foo {
FooImpl* impl;
}
// ...
- (void)load;
#end
#implementation Foo
- (void)load {
FooImpl* tmp = loadFromDisk();
if (tmp) {
FooImpl* old = impl;
impl = tmp;
[old release];
}
}
#end
self is a function argument to instance methods. Assigning to self is perfectly reasonable, just like assigning values to other function arguments is perfectly reasonable. Since the scope of self is the current function, your code leaks one object and releases another in a way that will most likely cause a crash.
The only time it is meaningful to assign to self is in an init method. Although it is almost never used, init methods are allowed to release self and allocate a new object to return or just return nil. The only reason this works is because the return value is self and callers of init expect to use the return value.
As gf pointed out, the correct approach is a load function that assigns new values to the members of your instance, not that tries to replace the instance.
I'm implementing a singleton class as follows:
static Singleton* _singletonInstance;
#implementation Singleton
+(void)initialize
{
_singletonInstance = [[Singleton alloc] init];
}
+(Singleton*)instance
{
return(_singletonInstance);
}
initialize only gets called the first time someone calls instance. I then have a method that I can call to set up some instance variables. So it ends up looking like this.
_singleton = [Singleton instance];
[_singleton setupWithParams: blah];
When i get an instance of this singleton inside an object, it works fine the first time; However, after i dealloc and create a new copy of the object that needs an instance of the singleton, i get a BAD ACCESS error when I try to call the setup function.
Just to test things I print out the address of the instance before I make the setup call and both times they report the same address, but when i check the error log for BAD ACCESS call, it lists a completely different memory address.
Does anyone have any ideas why this pointer to the instance seems to look fine when I print it, but when I make a call to it, it is seemingly pointing to random data?
The pointer value looks valid because it used to be, but most likely the memory has been free'd, which is why what it points to looks like random data.
You've acquired one reference with your [[Singleton alloc] init] above, but is there a release somewhere else that might be executing? I bet your code is calling instance, and then release-ing later, even though your code never acquired a reference. And that shouldn't be necessary for a singleton anyways. Just a guess...
Are you deallocing your _singletonInstance somewhere?
I'm using much more complex, but very stable version of Singleton template (taken with description from Brandon "Quazie" Kwaselow Blog):
static SampleSingleton *sharedSampleSingletonDelegate = nil;
+ (SampleSingleton *)sharedInstance {
#synchronized(self) {
if (sharedSampleSingletonDelegate == nil) {
[[self alloc] init]; // assignment not done here
}
}
return sharedSampleSingletonDelegate;
}
+ (id)allocWithZone:(NSZone *)zone {
#synchronized(self) {
if (sharedSampleSingletonDelegate == nil) {
sharedSampleSingletonDelegate = [super allocWithZone:zone];
// assignment and return on first allocation
return sharedSampleSingletonDelegate;
}
}
// on subsequent allocation attempts return nil
return nil;
}
- (id)copyWithZone:(NSZone *)zone
{
return self;
}
- (id)retain {
return self;
}
- (unsigned)retainCount {
return UINT_MAX; // denotes an object that cannot be released
}
- (void)release {
//do nothing
}
- (id)autorelease {
return self;
}
Valerii's code is better for implementing a singleton, but the problem is almost certainly that the code that calls [Singleton instance] is operating as if it has ownership without actually taking ownership using retain, and then is later releasing it.
Look there for your bug, and read the Memory Managment Rules.
Also, in Xcode, enable NSZombieEnabled and the console will show you when you try to message the object after its been released.