Why does setting self.object to nil cause a crash - objective-c

In the following scenario I get a crash
if (self.videoEngine != nil)
{
[self.videoEngine.player.view removeFromSuperview];
[videoEngine release];
self.videoEngine = nil;
}
The videoEngine object is (nonatomic, retain), and it is synthesized using videoEngine = _videoEngine. If I remove the self.videoEngine = nil line the code works properly. Is this correct behaviour, and why does the nil line cause a crash? Would the self.videoEngine = nil still cause an issue within the viewDidUnload function?

When you call "self.videoEngine = nil;" it calls its setter method and in the setter method by default it releases the object and then it sets it to the value provided by you, so in this case you are releasing your object once and then setter method is trying to release it again that is causing crash, now if you remove the "[videoEngine release];" that would be fine and there will be no memory leak.
Hope it is clear now.

You should only release _videoEngine because that is the the synthesized name. videEngine is only the name of the setter and getter, but the value is stored in the syntheseized name. So your code should be:
if (self.videoEngine != nil)
{
[self.videoEngine.player.view removeFromSuperview];
[_videoEngine release];
self.videoEngine = nil; // Unnecessary
}
But you donĀ“t need to call self.videEngine = nil after releasing the _videEngine because the setter will always return nil.
It is not considered a proper method of releasing by calling the setter method with nil, although it works, like is done with the line: self.videoEngine = nil; // Unnecessary.
The proper way of releasing is only [_videoEngine release];

Related

Passing nil object pointer in Objective-C

I create an object which I set to nil by default before including it in a method as a parameter. I do a check later if the object is still nil or not. The object is set to change in the method but the result is still nil. I'd expect the method to be able to alter the nil object. If I have the code that's inside the method replace the method call, it results in it being NOT nil.
MyObject *objectTemp = nil;
[self methodCallWithObject:objectTemp];
if (objectTemp == nil) NSLog(#"nil");
else NSLog(#"NOT nil");
// always results in "nil"
method:
-(void) methodCallWithObject:(MyObject)objectTemp {
objectTemp = [NSValue valueWithCGPoint:CGPointMake(5.3f, 2.6f)];
}
In order to change objectTemp outside of the method, you have to pass a pointer to objectTemp, which means that methodCallWithObject actually needs to take a pointer to a pointer:
-(void) methodCallWithObject:(MyObject **)objectTemp {
*objectTemp = [NSValue valueWithCGPoint:CGPointMake(5.3f, 2.6f)];
}
(However, it would probably make more sense to have methodCallWithObject just return a new object.)

Capture self in block (retain cycles), not always?

The following code is from the LazyTableImages sample code provided by Apple (source here).
In their completion block they have a reference to self which should cause a retain cycle... But I don't get a warning for this in Xcode whereas in similar code of mine I would.
Is this correct?
Perhaps I'm missing a subtlety of this.
- (void)startIconDownload:(AppRecord *)appRecord forIndexPath:(NSIndexPath *)indexPath
{
IconDownloader *iconDownloader = [self.imageDownloadsInProgress objectForKey:indexPath];
if (iconDownloader == nil)
{
iconDownloader = [[IconDownloader alloc] init];
iconDownloader.appRecord = appRecord;
[iconDownloader setCompletionHandler:^{
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
// Display the newly loaded image
cell.imageView.image = appRecord.appIcon;
// Remove the IconDownloader from the in progress list.
// This will result in it being deallocated.
[self.imageDownloadsInProgress removeObjectForKey:indexPath];
}];
[self.imageDownloadsInProgress setObject:iconDownloader forKey:indexPath];
[iconDownloader startDownload];
}
}
The retain cycle that you think you are seeing is because the object holds the the downloader in a dictionary.
It's true that there is a strong reference to self in the block, but, as long as the completion handler is always run, the downloader will be removed from the dictionary. And eventually this dictionary will be empty, which means there will be no objects holding on to self, and thus no retain cycle.
self doesn't have a strong pointer to iconDownloader. It's created and scoped just to this method:
IconDownloader *iconDownloader = [self.imageDownloadsInProgress objectForKey:indexPath];
If iconDownloader was a strong property (self.iconDownloader) then Xcode would detect a strong reference cycle.
Capturing self itself is no retain cycle. It is a single reference. One reference cannot build a cycle. The usual antipattern is, that additionale a reference to the block is stored in a strong property of self. Than there are two references building a cycle.
There's no warning because the compiler isn't yet capable of detecting all possible retain cycles.
For example:
- (void)foo
{
_block = ^ { [self done]; }; // Warning: Possible retain cycle
DSGenericBlock foo = ^ { [self done] };
_block = foo; // No warning.
}
If you were to assign the block directly to an instance variable of "self", you would get the "possible retain cycle" warning. Instead, the block is assigned to another object, which is then retained by self, so the compiler does not detect the cycle (even though the cycle does exist).

Obj-C ARC: How to remove an object from an array/set and then return it as an autoreleased object?

How do I rewrite this method for ARC?
- (KTThumbView *)dequeueReusableThumbView
{
KTThumbView *thumbView = [reusableThumbViews_ anyObject];
if (thumbView != nil) {
// The only object retaining the view is the
// reusableThumbViews set, so we retain/autorelease
// it before returning it so that it's not immediately
// deallocated when removed form the set.
[[thumbView retain] autorelease];
[reusableThumbViews_ removeObject:thumbView];
}
return thumbView;
}
The automatic ARC migrator gives me this error:
[rewriter] it is not safe to remove an unused 'autorelease' message; its receiver may be destroyed immediately
Just remove the [[thumbView retain] autorelease]; line. The first line will make a strong reference guaranteeing its around as needed.

Weird !=nil check issue

I have a UITableViewController which has a UIView* called errorView that is used to overlay an error message over the table view if a method fails to load web data.
In the init method, errorView is set to nil (0x0 in debugger).
In a load method, called at the end of init AND if a 'refresh' UIButton (on the errorView) is tapped, errorView is compared to nil, and removed from superview and released if it is not nil (Still shows 0x0 in debugger).
In the dealloc method, the same check is done before releasing; but for some reason the variable is never nil even though it hasn't been assigned (0xc000 in debugger) because the data failed method was never called. The app then crashes because it tries to dealloc a null pointer that is != nil.
Example:
-(id)init {
errorView = nil;
[self Load];
}
-(void)Load {
if(errorView != nil) {
[errorView removeFromSuperView];
[errorView release];
errorView = nil;
}
//Attempt to load data from web
}
-(void)dataFailedToLoad (e.g. UIWebView didFailLoadWithError) {
errorView = [[UIView alloc] initWithFrame, etc];
[self.tableView addSubview:errorView];
}
-(void)dealloc {
if(errorView != nil)
[errorView release]; //Always crashes because errorView is never nil even though it has been assigned nil?
}
I'm pulling hair out over this. The errorView variable IS NOT USED anywhere else but in these methods as described, and everything I can read into suggests it is the proper way to do it.
As a point of interest; sending a message to a nil object is not an error; a significant number of your checks are completely useless.
Exactly what is going on here is hard to point out without more information; if dataFailedToLoad actually is the only place where errorView is assigned, your code should work. However, it's failure indicates that something else is screwing your pooch.
Incidentally, a null pointer that != nil isn't a null pointer.
Just because you called init the view doesn't have to be initialized. You should use the standard method viewDidLoad to ensure that the view isn't nil.
Are you sure you are talking to the instance you think you are talking to?
I've seen quite a few SO questions that have boiled down to confusion between what the loading of an interface file (xib) instantiates and what the developer needs to instantiate.
I.e. do something like NSLog(#"%# %p", self, self); in all the methods and make sure the address-- the instance-- is the same.

Why setting to nil after releasing?

I came across this method:
-(void) someMethod {
NSMutableArray *anArray = [[NSMutableArray alloc] init];
// Do stuff with anArray ...
[anArray release];
anArray = nil;
}
Is setting the array to nil necessary?
In this code by sending a release message to the array, it will causes the array to be deallocated.
In this case, it is a pointless waste of key strokes because the variable anArray goes out of scope immediately.
In other cases, where the variable stays in scope for a while after you release the object its pointing to, it is a good idea, because, if you accidentally dereference it, you will get a EXC_BAD_ACCESS which is easy to spot, and if you send a message to it, it will be ignored (except for returning nil / 0).
As others have mentioned, setting it to nil will help your code not crash if you reference the dealloced object. If you reference a dealloced you will get EXC_BAD_ACCESS error and your app will crash. Since a nil object returns nil if a message is sent to it, your app will not crash.
In the example you provide, it is not necessary to nil it out, since it is contained in a method. However, you do not want to nil out a variable if you expect to use it somewhere else in the code, since the value will then be nil.
No it is not necessary.
It is just for safe reason (to not send a message to a zombie)
And you can test if your ivar is nil or not to realloc:
[ivar release];
ivar=nil;
...
if (ivar==nil) {
ivar = [[NSObject alloc] init];
}
[ivar setValue:#"toto"];
It's not necessary but considered good behaviour to set dangling pointers to nil.