How to enforce using `-retainCount` method and `-dealloc` selector under ARC? - objective-c

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.

Related

Possible deallocation of UIKit object on the non-main queue

I've faced several times in public APIs the following code style:
#interface UITextField (SomeFunctionalitySupport)
- (void) someMethod {
#weakify(self)
dispatch_async(someConcurrentQueue, ^{
#strongify(self)
self.text = #"Some text"
})
}
#end
Given that UITextField is UIKit object it should be deallocated on the main queue. As I understand, weakify/strongify macros are just a neat way to not declare __weak and __strong local variables manually and perform conditional check on the strong one.
But __strong increments reference counting, right? So, until the block completes we'll have one more strong reference. And block runs on the non-main queue. What if during execution of the block the strong reference became the last one? Logically, self'd be deallocated upon completion of the block. But this wouldn't happen on the main queue which could cause significant problems.
Was API creators wrong or I'm missing something?
self will not be deallocated while the block is running, because it holds a strong reference on it.
It will be deallocated after the block execution finished, because in your case it is the last strong reference. Who should care about this, since no one else has a strong reference on it?

Why the retain cycle warning not thrown?

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

Objective C memory management example

I'm trying to learn objective c and I'm still a bit confused with memory management.
Yes I know, I should use ARC, but my project uses TouchXML that does not support it.
Also there is a lot of documentation and threads about memory management that I have read but I still have some doubt that I hope you guys will help me to clarify.
I've learnt that who allocs an object is then responsible to free it. I've also learnt that "retain" increments the reference counter whereas "release" decrements it. When an object's reference counter reaches 0, it is automatically de-alloced.
I've finally learnt that "autorelease" releases the object automatically at the end of current event cycle. That's fine.
Now please consider the following case:
I alloc an array that I need to use for the full lifecycle of my object. I'm responsible to release it when my object is deleted:
#implementation MyClass
-(id) init {
myArray = [[NSMutableArray alloc] init]; // this is a #property
}
- (void) dealloc {
[myArray release];
[super dealloc];
}
#end
In this way, in dealloc method, myArray release also causes myArray o be deallocated.
If I then instance a new object from myClass and retain myArray like this...
// MyOtherClass
MyClass *o = [[[MyClass alloc] init] autorelease];
NSMutableArray *retainedArray = [[o.myArray] retain];
...at the end of current event cycle, "o" will be automatically deallocated, whereas retainedArray (actually pointing to o.myArray) will not be deallocated until I'll call [retainedArray release].
Is this correct up to here?
If so, I also guess the same applies if I call something like:
NSArray *contents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:#"somePath" error:nil];
I don't need (actually I can't otherwise it will give a runtime error) call either release or autorelease for "contents" unless I retain it somewhere in my code. Correct?
If so, summing everything up, in the end I only have to call release if I call either alloc or retain. The balance of reference counts in my class should always be 0, where alloc / retains gives +1 and release gives -1. Correct?
It is almost 100% correct what you said, but there are a few more cases where you get
a (+1) retained object that you have to release.
The basic rules are (see "Basic Memory Management Rules"):
You must eventually release or autorelease objects that you own.
You own objects that you created using a method whose name begins with “alloc”, “new”, “copy”, or “mutableCopy”
You own an object if you take the ownership using retain.
The "Static Analyzer" (Product -> Analyze in the Xcode menu) is quite good at finding
violations to this rule, so I can only recommend to use that.
You can use Touch XML with ARC just fine. I use TouchXML in my project also. Just go to project build phases, double click on each Touch XML.m file, and enter -fno-objc-arc. This will disable ARC for that file.
If you know a lot about memory management or really want to learn more about memory management, then use MRC, but if you want to avoid the hassle, use ARC.
no direct answer to your question, but you also can use non-ARC Code in an ARC Project:
http://www.codeography.com/2011/10/10/making-arc-and-non-arc-play-nice.html
Best regards
Bernhard

Why does iPhone sample code use so many intermediate variables?

I'm currently working through Apress's "Beginning iPhone 3 Development". A standard they use in their example applications is like the following code:
- (void)viewDidLoad {
BlueViewController *blueController = [[BlueViewController alloc]
initWithNibName:#"BlueView" bundle:nil];
self.blueViewController = blueController;
[self.view insertSubview:blueController.view atIndex:0];
[blueController release];
}
8.14.11 UPDATE (additional information)
blueViewController is declared as follows:
#property (retain, nonatomic) BlueViewController *blueViewController;
Whenever they perform an alloc they put it in some temp variable (here blueController) then they assign it, then they release it. This temp variable seems superfluous to me.
I simplified the code as follows:
- (void)viewDidLoad {
self.blueViewController = [[BlueViewController alloc]
initWithNibName:#"BlueView" bundle:nil];
[self.view insertSubview:blueViewController.view atIndex:0];
}
- (void)dealloc {
[blueViewController release];
[super dealloc];
}
My modified code ran just the same in the iPhone simulator.
Now, I know the rule that if you alloc something you need to release it. And I'm covering that in my dealloc method. But is there some advantage to having a release directly in the ViewDidLoad (the function where the alloc was called)? Or is it equally ok to have a release in your dealloc method like this?
Thanks for any help,
-j
Assuming blueViewController is a retain property, the temporary variable is not superfluous. Your simplification is creating a memory leak. This statement from the second snippet leaks:
self.blueViewController = [[BlueViewController alloc]
initWithNibName:#"BlueView" bundle:nil];
In terms of ownership, you own the object returned by alloc-init and then the property accessor claims ownership of the object again, resulting in the object being over-retained.
Using a temporary variable solves this problem. Another option is to use autorelease:
self.blueViewController = [[[BlueViewController alloc]
initWithNibName:#"BlueView" bundle:nil] autorelease];
Note that after this statement you effectively own the object and you must release it in dealloc.
You did not mention how the property blueViewController is declared. Anyway, whatever the semantics of the setter are (retain, copy, assign), that statement is wrong. I already explained the most likely scenario: retain. Let's have a look at the other two possibilites (without considering if they make sense at all):
If blueViewController happened to be a copy property, the statement would leak too. The property accessor copies the original object and now the property holds a pointer to the copy and you lost track of the original object, immediately leaking it.
The least likely scenario is that blueViewController is an assign property because this is most likely wrong and you really want retain. But, anyway, the assign properties are for objects you do not own, e.g. delegates, and you are not supposed to release them. You are assigning an object you own to it, so either you leak it or you incorrectly release the assign property.
#property (retain) MyCLass *obj;
MyClass *tmpObj = [[MyClass alloc] init];
self.obj = tmpObj;
[tmpObj release];
In the first line you get ownership once via alloc. Then in 2nd line you get ownership again as the property is retained. In 3rd line you release the ownership that you got via alloc. Now you have a single ownership via retain property which you may release in future, may be in dealloc.
Now consider what happens if you remove the tmpObj.
self.obj = [[MyClass alloc] init];
In this line you get ownership twice, once via alloc and once via property. Now [obj release] once is not enough. You need to release it twice to avoid the leak, and naturally releasing twice is extremely bad and possible source to further memory leak. If you make another call to self.obj = anotherObj then you are leaking the old one. To avoid this you need this temporary pointer.
There's two reasons I can think of off the top of my head; the first, more specific to the example, is that you will often see similar methods where blueController is allocated and initialized, then tested for validity before actually being assigned to self's ivar. Following this pattern in every such method will make it easier for you to do tests in between creating the object and assigning it, should you realize that needs to happen. To my knowledge, if such an intermediary indeed remains superfluous, the compiler should optimize it out.
The second, more general purpose for this pattern within Cocoa is simply that Obj-C and Cocoa encourage extremely long, verbose names for methods and variables, so a single method call could end up spanning multiple lines; using method calls as direct arguments for other methods can quickly become unreadable, so conventions encourage setting up each argument for a method ahead of time, placing them in intermediary variables, then using the variables as arguments to enhance readability, and make it easier to change a single argument without having to dig around nested method calls.

Double releasing when it shouldn't be happening

I am really puzzled by this. I believe I am managing memory the correct way but executing the code suggests that I am double releasing the object. Here is the code and then I'll explain what is happening.
#protocol SomeDelegate <NSObject>
#required
- (id)initWithCols:(NSUInteger)Cols Rows:(NSUInteger)Rows;
#end
#interface SomeObject : NSObject <SomeDelegate> {
}
- (id)initWithCols:(NSUInteger)Cols Rows:(NSUInteger)Rows;
#end
#interface Layout : UIView {
id<SomeDelegate> someDelegate;
}
#property(retain) id<SomeDelegate> someDelegate;
- (id)initWithFrame:(CGRect)aRect Cols:(NSUInteger)Cols Rows:(NSUInteger)Rows;
#end
#implementation Layout
#synthesize someDelegate;
- (id)initWithFrame:(CGRect)aRect Cols:(NSUInteger)Cols Rows:(NSUInteger)Rows {
if(self = [super initWithFrame:aRect]) {
cols = Cols;
rows = Rows;
id<SomeDelegate> delegate = [[SomeObject alloc] initWithCols:cols Rows:rows];
[self setSomeDelegate:delegate];
//[delegate release];
}
return self;
}
-(void)dealloc {
[someDelegate release];
[super dealloc];
}
#end
Now when I uncomment out the "//[delegate release];" line in the constructor of the Layout class, then I get a "EXC_BAD_ACCESS" error and the application crashes when it attempts to dealloc. I have traced the crash to the release of the someDelegate object in the dealloc method of Layout class. If I leave it commented then the application works fine.
Can someone please explain why this is happening as it appears to be going against everything I have read about memory management in Objective-C.
Just a note that the code example actually works, however my code doesn't which follows the example. Could there be something inside of my actual SomeObject that is causing an autorelease?
Thanks in advance.
First, go back and reread the memory management rules just to make sure you are not missing anything obvious in your use of delegate elsewhere.
Next, turn on NSZombieEnabled (in your executable settings, Arguments panel, add an environment variable NSZombieEnabled set to YES).
Then add a dealloc method to your delagate if it does not have one already (make sure you call [super dealloc]!) and put a break point on there - that will tell you when your delagate is deallocated which will tell you when it is being released.
Alternatively, add trivial release/autorelease methods to your delegate class which do nothing but call through, and then breakpoint them and that will tell you exactly when it is being released.
Three final comments: in the standard naming convention for Objective C/Cocoa, you should have lowercase parameter fields, ie it should be:
- (id)initWithFrame:(CGRect)aRect cols:(NSUInteger)Cols rows:(NSUInteger)Rows;
When your ivar and property are named identically, it is very easy to accidently use the wrong one, so I recommend using a different ivar name and property name to avoid confusion, either use an _ prefix like Apple, or some other prefix to avoid confusion with Apple as well:
id<SomeDelegate> _someDelegate;
#synthesize someDelegate = _someDelegate;
And Apple recomends against using setters/getters in init/dealloc, so your init code should be:
_someDelegate = [[SomeObject alloc] initWithCols:cols Rows:rows];
As alluded to in the comments, the problem does not appear to be in the code posted.
I could ask for more information, but I'm firmly in the teach a man to fish camp....
A crash in -release will often be misleading in that various optimizations -- tail call optimizations, generally -- will make it look like the crash happened a frame or two above the actual call that crashed. When the crash happens, there isn't enough info on the stack to really identify the culprit.
Whenever you suspect you have any kind of a crash in -release or -dealloc, immediately turn on Zombies. This can be done through Instruments or via an environment variable or by calling a function in the Foundation very early in your program's execution.
Search for "Zombies" or "NSZombie" in the documentation included with the development environment (that'd be more of the "teach a man to fish" thing).
The problem was a MutableArray deep in a subclass that was created through a factory (autoreleased) but I was also releasing too. Unfortunately the crash wouldn't indicate which inherited dealloc was causing the crash and just stop on the first overridden dealloc.
The Zombie thing helped a little in that it told me an array was the culprit but not much else. I think there is more to NSZombie and requires more experience to take full advantage of it.