I have a function that has a problem in it. The following line seems to require a delay in order to accurately process whatever data it's processing, but it's not getting that time and and as a result I'm getting the wrong result. The following line:
self.failure = failure
Seems to need about 5-7 seconds to execute, but the problem is that the next line of code is executed before it can finish.
I need to setup self.failure = failure in a way where the rest of the code in the function executes inside a completion block once that line has finished. I'm just not sure how to do it.
I've tried creating a typedef for a completion block, but I have no idea how and where to execute it.
- (void)start:(BMPlaybackStartupProcessCompleteBlock)completion
failure:(BMPlaybackStartupProcessFailureBlock)failure // what is the error code at the start and end of this method?
{
NSParameterAssert(completion);
NSParameterAssert(failure);
self.completion = completion;
self.failure = failure;
NSOperation *jailBreakOperation = [self jailBreakCheckOperation];
NSOperation *metadata = [self metadataOperation];
NSOperation *ads = [self adsOperation];
NSOperation *mediaPlayer = [self mediaPlayerOperation];
NSOperation *progress = [self progressTrackerOperation];
NSOperation *final = [self finalOperation];
[final addDependency:mediaPlayer];
[final addDependency:progress];
if (ads)
{
[final addDependency:ads];
[ads addDependency:metadata];
}
[mediaPlayer addDependency:metadata];
[progress addDependency:metadata];
[metadata addDependency:jailBreakOperation];
[self.queue addOperation:final];
if (ads)
{
[self.queue addOperation:ads];
}
[self.queue addOperation:mediaPlayer];
[self.queue addOperation:progress];
[self.queue addOperation:metadata];
[self.queue addOperation:jailBreakOperation];
// INPUTS:
// - content ID
// - metadata component
// - media player component
// - ads component
// - location manager if required
// - auto play?
// OUTPUTS:
// - Possibly an error if not successful
// - metadata
// - playback details
}
I want self.failure = failure to finish BEFORE the NSOperationQueue stuff executes.
NSOperation has completionBlock property.
#property(copy) void (^completionBlock)(void);
So you can perform your operation like this. This will handle failure if occured.
Create `error` block level property to save error while operation.
nsOperatorObject.completionBlock(^{
if (self.error) {
self.failure();
} else {
self.completion();
}
});
This might be wrong answer . Sorry for that :)
Related
I'm adding many block-operations to an operationsqueue in a for loop. In each operation I need to check on another thread if a condition is fulfilled. If the condition is fulfilled, all operations should be cancelled.
I made a sample code to show you my problem:
__block BOOL queueDidCancel = NO;
NSArray *array = [NSArray arrayWithObjects:#"1",#"2",#"3",#"4",#"5",#"6",#"7",#"8",#"9",#"10", nil];
NSOperationQueue *myQueue = [NSOperationQueue new];
myQueue.maxConcurrentOperationCount =1;
for (NSString *string in array) {
[myQueue addOperationWithBlock:^{
if (queueDidCancel) {return;}
NSLog(#"run: %#", string);
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if ([string isEqualToString:#"1"]) {
queueDidCancel = YES;
[myQueue cancelAllOperations];
}
});
}];
}
Expected output from NSLog:
run: 1
Output I got (it varies between 7 and 9):
run: 1
run: 2
run: 3
run: 4
run: 5
run: 6
run: 7
run: 8
I googled for hours, but I could not find a solution.
I think I found a solution. Here's the updated code:
NSArray *array = [NSArray arrayWithObjects:#"1",#"2",#"3",#"4",#"5",#"6",#"7",#"8",#"9",#"10", nil];
NSOperationQueue *myQueue = [NSOperationQueue new];
myQueue.maxConcurrentOperationCount =1;
for (NSString *string in array) {
[myQueue addOperationWithBlock:^{
[myQueue setSuspended:YES];
NSLog(#"run: %#", string);
dispatch_async(dispatch_get_main_queue(), ^{
if (![string isEqualToString:#"1"]) {
[myQueue setSuspended:NO];
}
});
}];
}
Let me use more space. You need to sync access to your variable. It is the correct idea but you are using it incorrectly. You need a lock or an atomic ivar or something like that to sync access to it.
Then if you cancel in the dispatch_async bit it happens looooong after all the blocks executed. That is what your output shows. As mentioned in the comment, if you add a NSLog e.g.
dispatch_async(dispatch_get_main_queue(), ^{
if ([string isEqualToString:#"1"]) {
queueDidCancel = YES;
// Add here
NSLog(#"Going to cancel now");
[myQueue cancelAllOperations];
}
you will see what I mean. I expect that to typically execute deep into your array or even after all of the array finished executing.
But the biggest problem is your logic. You need some logic to cancel those blocks. Just messaging cancelAllOperations or setSuspended is not enough and blocks that are already running will keep on running.
Here is a quick example.
NSObject * lock = NSObject.new; // Use this to sync access
__block BOOL queueDidCancel = NO;
NSOperationQueue *myQueue = [NSOperationQueue new];
myQueue.maxConcurrentOperationCount =1;
for (NSString *string in array) {
// Here you also need to add some logic, e.g. as below
// Note the sync access
#synchronized ( lock ) {
if (queueDidCancel) { break; }
}
[myQueue addOperationWithBlock:^{
// You need to sync access to queueDidCancel especially if
// you access it from main and the queue or if you increase
// the concurrent count
// This lock is one way of doing it, there are others
#synchronized ( lock ) {
// Here is your cancel logic! This is fine here
if (queueDidCancel) {return;}
}
NSLog(#"run: %#", string);
dispatch_async(dispatch_get_main_queue(), ^{
if ([string isEqualToString:#"1"]) {
// Again you need to sync this
#synchronized ( lock ) {
queueDidCancel = YES;
}
// This is not needed your logic should take care of it ...
// The problem is that running threads will probably
// keep on running and you need logic to stop them
// [myQueue cancelAllOperations];
}
});
}];
}
Now this example does what yours does but with a bit more locking and a bit more logic and NO cancelAllOperations nor suspended = YESs. This will not do what you want as even with this running threads tend to run to completion and you need logic to stop it.
Also, in this example, I left the exit or cancel condition as is in the main thread. Again here this will probably mean nothing gets cancelled, but in real life you'd typically cancel from some UI e.g. a button click and then you'd do it as here. But you could cancel anywhere using the lock.
EDIT
Based on lots of comments here is another possible way.
Here you check inside the block and based on the check add another block or not.
NSOperationQueue * queue = NSOperationQueue.new;
// Important
queue.maxConcurrentOperationCount = 1;
void ( ^ block ) ( void ) = ^ {
// Whatever you have to do ... do it here
xxx
// Perform check
// Note I run it sync and on the main queue, your requirements may differ
dispatch_sync ( dispatch_get_main_queue (), ^ {
// Here the condition is stop or not
// YES means continue adding blocks
if ( cond )
{
[queue addOperationWithBlock:block];
}
// else done
} );
};
// Start it all
[queue addOperationWithBlock:block];
Above I use the same block every time which is also quite an assumption but you can change it easily to add different blocks. However, if the blocks are all the same you will only need one and do not need to keep on scheduling new blocks and then can do it as below.
void ( ^ block1 ) ( void ) = ^ {
// Some logic
__block BOOL done = NO;
while ( ! done )
{
// Whatever you have to do ... do it here
xxx
// Perform check
// Note I run it sync and on the main queue, your requirements may differ
dispatch_sync ( dispatch_get_main_queue (), ^ {
// Here the condition is stop or not
// YES means stop! here
done = cond;
} );
}
};
I am testing a method that runs in background and executes a code block when it finishes. I am using expectations to handle the asynchronous execution of the tests. I wrote simple a test that shows the behaviour:
- (void) backgroundMethodWithCallback: (void(^)(void)) callback {
dispatch_queue_t backgroundQueue;
backgroundQueue = dispatch_queue_create("background.queue", NULL);
dispatch_async(backgroundQueue, ^(void) {
callback();
});
}
- (void) testMethodWithCallback {
XCTestExpectation *expectation = [self expectationWithDescription:#"Add collection bundle"];
[self backgroundMethodWithCallback:^{
[expectation fulfill];
usleep(50);
XCTFail(#"fail test");
}];
[self waitForExpectationsWithTimeout: 2 handler:^(NSError *error) {
if (error != nil) {
XCTFail(#"timeout");
}
}];
}
The XCTFail(#"fail test"); line should fail for this test but the test is passing.
I also noticed that this only happens when the code ran on the callback takes an amount of time (in my case, I was checking some files on the file system). This is why the usleep(50); line is necessary to reproduce the case.
The expectation must be fulfilled after all the test checks. Moving the line to the end of the callback block is enough to make the test fail:
- (void) testMethodWithCallback {
XCTestExpectation *expectation = [self expectationWithDescription:#"Add collection bundle"];
[self backgroundMethodWithCallback:^{
usleep(50);
XCTFail(#"fail test");
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout: 2 handler:^(NSError *error) {
if (error != nil) {
XCTFail(#"timeout");
}
}];
}
I did not find explicit documentation about this but in the apple developer guides, the fulfill message is sent at the end of the block and it makes a lot of sense.
Note: I first found an example in swift where the fulfill method is called at the start of the callback. What I don't know is if the example is not correct or there is a difference with Objective-C.
The block called by backgroundMethodWithCallback is immediately fulfilling the expectation, thereby letting the test finish before XCTFail is called. If the block fulfills the expectation before it finishes performing other actions, you end up with race condition, in which the behavior of the test is conditional upon the speed with with the rest of the block is performed. But one shouldn't reasonably expect XCTFail to be captured if the test, itself, has already finished.
Bottom line, if you move the [expectation fulfill] to the end of the block, this race condition is eliminated.
I'm calling four methods that I want to execute in synchronous order, the first two methods are synchronous, the last two methods are asynchronous (data fetching from URLs).
Pseudo-code:
- (void)syncData {
// Show activity indicator
[object sync]; // Synchronous method
[object2 sync]; // Synchronous method
BOOL object3Synced = [object3 sync]; // Async method
BOOL object4Synced = [object4 sync]; // Async method
// Wait for object3 and object4 has finished and then hide activity indicator
}
How can I achieve this?
Use a barrier:
void dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
Submits a barrier block for asynchronous execution and returns immediately.
When the barrier block reaches the front of a private concurrent
queue, it is not executed immediately.
Instead, the queue waits until its currently executing blocks finish executing.
At that point, the
queue executes the barrier block by itself. Any blocks submitted after
the barrier block are not executed until the barrier block completes.
This example outputs 1 2 3 4 done although being asynchronous, it could be 1 2 4 3 done. Since I understand you want to handle an activity indicator, this shouldn't matter.
#import <Foundation/Foundation.h>
int main(int argc, char *argv[]) {
#autoreleasepool {
dispatch_queue_t queue = dispatch_queue_create("com.myqueue", 0);
dispatch_sync(queue, ^(){NSLog(#"1");} );
dispatch_sync(queue, ^(){NSLog(#"2");});
dispatch_async(queue, ^(){NSLog(#"3");});
dispatch_async(queue, ^(){NSLog(#"4");});
dispatch_barrier_sync(queue, ^(){NSLog(#"done");});
}
}
For other ways to test asynchronous code, see: https://stackoverflow.com/a/11179523/412916
Assuming you actually have some sort of way of knowing when the asynchronous methods are done, what you probably want is something like:
- (void)syncData {
// Show activity indicator
[object sync]; // Synchronous method
[object2 sync]; // Synchronous method
_object3Synced = _object4Synced = NO;
[object3 syncWithCompletionHandler:
^{
_object3Synced = YES;
[self considerHidingActivityIndicator];
}]; // Async method
[object4 syncWithCompletionHandler:
^{
_object4Synced = YES;
[self considerHidingActivityIndicator];
}]; // Async method
}
- (void)considerHidingActivityIndicator
{
if(_object3Synced && _object4Synced)
{
// hide activity indicator, etc
}
}
You can make a subclass of UIActivityInidicator, add an activityCount property and implement these two additional methods:
- (void)incrementActivityCount
{
if(_activityCount == 0)
{
[self startAnimating];
}
_activityCount++;
}
- (void)decrementActivityCount
{
_activityCount--;
if(_activityCount <= 0)
{
_activityCount = 0;
[self stopAnimating];
}
}
Now whenever you start something that uses the activity counter call incrementActivityCount and in its completion block (or otherwise when it finishes) call decrementActivityCount. You can do other things if you want in these methods, the above is just a simple example which is probably sufficient in most cases (especially if you set hidesWhenStopped = YES).
You would need to launch the first Async method and use a completion block. In the completion block of the first async method, you would launch your second async method. Though this kind of makes using async methods irrelevant.
In the initialization method of a class I am declaring the thread as such:
NSThread* myThread = [[[NSThread alloc] initWithTarget:self selector:#selector(m_run_thread) object:nil] autorelease];
[myThread start];
I also have a boolean value which is set to NO. Later on in the code I set the boolean value to YES.
bool_run_progress_thread = YES;
The contents of the method m_run_thread is as follows:
-(void) m_run_thread
{
if (bool_run_progress_thread)
{
//do processing here
}
bool_run_progress_thread = NO;
}
The problem is that the method m_run_thread is never being accessed. What am I doing wrong?
P.S. I have also tried to set up the Thread using the following (and older)method:
[NSThread detachNewThreadSelector:#selector(m_run_thread)
toTarget:self
withObject:nil];
... but to no avail as well.
"...and I am only getting it to show once" Yes, that's exactly how it should be. After being started, a thread runs once from its start to its end (ignoring errors here for the moment), and having reached the end, the thread is essentially dead and gone.
If you want the thread to repeat its execution, you have to prepare for that yourself:
- (void) m_run_thread
{
for (;;)
{
if (bool_run_progress_thread)
{
//do processing here
bool_run_progress_thread = NO;
}
}
}
But there is still a lot wrong with this code: essentially, when run, the code forms a busy waiting loop. Assuming, that bool_run_progress_thread is only ever true for short periods of time, the background thread should be sleeping most of the time. Insead, if you try the code as its stands, it will instead consume CPU time (and lots of it).
A better approach to this would involve condition variables:
#class Whatsoever
{
NSCondition* cvar;
BOOL doProgress;
...
}
...
#end
and
- (void) m_run_thread
{
for (;;)
{
[cvar lock];
while (!doProgress)
{
[cvar wait];
}
doProgress = NO;
[cvar unlock];
... do work here ...
}
}
and in order to trigger the execution, you'd do:
- (void) startProgress
{
[cvar lock];
doProgress = YES;
[cvar signal];
[cvar unlock];
}
Doing things this way also takes care of another subtle problem: the visibility of the changes made to the global flag (your bool_run_progress_thread, my doProgess). Depending on the processor and its memory order, changes made without special protection might or might not become (ever) visible to other threads. This problem is taken care of by the NSCondition, too.
i have a method, in which i want to accomplish a given task, however, the asynchronous commands and delegates made it difficult
i can do this :
- (void) fooPart1
{
...
SomeAssynchronousMethos * assync = [[SomeAssynchronousMethos alloc] init];
assync.delegate = self;
[assync start];
}
- (void) fooPart2
{
...
possibly some other assync
}
- (void)someAssynchronousMethosDelegateDidiFinish
{
[self fooPart2];
}
But isn't there a way to do smith. like this
- (void) foo
{
...
SomeAssynchronousMethos * assync = [[SomeAssynchronousMethos alloc] init];
assync.delegate = self;
[assync start];
wait for signal, but class is not blocked
...
possibly some other assync
}
- (void)someAssynchronousMethosDelegateDidiFinish
{
continue in foo after [assync start]
}
I don't like the idea of splitting a function to 2 or more parts, but is this the way how it is done in cocoa? or is there a better practice?
why i dont like this concept and searching for a better way of doing it :
lets say, i want to use a variable only for compleating a task - if i have everything in one function, i just use it, and than the var dies as i leave the function, if its split, i have to keep the var somehow around, until it doesnt finish
the code becomes fragmented and more difficult to read and maintain
may lead to bug
i end up with a set of part function, that needs to be called in precise order to accomplish one task (for which one function would be more suitable)
i used to make a thread and do only synchronous calls there, but not everything supports a synchronous call
what would be realy nice, is to have smth, like
- (void) foo
{
...
int smth = 5;
SomeAssynchronousMethos * assync = [[SomeAssynchronousMethos alloc] init];
assync.delegate = self;
#freez([assync start]);
// when freez - the local function variables are frozen
// (next commands in function are not excuted until unfreez)
// from outer look, it looks like the function finished
// however when unfreeze, it is called from this point on
//int smth is still 5
}
- (void)someAssynchronousMethosDelegateDidiFinish
{
#unfreez([assync start]);
}
when the execution would reach freez, it would store all local vars allocated in function and when called unfreez, it would continue from that point on, when it was freez with that command
This seems like an ideal application of a completion handler block.
Alter your start method to take a parameter which is a block and call it like so:
- (void) fooPart1
{
...
SomeAssynchronousMethos * assync = [[SomeAssynchronousMethos alloc] init];
[assync startOnComplete: ^(NSError* error) // example, you can have any params or none
{
// handle error if not nil
if (error != nil)
{
// do something with it
}
// code to do on completion
}];
}
Your start method would look something like this
-(void) startOnComplete: (void(^)(NSError*)) completionBlock
{
// copy the block somewhere
theSavedCompletionBlock = [completionBlock copy];
// kick off async operation
}
-(void) someMethodThatRunsAttheEndOfTheAsyncOp
{
theSavedCompletionBlock(nilOrError);
[theSavedCompletionBlock release];
}