Sometimes I use a block nested in another block, here is my code
- (void)test {
__weak typeof(self) weakSelf = self;
[self.viewSource fetchData:^(BOOL succeed, NSError * _Nonnull error, id _Nonnull data) {
__strong typeof(weakSelf) strongSelf = weakSelf;
[strongSelf.dataSource disposalData:^{
// here is the strongSelf ok? do I have to do something to avoid retain cycle?
[strongSelf updateUI];
}];
}];
}
- (void)updateUI {
}
I doubt the inner block still has a retain cycle?
[strongSelf.dataSource disposalData:^{
[strongSelf updateUI];
}];
my question is what is the correct way to break the retain cycle in such situation?
here is the additional discussion, as many friend mentioned about this, if I remove __strong typeof(weakSelf) strongSelf = weakSelf;, the inner block has no retain cycle? Is it perfectly correct?
- (void)test {
__weak typeof(self) weakSelf = self;
[self.viewSource fetchData:^(BOOL succeed, NSError * _Nonnull error, id _Nonnull data) {
[weakSelf.dataSource disposalData:^{
[weakSelf updateUI];
}];
}];
}
- (void)updateUI {
}
I think you can just create new strong reference inside nested block, like this:
- (void)test {
__weak typeof(self) weakSelf = self;
[self.viewSource fetchData:^(BOOL succeed, NSError * _Nonnull error, id _Nonnull data) {
__strong typeof(weakSelf) strongSelf = weakSelf;
[strongSelf.dataSource disposalData:^{
__strong typeof(weakSelf) strongSelf = weakSelf; // <- new strong ref
[strongSelf updateUI];
}];
}];
}
It will override the first strongSelf in the nested block scope. And it will be only alive during the execution of the nested block without strong reference cycle created. I think so =)
Related
I am using dispatch_async method to execute task in main queue. But it causing retain cycle:
Following is the code snippet:
self.test = ^{
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"%#", self);
});
};
I am not able to get properly why it is creating retain cycle. As My controller does not have ownership of dispatch_async block.
Try using weakSelf:
__weak typeof(self) weakSelf = self;
self.test = ^{
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"%#", weakSelf);
});
};
If you don't want the outer block to retain self, but want the inner block to be able to keep self alive once dispatched, maybe something like this:
typeof(self) __weak weakSelf = self;
self.test = ^{
typeof(self) strongSelf = weakSelf;
if (strongSelf) {
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"%#", strongSelf);
});
}
};
In the following code, how would you avoid nested blocks increasing the retain count of 'self'.
This is how I avoid nested blocks
-(void)openSession {
[self.loginManager logInWithReadPermissions:#[#"user_photos"]
fromViewController:[self.datasource mediaAccountViewControllerForRequestingOpenSession:self]
handler:[self loginHandler]];
}
-(void(^)(FBSDKLoginManagerLoginResult *result, NSError *error))loginHandler {
__weak typeof (self) weakSelf = self;
return ^ (FBSDKLoginManagerLoginResult *result, NSError *error) {
__strong typeof(weakSelf) strongSelf = weakSelf;
if (error) {
[strongSelf.delegate mediaAccount:strongSelf failedOpeningSessionWithError:error];
} else if (result.isCancelled) {
[strongSelf.delegate mediaAccountSessionOpeningCancelledByUser:strongSelf];
} else {
[strongSelf.delegate mediaAccountDidOpenSession:strongSelf];
}
[strongSelf notifyWithCompletion:[strongSelf completionHandler]]
};
}
-(void)notifyWithCompletion:(void(^)(void))completion {
[self notify];
completion();
}
-(void(^)(void))completionHandler {
return ^ {
//do something
};
}
But how do you avoid many nested blocks, which is often the case when you use GCD within a block ?
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self parseLoadsOfData];
dispatch_async(dispatch_get_main_queue(), ^{
[self updateUI];
});
});
Are there retain cycles here ?
__weak typeof(self) *weakSelfOuter = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
__strong typeof(self) *strongInnerSelf1 = weakSelfOuter;
[strongInnerSelf1 parseLoadsOfData];
__weak typeof(self) *weakInnerSelf = strongInnerSelf1;
dispatch_async(dispatch_get_main_queue(), ^{
__strong typeof(self) *strongInnerSelf2 = weakInnerSelf;
[strongInnerSelf2 updateUI];
});
});
I am calling a function recursively from completion block which increases the memory foot print, this most probably is leading to a block retain cycle,following is the code:
- (void)tick {
if (counter > counterLimit) {
[self finish];
return;
}
counter++;
context = [[Context alloc] init];
//this is executed in another thread
[self.executer computeWithContext:(Context*)context completion:^(NSDictionary *dictionary, Context *context_)
{
[self handleResponse];
[self tick];
}];
}
self is owning executer, executer is owning the block, the block captures self strongly (same effect as owning). you have a retain cycle.
create a weak reference for self and use that in the block exclusively.
- (void)tick {
// ...
__weak typeof(self) weakSelf = self;
[self.executer computeWithContext:(Context*)context completion:^(NSDictionary *dictionary, Context *context_)
{
typeof(weakSelf) strongSelf = weakSelf;
if(strongSelf) { // ensure the receiver is still there to receive
[strongSelf handleResponse];
[strongSelf tick];
}
}];
}
With the following code:
#interface MyClass()
{
NSMutableArray * dataArray;
}
#end
#implementation MyClass
- (void) doSomething
{
__typeof__(self) __weak wself = self;
dispatch_async(dispatch_get_global_queue(0,0), ^{
__typeof__(self) sself = wself;
[sself->dataArray addObject: #"Hello World"];
dispatch_async(dispatch_get_main_queue(), ^{
[NSThread sleepForTimeInterval: 30];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle: sself->dataArray[0]
message: #"Message"
delegate: nil
cancelButtonTitle: #"OK"
otherButtonTitles: nil];
[alert show];
});
});
}
#end
Is this the proper way to access sself from within the main queue block?
Would sself go out of scope once the initial queue finished?
Should I add a second __typeof__(self) sself = wself; inside the main queue block?
Yes, sure it is.
No, you'll be able to use in main_queue block.
No, you don't have to. You don't have to add __typeof__(self) sself = wself; even in global_queue block; it's not necessary, you already have a weakened self object wself (moreover, you will retain self in __typeof__(self) part inside the block).
You don't have to use __typeof__. Use simply typeof(self)
You ask:
Is this the proper way to access sself from within the main queue block?
Almost. You also should be checking to make sure sself is not nil, too. You do this because dereferencing a ivar for an nil pointer may crash your app. Thus, check to make sure it's not nil:
- (void) doSomething
{
typeof(self) __weak wself = self;
dispatch_async(dispatch_get_global_queue(0,0), ^{
typeof(self) sself = wself;
if (sself) {
[sself->dataArray addObject: #"Hello World"];
dispatch_async(dispatch_get_main_queue(), ^{
[NSThread sleepForTimeInterval: 30];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle: sself->dataArray[0]
message: #"Message"
delegate: nil
cancelButtonTitle: #"OK"
otherButtonTitles: nil];
[alert show];
});
}
});
}
You then ask:
Would sself go out of scope once the initial queue finished?
It goes out of scope, but is retained by the inner block and will be retained until that inner block finishes.
Should I add a second __typeof__(self) sself = wself; inside the main queue block?
You can if you want to, but it's not necessary. It depends upon the desired behavior. If, for example, you want to see this alert even if the current object (presumably a view controller) is dismissed, then use sself pattern (because you presumably want to hang on to it so you have access to dataArray). If you don't want to show the alert anymore, then you'd repeat this weakSelf/strongSelf dance.
I am facing some weird results with addOperationWithBlock.
My function looks something like this:
-(void) myFunction{
NSLog(#"VISITED");
..
for (NSDictionary *Obj in myObjects)
{
[operationQueue addOperationWithBlock:^(void){
MyObject* tmp = [self tediousFunction:Obj];
// threadTempObjects is member NSMutableArray
[self.threadTempObjects addObject:tmp];
NSLog(#"ADDED");
}];
}
[operationQueue addOperationWithBlock:^(void){
[self.myArray addObjectsFromArray:self.threadTempObjects];
for(myObject *myObj in self.myArray)
{
// MAIN_QUEUE
[[NSOperationQueue mainQueue] addOperationWithBlock:^(void) {
[self updateUI:myObj];
}];
}
}];
[operationQueue addOperationWithBlock:^(void){
[[NSOperationQueue mainQueue] addOperationWithBlock:^(void) {
[self filterResults];
}];
}];
}
My dictionary contains 4 values, and therefore the ADDED shows in the log 4 times.
BUT,
when I check inside the filterResults, I see that there are only 2 objects inside myArray. Meaning that the 4 times the operationQueue was called did not end before the filterResults operation was called (although it was added later!)
I thought that the operationQueue is serial and that I can count on it that when I add an operation it would be added right after the last operation.
So it is weird that only 2 operations are in the array in the aftermath.
What am I missing? Thanks
From what you shared as your initialisation code we can learn that operationQueue is not serial, meaning it will execute operations, and allocate thread up until the system set maximal thread count (same as with GCD).
This mean that operations added to operationQueue are running in parallel.
To run them serially set the maxConcurrentOperationCount to 1.
Try something like:
__block __weak id weakSelf = self;
[operationQueue setMaxConcurrentOperationCount:1];
for (NSDictionary *Obj in myObjects)
{
[operationQueue addOperationWithBlock:^{
MyObject* tmp = [weakSelf tediousFunction:Obj];
// threadTempObjects is member NSMutableArray
[weakSelf.threadTempObjects addObject:tmp];
NSLog(#"ADDED");
}];
}
[operationQueue addOperationWithBlock:^{
[weakSelf.myArray addObjectsFromArray:weakSelf.threadTempObjects];
for(myObject *myObj in weakSelf.myArray)
{
// MAIN_QUEUE
[[NSOperationQueue mainQueue] addOperationWithBlock:^(void) {
[weakSelf updateUI:myObj];
}];
[[NSOperationQueue mainQueue] addOperationWithBlock:^(void) {
[weakSelf filterResults];
}];
}
}];
But, this is equal (or even less efficient) to simply:
__block __weak id weakSelf = self;
[operationQueue addOperationWithBlock:^{
for (NSDictionary *Obj in myObjects) {
MyObject* tmp = [weakSelf tediousFunction:Obj];
// threadTempObjects is member NSMutableArray
[[NSOperationQueue mainQueue] addOperationWithBlock:^(void) {
[weakSelf updateUI:tmp];
}];
[weakSelf.myArray addObject:tmp];
NSLog(#"ADDED");
}
[[NSOperationQueue mainQueue] addOperationWithBlock:^(void) {
[weakSelf filterResults];
}];
}];