I have a Class called ServiceBrowser. Inside this class I have a block based method that searches for NSNetServices.
I call the method as such :
[_serviceBrowser discoverServicesOfType:#"_theService._tcp."
inDomain:#"local."
didDiscoverServices:^(NSArray *services) {
NSLog(#"Services discovered %#", services);
[UIView fadeView:_button toAlpha:1.0 duration:0.5 completion:nil];
} didRemoveServices:^(NSArray *services) {
NSLog(#"Services removed %#", services);
} failure:^(NSString *message) {
NSLog(#"Failure %#", message);
}];
If I remove the call to fadeView:toAlpha:duration:completion: it finds the services and logs them out. Only when I use self inside this block Xcode crashes without any error logged to the console.
fadeView:toAlpha:duration:completion: is a category method on UIView that takes a view and fades it in or out and this works fine as a standalone method. The problem is when I use _button inside the block it crashes.
I have investigated this and I assume it is down to a retain cycle. From looking at other questions and blog posts I should use a weak self inside the block.
I have tried using __block id weakSelf = self; and also typeof(self) __weak w_self = self; and neither work.
You could try to modify your class so that you pass self as method parameter and the class passes the reference back to the block:
[_serviceBrowser discoverServicesOfType:#"_theService._tcp." inDomain:#"local." didDiscoverServices:^(NSArray *services, id sender) {
// ^^^^^^^^^ "sender" is "self"
} didRemoveServices:^(NSArray *services) {
} failure:^(NSString *message) {
} fromSender:self];
// ^^^^^^^^^^^^^^^ pass self here
The implementation inside the class would be kind of:
- (void)discoverServicesOfType:(NSString *)type inDomain:(NSString *)domain didDiscoverServices:^(NSArray *, id)discoveredBlock fromSender:(id)sender {
NSMutableArray *services = [NSMutableArray array];
// do some fancy stuff here
discoveredBlock(services, sender);
}
Anc Ainu could also be right. Please check whether or not the object still exists.
You might try the following code which captures a weak reference of self within the block (Foo is the class of self).
The block also takes care now, that UIKit methods will be executed on the main thread:
__weak Foo* weakSelf = self;
[_serviceBrowser discoverServicesOfType:#"_theService._tcp."
inDomain:#"local."
didDiscoverServices:^(NSArray *services) {
NSLog(#"Services discovered %#", services);
Foo* strongSelf = weakSelf;
if (strongSelf) {
dispatch_async(dispatch_get_main_queue(), ^{
[UIView fadeView:strongSelf.button
toAlpha:1.0
duration:0.5 completion:nil];
});
}
} didRemoveServices:^(NSArray *services) {
NSLog(#"Services removed %#", services);
} failure:^(NSString *message) {
NSLog(#"Failure %#", message);
}];
Should self be deallocated when the block executes, the strong reference strongSelf will be nil.
Edit
Why is capturing a weak pointer from a UI element preferred over capturing a strong pointer? While this is likely not the cause of your crash, it is a substantial improvement.
The block below strongly captures self to demonstrate this. Note, when you reference an ivar directly e.g. _button and not via the property self.button, self will be implicitly captured in the block, that is it will be retained until after the block has finished.
Now, the effect of the block is, that the UI element self will be held alive until after the block executes, no matter when this happens and no matter if the view is visible anymore: the user might have switched to numerous other views and controllers in the meantime. However, self don't get deallocated until after the block finishes. This unnecessary holds resources in memory (possibly large images) which don't get freed up to this point.
[_serviceBrowser discoverServicesOfType:#"_theService._tcp."
inDomain:#"local."
didDiscoverServices:^(NSArray *services) {
NSLog(#"Services discovered %#", services);
dispatch_async(dispatch_get_main_queue(), ^{
[UIView fadeView:self.button
toAlpha:1.0
duration:0.5 completion:nil];
});
} didRemoveServices:^(NSArray *services) {
NSLog(#"Services removed %#", services);
} failure:^(NSString *message) {
NSLog(#"Failure %#", message);
}];
Related
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.
In my view-controller usually I load some data from server and call some function to render data or do some action when request data comes back from server. following is the snippet of such code.
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
__weak typeof(self) weakSelf = self;
[MyRequestManager loadObjects: ^(MyObject* object, NSError* error) {
weakSelf.textField.text = object.text;
//do some other actions
}];
}
If before data comes from server view is disappeared/unloaded, will callback will be called? If it is called there are chances of crash of unexpected things to happen. So how can we prevent that from happening?
I can think of one way to set a variable in viewWillAppear and viewWillDisappear. What is recommended way of doing this?
As you're keeping weakself inside, self won't be retained, so it will be dealloced. You should make your weakself a strong value inside, use the strong one and it won't crash.
__weak typeof(self) weakSelf = self;
dispatch_group_async(_operationsGroup, _operationsQueue, ^{
typeof(self) strongSelf = weakSelf;
if (strongSelf) {
[strongSelf doSomething];
[strongSelf doSomethingElse];
}
});
You may find more depth information eg here: http://albertodebortoli.github.io/blog/2013/08/03/objective-c-blocks-caveat/
Recently after following Apple documentation
I used the following conventions to avoid retain cycle issues.
__weak __typeof(self) weak_self = self;
void(^completionBlock)(void) = ^(){
__typeof(self) strong_self = weak_self;
if (strong_self) {
if (strong_self->_completion != NULL) {
strong_self->_completion();
}
}
};
But this code is found to be crashing because self is getting deallocated before invoking the block.
When I used the following it is found to be working.
__block __typeof(self) block_self = self;
void(^completionBlock)(void) = ^(){
if (block_self->_completion != NULL) {
block_self->_completion();
}
};
Now I am confused when we should use __weak reference. Only in the following case of "self.completionBlock"
__weak __typeof(self) weak_self = self;
self.completionBlock = ^(){
if (weak_self->_completion != NULL) {
weak_self->_completion();
}
};
Any light on this conditions will be very useful to me.
My implementation code is given below.
=================================================
File MyViewController
#implementation MyViewController
//starting point
- (void)myPushMethod {
__weak __typeof(self) weak_self = self;
MyViewControllerTransitioning *delegateObj = [[MyViewControllerTransitioning alloc] initWithCompletion:^{
//resetting the delegate
__typeof(self) strong_self = weak_self;
if (strong_self) {
strong_self.navigationController.delegate = nil;
}
}];
self.navigationController.delegate = delegateObj;
[self.navigationController pushViewController:someViewController animated:_animated];
//it is found that delegateObj is getting deallocated while reaches this point
}
#end
=================================================
File MyViewControllerTransitioning
#implementation MyViewControllerTransitioning
- (instancetype)initWithCompletion:(completionHandler)completionHandler
{
if(self = [super init])
{
if (completionHandler != NULL) {
_completion = completionHandler;
}
}
return self;
}
- (id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC
{
id<UIViewControllerAnimatedTransitioning> animationTransitioning = nil;
//crashes here
__block __typeof(self) block_self = self;
void(^completionBlock)(void) = ^(){
if (block_self->_completion != NULL) {
block_self->_completion();
}
};
//showing presentation-up animation if Push
if (operation == UINavigationControllerOperationPush) {
animationTransitioning = [[MyAnimator alloc] initWithAnimationCompletion:completionBlock];
}
return animationTransitioning;
}
- (void)dealloc{
//dealloc is called before invoking completionBlock
}
#end
=================================================
File MyAnimator
#implementation MyAnimator
- (instancetype)initWithAnimationCompletion:(presentationUpCompletion)completionHandler
{
if(self = [super init])
{
if (completionHandler != NULL) {
_completion = completionHandler;
}
}
return self;
}
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
return my_duration;
}
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
//animation logic
[UIView animateWithDuration:duration animations: ^{
//animation logic
} completion: ^(BOOL finished) {
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
- (void)animationEnded:(BOOL) transitionCompleted {
if (transitionCompleted && _completion != NULL) {
_completion();
}
}
#end
In my original answer, below, I walk through the standard weakSelf pattern (and the weakSelf-strongSelf "dance"). My conclusion was that presentation slide you reference is absolutely correct regarding the weakSelf pattern (though that presentation is stylistically dated).
You subsequently provided a more complete code sample, and it turns out that it suffers from a different, unrelated problem. (Worse, it was a problem that only manifests itself when the strong reference cycles are resolved. lol.) Bottom line, the code sets the delegate of the navigation controller to a local object which falls out of scope. Because the navigation controller doesn't retain its delegate, you were ending up with a dangling pointer to this deallocated object.
If you keep your own strong reference to this delegate object (keeping it from being deallocated), the problem goes away.
My original answer is below.
You said:
used the following conventions to avoid retain cycle issues.
__weak __typeof(self) weak_self = self;
void(^completionBlock)(void) = ^(){
__typeof(self) strong_self = weak_self;
if (strong_self) {
if (strong_self->_completion != NULL) {
strong_self->_completion();
}
}
};
But this code is found to be crashing because self is getting deallocated before invoking the block.
No, this is a very common pattern (often jokingly called the "weak self, strong self dance"). There is nothing wrong with this. If it's crashing, it's for other reasons.
Sure, I'd use modern naming conventions (weakSelf and strongSelf rather than weak_self and strong_self). And I'd remove the __ at the start of __typeof. And I wouldn't be inclined to dereference and ivar, but rather use a property. So, I might end up with something like:
__weak typeof(self) weakSelf = self;
void(^completionBlock)(void) = ^(){
typeof(self) strongSelf = weakSelf;
if (strongSelf) {
if (strongSelf.completion != NULL) {
strongSelf.completion();
}
}
};
But, still, if that is crashing, you have some other problem. Frankly, you have a block calling a block so it's a little hard to guess where your problem rests, but the problem is not in the "weak self" pattern.
Later you go on to suggest using:
__block __typeof(self) block_self = self;
That does not do what you think it does. The goal of the "weak self" pattern is to break the strong reference cycle (formerly known as retain cycle), but in ARC, this __block reference does nothing to resolve that strong reference cycle. Note, in non-ARC code, this block_self pattern was used to break retain cycles, but it will not do the job in ARC.
Finally, you go on to suggest:
__weak __typeof(self) weak_self = self;
self.completionBlock = ^(){
if (weak_self->_completion != NULL) {
weak_self->_completion();
}
};
This has two serious problems. First, you are dereferencing a weak variable. If weak_self was nil, it would crash. Second, even if you fixed that by using property accessor methods rather than dereferencing ivars, you have another problem here, namely a race condition.
Bottom line, the "weak self" pattern from that presentation is correct. Likewise, the "weak self, strong self dance" from your first example is also correct. If it's crashing for you, you have to provide us a MCVE which reproduces the problem. But in ARC code, this is a perfectly valid pattern.
self.completionBlock
self has a strong reference to completionBlock (it's a property variable), then u need to weak reference to self to avoid the cycle.
(void)(^completionBlock)(void) = ^{}
self has NO strong reference to completionBlock (it's a local variable), no need to create a weak reference to self.
And the reason for the one you added __block works is that "__block causes the variable to be strongly referenced".
I have the following class:
File_Downloadmanager.h:
#import "ASINetworkQueue.h"
#interface File_Downloadmanager : NSObject {
}
-(void)addRequestToDownloadQueue:(NSString*)objectID :(NSString*)userID :(NSString*)filename;
-(void)initDownloadQueue; // creates a new download queue and sets delegates
-(void)startDownload; // starts the download queue
-(void)requestFinished;
-(void)requestFailed;
-(void)queueFinished;
#property(retain) ASINetworkQueue *downloadQueue;
#end
File_Downloadmanager.m:
#implementation File_Downloadmanager
#synthesize downloadQueue;
-(void)initDownloadQueue{
NSLog(#"Init DownloadQueue");
// Stop anything already in the queue before removing it
[[self downloadQueue] cancelAllOperations];
[self setDownloadQueue:[ASINetworkQueue queue]];
[[self downloadQueue] setDelegate:self];
[[self downloadQueue] setRequestDidFinishSelector:#selector(requestFinished:)];
[[self downloadQueue] setRequestDidFailSelector:#selector(requestFailed:)];
[[self downloadQueue] setQueueDidFinishSelector:#selector(queueFinished:)];
[self downloadQueue].shouldCancelAllRequestsOnFailure = NO;
}
-(void)startDownload{
NSLog(#"DownloadQueue Go");
[downloadQueue go];
}
- (void)requestFinished:(ASIHTTPRequest *)request
{
// If no more elements are queued, release the queue
if ([[self downloadQueue] requestsCount] == 0) {
[self setDownloadQueue:nil];
}
NSLog(#"Request finished");
}
- (void)requestFailed:(ASIHTTPRequest *)request
{
// You could release the queue here if you wanted
if ([[self downloadQueue] requestsCount] == 0) {
[self setDownloadQueue:nil];
}
//... Handle failure
NSLog(#"Request failed");
}
- (void)queueFinished:(ASINetworkQueue *)queue
{
// You could release the queue here if you wanted
if ([[self downloadQueue] requestsCount] == 0) {
[self setDownloadQueue:nil];
}
NSLog(#"Queue finished");
}
-(void)addRequestToDownloadQueue:(NSString*)objectID :(NSString*)userID :(NSString*)filename{
...SourceCode for creating the request...
// add operation to queue
[[self downloadQueue] addOperation:request];
}
In another class a function is called an inside that function I'm doing the following:
-(void)downloadFiles{
File_Downloadmanager * downloadhandler = [[File_Downloadmanager alloc]init];
// initialize download queue
[downloadhandler initDownloadQueue];
for (int i = 0; i < [meetingObjects count]; i++) {
....some other code to get the objectID, userID, etc.
[downloadhandler addRequestToDownloadQueue:ID :[loginData stringForKey:#"userId"] :[NSString stringWithFormat:#"%#%#",currentObject.id,currentObject.name]]
}
[downloadhandler startDownload];
}
Everything works fine and the download begins. But when the first file is downloaded, I get an error in the ASINetworkQueue class that my selector "requestFinished" can't be called (I don't have the exact message, can't start the app at the moment, but the failure code was exc_bad_access code=1).
Is the time of declaration / initialization of my File_Downloadmanager object the problem? Because the function "downloadFiles" is called, the DownloadManager object created, the requests added and then the "downloadFiles" method returns because the queue works async?
I haven't used the ASI networking stuff before, but have seen lots of references to it on the net.
It sounds to me like the ASINetworkQueue class expects it's delegate to conform to a specific protocol. If it's set up correctly, you should get a warning when you try to assign yourself as the delegate of the ASINetworkQueue object but have not declared that your class conforms to the appropriate protocol. If you DO include a protocol declaration, then you should get a warning that you have not implemented required methods from that protocol.
Try cleaning your project and rebuilding, and then look carefully for warnings, specifically on your line:
[[self downloadQueue] setDelegate:self];
EDIT: I just downloaded one of the ASIHTTPRequest projects, and to my dismay, the delegate property of the ASINetworkQueue class does not have to conform to a specific protocol. This is bad programming style. If you set up a delegate, you should make the delegate pointer conform to a specific protocol.
Also, be aware that the ASI networking classes have not been maintained for several years now and are getting badly out of date. There are better alternatives out there, and you should look at moving to a different networking framework.
It looks like the downloadhandler object that ASINetworkQueue is attempting to send the requestFinished message to no longer exists at the time that the message is sent to it, as it's probably being deallocated when the downloadFiles method finishes executing. Instead of making the downloadhandler object local to the downloadFiles method, instead make it a (strong, nonatomic) property within the class that contains the downloadFiles method. That way, you can ensure that it will still exist when requestFinished is called.
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.