I would like to understand why it could be useful to do this (assuming "object" was previously allocated):
[object release];
object=nil;
Thx for helping,
Stephane
Even though you release an object, your variable will still point to something. What it points to is undefined. It could still point to the old object, or to some point in memory. Setting it to nil avoids sending messages to whatever it points to, and prevents errors (messaging nil does nothing).
Here's an answer that states it better: Setting pointers to nil, objective-c
If you just released it, object would still point to the memory address that it had before. If you checked it to be nil (object == nil) it would return NO. It is better to make sure that it points to nil after you release it.
This mostly has to do with multi threading, if an other thread tries to access the object you released and set to nil it will not crash. You can send message to nil object and it will just do nothing.
But if it is just deallocated you are sending a message to the deallocated object and your app will crash.
Here is nice reading material:
http://iphonedevelopment.blogspot.com/2010/09/dealloc.html
http://www.red-sweater.com/blog/1423/dont-coddle-your-code
Related
Can we check the object is nil or not before going to release in dealloc method. I am new to objective C. Is this right way to avoid segmentation issues?.
-(void)dealloc{
if(stringObject!=nil){
[stringObject release];
}
}
Testing for nil before release is fully redundant in Objective C and will not add any resiliency to your code.
Indeed, the whole point of segmentation faults (EXC_BAD_ACCESS) is having a pointer which is not nil, thus points to some memory, and accessing that piece of memory after it has been freed.
If the pointer is nil in the first place you will not be possibly able to access any memory with it and you will not have a segmentation fault in Objective C (unlike C or C++).
The real solution to segmentation faults is correct memory management. If retain/release management seems too complex, you can have a look at ARC, which has its own intricacies, though (although much less than manual retain/release management).
A simple
if(stringObject)
will do, if you just want to check if the variable points to an object.
But with Objective C it is not necessary to test for nil because an message to a null object will simply do nothing. So it is enough to say:
-(void)dealloc
{
[stringObject release];
stringObject = nil;
[super dealloc]; //added because of the comments
}
Under ARC, you can leave out the whole dealloc in most situations, because 1) the release is managed automatically, and 2) the call to dealloc is the made just before the object ends it's life, so the nil is not necessary. However, if you use custom c-style allocation you may still need an alloc method. But this belongs to an advanced topic.
Here's a link to the dev guide on working with objects in general:
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithObjects/WorkingwithObjects.html#//apple_ref/doc/uid/TP40011210-CH4-SW1
This suffices as a nil test (your way is also correct, just slightly longer):
if (stringObject)
{
//do something
}
But in a simple case like yours, you don't even have to do that, because in Objective-C, sending messages to nil objects just means nothing happens:
[aNilObject doSomething]; //nothing happens, this is perfectly valid and won't throw an exception; it won't even log anything. Once you're used to it, it's great and avoids lots of boilerplate nil checks.
Going further, in your case, you should switch to ARC (after you're done learning manual retain and release, which is a good exercise).
Your code would then look like this:
//you don't need to call dealloc at all in ARC :)
Yes you can. Before ARC there was a very common macro defined
#define SAFE_RELEASE(object) if (object != nil) { [object release]; object = nil; }
NOTE: However, sending a message to nil will return nil.
I am testing the following code below. ffv is declared in the interface file.
ffv = [[FullFunctionView alloc] initWithFrame:self.view.bounds];
NSLog(#"%i", [ffv retainCount]); // prints 1
[self.view insertSubview:ffv belowSubview:switchViewsBtn];
NSLog(#"%i", [ffv retainCount]); // prints 2
[ffv release]; // you can release it now since the view has ownership of ffv
NSLog(#"%i", [ffv retainCount]); // prints 1
if (ffv == nil)
NSLog(#"ffv is nil");
// "ffv is nil" is not printed
[ffv testMethod]; // "test method called" is printed
this is my [ffv testMethod] implementation
- (void)testMethod
{
NSLog(#"test method called");
}
What I deduce in this case is that even if you release an object with retain count 2, you lose ownership of that object however, the reference is still kept.
Now, my question are:
Is my deduction correct?
Is there anything else important that can be deduced from this?
What are the complications caused by still keeping (using) ffv and calling methods from ffv? (My opinion is that this is ok since the view will always own ffv and won't release it until someone calls viewDidUnload. And as long as I don't pass ffv's reference to other objects.)
There are a couple of problems with using ffv after you have released it and it's only retained by your view controller's view.
1) It introduces a potential for future bugs, because later you might not remember that ffv is otherwise not retained. When you release the view (e.g. by replacing it with another view), you have a dangling pointer that you still hold a reference to.
2) In the special case of a UIViewController the view could be released at any time (you usually never call viewDidUnload yourself). The default behavior of UIViewController, when receiving a memory warning and the view is currently not visible, is to release the view, so unless you set the reference to nil in viewDidUnload, you have a dangling pointer again, even though you never explicitly released the view yourself.
1) Is my deduction correct?
Your deduction is correct. The Memory Management Programming Guide explains that each object has one or many owners. You own any object you create using any method starting with alloc, new, copy, or mutableCopy. You can also take ownership of an object using retain. When you're done with an object, you must relinquish ownership using release or autorelease.
Releasing the object doesn't change the value of any variables that reference that object. Your variable contains the object's memory address until you reassign it, no matter what retain count the object has. Even if the object's retain count goes to zero, causing the object to get deallocated, your variable will still point at that same address. If you try to access the object after it's been deallocated, your app will normally crash with EXC_BAD_ACCESS. This is a common memory management bug.
2) Is there anything else important that can be deduced from this?
Nothing comes to mind.
3) What are the complications caused by still keeping (using) ffv and calling methods from ffv? (My opinion is that this is ok since the view will always own ffv and won't release it until someone calls viewDidUnload. And as long as I don't pass ffv's reference to other objects.)
When you call release, you are telling the Objective C runtime that you no longer require access to the object. While there may be many cases like this one in which you know the object will still exist, in practice you really shouldn't access an object after calling release. You'd just be tempting fate and setting yourself up for future bugs.
I personally don't like peppering my code with release statements, because I don't trust myself to remember them 100% of the time. Instead, I prefer to autorelease my variables as soon as I allocate them like this:
ffv = [[[FullFunctionView alloc] initWithFrame:self.view.bounds] autorelease];
This guarantees that ffv will exist at least until the end of the method. It will get released shortly thereafter, typically before the next iteration of the run loop. (In theory this could consume excessive memory if you're allocating a large number of temporary objects in a tight loop, but in practice I've never encountered this case. If I ever do, it will be easy to optimize.)
The object is not deallocated until the retain count goes to 0. As long as it's not deallocated, you can keep using it without trouble. By retaining it you ensure that it won't be deallocated under your feet; however, if you retain another object that you know retains the first object, you can get away with this form of "indirect retaining". Don't complain when you move things around later and things start breaking, though.
if (ffv == nil)
NSLog(#"ffv is nil");
// "ffv is nil" is not printed
That's correct, releasing an object does not set the pointer to nil even if it is dealloced at that time. Good practice is to always set your pointer to nil after you release it.
You are correct to say that after you released it, it wasn't dealloced because the view still had a retain on it. But that's not how you should be thinking about it. If you want to use that object and you want it to be alive, retain it. Doesn't matter who else is retaining it. Your object has nothing to do with those other objects. You want it, retain it. You're done with it, release it and set your pointers to nil. If you don't set it to nil and everyone else also released it, you will have a dangling pointer to an object that was dealloced, and that will cause you a crash and much grievance.
so this:
[ffv release]; // you can release it now since the view has ownership of ffv
ffv = nil; // you released it, so that means you don't want it anymore, so set the pointer to nil
if you still want to use it, don't release it until you're done with it.
Well, I'm not sure 'losing' ownership is the right term. In Objective-C you have to carefully marshal your ownership of the object. If you create or retain an object, you are responsible for releasing it (either directly or via an autorelease pool). When you call release however, you don't lose a reference to the object, if something else has retained it, it will still be in memory, and your pointer will still potentially point to it.
You have a pointer ffv which is just a pointer to some memory, and you have the object which is created in the first line that ffv points to.
By calling release, you are stating that you no longer require the ponter ffv to point to a valid object, that in this context you would be happy for the object to be deallocated. The pointer still points to that bit of memory, and it is still there because its retain count was increased by assigning it to the view.
The line [ffv testMethod] is in danger of not working, as it follows the release and may not point to a valid object. It only works because something else is keeping it alive. ffv still has the same address value that it had when it was first assigned.
So in order:
Your deduction is correct.
Not really.
You shouldn't use ffv after the release call. You have no guarantee that the object is going to be there for you.
These are pointers we are using here, not references like you find in Java or C#. You have to marshal your ownership of the object, you create it, have some pointers to it and by careful management of retain and release calls you keep it in memory for as long as you need it.
i am using NSArray named as allAvailableServices .
first i initialize it and after wrote [allAvailableServices release];
then i checked the condition if(allAvailableServices).
it came as true!may i know the reason.
then i wrote
allAvailableServices = nil; and [allAvailableServices release];
after the above code if(allAvailableServices) came false.is there any problem that i wrote [allAvailableServices release]; after allAvailableServices = nil;
whenever allAvailableServices = nil i need to allocate new array.so if i didn't call [allAvailableServices release]; i think there is a memory problem.am i correct?
can any one provide me a best way to deal with it?
Release just reclaims the memory, but the pointer value isn't changed; it still contains a value, but it points to garbage.
This is why many people nil their pointer after releasing it:
[allAvailableServices release], allAvailableServices = nil;
In Objective-C, it is safe to send a message to nil, but sending a message to an object that has been released may cause the app to crash (usually with EXC_BAD_ACCESS)
Note, that I've released the object before setting it to nil. If you do it the other way around, you've lost the pointer to which you send the release message, and so you've leaked the object.
If you use accessors then you can directly set the values to nil, it calls the release and sets them to nil. A release decrements the reference count by 1 and if it reaches 0 the object is freed.
The memory still has some value if you do not set to nil and hence the problem in your if condition.
Cleaner way is to use accessors and call self.yourvariable = nil; .
I have several objects in my app that can become nil at some point and I have methods that in theory are used to put these objects to nil.
But, if I try to put to nil an object that does not exist, the app will crash.
for example...
[object1 release];
object1 = nil;
//... and after that
[object1 removeFromSuperview]; // this will crash
Then I thought, why not testing to see if the object exists before removing...
if (object1 != nil)
[object1 removeFromSuperview];
// this will crash too, because object1 cannot be tested for nil because it does not exist
How can I check if the object exists before testing if it is nil?
something as
if (object1 exists( {
if(object1 != nil))
[object1 removeFromSuperview)
}
is this possible?
I ADDED THIS TO CLARIFY...
what I mean is this: imagine I have object1 declared on the header and initialized on the code. So, it exists and points to a valid reference. At some point, the object may be released, so the reference still points to an object but the object was freed. Even if I put the object to nil after releasing, I cannot do anything with it.
The problem is this: I have some methods that are asynchronous. One of them scans for some objects and remove them if they are found. I must check if the object exists and the reference points to a valid object before releasing it again. This is the point: how do I test if the object exists and its reference points to a valid existent object before releasing it again, to void releasing again an object that is already released and crash the app.
In short, your question of determining if the pointer is still valid is going down the wrong path.
After you release an object, you must immediately set its value to null so that you no longer control it. If two methods are running asynchronously accessing the same object, they must be synchronized at that point so that releasing and setting to null happens at the same time in one thread before the other has a chance to interrupt.
Are you just speculating, or have you actually tried this? Because in other programming languages, invoking a method on nil will cause a crash. In Objective-C this is not true. In Objective-C, you CAN send messages to nil without causing a crash. These messages simply have no effect.
In fact, you can be really obscene and do the following without a crash:
[(id)nil setTitle:#"Testing"]; // This will not cause a crash
If your app is crashing, it is not because you are messaging nil. It is possible, however, that you are failing to set a pointer to nil and you are messaging an object in memory that you do not own. Given the details you provided in your update, namely that you have an asynchronous thread accessing these objects, I think it is very likely you are sending messages to pointers whose object has already been released, but have not yet been set to nil.
I have compared an object to nil before without any problems.
I've got a very simple line of code in Objective-C:
if ((selectedEntity != nil) && [selectedEntity isKindOfClass:[MobileEntity class]])
Occasionally and for no reason I can tell, the game crashes on this line of code with an EXC-BAD-ACCESS. It usually seems to be about the time when something gets removed from the playing field, so I'm guessing that what was the selectedEntity gets dealloc'd, then this results. Aside from being impossible to select exiting Entities (but who knows, maybe this isn't actually true in my code...), the fact that I am specifically checking to see if there is a selectedEntity before I access it means that I shouldn't be having any problems here. Objective-C is supposed to support Boolean short-citcuiting, but it appears to not be EDIT: looks like short-circuiting has nothing to do with the problem.
Also, I put a #try/#catch around this code block because I knew it was exploding every once in a while, but that appears to be ignored (I'm guessing EXC-BAD-ACCESS can't be caught).
So basically I'm wondering if anybody either knows a way I can catch this and throw it out (because I don't care about this error as long as it doesn't make the game crash) or can explain why it might be happening. I know Objective-C does weird things with the "nil" value, so I'm guessing it's pointing to some weird space that is neither an object pointer or nil.
EDIT: Just to clarify, I know the below code is wrong, it's what I was guessing was happening in my program. I was asking if that would cause a problem - which it indeed does. :-)
EDIT: Looks like there is a fringe case that allows you to select an Entity before it gets erased. So, it appears the progression of the code goes like this:
selectedEntity = obj;
NSAutoreleasePool *pool = ...;
[obj release];
if (selectedEntity != nil && etc...) {}
[pool release];
So I'm guessing that because the Autorelease pool has not yet been released, the object is not nil but its retain count is at 0 so it's not allowed to be accessed anyway... or something along those lines?
Also, my game is single-threaded, so this isn't a threading issue.
EDIT: I fixed the problem, in two ways. First, I didn't allow selection of the entity in that fringe case. Second, instead of just calling [entities removeObjectAtIndex:i] (the code to remove any entities that will be deleted), I changed it to:
//Deselect it if it has been selected.
if (entity == selectedEntity)
{
selectedEntity = nil;
}
[entities removeObjectAtIndex:i];
Just make sure you are assigning nil to the variable at the same time you release it, as jib suggested.
This has nothing to do with short circuiting. Objective-C eats messages to nil, so the check for selectedEntity != nil isn't necessary (since messages-to-nil will return NO for BOOL return types).
EXC_BAD_ACCESS is not a catchable exception. It is a catastrophic failure generally caused by trying to follow in invalid pointer.
More likely than not, whatever object selectedEntity points to has been released before the code is executed. Thus, it is neither nil nor a valid object.
Turn on NSZombies and try again.
If your app is threaded, are you synchronizing selectedEntity across threads properly (keeping in mind that, in general, diddling the UI from secondary threads is not supported)?
Your post was edited to indicate that the fix is:
//Deselect it if it has been selected.
if (entity == selectedEntity)
{
selectedEntity = nil;
}
[entities removeObjectAtIndex:i];
This fixes the issue because the NSMutableArray will -release objects upon removal. If the retain count falls to zero, the object is deallocated and selectedEntity would then point to a deallocated object.
if an object (selectedEntity) has been released and dealloc'd it is not == nil. It is a pointer to an arbitrary piece of memory, and deferencing it ( if(selectedEntity!=nil ) is a Programming Error (EXC_BAD_ACCESS).
Hence the common obj-c paradigm:-
[selectedEntity release];
selectedEntity = nil;
i just read this http://developer.apple.com/mac/library/qa/qa2004/qa1367.html which indicated that the error you are getting is a result of over-releasing the object. this means that altough selectedEntity is nill, you released it to many times and it just not yours to use anymore..
Put a breakpoint on OBJC_EXCEPTION_THROW and see where it is really being thrown. That line should never throw a EXC_BAD_ACCESS on its own.
Are you perhaps doing something within the IF block that could cause the exception?
selectedEntity = obj;
NSAutoreleasePool *pool = ...;
[obj release];
if (selectedEntity != nil && etc...) {}
[pool release];
You have a dangling pointer or zombie here. The selectedEntity is pointing at obj which gets releases right before you reference selectedEntity. This makes selectedEntity non-nil but an invalid object so any dereference of it will crash.
You wanted to autorelease the obj rather than release it.