I tested it with the following code,and I found that the autorelease objc never release.
__weak id ref;
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSString *str = [NSString stringWithFormat:#"test"]; // add autoreleasePool
ref = str;
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(#"viewWillAppear:%#",ref); // result test
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSLog(#"viewDidAppear:%#",ref); // result test
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSLog(#"%#",ref); // If I click long after,But it has not been released
}
[NSString stringWithFormat:] doesn't promise you an object on the autoreleasepool. It promises you an object that you do not have to release. In this case, it's returning you a constant string, and a constant string is never destroyed. It is also free to return you a cached value, or a value shared with other readers, a tagged pointer, or a singleton. NSNumber has lots of optimizations like this.
To the underlying question, when the local autorelease pool is drained, one release will be sent to the object for each autorelease that was previously sent to the object on that pool. Whether this destroys the object or not depends on what other retains have been placed on it.
I would expect a value like this to behave closer to what you're expecting, but there are no promises:
[NSString stringWithFormat:#"something a little long and computed: %d", rand()]
Related
I have read about __bridge, _bridge_retain and _bridge_transfer and did some experiments. However the output does not coincide with what I was expecting. In particular, I have the following code:
#interface ViewController ()
#property (nonatomic, strong) NSString *test;
#end
#implementation ViewController
CFStringRef cfString;
- (void)viewDidLoad
{
[super viewDidLoad];
self.test = #"123";
cfString = (__bridge CFStringRef)self.test;
self.test = nil;
}
- (void)viewDidAppear:(BOOL)animated
{
NSLog(#"%#", cfString);
NSLog(#"%#", self.test);
}
I expect the program to crash, based on the following reasoning: _bridge does not transfer ownership, so while casting self.test to cfString, there is no retainCount increment. Once self.test is set to nil, ARC will step in and dealloc the string. So that portion of memory is freed, but cfString is still pointing there, resulting in a pointer bad access exception. In contrast to my reasoning, the output is 123 and null, and of course the program does not crash.
Moreover, if I replace
self.test = nil;
with
CFRelease(cfString);
I expect the program to crash as well due to a similar reasoning. Even stranger is that the output is now 123 and 123.
Can anyone kindly elaborate why? Btw, the term ownership always troubles me, some explanation will be greatly appreciated.
Your problem is that you're using a constant string. This is put straight into the programs memory so the reference is unexpectedly remaining valid despite the fact that it shouldn't. Use something less constant than a constant string and your program will brake like you think.
The problem is that you base your example on a literal NSString value.
In objective-C, constant NSString (constant values known at compile time) are never released. In fact, their main memory managment methods are like:
+ (id)allocWithZone:(NSZone *)zone {
id _uniqueInstance = [self _singletonInstanceOfClass];
if( _uniqueInstance == nil )
_uniqueInstance = [super allocWithZone:zone];
return _uniqueInstance;
}
- (id)copyWithZone:(NSZone *)zone {
(void)zone;
return self;
}
- (id)retain {
return self;
}
- (NSUInteger)retainCount {
return NSUIntegerMax; // denotes an object that cannot be released
}
- (oneway void)release {
//do nothing
return;
}
- (id)autorelease {
return self;
}
As you can see, releasing them is not possible.
I have a method called in various places called "cancelAllPendingDownloads"
This is a general method that cancels various jobs and updates internal counters.
Problem happens when it is called within the dealloc method
-(void)dealloc
{
[self cancelAllPendingDownloads]; // want to cancel some jobs
}
-(void)cancelAllPendingDownloads // updates some internals
{
__weak __typeof__(self) weakSelf = self; // This line gets a EXC_BAD_INSTRUCTION error in runtime
for(Download *dl in self.downloads)
{
dl.completionHandler = ^{ // want to replace the previous block
weakSelf.dlcounter--;
}
[dl cancel];
}
}
Not sure why it fails in the dealloc method as "self" still exists
When I change the code to
__typeof__(self) strongSelf = self; //everything works fine
__weak __typeof__(self) weakSelf = strongSelf; (or "self") BAD_INSTRUCTION error
The error happens on the second line
Just to make the "you are not supposed" or "You can't" part of the other good answers
more precise:
The runtime function for storing a weak reference is objc_storeWeak(), and the
Clang/ARC documentation states:
id objc_storeWeak(id *object, id value);
...
If value is a null pointer or the object to which it points has begun
deallocation, object is assigned null and unregistered as a __weak
object. Otherwise, object is registered as a __weak object or has its
registration updated to point to value.
Since the self object has already begun deallocation, weakSelf should be set to NULL
(and therefore is not of any use).
However, there seems to be a bug (as discussed here http://www.cocoabuilder.com/archive/cocoa/312530-cannot-form-weak-reference-to.html)
that objc_storeWeak() crashes in this case, instead of returning NULL.
If an object is in dealloc state, you are not supposed to create any new references to it. Consider the object as already destroyed. Don't use it in a callback/delegate any more.
Note that dlcounter won't ever be read. Just cancel the connections without reading the results.
TL;DR
- How can I reference __weak self in dealloc method?
- Don't reference it.
You can't initialize a week (or a strong) reference to self in the dealloc method and use it elsewhere - it's too late, the object will be inevitably destroyed.
However, you might try this:
-(void)dealloc
{
NSArray* localDownloads = self.downloads;
for(Download* dl in localDownloads) {
[dl cancel];
}
}
It should be clear that there are better places to invoke cancellation, for example, in a view controller, you may override viewWillDisappear:.
I am assuming you are using ARC for your project.
Straight from Apple:
Apple Talked about Weak and Strong
__strong is the default. An object remains “alive” as long as
there is a strong pointer to it.
__weak specifies a reference that does not keep the referenced object alive.
A weak reference is set to nil when there are no strong references to the object.
This is an Article Explaining Dealloc:
Dealloc Method Explained and More
This method will be called after the final release of the object
but before it is deallocated or any of its instance variables are destroyed.
The superclass’s implementation of dealloc will be called automatically when
the method returns.
After this being pointed out... I highly recommend you revise your code design because there is no reason for you to call a weak typeof(self) to solve your problem of cancelling those downloads at dealloc or any type of deallocing that involves _weak_typeof__self for that matter.
What I can recommend though is that that class that you are trying to cancel those downloads frin, make it keep track of those downloads with a Download UniqueID and just stop them or delete them at dealloc. Its simpler and easier to manage rather than that wierd call to __weak self and all that code you are doing.
In short: you can use a __strong reference to self in dealloc instead of __weak for your purposes but if and only if that strong reference won't outlive the end of dealloc. Otherwise, I would advise using __unsafe_unretained, which is still unsafe if it outlives the dealloc but is clearer to read.
Longer: I had a similar situation where the object (view controller) during dealloc should unsubscribe from notifications. That's a custom notifications system and unsubscribing requires creating an object with a reference to the entity that's being unsubscribed.
I ended up with the same situation: in dealloc there's no way to create that object because it required a weak reference which caused a crash (here's some stupid demo code, not something you would have in production):
#interface Dummy : NSObject
#property(nonatomic, weak) id weakProperty;
#property(nonatomic, strong) id strongProperty;
#property(nonatomic, unsafe_unretained) id unsafeProperty;
- (instancetype)initWithWeakStuff:(id)stuff;
- (instancetype)initWithStrongStuff:(id)stuff;
- (instancetype)initWithUnsafeStuff:(id)stuff;
#end
#implementation Dummy
- (instancetype)initWithWeakStuff:(id)stuff {
self = [super init];
if (self) {
_weakProperty = stuff;
}
return self;
}
- (instancetype)initWithStrongStuff:(id)stuff {
self = [super init];
if (self) {
_strongProperty = stuff;
}
return self;
}
- (instancetype)initWithUnsafeStuff:(id)stuff {
self = [super init];
if (self) {
_unsafeProperty = stuff;
}
return self;
}
- (void)dealloc {
}
#end
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)dealloc {
Dummy *dummy = [[Dummy alloc] initWithStrongStuff:self];
[[NSNotificationCenter defaultCenter]
postNotificationName:#"some notification"
object:dummy]; // do something with it
}
#end
If, on the other hand, the reference was strong, all seems to work well (during dealloc). The problem would arise if that newly created object would outlive self:
- (void)dealloc {
Dummy *dummy = [[Dummy alloc] initWithStrongStuff:self];
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter]
postNotificationName:#"some notification"
object:dummy]; // do something with it
}); //Crash at the end of the block during dummy's dealloc
}
This would mean that whenever the dummy object would need to dealloc it would try to decrease the ref count of its strongProperty. And at that point the ViewController has been deallocated and released already.
However, IMHO the "safest" way to proceed is to use unsafe_unretained in this case. Technically it's the same as using assign: pointer will be assigned regardless of memory management and that reference will not need to be released when it goes out of scope. But using unsafe_unretained tells the readers of your code (or future you) that you were aware of the risk and there must have been a reason to do what you did.
When to copy a block? The document says, blocks are "deleted when execution returns from the scope in which they are defined.This means you can’t return them directly from a function. If blocks could only be used while their defining scope was still on the call stack, they wouldn’t be nearly as useful as they actually are"
So, here is code which I tried, hoping the block will be deleted once execution is completed in viewDidLoad.
MyReaderController.h
#interface MyReaderController : UIViewController
{
myBlockVar aBlockVar;
}
-(myBlockVar) getABlock;
#end
MyReaderController.m
#implementation MyReaderController
- (void)viewDidLoad
{
[super viewDidLoad];
aBlockVar=[self getABlock];
NSLog(#"Block Result = %f",aBlockVar(1));
}
-(void) viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
NSLog(#"Block Exists even after the execution completes=%# %f",aBlockVar,aBlockVar(5));
}
-(myBlockVar) getABlock{
return ^(int var){return 4.0f;};
}
#end
So, does this code require viewDidLoad to be changed to as coded below, if not then when should I use it.
- (void) viewDidLoad{
[super viewDidLoad];
aBlockVar=Block_copy([self getABlock]);
NSLog(#"Block Result = %f",aBlockVar(1));
}
PART 2
Later on I tried with this following code, hoping now it will return aBlockVar as nil obj in
viewDidDisappear.
- (void)viewDidLoad
{
[super viewDidLoad];
Blocker *blocker=[[Blocker alloc] init];
myBlockVar myVar=[blocker getABlock];
aBlockVar=myVar;
NSLog(#"Block Result = %f",aBlockVar(1));
blocker=nil;
myVar=nil;
}
Blocker.m
#import "Blocker.h"
#implementation Blocker
-(myBlockVar) getABlock{
return ^(int var){return 4.0f;};
}
#end
Are you using ARC? If so, you don't need to use Block_copy or Block_release.
If you are, then you are correct with your revised code, as Block_copy takes it off the stack and into the heap where it is has an effective retain count of 1. You would also need to call Block_release where appropriate, when finally finished with the block, to bring its balance the copy, effectively bringing the retain count back to 0.
use #property (nonatomic, copy) (int)(^myBlock)(void);
let the system do all right memory management for you!
initialize:
self.myBlock = ^int(void){
return 4.0;
};
if you want to destroy your block somewhere do self.myBlock = NULL;
An addendum to the existing answers:
Even if you're using ARC, there are certain situations where you still need Block_copy.
For example, extracting a block argument from an NSInvocation and using it after the function returns.
- (void)interceptInvocation:(NSInvocation *)call {
BlockType block;
[call getArgument:&block atIndex:2]; // ARC cannot see this happen
block = (__bridge BlockType)Block_copy((__bridge void *)block);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
block();
});
}
Without the copy, the block will have been deallocated by the time it is run.
It appears a matching Block_release is not necessary, as when I added one it crashed due to too many releases.
with arc never, without arc:
: when you have a STACK block and want to keep it as a HEAP block (e.g. when you have a block in a function and want it to live after you exited the function!)
You need to block_copy it then.
also you need retain/release it as you would a NSString so (using block_copy/block_release)
There's a little bit uncommon situation in my app, that is,
I have to reload some retain properties everytime when the view is going to appear,
the code looks like this:
// .h
#property (nonatomic, retain) NSArray *myData;
// .m
#synthesize myData;
- (void)viewWillAppear:(BOOL)animated {
... // get FetchRequest and so on
self.myData = [self.context executeFetchRequest:request error:&error]; // Line 1
[super viewWillAppear:animated];
}
- (void)viewDidUnload {
self.myData = nil;
[super viewDidUnload];
}
- (void)dealloc {
[myData release]; // Line 2
[super dealloc];
}
there are several points:
1st. as you see, the property "myData" is retain, so I think every I set some object for it, it would automatically retain that object?
2nd. I have to reload "myData" everytime the view will appear, just like the code of Line 1 above.
3rd. Since it is a retain property, I have to release it myself correctly.
Now, question is, do I correctly managed the memory without any leaking of "myData" using the codes above?
If the view would appear many times before it is dealloc, (like push in a further view in a UINavigationController and pop out for several times),
then myData would retain some object more than once, but I only release it in the dealloc for 1 once in Line 2, so is that ok?
But if I add this method the to viewController,which I think is more safe for avoiding memory leaks:
- (void)viewWillDisappear:(BOOL)animated {
self.myData = nil;
[myData release];
[super viewWillDisappear:animated];
}
- (void)dealloc {
// [myData release]; // don't release it here.
[super dealloc];
}
my app would crash after one or two times I push in and pop out the view,
So which one is really wrong?
Thanks a lot!
You are not only releasing it in Line 2, it will be also released in Line 1 when replaced as well as in viewDidUnload, so your code on top is just fine. The key is that
self.myData = anything;
is expanded to
[self->myData release];
self->myData = [anything retain];
so by assigning anything (including nil) you are already calling release implicitly. You could in fact replace Line 2 with self.myData = nil; to have never to call release since you don't have any explicit retain.
.h
#property (nonatomic, retain) NSArray *myData;
.m
#synthesize myData;
By including these lines in your code a setter and getter is created for your property myData. The setter generated at run time for objects looks something like this,
- (void)setMyData: (id)newValue
{
if (myData != newValue)
{
[myData release];
myData = newValue;
[myData retain];
}
}
The total effect is that whenever you access the property by appending self in front you are actually calling the setters and getters. So the following two lines are the exact same.
self.myData = nil;
[self setMyData:nil];
So your original code was already correct.
i'm a little bit confused with memory management in view controllers.
Lets say i have header file like this:
#interface MyController : UIViewController {
NSMutableArray *data;
}
#property (nonatomic, retain) NSMutableArray *data;
#end
and .m file looks like that:
#implementation MyController
#synthesize data;
- (void)dealloc
{
[self.data release];
[super dealloc];
}
- (void)viewDidLoad
{
[super viewDidLoad];
if (self.data == nil)
self.data = [[NSMutableArray alloc] init];
}
- (void)viewDidUnload
{
[super viewDidUnload];
[self.data release];
self.data = nil;
}
Is that ok from the correct memory management point of view? Will that work after dealloc via Memory Warning? How You do that in your apps?
Thanks for your answers ;)
While the alloc-retain calls balance out in viewDidLoad and viewDidUnload and should prove no problem memory-wise, it would be cleaner to take ownership only once and relinquishing it once rather than twice.
- (void)viewDidLoad
{
[super viewDidLoad];
if (self.data == nil)
self.data = [NSMutableArray array];
}
and
- (void)viewDidUnload
{
[super viewDidUnload];
self.data = nil;
}
You are not guaranteed that viewDidUnload will ever get called. Unlike init/dealloc, which get called in pairs, viewDidUnload is undeterministically called. viewDidUnload is only called if there is a low memory situation and your view is not the active view.
Depending on how your model is created and the implications of it remaining in memory, it may make more sense for you not to get rid of it. An example of this may be that recreating that data may involve an expensive web service call. It therefore would be a bad user experience to have to wait for that data to get recreated. If it must absolutely go, a better strategy may be to cache the data to disk so that you can easily reconstruct it.
viewDidUnload should only contain cleaning up your IBOutlets and flushing easily recreatable data.
These lines from -viewDidUnload both release data:
[self.data release];
self.data = nil;
Since you're using the property setter in the second line, and data is a retained property, the setter will release data. This is an over-release, and it'll cause a crash either right away or later, depending on whether other objects also retain that object. To fix, simply delete the first line and rely on the setter to do the right thing.
The -dealloc method, on the other hand, shouldn't use the setter as it does now. You should change:
[self.data release];
to:
[data release];
data = nil; // this line isn't strictly necessary, but often considered good form
The reasoning here is that it's conceivable that this class could be subclassed, and someone might override the property setter in such a way that it has some side effects that could cause problems when the object is being deallocated. You should access the ivar directly -- notice that I left off the "self." so that we're dealing with the ivar and not the property accessor. (-init and -dealloc are the only places where you have to worry about that; use the property accessors everywhere else.)