I need to perform an action after the complete enumeration of all the objects in an array. How can I add a completion block to enumerateObjectsWithOptions(_:usingBlock:) in Swift.
Or how to know when enumerateObjectsWithOptions(_:usingBlock:) has completed.
allVisitors.enumerateObjectsWithOptions( NSEnumerationOptions.Concurrent, usingBlock: { (obj, idx, stop) -> Void in
})
Method enumerateObjectsWithOptions in NSArray is synchronous.
So what you write on the next line will be executed after your block has been executed for every element in the array. These users tested the version without options.
E.g.
allVisitors.enumerateObjectsWithOptions( NSEnumerationOptions.Concurrent, usingBlock: { (obj, idx, stop) -> Void in
// do your stuff
})
println("now allVisitors.enumerateObjectsWithOptions has done")
The method: enumerateObjectsWithOptions(_:usingBlock:) is synchronous.
By default, the enumeration starts with the first object and continues
serially through the array to the last object. You can specify
NSEnumerationConcurrent and/or NSEnumerationReverse as enumeration
options to modify this behavior. This method executes synchronously.
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSArray_Class/#//apple_ref/occ/instm/NSArray/enumerateObjectsWithOptions:usingBlock:
So, you don't need to add a completion block to know when finishes, just write what you want to execute below enumerateObjectsWithOptions(_:usingBlock:)
If you want, I can try to help you to add a block at the end, but I think it not make sense.
Related
I have the following async recursive code:
func syncData() {
dal.getList(...) { [unowned self] list, error in
if let objects = list {
if oneTime {
oneTime = false
syncOtherStuffNow()
}
syncData() // recurse until all data synced
} else if let error = error {... }
func syncOtherStuffNow() { } // with its own recursion
My understanding is that the recursion will build the call stack until all the function calls complete, at which point they will all unwind and free up the heap.
I also want to trigger another function (syncOtherStuffNow) from within the closure. But don't want to bind it to the closure with a strong reference waiting for it's return (even though it's async too).
How can I essentially trigger the syncOtherStuffNow() selector to run, and not affect the current closure with hanging on to its return call?
I thought of using Notifications, but that seems overkill given the two functions are in the same class.
Since dal.getList() takes a callback I guess it is asynchronous and so the the first syncData starts the async call and then returns immediately which lets syncData() return.
If syncOtherStuffNow() is async it will return immediately and so dataSync() will not wait on it finishing its job and so continue with its execution to the end.
You can test whether sth builds a callstack by putting a breakpoint on every recursion and look on the callstack how many calls of the same function are ontop.
What I do is recurse with asyncAfter, which unwinds the call stack.
The goal is to animate multiple SCNNodes at the same time then call a completion block once all the animations complete. The parallel animations have the same duration so will complete at the same time if started together.
This SO answer suggested using the group function for Sprite Kit, but there is no analog in Scene Kit because the SCNScene class lacks a runAction.
One option is run all the actions individually against each node and have each one call the same completion function, which must maintain a flag to ensure it's only called once.
Another option is to avoid the completion handler and call the completion code after a delay matched to the animation duration. This creates race conditions during testing, however, since sometimes the animations get held up before completing.
This seems clunky, though. What's the right way to group the animation of multiple nodes in SceneKit then invoke a completion handler?
The way I initially approached this was, since all the initial animations have the same duration, to apply the completion handler to just one of the actions. But, on occasion, the animations would hang-up (SCNAction completion handler awaits gesture to execute).
My current, successful solution is to not use the completion handler in conjunction with an SCNAction but with a delay:
func delay(delay:Double, closure:()->()) {
dispatch_after(
dispatch_time(
DISPATCH_TIME_NOW,
Int64(delay * Double(NSEC_PER_SEC))
),
dispatch_get_main_queue(), closure)
}
An examlpe of invocation:
delay(0.95) {
self.scaleNode_2.runAction(moveGlucoseBack)
self.fixedNode_2.runAction(moveGlucoseBack)
self.scaleNode_3.hidden = true
self.fixedNode_3.hidden = true
}
I doubt this can be called "the right way" but it works well for my uses and eliminates the random hang-ups I experienced trying to run animations on multiple nodes with completion handlers.
I haven't thought this through completely but I'll post it in hopes of being useful.
The general problem, do something after the last of a set of actions completes, is what GCD's dispatch_barrier is about. Submit all of the blocks to a private concurrent queue, then submit the Grand Finale completion block with dispatch_barrier. Grand Finale runs after all previous blocks have finished.
What I don't see right away is how to integrate these GCD calls with SceneKit calls and completion handlers.
Maybe dispatch_group is a better approach.
Edits and comments welcome!
Try something like this:
private class CountMonitor {
var completed: Int = 0
let total: Int
let then: ()->Void
init(for total: Int, then: #escaping(()->Void)) {
self.total = total
self.then = then
}
func didOne() {
completed += 1
if completed == total {
then() // Generally you should dispatch this off the main thread though
}
}
}
Then creating the actions looks something like:
private func test() {
// for context of types
let nodes: [SCNNode] = []
let complexActionsToRun: SCNAction = .fadeIn(duration: 100)
// Set up the monitor so it knows how many 'didOne' calls it should get, and what to do when they are all done ...
let monitor = CountMonitor(for: nodes.count) { () in
// do whatever you want at the end here
print("Done!")
}
for node in nodes {
node.runAction( complexActionsToRun ) { () in
monitor.didOne()
}
}
}
Note you should also account for the nodes array being empty (you might still want to do whatever you wanted to do at the end, just immediately in that case).
I have a method that inits the object and it has a completion block: typedef void(^initCompletionHandler)(BOOL succesful);
In this method I want to call the handler but I am not sure how to do it because if I call it before the return the object won't be finished initialising which is used immediately in the next line. I also obviously can't call the handler after the return. i,e:
if(haveError){
handler(NO);
}
else{
handler(YES);
}
return self;
Is there any way I can return and call the handler at the same time?
A couple of observations:
I'm unclear as to why you say "because ... the return object won't be finished initialising." You're doing the initialization, so just ensure it finishes all of the associated initialization before calling that handler. If the issue is that the caller won't have a valid reference to that object yet, you could always include a reference to it in the parameter of the block, e.g.
typedef void(^initCompletionHandler)(MyObject object, BOOL succesful);
and then supply that parameter, e.g.:
if (haveError){
handler(self, NO);
} else {
handler(self, YES);
}
Also, you say "I obviously can't call the handler after the return". But you can. You could just do a dispatch_async, if you wanted:
dispatch_async(dispatch_get_main_queue(), ^{
if (haveError){
handler(NO);
} else {
handler(YES);
}
});
return self;
That's a little inelegant, as if you call it from another thread, you have some potential race conditions that you might have to coordinate/synchronize, but you get the idea: You don't have to call the handler synchronously.
Having made both of those observations, I must confess that I'm not a fan of having init actually launching some asynchronous process and having its own completion block. I'd be inclined to make those two different steps. If you look at the Cocoa API, Apple has largely shifted away from this pattern themselves, generally having one method for instantiation, and another for starting the asynchronous process.
I'd like to iterate through an array in Swift and I need to know when the last item has been reached. I am using enumerateObjectsUsingBlock. As there is no optional completion block (like in some of the CoreAnimation methods) I assume the stop parameter signals the end point – is this correct?
itemsArray.enumerateObjectsUsingBlock {
(object, index, stop) -> Void in
self.displayedItems.addObject(object as SpecialItem)
if stop == true {
println("the end.")
}
}
stop seems to be of the type UnsafeMutablePointer. Therefore I cannot simply test it for being true/false or nil. How do I know when the enumeration is completed?
enumerateObjectsUsingBlock executes synchronously. The enumeration is finished
when the method returns.
stop is a reference to a Boolean which can be set to true to stop further processing,
for example:
itemsArray.enumerateObjectsUsingBlock {
(object, index, stop) -> Void in
// process at most 5 elements:
if index == 4 {
stop.memory = true
}
}
Testing if stop == true inside the block does not make sense.
Get the count prior to the block and then compare the index to the count to determine when you are at the last item.
If you just want to execute code after the enumeration is complete just put the code immediately following the block code. This block is not an asynchronous.
Note: Stop is a variable that can be set in the block to terminate enumeration early. See the documentation.
I have an app wherein I need to loop through and load stored data in order to generate information to save to PDF file, pages. I created a loop, but it runs too fast, so to speak. The saved pages cannot load fast enough, so the resulting data is incomplete. I think I need to create a for loop with delay or completion handler. Something like:
for (int i = 0, i < numberOfPages, doTaskWithCompletion:i++) {
arrayOfPages = [self loadDataAtIndex:i withCompletionHandler:handler];
[self writeDataToFile:arrayOfPages];
}
Not even sure how to write that pseudo-code, but basically I only want to jump to the next iteration after a completion handler has fired for the task of loading the data.
Note: Source data comes from core data, saved as PDF file. The data is pages and views for page. Each page can have 1 or unlimited views. Each view might have data too, like an image. What happens currently is that the number of pages written is correct, but the page's views that are rendered to PDF are identical, because they all read the same data, as the pages are not loaded before the PDF is written.
You may approach the problem as follows:
First define a generic completion block type. Note, param result can be anything. If it's an NSError this signals a failure, otherwise success.
typedef void (^completion_t)(id result);
Define a block type for your asynchronous task. Your task takes an index as input, and - since it is asynchronous, it takes a completion block as last parameter:
typedef void (^unary_async_t)(int index, completion_t completion);
Your task block object can be defined as follows:
unary_async_t task = ^(int index, completion_t completion) {
[self loadDataAtIndex:index withCompletion:^(id result){
if (![result isKindOfClass:[NSError class]]) {
[self writeDataToFile:result];
result = #"OK";
}
if (completion) {
completion(result);
}
}];
};
Note: your loadDataAtIndex:withCompletion: is asynchronous, and thus, it takes a completion block as last parameter. The completion block's parameter result is the result of the asynchronous task, namely the "array of pages" - or an NSError object if that fails.
In this completion block, you safe your result (the pages) to disk, invoking writeDataToFile:. (We assuming this won't fail). If that is all finished, task invokes the provided completion block completion (if not nil) passing the result of the whole operation, which is #"OK" in case of success, and an NSError in case of failure.
Now, the more interesting part: how to make this in a loop, where numerous _task_s will be executed sequentially, one after the other:
We define two helper methods - or functions:
What we finally need is a function with this signature:
void apply_each_with_range(int lowerBound, int upperBound,
unary_async_t task, completion_t completion);
This is the function which shall invoke task N times, passing it parameter index in the range from lowerBound (including) to upperBound (not including), where N equals upperBound - lowerBound and index starts with lowerBound.
It's an asynchronous function, and thus, takes a completion block as last parameter. Do I repeat myself? Well, you should recognize the pattern! ;)
Here is the implementation:
void apply_each_with_range(int lowerBound, int upperBound,
unary_async_t task, completion_t completion)
{
do_apply(lowerBound, upperBound, task, completion);
}
And the other helper - which is finally performing some sort of "for_each index in range[upperBound, lowerBound] sequentially invoke task with parameter index":
static void do_apply(int index, int upperBound,
unary_async_t task, completion_t completion)
{
if (index >= upperBound) {
if (completion)
completion(#"OK");
return;
}
task(index, ^(id result) {
if (![result isKindOfClass:[NSError class]]) {
do_apply(index + 1, upperBound, task, completion);
}
else {
// error occurred: stop iterating and signal error
if (completion) {
completion(result);
}
}
});
}
The function do_apply first checks whether the index is out of range. If, then it is finished and calls the completion handler with an #"OK".
Otherwise, it invokes task with argument index and provides a completion handler which gets invoked when task finished, which itself passes the result of the task. If that was successful, do_apply will invoke itself with argument index incremented by one. This may look like a "recursion" - but it is not. do_apply already returned, when it invokes itself.
If task returned and error, do_apply stops, "returning" the error from task in its completion handler (which is finally provided by the call-site).
Now, you just need to put these pieces together in your project - which should be fairly easy.
You can test to see if the data is saved and set a flag to see whether or not the data is saved. Then put an if statement like if the flag is set to yes then run the for loop. You can do something like that for all of your data to make sure all data is stored. Good luck!! Hope this helps!!