I have a long series of events that needs to happen in my Objective-C code. Lets say I have 6 things - thingA, thingB, thingC, thingD, thingE and thingF. thingB and thingD return a BOOL. If thingB is NO, then thingC doesn't need to be called. If thingD is NO, then thingE doesn't need to be called.
- (void)doThings:(void(^)())completion {
[self thingA: ^{
[self thingB: ^(BOOL success) {
if (success) {
[self thingC: ^{
[self thingD: ^(BOOL success) {
if (thingD) {
[self thingE: ^{
[self thingF];
completion();
}];
return;
}
[self thingF];
completion();
}];
}];
return;
}
[self thingD: ^(BOOL success) {
if (success) {
[self thingE: ^{
[self thingF];
completion();
}];
return;
}
[self thingF];
completion();
}];
}];
}];
}
This can quickly become unwieldy. You can take the things that have different outcomes but lead back into the loop, and make them into new methods, as such:
- (void)doThings:(void(^)())completion {
[self thingA: ^{
[self attemptThingB: ^{
[self attemptThingD: ^{
[self thingF];
completion();
}]
}];
}]
}
- (void)attemptThingB:(void(^)())completion {
[self thingB: ^(BOOL success) {
if (success) {
[self thingC: {
completion();
}];
return;
}
completion();
}];
}
- (void)attemptThingD:(void(^)())completion {
[self thingD: ^(BOOL success) {
if (success) {
[self thingE: ^{
completion();
}];
return;
}
completion();
}];
}
This reduces code duplication, but is still messy and difficult to keep track of. It even results in methods with awkward names, which are really just helper methods to this particular case.
There must be a better way. Something that looks a lot like synchronous coding, but is asynchronous. The above code is difficult to read, which makes it dangerous if I ever want to add something new to the flow.
Suggestions of a better solution? Something like this?
- (void)doThings {
[self thingA];
BOOL thingBSuccess = [self thingB];
if (thingBSuccess == YES) {
[self thingC];
}
BOOL thingDSuccess = [self thingD];
if (thingDSuccess == YES) {
[self thingE];
}
[self thingF];
return;
}
The immediately evident problem with that proposed solution is that the completion handler can pass out multiple objects, whereas the return value of a block can only handle 1 object. But something with a format similar to this? It's so much cleaner and easy to maintain.
I think you've discovered dispatch groups!
there are 1000s articles on them, no need to pointlessly paste something in here,
Wait until multiple networking requests have all executed - including their completion blocks
cheers!
On a simpler level, it's possible what you're looking for is just simply "breakaway code" which is a critical part of writing, simply, tidy code. Note that this is not always the solution, but often - also, I'm not 100% clear on what you're asking. But in break away code you go like this ...
{
do something;
if ( failure, and you don't want to do any more ) return;
do some other important thing;
if ( failure of that thing, and you don't want to do any more ) return;
do yet another routine here;
if ( some other failed conditions, and you don't want to do any more ) return;
}
Here's a typical example. Often you will see code something like this...
-(void)loadNameInToTheCell
{
if ( self.showname != nil && [self.showname notLike:#"blah"] && kount<3 )
{
// many many lines of code here
// many many lines of code here
// many many lines of code here
}
}
You with me? But it's much better to write it like this:
-(void)loadNameInToTheCell
{
if ( self.showname == nil ) return;
if ( [self.showname notLike:#"blah"] return;
if ( kount<3 ) return;
// many many lines of code here
// many many lines of code here
// many many lines of code here
}
makes sense?
Note that critically, in any real project it's about the documentation not the code, so you can properly discuss and comment on that ...
-(void)loadNameInToTheCell
{
if ( self.showname == nil ) return;
// don't forget Steve's routine could return nil
if ( [self.showname notLike:#"blah"] return;
// should we worry about caps here?
if ( kount<3 ) return;
// client wants to change this to 4 in future - Steve
// screw that ... Biff, 8/2014
// many many lines of code here
// many many lines of code here
// many many lines of code here
}
Makes sense right? I hope that helps with what you were asking. You have to think the "other way around" you know?
A possible rule of thumb is if you ever have a condition and then a "very long block of code" - it's sort of wrong. Turn it around the other way and have breakaway conditions. Only then, have "the actual relevant code". In a sense .. never put a long slab of important code inside an if block; you're sort of thinking the wrong way around if so.
Related
I'am now using dispatch_group to manage multi-threads in my project. something like this:
- (void)functionA{
self.taskGroup = dispatch_group_create();
// Call functionB more than one time
[self functionB];
...
[self functionB];
dispatch_group_notify(..., ^{
// Do something if all [self functionB] complete
});
- (void)functionB{
dispatch_group_enter(self.taskGroup);
if (condition) {
dispatch_group_leave(self.taskGroup);
return;
}
[self doSomethingInBackground:^{
NSLog(#"completed!");
dispatch_group_leave(self.taskGroup);
}];
}
My question is how to automatically call dispatch_group_leave when functionB is complete, so that I don't need to call it before every return statement in functionB or any background task called by functionB.
As #Sk0prion mentioned in comments, I can simply involve macro here.
#define m_return dispatch_group_leave(self.taskGroup);return
I'm struggling with GCD and blocks. I'm trying to create a series of methods that require data from a previous one. I was thinking about 2 different ways to achieve it.
dispatch_sync serial queue
nested completion blocks
Don't you think the following 2 options return the same value? AS far as I read in Apple's dispatch queues, DISPATCH_QUEUE_SERIAL runs in FIFO order. So both options should return identical values.
What am I doing wrong here? and which one is the best approach?
Thanks for your help!
//Option 1
dispatch_queue_t delete_queue = dispatch_queue_create("delete_queue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(delete_queue, ^{
[self dosomething];
});
dispatch_sync(delete_queue, ^{
[self dosomething2];
});
dispatch_sync(delete_queue, ^{
[self dosomething3];
});
//Option 2
-(void)dosomething1:(dispatch_block_t)completion;
-(void)dosomething2:(dispatch_block_t)completion;
-(void)dosomething3:(dispatch_block_t)completion;
[self dosomething:^{
[self dosomething2:^{
[self dosomething3:^{}];
}];
}];
-(void)dosomething:(dispatch_block_t)completion {
/*method logic here*/
completion();
}
-(void)dosomething2:(dispatch_block_t)completion {
/*method logic here*/
completion();
}
-(void)dosomething3:(dispatch_block_t)completion {
/*method logic here*/
completion();
}
Both code samples you have shown are equivalent to just:
[self dosomething];
[self dosomething2];
[self dosomething3];
In other words, both ways execute the methods synchronously, in order, and block the thread until they are done.
Also, as Ken Thomases said, none of your methods "return" anything, so your question about returning doesn't make sense.
It doesn't really make sense to do three separate calls to dispatch_sync() here:
dispatch_sync(delete_queue, ^{
[self dosomething];
});
dispatch_sync(delete_queue, ^{
[self dosomething2];
});
dispatch_sync(delete_queue, ^{
[self dosomething3];
});
You should instead just do them all in a single block:
dispatch_sync(delete_queue, ^{
[self dosomething];
[self dosomething2];
[self dosomething3];
});
As for the use of completion blocks, you can certainly obtain a similar result, except that the completion-handler result would need to be asynchronous.
I think you need to take a step back and explain what kind of API you are trying to design in order to determine how you want to use the tools of the system and language to achieve that API design.
I've gotten in a few cases when something receives multiple refresh calls in quick succession, eg:
- ViewController receives multiple KVO notifications.
- Datamanger class that is called from setters to refresh when multiple settings change.
Ideally I would like to execute only the last refresh call from a series (drop all the intermediate ones).
Right now I'm using an isRefreshing property and a needRefresh to block excessive refreshes, eg:
- (id)init {
...
[self observeValueForKeyPath:#"isRefreshing" ....];
}
- (void)setParameter:(NSInteger)parameter {
....
[self refresh];
}
/* and many more kinds of updates require a refresh */
- (void)setAnotherProperty:(NSArray*)array {
....
[self refresh];
}
- (void)refresh {
if (self.isRefreshing) {
self.needRefresh = YES;
return;
}
self.isRefreshing = YES;
...
self.isRefreshing = NO;
}
- observeValueForKeyPath..... {
if (!self.isRefreshing && self.needsRefresh) {
self.needsRefresh = NO;
[self refresh];
}
}
Is there a better solution for this kind of problem?
You can create a NSOperationQueue with concurrency set to one and only submit a new operation to it when its operation count is zero. (Or use cancellation logic to remove pending jobs so that only one new one is queued if there's a job in progress.)
What you're doing is reasonable for a single-threaded system but would become fairly complicated for multiple threads.
Looks like you should delay refreshing for a while.
You can use different techniques to do so. It is enough only one flag.
For example you may use async block to make a delay for a one main run-loop cycle
- (void)setParameter:(NSInteger)parameter {
....
[self requestRefrhesh];
}
- (void)setAnotherProperty:(NSArray*)array {
....
[self requestRefrhesh];
}
...
-(void) requestRefrhesh {
if (self.refreshRequested) {
return;
} else {
self.refreshRequested = YES;
dispatch_async(dispatch_get_main_queue(), ^(void){
//Run in main UI thread
//make your UI changes here
self.refreshRequested = NO;
});
}
}
I've got class:
ClassX.m
#property (assign) BOOL wasProcessed;
-(void) methodA { //<- this can be called many times in short period of time
dispatch_async(dispatch_get_main_queue(), ^{
[self methodB];
});
}
- (void) methodB {
if (!self.wasProcessed) {
self.wasProcessed = YES;
//... some code
}
}
Since dispatch_async is used so a few calls to methodB can be processed concurrently at the same time and following code needs to be atomic:
if (!self.wasProcessed) {
self.wasProcessed = YES; //e.g two calls can enter here before setting YES and it would be bad because I want to process it only one time
How can those 2 lines be made atomic (checking and setting variable)? I dont want to make atomic code that is after "self.wasProcessed = YES;" so moving whole if to #synchronize(self) won't be good solution. If there is anything wrong with my thinking please point it out as I'm not very experienced in those topics, Thank you.
Try #synchronized. While the enclosed code is being executed on a thread, it will block other threads from executing it.
- (void) methodB {
#synchronized(self) {
if (!self.wasProcessed) {
self.wasProcessed = YES;
//... some code
}
}
}
-(void) methodA {
dispatch_async(dispatch_get_main_queue(), ^{
[[NSOperationQueue mainQueue] addOperationWithBlock:^(){
[self methodB];
}];
});
}
Your's methodB will be only called in main thread, so it will be never performed simultaneously.
I have a method like:
- (BOOL)shouldDoSomeWork {
BOOL result = // here I need do hard work with data in background thread and return result, so main thread should wait until the data is calculated and then return result;
return result;
}
How to implement that?
Are you looking for this:
-(void) startWork
{
//Show activity indicator
[NSThread detachNewThreadSelector:#selector(doSomeWork) toTarget:self withObject:nil];
}
-(void) doSomeWork
{
NSAutoreleasePool *pool = [NSAutoreleasePool new];
//Do your work here
[pool release];
[self performSelectorOnMainThread:#selector(doneWork) withObject:nil waitUntilDone:NO];
}
-(void) doneWork
{
//Hide activity indicator
}
Example how to do it with GCD:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Your hard code here
// ...
//BOOL result = ...
dispatch_async(dispatch_get_main_queue(),^{
[self callbackWithResult:result]; // Call some method and pass the result back to main thread
});
});
That's not typically how you would do it. You need something structured more like this:
- (void)doSomeWorkAndThen:(^void)block {
dispatch_async(dispatch_get_global_queue(0, 0), ^ {
// do
// some
// work
dispatch_sync(dispatch_get_main_queue(), ^ {
block();
});
});
That is, you keep the request and what you do afterwards in one place.
Common advice is to use the highest level of abstraction available to you to perform a task. As such NSThread should be relatively low down in the list of things you can do to execute work in the background.
The order you investigate APIs should be like this:
NSOperation / NSOperationQueue
Grand Central Dispatch (libdispatch)
NSThread
POSIX threads
With the first two you write your code as a "unit of work" and then put this work on a queue to be executed at some point. The system takes care of creating and destroying threads for you and the APIs are easy to work with. Here's an example using NSOperationQueue.
NSBlockOperation * blockOperation = [NSBlockOperation blockOperationWithBlock:^{
//Do work
//update your UI on the main thread.
[self performSelectorOnMainThread:#selector(workDone:) withObject:workResults waitUntilDone:NO];
}];
[self.operationQueue addOperation:blockOperation];
easy as that.