I'm converting a library to ARC atm. where I have an NSViewController descendant that loads a xib the usual way:
- (instancetype)initWithModule: ...
{
self = [super initWithNibName: #"mynib" bundle: [NSBundle bundleForClass: [self class]]];
if (self != nil) {
[self view];
}
return self;
}
When I executed this without ARC the retain count of that controller is 2 after the call to view (which loads the nib and connects the outlets, as you know). However with ARC enabled this increases the retain count to 3, which later causes this controller to leak, because the count never goes back to 0.
I changed all outlets to use weak references (except for NSTextView instances, but they never appear as top level objects). But this doesn't seem to help.
Update: It seems to affect every view controller I have, at least all those I checked. So this seems to be a fundamental problem, not related to the xib content.
How can I find out what causes the additional retain on load?
The absolute retain count is meaningless. You need to find all the spots where retain is called (or called by implication, in the case of ARC).
To do that, use the Allocations instrument and turn on reference count tracking. That'll give you access to the backtrace of every single retain and you can find the extra one.
More likely than not it'll be a strong reference to self in a block held by something in self. Or it'll be a cycle of strong references; self -> other -> self kinda thing.
Related
Under ARC, compiler will forbid using any method or selector of -retainCount, -retain, -dealloc, -release, and -autorelease.
But sometimes I want to know the retain counts at runtime, or using method swizzling to swap the -dealloc method of NSObject to do something.
Is it possible to suppress (or bypass) the compiler complaining just a couple of lines of code? I don't want to modify ARC environment for whole project or whole file. I think preprocessor can do it, but how?
Additions:
Thanks guys to give me a lesson about the use of -retainCount. But I wonder whether it is possible to enforce invoking/using those forbidden methods/selectors.
I know Instruments is a powerful tool to do this job. But I am still curious about those question.
Why do I want to use -retainCount:
When using block, if you don't specify a __weak identifier on the outside variables, the block will automatically retain those outside objects in the block after the block is copied into the heap. So you need to use a weak self to avoid retain cycle, for example:
__weak typeof(self) weakSelf = self;
self.completionBlock = ^{
[weakSelf doSomething];
};
However, it will still cause retain cycle when you only use instance variables in the copied block (YES, although you didn't use any keyword self in the block).
For example, under Non-ARC:
// Current self's retain count is 1
NSLog(#"self retainCount: %d", [self retainCount]);
// Create a completion block
CompletionBlock completionBlock = ^{
// Using instance vaiable in the block will strongly retain the `self` object after copying this block into heap.
[_delegate doSomething];
};
// Current self's retain count is still 1
NSLog(#"self retainCount: %d", [self retainCount]);
// This will cuase retain cycle after copying the block.
self.completionBlock = completionBlock;
// Current self's retain count is 2 now.
NSLog(#"self retainCount: %d", [self retainCount]);
Without using -retainCount before/after the copied block code, I don't think this retain cycle caused by using instance variables in the completion block will be discovered easily.
Why do I want to use -dealloc:
I want to know whether I can use method swizzling to monitor which object will be deallocated by logging messages on the Xcode console when the -dealloc is invoked. I want to replace the original implementation of -dealloc of NSObject.
That's not recommened at all, I dont know your intentions but they dont sound very safe.
The use of retainCount is not recommended.
From AppleDocs:
This method is of no value in debugging memory management issues.
Because any number of framework objects may have retained an object in
order to hold references to it, while at the same time autorelease
pools may be holding any number of deferred releases on an object, it
is very unlikely that you can get useful information from this method
And, if there's any doubt, check this link:
http://whentouseretaincount.com/
Whatever you are trying to do, please dont.
For future references, I'm going to add some linsk to help you understand the process of how memory works in iOS. Even if you use ARC, this is a must know (remember that ARC is NOT a garbage collector)
Beginning ARC in iOS 5 Tutorial Part 1
Understand memory management under ARC
Memory Management Tutorial for iOS
Advance Memory Managment
And, of course, once you understand how memory works is time to learn how to profile it with instruments:
Instruments User Guide
Agreed 100% with the other commenters about the fact that you do not want to use -retainCount. To your other question, however, about -dealloc:
You also do not want to swizzle -dealloc. If you think you want to swizzle it, you don't understand how it works. There are a lot of optimizations going on there; you can't just mess with it. But, as #bbum hints at, you can easily get notifications when objects are deallocated, and this can be very useful.
You attach an associated object to the thing you want to watch. When the thing you want to watch goes away, so does the associated object, and you can override its dealloc to perform whatever action you want. Obviously you need to be a little careful, because you're in the middle of a dealloc, but you can generally do most anything you'd need to here. Most importantly for many cases, you can put a breakpoint here or add a logging statement, so you can see where the object was released. Here's a simple example.
With ARC
const char kWatcherKey;
#interface Watcher : NSObject
#end
#import <objc/runtime.h>
#implementation Watcher
- (void)dealloc {
NSLog(#"HEY! The thing I was watching is going away!");
}
#end
NSObject *something = [NSObject new];
objc_setAssociatedObject(something, &kWatcherKey, [Watcher new],
OBJC_ASSOCIATION_RETAIN);
Without ARC
const char kWatcherKey;
#interface Watcher : NSObject
- (void)lastRetainDone;
#end
#import <objc/runtime.h>
// turn off ARC!
#implementation Watcher
{
BOOL noMoreRetainsAllowed;
}
- (void)lastRetainDone {
noMoreRetainsAllowed = YES;
}
- (id) retain {
if (noMoreRetainsAllowed) abort();
return [super retain];
}
- (void)dealloc {
NSLog(#"HEY! The thing I was watching is going away!");
[super dealloc];
}
#end
...
NSObject *something = [NSObject new];
Watcher *watcher = [Watcher new];
objc_setAssociatedObject(something, &kWatcherKey, watcher,
OBJC_ASSOCIATION_RETAIN);
[watcher lastRetainDone];
[watcher release];
Now, when something goes away, -[Watcher dealloc] will fire and log for you. Very easy. Completely supported and documented.
EDIT:
Without using -retainCount before/after the copied block code, I don't think this retain cycle caused by using instance variables in the completion block will be discovered easily.
You are somewhat correct here, but there are two lessons to be learned, and neither is to use retainCount (which won't actually help you in this case anyway because retainCount can very often be something you don't expect).
The first lesson is: Do not allow any warnings in ObjC code. The situation you're describing will generally create a compiler warning in recent versions of clang. So it's actually quite easy to discover in many cases. The way you've separated it into multiple assignments, the compiler may miss it, but the lesson there is to change your coding style to help the compiler help you.
The second lesson is: don't access ivars directly outside of init and dealloc. This is one of the many little surprises that can cause. Use accessors.
I use Xcode 5 and have some code
#interface Controller {
__weak IBOutlet UIView *someView;
}
#implementation Controller {
- (void)doSomething
{
[UIView animateWithDuration:0.5 animations:^{
someView.hidden = YES;
}];
}
- (void)doSomething1
{
[UIView animateWithDuration:0.5 animations:^{
[self doSomething];
}];
}
Why the retain cycle warning not thrown there? Should I use weak references on self every time I use self in blocks?
Also I enabled Implicit retain of self within blocks warning and it gave me 100 warnings with advice to write self->ivar.prop (not ivar.prop) in blocks. Should I do so after that warning is disabled by default?
Why the retain cycle warning not thrown there?
The block retains you, but you don't retain the block. It'll be destroyed after animation is complete. So, no cycles.
Should I use weak references on self every time I use self in blocks?
If your block doesn't get destroyed automatically (e.g. a recurring timer), then you should.
Should I do so after that warning is disabled by default?
Depends upon the context. Again, if your blocks live for a long time, you might want to declare non-retained weakSelf.
But basically you're fine if your blocks don't get saved somewhere.
See also How do I avoid capturing self in blocks when implementing an API?
This is not a retain cycle. This is two methods calling each other in a loop. A retain cycle happens when two object instances have strong (retained) references to each other that are never broken, and those two objects remain in memory unnecessarily.
Example with code: Retain cycle in ARC
Should I use weak references on self every time I use self in blocks?
Absolutely not. Blocks retain captured object pointers for a reason -- to keep the objects alive until so that they'll still be there when the block is run. If there is no retain cycle and there is no other thing keeping a reference to the object pointed to by self, it could be deallocated before the block is run (asynchronously, for example).
I'm getting an strange case of excessive retain counts for a view controller that I'm loading when a button is pushed.
This is the code:
-(IBAction)new
{
if (!viewSpace)
viewSpace = [[ViewSpace alloc] initWithNibName:#"ViewSpace" bundle:nil];
viewSpace.delegate = self;
viewSpace.view.frame = CGRectMake(0, 0, viewSpace.view.frame.size.width, viewSpace.view.frame.size.height);
[self presentModalViewController:viewSpace animated:YES];
NSLog(#"Count Retain: %d",[viewSpace retainCount]);
}
-(void)viewSpaceWasDissmissed:(id)sender
{
[self dismissModalViewControllerAnimated:YES];
[viewSpace release];
NSLog(#"Count Retain: %d",[viewSpace retainCount]);
}
When the IBAction New is executed first time, the retain count is 5 when just is created. (It must be 1).
When the ViewSpace object must be unload calls viewSpaceWasDismissed function in order to remove the modal view and release the previous object.
The problem is that never the retain count reach 0 and the dealloc method of ViewSpace never is called causing memory leaks.
My question is how is possible that a recently created ViewController have 5 retains? I made sure that is never created before.
Thanks.
Cocoa is probably retaining the view controller 4 times internally for reasons of its own. This isn't a problem.
More generally, the -retainCount method is useless for reasons like this, and you should never call it. It will not help you, and it will confuse you.
To debug your leak, I suggest using the leaks Instrument, inspecting the object, and analyzing where each retain and release is coming from to determine whether any are incorrect.
Check the documentation for -retainCount. I believe it says that you should not be calling it yourself - you just need to take care of any retains that you cause, and don't worry about the 'actual' retain count.
You're doing two things wrong here:
The Current view controller retains the modally presented view controller and releaseds it when it is dismissed. So you should release viewSpace after it is presented, and you don't need the release message in the dismissModalViewController method. As an aside ViewSpace is a poor name for a view controller. I had to read to the line where you are presenting it as a view controller before I knew it was a view controller. I think ViewSpaceController is a more descriptive name.
You are using retainCount which is always a bad idea. All that matters is that in your new method you created an owned object (with the alloc) and you balanced that ownership with a release (or at least you will do when you put in the correction I suggested in point 1) That's it. You took ownership of an object and you released it. The retainCount method tells you absolutely nothing that can be of any use to you. Don't do it. Just balance ownerships with release, and that is all that matters.
I'm not 100% sure of every count but here are some:
Instantiation - 1
NIB - 1+
Strong Properties (1+)
Additionally any properties that list it as a strong property (in ARC).
I noticed that when you launch a nib and you use components of the controller in the nib design, it will increase reference counts (in a strong manner) on the controller instance.
does this retain my subview twice?
- (void)viewDidLoad
{
CGRect frame=CGRectMake(0, 0, 320, 460);
mapButtons*newButtons=[[mapButtons alloc] initWithFrame:frame];
self.mapButtons=newButtons;
[newButtons release];
[self.view addSubview:self.mapButtons];
[self.mapButtons addButtons:#"yo"];
once it is added to the view hierarchy with addSubview, does it get an additional retain count beyond that retained by the ivar, self.mapButtons?
i want to be able to manipulate this subview easily, hence the ivar; is this a good way, or is there a better way?
EDIT
You mention memory-wise so I think it may need some clearing up. Each object has a retain count which is incremented with retain and decremented with release. When the retain count reaches 0 a dealloc message is sent. So when you put an additional retain on an object you are not using anymore memory you are simply incrementing the counter and not doing any kind of duplication.
There are a couple of ways you can grab a reference to a view but the way you are doing it is a good way. An alternative would be to tag the view and retrieve it from self.view using
UIView *view = [self.view viewWithTag:tagId];
I prefer the ivar way e.g. how you have done it (this will change when ARC comes in) but I tend not to worry about the the actual retain count of an object. I concentrate on balancing my retain/releases.
Therefore I use the rule that if it's a local variable I try as far as possible to match my retain/release's within the scope it is defined in. The exception being ivar which are released in dealloc
In Xcode I have a UINavigationController on wichh I push a UIViewController.
In that UIViewController I init an UIScrollView.
All good.
However, after switching to another view I log the retain count of the controller and to my surprise it's one instead of zero.
Here's the code:
scroller = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
[self.view addSubview:scroller];
and:
- (void)dealloc {
[super dealloc];
NSLog(#"retaincount, %d", [scroller retainCount]); //displays 2
[scroller release];
NSLog(#"retaincount, %d", [scroller retainCount]); // displays 1
}
I only init it ones and add it to the UIViewControllers view.
Kind regards,
Tom
Do not use retainCount! From the apple documentation:
Important: This method is typically of no value in debugging memory management issues. Because any number of framework objects may have retained an object in order to hold references to it, while at the same time autorelease pools may be holding any number of deferred releases on an object, it is very unlikely that you can get useful information from this method.
To understand the fundamental rules of memory management that you must abide by, read “Memory Management Rules”. To diagnose memory management problems, use a suitable tool:
The LLVM/Clang Static analyzer can typically find memory management problems even before you run your program.
The Object Alloc instrument in the Instruments application (see Instruments User Guide) can track object allocation and destruction.
Shark (see Shark User Guide) also profiles memory allocations (amongst numerous other aspects of your program).
Having said that: You have to call [super dealloc] in the very last line of your dealloc method. Besides that everything in your code should be ok. Don't try to manually lower the retainCount. Use proper memory management. Again, do not look at the retainCount.
And you are not allowed to use an object after you have released it. If the scroller would be deallocated because of your release the second NSLog would result in a BAD_ACCESS exception.
self.view is retaining it. When your UIViewController deallocs, it'll release self.view, which will release all its retained subviews. In fact, you should probably release it soon after adding it to self.view.
That said, I strongly second #fluchtpunkt's answer. Examining the retainCount property looking for debugging info will only lead to confusion and increasingly coherent and ranty posts on Stack Overflow.
This is why:
scroller = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
//retain count = 1
[self.view addSubview:scroller];
//retain count + 1 + 1 = 3
//later the AutoreleasePool decrements the count by 1, resulting in a retain count of 2.
Methods beginning with init…
return non-autoreleased instances
with a retain count of 1.
Accessing your
subview via self.view: returnes an autoreleased retained pointer to subview.
Adding a
subview to a view via addSubview: retains the
subview.
Removing a subview from its superview via removeFromSuperview: releases it.
You should not rely on an object's retain count though.
Instead you must take care to level out retain and release calls on an object.
Make sure to read Apple's Memory Management Programming Guide in this matter! (the rules section in particular!)
Previous answers are correct: don't use -retainCount, and make sure that you call [super dealloc] last in your own -dealloc implementation.
I want to add that you'll probably never see a case (not that you should be looking) where -retainCount returns 0. If an object's retainCount drops to zero, the object is deallocated. In fact, it seems that Cocoa never even bothers to set the retainCount to zero... -release appears to deallocate the object if the previous retainCount was 1. Just one more reason to consider -retainCount to be a private implementation detail.