why this RACObserve block caused retain cycle? - ios

Consider I in my view controller, I added RACObserve of property of Singleton, and inside subscribNext I have a self reference in it.
The code is as below:
[RACObserve([Singleton shared], singletonFlag) subscribeNext:^(NSNumber *singletonFlag) {
self.flag = [singletonFlag boolValue];
}];
Based on my understanding, self don't hold a strong reference of the block(while block hold a strong reference of self), this shouldn't cause retain cycle.
I have read memory management of reactive cocoa as well https://github.com/ReactiveCocoa/ReactiveCocoa/blob/master/Documentation/Legacy/MemoryManagement.md
In which they provide an example as
[RACObserve(self, username) subscribeNext:^(NSString *username) {
[self validateUsername];
}];
I totally understand why it caused the retain cycle in above case and we need a weak self inside the block.
I am confused why in the first case, it will cause a retain cycle. To confirm this, just paste that code snippet after viewDidLoad and see whether the view controller was dealloc-ed when it should be.
If you need see more implementations of the singleton, this is the code,
#interface Singleton : NSObject
#property (readwrite,nonatomic) BOOL singletonFlag;
#end
#implementation Singleton
+ (Singleton *)shared {
static dispatch_once_t pred = 0;
__strong static id _sharedObject = nil;
dispatch_once(&pred, ^{
_sharedObject = [[self alloc] init];
});
return _sharedObject;
}
- (id)init {
if (self = [super init]) {
NSLog(#"init of %#",NSStringFromClass(self.class));
}
return self;
}
#end
Anyone enlighten me about this?

The internal implementation is quite complicated, It's not important whether there is a real retain cycle.
Here the reason why memory leaks is just the same in your two examples:
self is retained by the block
The block is retained by an internal subscriber object
The subscriber is retained by some internal thing of the RACObserve signal until the signal terminates.
The RACObserve signal terminates when either the target (the singleton instance) or self (the RACObserve micro is implicitly using self) is deallocated.
But now the singleton instance won't dealloc, and self won't dealloc neither since it's already retained. So the signal never terminates, then memory leaks.

Anyway, you shouldn't write such things as
[RACObserve([Singleton shared], singletonFlag) subscribeNext:^(NSNumber *singletonFlag) {
self.flag = [singletonFlag boolValue];
}];
Instead, write
RAC(self, flag) = RACObserve([Singleton shared], singletonFlag);

The problem is that RACObserve() will return you a RACDisposable object, that you have to dispose your self. If you use it the way RAC()=RACObserve(), then the RAC() part will take care of killing the RACDisposable object that is returned by RACObserve() method.
One quick fix that you can make when using the RACObserver like this:
[RACObserve(self, username) subscribeNext:^(NSString *username) {
[self validateUsername];
}];
Is to turn it into this:
(RACDisposable *disposableSignal; declared in .h for example)
disposableSignal=[RACObserve(self, username) subscribeNext:^(NSString *username) {
[self validateUsername];
}];
And use [disposableSignal dispose] to deallocate the signal. For example in viewWillDisappear method. Basically you have to kill it with dispose method to get rid of it.

Related

Preventing `self` from creating a strong reference in blocks

With the recent XCode update some code blocks are displaying as warnings where "Block implicitly retains 'self'"
It is my understanding that the when you create blocks it is best practice to create a weak self to keep from creating a strong reference that will not be garbage collected.
In the below example I set the myArray to self->myArray as recommended by XCode. Does this create the strong reference? Why can't I use 'weakSelf->myArray`? Attempting to do so results in this error:
Dereferencing a __weak pointer is not allowed due to possible null
value caused by race condition, assign it to strong variable first
I thought the whole point was to create weak refrences? Isn't weakSelf just a pointer to self?
Is the self-> even necessary in the below instance?
#interface SomeViewController (){
NSMutableArray * myArray;
}
#end
- (void) doSomethingInBackground {
// Do NSURLSessionTask on the background and onCompletion call mySuccessBlock.
}
- (SomeBlock) mySuccessBlock {
__block __typeof__(SomeViewController) __weak * weakSelf = self;
return ^(NSDictionary* result){
//this line is my related to my question
self->myArray = [weakSelf sortResultsAlphabetically: result];
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.tableView reloadData]
});
};
}
Would recasting to be the correct way?
SomeViewController * strongSelf = weakSelf;
strongSelf->myArray = [weakSelf sortResultsAlphabetically: result];
The error message is right. You have to do the "weak-strong dance". You are only doing half of the dance. Pass self into the block as weak, but then immediately assign it, inside the block, to a strong reference (as in your edited "Would recasting to be the correct way?").

Retain Cycle Even when using Weak/Strong ARC Semantics

`I admit that I am not an expert on ARC and retain cycles though through some research and some great articles (like this), I believe I get the basics.
However, I am currently stumped. I have a property defined as follows.
#property (nonatomic,retain) Foo *foo;
Inside my init, I do the following.
if(self = [super init]) {
_foo = [[Foo alloc] initWithDelegate:self];
// async the rest
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
(unsigned long)NULL), ^(void) {
__strong typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf.foo != nil) {
[strongSelf.foo runTests];
}
});
}
return self;
}
and here is my dealloc
- (void)dealloc {
_foo = nil;
}
If the dispatch_aync block is commented out, I see my Foo dealloc get called immediately after foo is set to nil. With the block commented in, foo's delloc is not called.
I've got a retain cycle correct? Any idea how?
No, you do not necessarily have a retain cycle (now known as a "strong reference cycle" in ARC). You have code that, if foo existed by the time strongSelf was defined, foo will be retained until the dispatched code finishes.
The only potential strong reference cycle here is the delegate you passed to foo. If that delegate is defined as strong property of the Foo class, then you have a strong reference cycle. If it's defined as a weak property, then you have no strong reference cycle.

How can I reference __weak self in dealloc method

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.

How to keep a strong reference inside of a block without having a retain cycle

I have a block attached to a button (using this category):
__unsafe_unretained typeof(UIImage) *weakPic = originalPic;
[button addEventHandler:^{
switch (state) {
case state1:
{
UIViewController *vc = //some VC
vc.pic = weakPic; // weakPic is nil at this point
// putting originalPic here would solve my problem
// but then I would have a retain cycle
}
case state2:
{
// other stuff
}
}
}];
the action associated with the button is different depending on the state.
Here is the problem: I must keep the above __unsafe_unretained to avoid having a retain cycle. However, this code is called at a point where originalPic = nil.. and so when I assign weakPic to vc.pic I'm assigning it a nil value. If I replace weakPic with just originalPic, then it works fine.. (originalPic will have the updated value) but then I get the retain cycle.. ideas?
Without knowing more about your code I suggest you consider declaring a weakSelf and implementing an accessor on self.
//the accessor
-(UIImage*)pic{
return originalPic;
}
-(void)someSetupMethod{
__weak id weakSelf = self;
[button addEventHandler:^{
switch (state) {
case state1:
{
UIViewController *vc = //some VC
vc.pic = [weakSelf pic]; // if weakself is nil at this point, then
// originalPic is likely invalid
}
case state2:
{
// other stuff
}
}
}];
}
It may not be a weakSelf you want, but some other object. In that case just declare that other object to be weak, as long as you can be relatively sure it will exist as long or longer than the button.
You might also want to look at declaring it as a __block variable. The __block keyword prevents a copy of the object from being made inside the block.
See this thread for a better explanation of __weak and __block references.

block_copy when to use

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)

Resources