Can not assign values to self properties from within a block - ios

I have a viewcontroller class and another class of NSObject. I call from the viewcontroller class with the following method the NSObject class.
SubmitContentViewController class
#implementation SubmitContentViewController
-(void)viewDidLoad{
[self callUploadQueueClass];
}
-(void)callUploadQueueClass{
UploadQueueClass *queue = [UploadQueueClass new];
[self generateIDforImage];
}
#end
UploadQueueClass
#implementation UploadQueueClass
-(void)generateIDforImage{
#weakify(self)
[[[ApiServicesProvider shared] userService] getCreatorsContentID:^(NSDictionary * _Nullable result, NSError * _Nullable error) {
#strongify(self)
if(nil==error){
NSString* ccID = result.creatorsContentId;
self.creatorsContentID = ccID;
NSLog(#"creatorsContentID %#",self.creatorsContentID);
[self getImageUploadURL:ccID withNumberOfAttempts:10];
}
else{
self.isUploading = NO;
}
}];
}
#end
at this line
NSLog(#"creatorsContentID %#",self.creatorsContentID);
creatorsContentID is null although at this line
self.creatorsContentID = ccID;
ccID is not null so self.creatorsContentID should not be null.
Moreover at this line
[self getImageUploadURL:ccID withNumberOfAttempts:10];
is never get called.
What am i missing?

You are creating your UploadQueueClass instance as a local variable *queue in callUploadQueueClass.
This local variable holds a strong reference to the UploadQueueClass instance.
As soon as the function returns, that local variable is released and it no longer holds the strong reference.
This happens before getCreatorsContentID has completed its work. Before the completion handler block is called.
You have used #weakify so that the self captured by the block does not hold a strong reference to the UploadQueueClass instance. The local variable has been released and the block self doesn't hold a strong reference to the instance. Nothing does, so it is released.
The self in the block is now nil. Using #Strongify won't help you here; the object has already gone away;
In this case you don't need to use #weakify; There is no danger of a circular reference causing a memory leak; The blocks capture of self only lasts until the completion handler has done its work.
However, removing #weakify doesn't seem like it would really help since there doesn't seem to be any way for the UploadQueueClass instance to communicate its results back to the calling view controller.
It would be more typical for the view controller to provide the completion handler block to the function it is calling, or at least provide some block to be executed. This is where you could use #weakify since the view controller instance would be self, but the block doesn't need to hold a strong reference to it to keep it around; The view controller hierarchy is doing that.
Since you don't want this object to report back to the view controller, simply remove the #weakify/#strongify. Then the block itself will hold a strong reference to the UploadQueueClass instance until it returns and then the object will be released by ARC.

Related

Strong reference to self inside a method running from a block

So, I know that if you save a block inside of self, then access self inside of that block you need to create and use something like __weak id weakSelf = self;.
My question is, does this also extend to functions being called from that block? As in, would the following lead to a retain cycle:
self.block = ^{ [weakSelf myFunction]; }
- (void) myFunction { self.counter++; }
Thanks for your time!
This does not create a retain cycle, because the self in the method is actually a parameter passed by the Objective C runtime (using objc_msgSend and similar). So let's consider the two scenarios possible in your code example. weakSelf has been zeroed out due to release of the holding object - a message passed to nil is ignored. weakSelf is not zeroed, in which case, it is passed by the Objective C runtime to the message as its self parameter.

Accessing a property inside a block

I am new to blocks in iOS and had a quick question regarding their use. Say I have the following setup:
viewController.rowLabels = #[#"Hello", #"World"];
viewController.testBlock = ^(NSInteger itemIndex) {
// here i want to access another property of the viewController called foo
};
So, as seen above, I want to access another property of the view controller within the block itself. Do i need to do a *__weak -> strong assignment to achieve this or can i simply access it like NSLog(viewController.foo)?
The simple, but possibly problematic answer, is just to access it as you do the other properties:
viewController.rowLabels = #[#"Hello", #"World"];
viewController.testBlock = ^(NSInteger itemIndex) {
... viewController.foo ...
};
From your fragment we cannot know what viewController is - e.g. it could be a local variable from the method this fragment is in or a global variable etc. If you are just reading the value in viewController, as you are here, this does not matter[1].
The above works but there might be a problem: you probably have a strong reference cycle. The viewController instance references the block through it's testBlock property, and the block references the viewController instance. If both these references are strong (likely) then you have a circular dependency and the viewController instance and the block can never be freed by the system. You can break this cycle using a weak reference:
viewController.rowLabels = #[#"Hello", #"World"];
__weak ViewController *weakViewController = viewController; // make a weak reference to the instance
viewController.testBlock = ^(NSInteger itemIndex)
{
// temporarily make a strong reference - will last just as long as the block
// is executing once the block finishes executing the strong reference is
// removed and no strong reference cycle is left.
ViewController *myController = weakViewController;
// only execute if the `ViewController still exists
if (myController != nil)
{
... myController.foo ...
}
};
HTH
[1] note that the value you are reading is a reference to a ViewController instance and you can modify properties of that instance, what you cannot do (and are not trying to do) is modify which instance the viewController references if viewController is a local variable.
Declare a __block variable containing the view controller object, like so:
__block __weak ViewController *blockVC = viewController;
viewController.testBlock = ^(NSInteger itemIndex) {
NSLog(#"%#", blockVC.foo);
};
I've used both __block and __weak since __block is implicitly strong, but adding __weak as a reference can help break the strong reference cycle while still using __block's strong reference to prevent deallocation.
As of iOS 5.0, it seems as if you can just create a __weak reference as opposed to using a __block variable to access a variable and its properties within a block:
__weak ViewController * weakVC = viewController;
viewController.testBlock = ^(NSInteger itemIndex) {
ViewController * strongVC = weakVC;
if (strongVC) {
NSLog(#"%#", strongVC.foo);
}
};
But note that unlike using __weak in combination with the __block storage type, __weak specifies a reference that might not keep the object alive, i.e. may deallocate, so even within the block, weakVC may be nil before you actually need it. (This is why the if (strongVC) conditional is required when just using a __weak variable alone.)

Passing blocks between view controllers

Iv searched a lot for this and cant find an answer to my specific question. But basically my question is, can i pass a completion block into another view controller and redefine it in the new view controller.
So for example in view controller A i have a method to perform a download with a completion block. First i create my block property in view controller A.
#property (copy)void (^downloadCompleteBlock)(NSArray *downloadItems);
I tried changing this to strong as opposed to copy but this did not solve my problem.
Then i define the completion block as follows.
self.downloadCompleteBlock = ^(NSArray *downloadItems) {
NSLOG(#"download complete in view controller A";
};
Then i call my download method passing in this completion block.
[self download:self.downloadCompleteBlock];
However, if this completion handler is not called by the time i leave this view controller (if the download isn't complete) I would want the completion block to perfrom something different on the next view controller. So in my prepare for segue i attempted to pass in this block to view controller B.
[controllerB setCompletionBlock:self.downloadCompleteBlock];
And this method in view controller B then redefines what happens when this completion block gets called.
- (void)setCompletionBlock:(void(^)(NSArray *downloadItems))downloadFinishedBlock {
downloadFinishedBlock = ^(NSArray *downloadItems) {
self.collectionData = downloadItems;
[self.collectionView reloadData];
};
}
However, the original block in view controller a still gets called when the download finishes as opposed to the block in view controller B. Anyone know how to have the completion block in view controller B called if that view is loaded before the download completes? I know i could use a notifier but i'm curious if i can do this with blocks.
Thanks
This is kind of a tough problem. At its heart is the problem of how to keep the block around after the first view controller goes away. Your current code solves that problem unwittingly by having the block refer to self. The vc is retained by that reference, which is good news if it needs to be around when the request finishes, but it's bad news because now the vc and the block will retain each other forever. (Google 'retain cycle'.)
So how do we get a long-running process that runs a block on completion and might outlive two or more view controllers? For starters, break that process into its own object. The interface of that object would look like:
#interface DownloadThingy
#property (copy)void (^downloadCompleteBlock)(NSArray *); // note, no need for dummy param names here
- (id)initWithRequestParams:(id)whateverIsNeededToStart;
- (void)start;
#end
Now, the view controller that want to start this can declare a strong property to it, create one, give it a completion block (see below**), and start it. When it's time for a segue, it can pass the downloadThingy to another vc, who can give it a different completion block.
** Since the request object is being kept as a property in one or more vcs, and since it retains the block, you still need to look out for a retain cycle:
(vc->downloadThingy->block->vc)
In VcA, do this:
- (void)startADownloadThingy {
self.downloadThingy = [[DownloadThingy alloc] initWithRequestParams:someParams];
__weak VcA *weakSelf = self;
self.downloadThingy.downloadCompleteBlock = ^(NSArray *downloadItems) {
// don't use self in here, use weakSelf
}
}
VcB will get called on the segue; it might or might not need to follow the same precaution. The distinction is whether this second vc retains a downloadThingy property. If it doesn't plan to hand it off to any other vc, it can skip the property, and thereby skip the worry about a retain cycle.
// another vc is handing off a running downloadThingy
- (void)heresARunningDownloadThingy:(DownloadThingy *)downloadThingy {
// if we have our own property, then
self.downloadThingy = downloadThingy;
// and we need to do the weakSelf trick
__weak VcA *weakSelf = self;
self.downloadThingy.downloadCompleteBlock = ^(NSArray *downloadItems) {
// don't use self in here, use weakSelf
}
}
Or...
// another vc is handing off a running downloadThingy
- (void)heresARunningDownloadThingy:(DownloadThingy *)downloadThingy {
// we do not have our own property
downloadThingy.downloadCompleteBlock = ^(NSArray *downloadItems) {
// feel free to use self in here
}
}
One last thing: it's a good practice for the DownloadThingy to aggressively nil out its block after it's through invoking it. So when the request is done, have it do this...
// DownloadThingy.m
// request is complete
self.downloadCompleteBlock(arrayFullOfResults);
self.downloadCompleteBlock = nil;

Recursive block gets deallocated too early

I have written a recursive block following these guidelines:
NSMutableArray *groups = [NSMutableArray arrayWithArray:#[#"group1", #"group2", #"group3", #"group4"];
__block CommunicationCompletionHandler completion = [^{
[groups removeObjectAtIndex:0];
if ([groups count] > 0) {
// This will send some information to the network, and calls the completion handler when it receives a response
[mySocket saveGroup:groups[0] completion:completion];
}
} copy]; // Removing copy here doesn't work either
[mySocket saveGroup:groups[0] completion:completion];
In the saveGroup:completion: method, I add the completion handler to an array:
self.completionHandlers[SaveGroupCompletionHandlerKey] = [completion copy];
And when I receive a response, I call the following method (key is in this case SaveGroupCompletionHandlerKey):
- (void)performCompletionHandlerForKey:(NSString *)key {
if (self.completionHandlers[key]) {
((CommunicationCompletionHandler)self.completionHandlers[key])();
[self.completionHandlers removeObjectForKey:key];
}
}
The problem is that the completion handler only gets called once. The removeObjectForKey: line makes the block deallocate. If I uncomment that line, everything works fine. I'm not sure how the array has the last reference to this block, since I add a copy (which I believe is being optimized to a retain).
For clarity, the flow of the app is:
Send data for first group over network
Receive response
Call completion handler
In the completion handler, send data for next group (this is the recursive part).
Anybody here who can point out what I'm doing wrong?
In -performCompletionHandlerForKey: you remove the completion handler from your dictionary after executing the block, which means that the handler will always be removed from the dictionary after one run.
Instead, store the block in a temporary variable and remove it from the dictionary before executing the block.
By the way, the advice to remove the weak reference is wrong. As your code is written now, your block will never be deallocated. The typical block recursion pattern is this:
__weak __block MyBlock weakHandler;
MyBlock handler = ^ {
if (foo) {
MyBlock strongHandler = weakHandler;
[bar asyncOperationWithCompletion:strongHandler];
}
};
weakHandler = handler;
[bar asyncOperationWithCompletion:handler];
A popular way to avoid retain retain cycles is to create a weak reference to the object before defining the block, then create a strong reference inside the block and set it to that weak reference. This method is frequently used to avoid strongly capturing self inside of blocks:
- (void)someMethod {
__weak MyType *weakSelf = self;
[self someMethodWithABlockArg:^{
MyType *strongSelf = weakSelf;
[strongSelf someOtherMethod];
}];
}
The strong reference created inside the block prevents the object from being deallocated while the block is running. You can, of course, do the same with any object type.
Edit2: Looks like [someBlock copy] is indeed fine. Have you tried running Analyze on the code? It may be that completion is not yet initialized when it is referred to inside of the block.

A strange block issue in Objective-C

I have a AuthService class that has a method to perform asynchronous connection to login. This class has implemented NSURLConnectionDataDelegate protocol so that when the server responses, it calls the completion handler previously set by a View Controller to update UI.
This is the definition of that completion handler
#property void (^completionHandler)(LoginResult *result);
This is when the class receives server response
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSString *response = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
//Do something with the response and create an instance of LoginResult class
self.completionHandler(loginResult);
}
If the completion handler block merely just calls NSLog to write to console the information of the login result which is passed as argument, then it runs perfectly with no error. But when I want to call methods of the ViewController that owns the block, something strange just happens.
I know that there is a retain cycle when you include an object in a block which owns that block. So this is the way how I code it.
__block typeof(self) bself = self;
[authService login:blablabla completionHandler:^(LoginResult *result) {
[bself didReceiveLoginResult:result];
}
I assumed this will prevent from running into a retain cycle. But I got "Thread: EXC_BAD_ACESS" error when debugging.
P.S.
Following codes for example run perfectly even if that property is not declared as "copy"
[authService login:blablabla completionHandler:^(LoginResult *result) {
NSLog(#"Login %#", result.success ? #"success" : #"failed");
}
The property should be declared as copy otherwise the block will stay on stack and can be already deallocated when you call it.
Also, there are simple ways how to prevent a retain circle. Just release the block when you have used it, e.g.
self.completionHandler(loginResult);
self.completionHandler = nil;
No clever magic with __block is neccessary. Retain cycles are allowed when they are temporary.
Edit:
If there is no reference to self in the block, the compiler will make it a global block and it won't ever get deallocated. See http://www.cocoawithlove.com/2009/10/how-blocks-are-implemented-and.html
Blocks need to be copied if you want to use them outside of the current function, so you will need to copy it before storing it in your property:
- (void)setCompletionHandler:(void (^)(LoginResult *))handler {
_completionHandler = [handler copy];
}
Then when you assign the completion handler in your login:completionHandler: method, it will be copied before being stored in the instance variable.
In this way, the block you pass to the function will be copied before being stored in the property, and the copy will be located on the heap, not the stack, so it'll still exist when you run it later.

Resources