I would like to make a function that only run once and cancel if is still running.
I tried it using a simple lock boolean on start/end, but sometimes it's "overlapping".
There's a better and secure way to do that?
#property (assign) BOOL lock;
- (void)myFuntion
{
if (self.lock) {
NSLog(#"(Canceled) Syncing is already running...");
return;
}
self.lock = YES;
// My Code
self.lock = NO;
}
The NSLock class should be able to help you here. I have not tried this example directly, but something like:
NSLock *myFunctionLock=[NSLock new]; // this should be a class data member/property/etc.
- (void)myFuntion
{
if (![myFunctionLock tryLock])
return; /* already running */
// My Synchronized Code
[myFunctionLock unlock];
}
We are all assuming that you are talking about concurrent programming, where you are running the same code on different threads. If that's not what you mean then you would need to explain what you DO mean, since code that runs on the same thread can only execute a function once at any particular moment.
Take a look at NSLock's tryLock function. The first caller to assert the lock gets back a TRUE, and can proceed to access the critical resource. Other callers get back FALSE and should not access the critical resource, but won't block.
Related
I would like to understand what is the correct behavior for an NSOperation subclass.
I have my subclasses with different isReady conditions. Yes, I check in the code if the operation is cancelled and I act in consequence. This is great while the operation is in execution. It stops its task, sets finished to true and it gets deleted from the queue. But what about its dependencies? They are not yet executing, so they stay in the NSOperationQueue in cancelled state forever.
Should I set ready = true for cancelled operations so the queue will call the start method that will set in executing and immediately finish the task setting finished to true?
The dependent operation will be executed. No matter a is cancelled or not.
You should set operation.finished==true at the final state to make NSOperationQueue removes the operation.
You should set .ready==true whenever it's ready to begin the operation.
In life cycle of operation, you should frequently check .cancelled whenever .cancelled == true, you should stop operation and set .finished == true.
I just dealt with this problem myself. I could not understand why when I cancelled my NSOperation subclass, it was never removed from the NSOperationQueue. It turns out that my overridden isReady method was to blame. The isReady method was checking the super implementation AND whether a property had been set, which it hadn't, which is why the operation was getting cancelled. However, apparently if an NSOperation instance never reaches the ready state, even if it is cancelled, the NSOperationQueue will not remove that operation until is has reached the ready state. Hence, the solution is to OR your custom conditions for readiness with the isCancelled property value. Alternatively, you can change the logic such that the operation will always be ready, but do nothing in the main method if your conditions for readiness are not met, depending on your application logic.
Example:
// for proper KVO compliance
+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key
{
NSSet *keys = [super keyPathsForValuesAffectingValueForKey:key];
NSString *readyKey = NSStringFromSelector(#selector(isReady));
if ([readyKey isEqualToString:key]) {
keys = [keys setByAddingObjectsFromArray:#[NSStringFromSelector(#selector(url)), NSStringFromSelector(#selector(isCancelled))]];
}
return keys;
}
- (BOOL)isReady
{
return (this.url != nil || this.isCancelled) && super.isReady;
}
- (void)setUrl:(NSURL *)url
{
if (self.url == url) {
return;
}
NSString *urlKey = NSStringFromSelector(#selector(url));
[self willChangeValueForKey:urlKey];
_url = url;
[self didChangeValueForKey:urlKey];
}
Thanks to NSHipster's KVO article for the above KVO-related code.
I'm new to iOS development and trying to solve following problem.
In my app (which speaks with REST API) I want to make initial request to server on app start to get user info. I decided to use separate service class with singleton method. It makes request to server once and then returns user instance.
#implementation LSSharedUser
+ (LSUser *)getUser {
// make request to api server on the first call
// on other calls return initialized user
static LSUser *_sharedUser = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
LSHTTPClient *api = [LSHTTPClient create];
[api getUser:^(AFHTTPRequestOperation *operation, id user) {
_sharedUser = [[LSUser alloc] initWithDictionary:user];
} failure:nil];
});
return _sharedUser;
}
#end
My question is it a proper way of initializing global data from server? As you see request is async (with AFNetworking lib) so it will return null until request is finished.
Another problem here is that once it failed (bad connection for example) user will be null forever.
update your code like this
static LSUser *_sharedUser ;
+ (LSUser *)getUser {
// make request to api server on the first call
// on other calls return initialized user
if(_sharedUser){
//this will execute only at first time
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
LSHTTPClient *api = [LSHTTPClient create];
[api getUser:^(AFHTTPRequestOperation *operation, id user) {
_sharedUser = [[LSUser alloc] initWithDictionary:user];
return _sharedUser;
} failure:nil];
});
}
//this will execute 2nd time
return _sharedUser;
}
for answer to line ->
Ques 2 )Another problem here is that once it failed (bad connection for example) user will be null forever.
->once _sharedUser is initialized user will get _sharedData. but until shared data is not initialized it will return null whenever called.
Ques 1 )My question is it a proper way of initializing global data from server? As you see request is async (with AFNetworking lib) so it will return null until request is finished.
a better way is to implement your own custom delegate methods. once request is fetched or when call that do your work in that delegate method.
for 1st time: execute calling delegate methods when request is fetched or failed.
for 2nd time: after if block call delegate methods.
The basic approach requires an asynchronous design.
Say you have an asynchronous method:
- (void) loadUserWithCompletion:(void (^)(NSDictionary* user, NSError* error))completion;
You execute whatever you need to execute in the "Continuation" (the completion block):
[self loadUserWithCompletion:^(NSDictionary* params, NSError*error) {
if (user) {
User* user = [[User alloc] initWithDictionary:params];
// better we ensure we execute the following on the main thread:
dispatch_async(dispatch_get_main_queue(), ^{
self.model.user = user;
// If we utilize KVO, observers may listen on self.model.user
// which now get a notification.
// We also may to notify the user through an alert:
...
});
}
else {
// handler error
}
}];
With the asynchronous programming style you need to be more carefully when letting the user execute arbitrary tasks (say, tabbing buttons). You may consider to disable tasks which require a user model. Alternatively, display an alert sheet when the user model is not yet available.
Usually, you use some "observer" technique to get notified when the user model is eventually available. You may use KVO or completion handlers, or use some dedicated third party library which is specialized for those problems (e.g. a Promise library).
You should also let the user cancel that task at any point. This however requires a more elaborated approach, where you have "cancelable" tasks and where you hold references to these tasks in order to able to send them a cancel message.
I have a really heavy task to perform, and I dont want to block the main thread. So I'm starting a separate Concurrent queue for it. There can be 4 instances of the said task.
-(dispatch_queue_t)getConcurrentQueue
{
if(concurrentQueue == nil)
{
concurrentQueue = dispatch_queue_create("com.myself.HeavyTask", DISPATCH_QUEUE_CONCURRENT);
}
return concurrentQueue;
}
Now to start the heavy task I have -
-(void)beginTask
{
//.....
//.....
__weak typeof(self) weakSelf = self;
dispatch_queue_t queue = [self getConcurrentQueue];
dispatch_async(queue, ^{
[weakSelf heavyTask];
});
}
Now the method heavyTask goes like this --
-(void)heavyTask
{
//...
dispatch_sync(dispatch_get_current_queue(), ^{
// Initialising code for heavy task
// This is the critical section. Only one of the 4 concurrent threads can enter this at a time
}
//....
while(condition)
{
// Perform meat of the task
}
//...
dispatch_sync(dispatch_get_current_queue(), ^{
// Teardown code. Freeing memory etc.
// This is also a critical section.
}
//...
}
The initialising code and teardown code uses some 3rd party C methods which are not thread safe. So making them thread safe is not the scope of the question.
Now I've put the "initialising code" and and the "teardown code" within -
dispatch_sync(dispatch_get_current_queue(), ^{
}
My code is crashing and I'm getting error messages stating that there is insufficient thread locking around the critical section code.
I read that dispatch_get_current_queue() is not safe, so I replaced it with concurrentQueue. I also tried replacing with dispatch_get_main_queue(). Still the code crashes, complaining about insufficient thread locking.
I know there is something wrong in my understanding of implementing critical section using GCD.
Can anyone clearly show me how to make my code work properly here??
Side question -- Can I use #synchronized { } blocks here??
There's a lot wrong with your code, including not adhering to naming conventions.
So basically, if you want to execute the same task concurrently with respect to each other, use the global concurrent queue to execute those tasks.
If you want to concurrently access shared resources from within these tasks (or from elsewhere), define a dedicated queue, say "sync_queue" where you exclusively access these resources. This "sync_queue" executes your "critical sections".
The "sync_queue" can be serial or concurrent.
If you use a serial queue, use dispatch_async(sync_queue, block) for write access and dispatch_sync(sync_queue, block) for read access to shared resources.
If you use a concurrent queue, use dispatch_barrier_async(sync_queue, block) for write access and dispatch_barrier_sync(sync_queue, block) for read access to shared resources.
Example:
// Read access using a serial sync_queue:
...
__block int counter;
dispatch_sync(sync_queue, ^{
counter = _counter;
});
// Write access using a serial sync_queue:
...
dispatch_async(sync_queue, ^{
_counter = counter;
});
// Read access using a concurrent sync_queue:
...
__block int counter;
dispatch_barrier_sync(sync_queue, ^{
counter = _counter;
});
// Write access using a concurrent sync_queue:
...
dispatch_barrier_async(sync_queue, ^{
_counter = counter;
});
Example for your "heavy task":
-(void)heavyTask
{
dispatch_barrier_async(sync_queue, ^{
// Initialize heavy task
...
// Continue with the task:
dispatch_async(dispatch_get_global_queue(0,0), ^{
BOOL condition = YES; // condition must be local to the block (it's not a shared resource!)
while(condition)
{
// Perform meat of the task
condition = ...;
}
dispatch_barrier_async(sync_queue, ^{
// Teardown code. Freeing memory etc.
// This is also a critical section.
...
}
});
}
}
You called it "getSerialQueue" but really you are creating a "concurrent" queue in it. Try to fix it substituting DISPATCH_QUEUE_CONCURRENT with DISPATCH_QUEUE_SERIAL in getSerialQueue.
Keep in mind that:
dispatch_sync means: I will wait here until this block finishes
dispatch_async means: I will not wait
This is not related to concurrent or serial. If two tasks in a concurrent queue call dispatch_sync(block), 'block' will be executed concurrently.
Hope this helps.
I wanted to be able to serialize 'genuinely' async methods, for example:
making a web request
showing a UIAlertView
This is typically a tricky business and most samples of serial queues show a 'sleep' in an NSBlockOperation's block. This doesn't work, because the operation is only complete when the callback happens.
I've had a go at implementing this by subclassing NSOperation, here's the most interesting bits of the implementation:
+ (MYOperation *)operationWithBlock:(CompleteBlock)block
{
MYOperation *operation = [[MYOperation alloc] init];
operation.block = block;
return operation;
}
- (void)start
{
[self willChangeValueForKey:#"isExecuting"];
self.executing = YES;
[self didChangeValueForKey:#"isExecuting"];
if (self.block) {
self.block(self);
}
}
- (void)finish
{
[self willChangeValueForKey:#"isExecuting"];
[self willChangeValueForKey:#"isFinished"];
self.executing = NO;
self.finished = YES;
[self didChangeValueForKey:#"isExecuting"];
[self didChangeValueForKey:#"isFinished"];
}
- (BOOL)isFinished
{
return self.finished;
}
- (BOOL) isExecuting
{
return self.executing;
}
This works well, here's a demonstration...
NSOperationQueue *q = [[NSOperationQueue alloc] init];
q.maxConcurrentOperationCount = 1;
dispatch_queue_t queue = dispatch_queue_create("1", NULL);
dispatch_queue_t queue2 = dispatch_queue_create("2", NULL);
MYOperation *op = [MYOperation operationWithBlock:^(MYOperation *o) {
NSLog(#"1...");
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(#"1");
[o finish]; // this signals we're done
});
}];
MYOperation *op2 = [MYOperation operationWithBlock:^(MYOperation *o) {
NSLog(#"2...");
dispatch_async(queue2, ^{
[NSThread sleepForTimeInterval:2];
NSLog(#"2");
[o finish]; // this signals we're done
});
}];
[q addOperations:#[op, op2] waitUntilFinished:YES];
[NSThread sleepForTimeInterval:5];
Note, I also used a sleep but made sure these were executing in background thread to simulate a network call. The log reads as follows
1...
1
2...
2
Which is as desired. What is wrong with this approach? Are there any caveats I should be aware of?
"Serializing" asynchronous tasks will be named actually "continuation" (see also this wiki article Continuation.
Suppose, your tasks can be defined as an asynchronous function/method with a completion handler whose parameter is the eventual result of the asynchronous task, e.g.:
typedef void(^completion_handler_t)(id result);
-(void) webRequestWithCompletion:(completion_handler_t)completionHandler;
-(void) showAlertViewWithResult:(id)result completion:(completion_handler_t)completionHandler;
Having blocks available, a "continuation" can be easily accomplished through invoking the next asynchronous task from within the previous task's completion block:
- (void) foo
{
[self webRequestWithCompletion:^(id result) {
[self showAlertViewWithResult:result completion:^(id userAnswer) {
NSLog(#"User answered with: %#", userAnswer);
}
}
}
Note that method foo gets "infected by "asynchrony" ;)
That is, here the eventual effect of the method foo, namely printing the user's answer to the console, is in fact again asynchronous.
However, "chaining" multiple asynchronous tasks, that is, "continuing" multiple asynchronous tasks, may become quickly unwieldy:
Implementing "continuation" with completion blocks will increment the indentation for each task's completion handler. Furthermore, implementing a means to let the user cancel the tasks at any state, and also implement code to handle the error conditions, the code gets quickly confusing, difficult to write and difficult to understand.
A better approach to implement "continuation", as well as cancellation and error handling, is using a concept of Futures or Promises. A Future or Promise represents the eventual result of the asynchronous task. Basically, this is just a different approach to "signal the eventual result" to the call site.
In Objective-C a "Promise" can be implemented as an ordinary class. There are third party libraries which implement a "Promise". The following code is using a particular implementation, RXPromise.
When utilizing such a Promise, you would define your tasks as follows:
-(Promise*) webRequestWithCompletion;
-(Promise*) showAlertViewWithResult:(id)result;
Note: there is no completion handler.
With a Promise, the "result" of the asynchronous task will be obtained via a "success" or an "error" handler which will be "registered" with a then property of the promise. Either the success or the error handler gets called by the task when it completes: when it finishes successfully, the success handler will be called passing its result to the parameter result of the success handler. Otherwise, when the task fails, it passes the reason to the error handler - usually an NSError object.
The basic usage of a Promise is as follows:
Promise* promise = [self asyncTasks];
// register handler blocks with "then":
Promise* handlerPromise = promise.then( <success handler block>, <error handler block> );
The success handler block has a parameter result of type id. The error handler block has a parameter of type NSError.
Note that the statement promise.then(...) returns itself a promise which represents the result of either handler, which get called when the "parent" promise has been resolved with either success or error. A handler's return value may be either an "immediate result" (some object) or an "eventual result" - represented as a Promise object.
A commented sample of the OP's problem is shown in the following code snippet (including sophisticated error handling):
- (void) foo
{
[self webRequestWithCompletion] // returns a "Promise" object which has a property "then"
// when the task finished, then:
.then(^id(id result) {
// on succeess:
// param "result" is the result of method "webRequestWithCompletion"
return [self showAlertViewWithResult:result]; // note: returns a promise
}, nil /*error handler not defined, fall through to the next defined error handler */ )
// when either of the previous handler finished, then:
.then(^id(id userAnswer) {
NSLog(#"User answered with: %#", userAnswer);
return nil; // handler's result not used, thus nil.
}, nil)
// when either of the previous handler finished, then:
.then(nil /*success handler not defined*/,
^id(NEError* error) {
// on error
// Error handler. Last error handler catches all errors.
// That is, either a web request error or perhaps the user cancelled (which results in rejecting the promise with a "User Cancelled" error)
return nil; // result of this error handler not used anywhere.
});
}
The code certainly requires more explanation. For a detailed and a more comprehensive description, and how one can accomplish cancellation at any point in time, you may take a look at the RXPromise library - an Objective-C class which implements a "Promise". Disclosure: I'm the author of RXPromise library.
At a first glance this would work, some parts are missing to have a "proper" NSOperation subclass though.
You do not cope with the 'cancelled' state, you should check isCancelled in start, and not start if this returns YES ("responding to the cancel command")
And the isConcurrent method needs to be overridden too, but maybe you omitted that for brevity.
When subclassing NSOperation I would strongly suggest only overriding main unless you really know what you are doing as it is really easy to mess up thread safety. While the documentation says that the operation will not be concurrent the act of running them through an NSOperationQueue automatically makes them concurrent by running them on a separate thread. The non-concurrency note only applies if you call the start method of the NSOperation yourself. You can verify this by noting the thread ID that each NSLog line contains. For example:
2013-09-17 22:49:07.779 AppNameGoesHere[58156:ThreadIDGoesHere] Your log message goes here.
The benefit of overriding main means that you don't have to deal with thread safety when changing the state of the operation NSOperation handles all of that for you. The main thing that is serializing your code is the line that sets maxConcurrentOperationCount to 1. This means each operation in the queue will wait for the next to run (all of them will run on a random thread as determined by the NSOperationQueue). The act of calling dispatch_async inside each operation also triggers yet another thread.
If you are dead set on using subclassing NSOperation then only override main, otherwise I would suggest using NSBlockOperation which seems like what you are somewhat replicating here. Really though I would avoid NSOperation altogether, the API is starting to show its age and is very easy to get wrong. As an alternative I would suggest something like RXPromise or my own attempt at solving this problem, FranticApparatus.
This has been a hard one to search.
I found a similar question, iOS 5 Wait for delegate to finish before populating a table?, but the accepted answer was 'Refresh the table view,' and that does not help me. The other results I found tended to be in c#.
I have an app that streams from iPhone to Wowza servers. When the user hits record, I generate a unique device id, then send it to a PHP script on the server that returns a JSON document with configuration settings (which includes the rtmp dump link).
The problem is, the delegate methods are asynchronous, but I need to get the config settings before the next lines of code in my - (IBAction)recordButtonPressed method, since that code is what sets the profile settings, and then records based on those settings.
I've realized I could make the NSURLConnection in -recordButtonPressed like I am currently, and then continue the setup code inside the delegate method connectionDidFinishLoading (or just encapsulate the setup and method call it from there) but that's sacrificing coherent design for functionality and that sucks.
Is there not some simple waitUntilDelegateIsFinished:(BOOL)nonAsyncFlag flag I can send to the delegator so I can have sequential operations that pull data from the web?
I've realized I could make the NSURLConnection in -recordButtonPressed like I am currently, and then continue the setup code inside the delegate method connectionDidFinishLoading (or just encapsulate the setup and method call it from there) but that's sacrificing coherent design for functionality and that sucks.
You have analyzed and understood the situation and you have described its possible solutions perfectly. I just don't agree with your conclusions. This kind of thing happens all the time:
- (void) doPart1 {
// do something here that will eventually cause part2 to be called
}
- (void) doPart2 {
}
You can play various games with invocations to make this more elegant and universal, but my advice would be, don't fight the framework, as what you're describing is exactly the nature of being asynchronous. (And do not use a synchronous request on the main thread, since that blocks the main thread, which is a no-no.)
Indeed, in an event-driven framework, the very notion "wait until" is anathema.
Why not to use synchronous request?
Wrap your asynchronous NSURLConnection request in a helper method which has a completion block as a parameter:
-(void) asyncDoSomething:(void(^)(id result)completionHandler ;
This method should be implemented in the NSURLConnectionDelegate. For details see the example implementation and comments below.
Elsewhere, in your action method:
Set the completion handler. The block will dispatch further on the main thread, and then perform anything appropriate to update the table data, unless the result was an error, in which case you should display an alert.
- (IBAction) recordButtonPressed
{
[someController asyncConnectionRequst:^(id result){
if (![result isKindOfClass:[NSError class]]) {
dispatch_async(dispatch_get_main_queue(), ^{
// We are on the main thread!
someController.tableData = result;
});
}
}];
}
The Implementation of the method asyncConnectionRequst: could work as follows: take the block and hold it in an ivar. When it is appropriate call it with the correct parameter. However, having blocks as ivars or properties will increase the risk to inadvertently introduce circular references.
But, there is a better way: a wrapper block will be immediately dispatched to a suspended serial dispatch queue - which is hold as an ivar. Since the queue is suspended, they will not execute any blocks. Only until after the queue will be resumed, the block executes. You resume the queue in your connectionDidFinish: and connectionDidFailWithError: (see below):
In your NSURLConnectionDelegate:
-(void) asyncConnectionRequst:(void(^)(id result)completionHandler
{
// Setup and start the connection:
self.connection = ...
if (!self.connection) {
NSError* error = [[NSError alloc] initWithDomain:#"Me"
code:-1234
userInfo:#{NSLocalizedDescriptionKey: #"Could not create NSURLConnection"}];
completionHandler(error);
});
return;
}
dispatch_suspend(self.handlerQueue); // a serial dispatch queue, now suspended
dispatch_async(self.handlerQueue, ^{
completionHandler(self.result);
});
[self.connection start];
}
Then in the NSURLConnectionDelegate, dispatch a the handler and resume the
handler queue:
- (void) connectionDidFinishLoading:(NSURLConnection*)connection {
self.result = self.responseData;
dispatch_resume(self.handlerQueue);
dispatch_release(_handlerQueue), _handlerQueue = NULL;
}
Likewise when an error occurred:
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
self.result = error;
dispatch_resume(self.handlerQueue);
dispatch_release(_handlerQueue), _handlerQueue = NULL;
}
There are even better ways, which however involve a few more basic helper classes which deal with asynchronous architectures which at the end of the day make your async code look like it were synchronous:
-(void) doFourTasksInAChainWith:(id)input
{
// This runs completely asynchronous!
self.promise = [self asyncWith:input]
.then(^(id result1){return [self auth:result1]);}, nil)
.then(^(id result2){return [self fetch:result2];}, nil)
.then(^(id result3){return [self parse:result3];}, nil)
.then(^(id result){ self.tableView.data = result; return nil;}, ^id(NSError* error){ ... })
// later eventually, self.promise.get should contain the final result
}