While reading a blog about concurrency in iOS, I stumbled upon the next code:
__weak id weakSelf = self;
[self.operationQueue addOperationWithBlock:^{
NSNumber* result = findLargestMersennePrime();
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
MyClass* strongSelf = weakSelf;
strongSelf.textLabel.text = [result stringValue];
}];
}];
The author explains that the use of weakref is needed since:
we need to make a weak reference to self, otherwise we create a retain
cycle (the block retains self, the private operation queue retains the
block, and self retains the operation queue). Within the block we
convert it back to a strong reference to make sure it doesn’t get
deallocated while running the block.
I can understand why the block would have retained self, but I don't understand why (and where exactly) the private operation queue retains the block and when/where self retaind the opeation queue. Any explanation would be much appreciated.
try to write this code without weak reference:
[self.operationQueue addOperationWithBlock:^{
NSNumber* result = findLargestMersennePrime();
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.textLabel.text = [result stringValue];
}];
}];
to make this code work - compiler retains reference to self in operationQueue to avoid situation when self is deleted from memory and self.textLabel.text = .. is executed, so it tries to guarantee that object will be alive.
This is where cycle is actually created:
self retains operationQueue (this means that operationQueue cannot be deleted while self is alive)
operationQueue retains self (this means that self cannot be deleted while operationQueue is alive)
to avoid this - you're creating week reference, so you're eliminating 2nd retention
PS. "block" is part of operationQueue, so we can assume just 2 items in this scheme.
Related
I have been trying to understand the reason behind this crash for the sake of understanding more about how blocks behave. I have a really simple class to trigger this crash.
#implementation BlockCrashTest
- (void)doSomething
{
dispatch_queue_t queue = dispatch_queue_create("com.queue.test", DISPATCH_QUEUE_SERIAL);
__weak typeof(self) weakSelf = self;
dispatch_block_t block = ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
dispatch_group_t group = dispatch_group_create();
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC);
dispatch_group_enter(group);
[strongSelf performSomethingAsync:^{
dispatch_group_leave(group);
}];
if(dispatch_group_wait(group, time) != 0) {
NSLog(#"group already finished");
}
};
dispatch_async(queue, block);
}
- (void)performSomethingAsync:(void(^)(void))completion
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(5);
completion();
});
}
- (void)dealloc
{
NSLog(#"released object");
}
#end
Now, if I allocate the class and simply call method doSomething to it,
BlockCrashTest *someObject = [[BlockCrashTest alloc] init];
[someObject doSomething];
It crashes with the exception, EXC_BAD_INSTRUCTION and following stack traces,
#0 0x000000011201119a in _dispatch_semaphore_dispose ()
#1 0x0000000112013076 in _dispatch_dispose ()
#2 0x0000000112026172 in -[OS_dispatch_object _xref_dispose] ()
#3 0x000000010ef4c2fd in __29-[BlockCrashTest doSomething]_block_invoke at /Users/Sandeep/Desktop/Test Block Crash/Test Block Crash/ViewController.m:35
#4 0x0000000112005ef9 in _dispatch_call_block_and_release ()
If I modify the method doSomething, such that it do not use weak but uses self then the crash do not occur and the methods seem to execute as expected,
- (void)doSomething
{
dispatch_queue_t queue = dispatch_queue_create("com.queue.test", DISPATCH_QUEUE_SERIAL);
dispatch_block_t block = ^{
dispatch_group_t group = dispatch_group_create();
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC);
dispatch_group_enter(group);
[self performSomethingAsync:^{
dispatch_group_leave(group);
}];
if(dispatch_group_wait(group, time) != 0) {
NSLog(#"group already finished");
}
};
dispatch_async(queue, block);
}
Why does it crash, my understanding is that using weak inside the block would make sure that the method would not be called, if the object is released and I thought that weak is safer than using self inside the block.
The above code works fine with weakSelf, if I retain the object BlockCrashTest and call the method to it.
I would be really happy if someone could explain the reason behind the crash and what exactly happens with these 3 different variants of code above that one crash and other seem to work fine.
Note: This is in one way or other related to the crash listed in
thread,
Objective-C crash on __destroy_helper_block_.
I have been able to reproduce the exact same stack traces with my code
above.
A couple of observations:
You cannot have a dispatch group with unbalanced "enter" and "leave" when the dispatch_group_t object is deallocated. And as ilya pointed out, because of your pattern, strongSelf is nil, so you're entering the group, but not leaving it.
A very common pattern in the weakSelf and strongSelf dance is to just check to see if strongSelf was nil or not, resolving the imbalance. Thus, if strongSelf is nil, it bypasses the dispatch group stuff altogether, but if it is not nil, both "enter" and "leave" will be called:
- (void)doSomething {
dispatch_queue_t queue = dispatch_queue_create("com.queue.test", DISPATCH_QUEUE_SERIAL);
typeof(self) weakSelf = self;
dispatch_async(queue, ^{
typeof(self) strongSelf = weakSelf;
if (strongSelf) {
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
[strongSelf performSomethingAsync:^{
dispatch_group_leave(group);
}];
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
}
});
}
Clearly, you must make sure that performSomethingAsync method, itself, always calls the block which leaves the group.
The other way of solving this (if you don't have assurances that all of the "enter" and "leave" will be balanced), is to use semaphores:
- (void)doSomething {
dispatch_queue_t queue = dispatch_queue_create("com.queue.test", DISPATCH_QUEUE_SERIAL);
typeof(self) weakSelf = self;
dispatch_async(queue, ^{
typeof(self) strongSelf = weakSelf;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[strongSelf performSomethingAsync:^{
dispatch_semaphore_signal(semaphore);
}];
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC);
if (dispatch_semaphore_wait(semaphore, time) != 0) {
NSLog(#"semaphore not received in time");
}
});
}
Frankly, even when using semaphores, like I have above, I still think one would want to check to confirm that strongSelf was not nil. Concurrent programming is confusing enough without adding the possibility of the message to a nil object resulting in an no-op.
- (void)doSomething {
dispatch_queue_t queue = dispatch_queue_create("com.queue.test", DISPATCH_QUEUE_SERIAL);
typeof(self) weakSelf = self;
dispatch_async(queue, ^{
typeof(self) strongSelf = weakSelf;
if (strongSelf) {
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[strongSelf performSomethingAsync:^{
dispatch_semaphore_signal(semaphore);
}];
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC);
if (dispatch_semaphore_wait(semaphore, time) != 0) {
NSLog(#"semaphore not received in time");
}
}
});
}
You'll get the same crash even if you remove call of self performSomethingAsync:. This crash caused by libdispatch semaphore API. You can see assembly trace of crashed function _dispatch_semaphore_dispose in Xcode:
If we try to figure out what happens in this code we'll see that you explicitly mark current block entered the group by calling dispatch_group_enter. Then performSomethingAsync doesn't call because strongSelf == nil. Which means that dispatch_group_enter is not balanced with dispatch_group_leave this cause the group couldn't dispose properly and crashed (see asm listing).
If you use self this code also crashed because dispatch_group_leave(group); called from the different thread with dispatch_group_enter Which is also cause the same crash but in another perspective: calls not balanced in the same thread. performSomethingAsync called completion block in different queue not in yours "com.queue.test".
This example is just wrong using of dispatch_groups APIs. To see how properly use it see apple doc.
MacOS 10.8 and iOS 6.0 introduced ARC to dispatch objects. From the documentation GCD Objects and Automatic Reference Counting:
When you build your app using the Objective-C compiler, all dispatch objects are Objective-C objects. As such, when automatic reference counting (ARC) is enabled, dispatch objects are retained and released automatically just like any other Objective-C object. When ARC is not enabled, use the dispatch_retain and dispatch_release functions (or Objective-C semantics) to retain and release your dispatch objects. You cannot use the Core Foundation retain/release functions.
If you need to use retain/release semantics in an ARC-enabled app with a later deployment target (for maintaining compatibility with existing code), you can disable Objective-C-based dispatch objects by adding -DOS_OBJECT_USE_OBJC=0 to your compiler flags.
In your case ARC is happily managing the life cycle of your dispatch_group_t. And, unfortunately, your code is causing the group to be released while the lock is still waiting. When the group times out it is released - so when dispatch_group_leave is called it crashes, as the group has already been released.
I would suggest at the very least checking wether the group is NULL before attempting to leave it.
Additionally, your wait result logic is reversed. A zero result indicates the group was emptied before the timeout, a non zero result indicates the timeout was hit.
why does it work when we pass the weak reference to a strong reference inside the block? If a local variable in a block is retained, this should add a retain to self and thus create this bad retain cycle?
Here is the example :
__weak id weakSelf = self;
[self.operationQueue addOperationWithBlock:^{
NSNumber* result = findLargestMersennePrime();
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
MyClass* strongSelf = weakSelf;
strongSelf.textLabel.text = [result stringValue];
}];
}];
When you create or copy a block (it could be copied when you, for example, schedule it to gcd), referenced variables are captured (unless declared with __block specifier). Strong references are retained, weak references are not.
When you create local strongSelf variable it keeps self alive while block executes (i.e. while it's not executed and sits in a property there's no strong reference). When you reference self directly - self is captured and retained, now it keeps self while block is alive.
__weak id weakSelf = self;
[self.operationQueue addOperationWithBlock:^{
NSNumber* result = findLargestMersennePrime();
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
MyClass* strongSelf = weakSelf; // strong reference when block executes
[self foo]; // strong reference when block created/copied
strongSelf.textLabel.text = [result stringValue];
}];
}];
See the difference? If you kill all strong pointers to object with direct self reference there is still one strong reference inside the block, the one which was captured and retained. At the same time local strongSelf pointer only holds strong reference to self while block is executed, so, if self was already dead, weakSelf would be nil and strongSelf will get nil value.
no it doesn't create a cycle since self isn't captured as strong! :)
strongSelf is a strong reference that retains self BUT since strongSelf is a local var, it is released when the block is done and the retain count drops fine
There are a bunch of questions about all these weak and strong selves but I want that you guys took a look at my particular example:
- (void)getItemsWithCompletionHandler:(void (^)(NSArray*items))completionHandler {
__weak __typeof__(self) weakSelf = self;
[self doWorkWithCompletionHandler:^(Response *response) {
// this completion is not on main thread
dispatch_async(dispatch_get_main_queue(), ^{
...
[weakSelf doAnotherWorkWithCompletionHandler:^(Response *response) {
// this completions is not on main thread either
dispatch_async(dispatch_get_main_queue(), ^{
__typeof__(self) strongSelf = weakSelf;
NSArray *itemsIds = [strongSelf doWorkOnMainThread1];
NSArray *items = [strongSelf doWorkOnMainThread2];
completionHandler(items);
});
}];
});
}];
}
Is everything correct here or not? Also you are welcome to suggest a refactoring
If you turn all warnings on, then you will get a warning for
[weakSelf doAnotherWorkWithCompletionHandler... ];
You shouldn't send messages to weak objects. A weak object could disappear while the method that is called is running. Store the weak object into a strong one, the result is either nil or not. If you then call
[strongSelf doAnotherWorkWithCompletionHandler... ];
you know that either strongSelf == nil and nothing happens, or strongSelf is and stays not nil while the method is executing.
You should check if completionHandler is not NULL and then call it.
if (completionHandler) {
completionHandler(items);
}
Otherwise you'll crash if completionHandler is NULL
You may also reconsider if you want to call completionHandler(items) at all if self == nil at any point. I say that, because there is a slight inconsistency.
In line
[weakSelf doAnotherWorkWithCompletionHandler:^(Response *response) {
if weakSelf is nil already, then it's completionHandler will not be called and in result completionHandler(items) will also not be called.
But in here:
__typeof__(self) strongSelf = weakSelf;
NSArray *itemsIds = [strongSelf doWorkOnMainThread1];
NSArray *items = [strongSelf doWorkOnMainThread2];
completionHandler(items);
if self == nil then completionHandler will in fact be called.
Of course I don't see the whole picture and maybe it's completely irrelevant, but something you might want to take under consideration.
Going further, I assume you'd rather have completionHandler called in each and every scenario, even if self == nil at any point. Or add an error parameter to it in case of anything going wrong.
And if you want to be super pedantic, you might want to put __weak after the type, as such:
__typeof__(self) __weak weakSelf = self;
It's the preferred way: https://developer.apple.com/library/ios/releasenotes/objectivec/rn-transitioningtoarc/Introduction/Introduction.html#//apple_ref/doc/uid/TP40011226-CH1-SW4 (look for "You should decorate variables correctly.")
I have problem with NSOperations. Everything works fine but sometimes (I don't know why) Operation block is simply skipped. Am I missing something? How is it possible that operation is not even NSLogging "operation entered"? Here is some code from viewDidLoad:
//I'm using weakOperation in order to make [self.queue cancelAllOperation] method when viewWillDisappear
NSBlockOperation* operation = [[NSBlockOperation alloc] init];
__weak NSBlockOperation* weakOperation = operation;
NSString *session=#"";
#try{
session = [self getSessionId];//getting data from CoreData
}#catch(NSException *e)
{
NSLog(#"EXCEPTION WITH SESSION");
}
weakOperation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(#"operation entered");
[self downloadJSONArray]; //doing some connection downloading and using session
[self downloadImages]; //downloading images from urls from JSONs
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self refresh]; //update mainThread
}
}
[self.queue addOperation:weakOperation];
What could be scenario that coul make skip this block ?
Is there max number of threads created in iOS?
EDIT: Hey, I'have found why this happends - when a lot of applications run in the background and iOS does not have resources to queue another thread it simply skips that, how to behave in this situation?
You are assigning a new NSBlockOperation to a weak variable. Whenever you assign a new object to a weak variable, you risk having it released immediately.
If you needed a weak reference to the operation, you'd assign the object to some local variable first, and then get the weak reference for that object:
NSBlockOperation* operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(#"operation entered");
[self downloadJSONArray]; //doing some connection downloading and using session
[self downloadImages]; //downloading images from urls from JSONs
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self refresh]; //update mainThread
}
}
__weak NSBlockOperation* weakOperation = operation;
[self.queue addOperation:weakOperation];
But, as the method stands, the weakOperation is unnecessary. You generally only need weak references to avoid strong reference cycles. But no such cycle is present currently, so you can just do:
NSBlockOperation* operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(#"operation entered");
[self downloadJSONArray]; //doing some connection downloading and using session
[self downloadImages]; //downloading images from urls from JSONs
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self refresh]; //update mainThread
}
}
[self.queue addOperation:operation];
Looking at your code comment, you say "I'm using weakOperation in order to make [self.queue cancelAllOperation] method when viewWillDisappear". Using weakOperation like this will not accomplish what you want because your operation is not checking to see if it was canceled and thus it will not respond when the NSOperationQueue tries to cancel it.
If you wanted to do that, then a variation on your weakOperation pattern can be useful, but rather than using this weakOperation to add it to the queue, you can use the weak reference within the block to check to see if the operation was canceled (and you want the weak reference in the block to avoid the block from retaining the operation, itself, causing a strong reference cycle). The other key observation is that rather than creating a new NSBlockOperation, simply add an execution block to the original operation you created:
NSBlockOperation* operation = [[NSBlockOperation alloc] init];
__weak NSBlockOperation* weakOperation = operation;
[operation addExecutionBlock:^{
NSLog(#"operation entered");
if ([weakOperation isCancelled]) return;
[self downloadJSONArray]; //doing some connection downloading and using session
if ([weakOperation isCancelled]) return;
[self downloadImages]; //downloading images from urls from JSONs
if ([weakOperation isCancelled]) return;
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self refresh]; //update mainThread
}];
}];
[self.queue addOperation:operation];
Clearly, if the operation is tied up in downloadJSONArray or downloadImages, it won't respond to the cancelation event until it returns from those methods. You'd have to check the cancelation status with those methods, too, if you want this operation to respond reasonably quickly to the cancellation event.
In answer to your second question, yes, there is a maximum number of threads, but it's a reasonably large number and there are other factors that come into play before the number of threads becomes an issue. The constraining factor is likely to be the downloadImages method (as you can only have 5 concurrent download requests). And even if that wasn't an issue, you'd want to constrain the number of concurrent operations, anyway, to mitigate the app's peak memory usage. If there are any network operations involved, you generally want to do something like:
self.queue.maxConcurrentOperationCount = 4; // or 5
That way, you minimize how much of the limited system resources (including threads) you are using.
By the way, I assume that downloadJSONArray and downloadImages are synchronous methods. If those are performing asynchronous network requests, you might want to consider further refactoring of the code to ensure the operation doesn't complete prematurely (e.g. wrap this in a concurrent NSOperation subclass or change those methods to run synchronously).
This question already has answers here:
capturing self strongly in this block is likely to lead to a retain cycle
(7 answers)
Closed 9 years ago.
I have reqest with block. But the compiler issues a warning
"Capturing 'self' strongly in this block is likely to lead to a retain
cycle"
__weak typeof(self) weakSelf = self;
[generalInstaImage setImageWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:data[#"images"][#"low_resolution"][#"url"]]] placeholderImage:[UIImage imageNamed:#"Default"] success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
NSLog(#"success");
[generalInstaImage setImage: image];
[weakSelf saveImage:generalInstaImage.image withName:data[#"id"]];
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
NSLog(#"fail");
}];
I try example write like weakSelf.generalInstaImage, but then the compiler generates an error and do not compile.
Consider this warning:
Capturing self strongly in this block is likely to lead to a retain
cycle
When you receive the above warning, you should review your block for:
any explicit references to self; or
any implicit references to self caused by referencing any instance variables.
Let's imagine that we have some simple class property that was a block (this will experience the same "retain cycle" warnings as your question, but will keep my examples a little simpler):
#property (nonatomic, copy) void (^block)(void);
And let's assume we had some other class property we wanted to use inside our block:
#property (nonatomic, strong) NSString *someString;
If you reference self within the block (in my example below, in process of accessing this property), you will obviously receive that warning about the retain cycle risk:
self.block = ^{
NSLog(#"%#", self.someString);
};
That is remedied via the pattern you suggested, namely:
__weak typeof(self) weakSelf = self;
self.block = ^{
NSLog(#"%#", weakSelf.someString);
};
Less obvious, you will also receive the "retain cycle" warning if you reference an instance variable of the class inside the block, for example:
self.block = ^{
NSLog(#"%#", _someString);
};
This is because the _someString instance variable carries an implicit reference to self, and is actually equivalent to:
self.block = ^{
NSLog(#"%#", self->_someString);
};
You might be inclined to try to adopt weak self pattern here, too, but you can't. If you attempt the weakSelf->_someString syntax pattern, the compiler will warn you about this:
Dereferencing a __weak pointer is not allowed due to possible null value caused by race condition, assign it to strong variable first
You therefore resolve this by using the weakSelf pattern, but also create a local strong variable within the block and use that to dereference the instance variable:
__weak typeof(self) weakSelf = self;
self.block = ^{
__strong typeof(self) strongSelf = weakSelf;
if (strongSelf) {
NSLog(#"%#", strongSelf->_someString);
// or better, just use the property
//
// NSLog(#"%#", strongSelf.someString);
}
};
As an aside, this creation of a local strong reference, strongSelf, inside the block has other advantages, too, namely that if the completion block is running asynchronously on a different thread, you don't have to worry about self being deallocated while the block is executing, resulting in unintended consequences.
This weakSelf/strongSelf pattern is very useful when dealing with block properties and you want to prevent retain cycles (aka strong reference cycles), but at the same time ensuring that self cannot be deallocated in the middle of the execution of the completion block.
FYI, Apple discusses this pattern in the "non-trivial cycles" discussion further down in the Use Lifetime Qualifiers to Avoid Strong Reference Cycles section of the Transitioning to ARC Release Notes.
You report that you received some "error" when you referenced weakSelf.generalInstaImage in your example. This is the correct way to resolve this "retain cycle" warning, so if you received some warning, you should share that with us, as well as show us how you declared the property.
Use __unsafe_unretained typeof(self) weakSelf = self