Background Sync in iOS - ios

I have a Sync class which basically syncs the data to server in background.
The use of Sync class object is such that the View Controller from where I make a sync request on the Sync class object may get deallocated before the sync gets actually complete.
The problem I am facing here is the Sync class object gets deallocates too and hence the sync request never gets complete.
I thought of making Sync class as a Singleton but that would be more of abuse of the Singleton pattern.
How should I proceed?

You could keep strong pointer to the object before starting operation:
__block StubClass *strongSelf = self;
And start method on it:
[strongSelf dataSync: ^... { //completionBlock
strongSelf = nil;
}];
Or do it with semaphore:
__block StubClass *strongSelf = self;
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
[strongSelf dataSync: ^... {
dispatch_semaphore_signal(sem);
}];
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
strongSelf = nil;

Related

Crash with dispatch_block

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.

Weak and strong self usage, blocks memory management

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.")

NSOperation fails on execution

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).

Retain cycle with an operation queue

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.

Difference between NSOperationQueue: currentQueue/new?

I'm trying to figure out what is the difference between those 2.
Does it mean currentQueue cannot be mainQueue or it's a wrong assumption?
currentQueue return mainQueue if you call it in main-thread.
new is new queue with new threads
new is initializer method which NSOperationQueue inherits from NSObject
Documentation says:
This method is a combination of alloc and init. Like alloc, it initializes the isa instance variable of the new object so it points to the class data structure. It then invokes the init method to complete the initialization process.
This means calling +new creates new instance of NSOperationQueue
currentQueue returns you the queue on which the method was called.
NSOperationQueue docs of currentQueue says:
The operation queue that started the operation or nil if the queue could not be determined
I.e. if method calling +currentQueue is mainQueue, it can return mainQueue
prove of concept write by #Cy-4AH. Hop this can clarify a bit.
-(void) viewDidLoad {
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^(void){
//this block will executed in a separate thread (not the main thread)
if ([NSOperationQueue currentQueue] == [NSOperationQueue mainQueue]) {
NSLog(#"= in block");
} else {
NSLog(#"not = in block"); //This will be log
}
}];
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
[operationQueue addOperation:blockOperation];
//this block will executed in the main thread
if ([NSOperationQueue currentQueue] == [NSOperationQueue mainQueue]) {
NSLog(#"= outside of block"); //This will be log
} else {
NSLog(#"not = outside of block");
}
}

Resources