Idiomatic way to execute an array of blocks - ios

I have an object that can execute an arbitrary queue of updates. I use blocks to embody the updates. I add an update using my addUpdate: method.
- (void) addUpdate: (void(^)())block {
[self.updates addObject: block];
}
Later, I want to execute all of them. I don't care if they run concurrently or not. The basic primitive way would seem to be something like:
for (NSUInteger index = 0; index < self.updates.count; index++) {
void (^block)() = self.updates[index];
block();
}
or with fast enumeration
for (void (^block)() in self.updates) {
block();
}
Or is there something I should be doing with GCD to make this happen?

The most terse way I can think of to do this would be:
[self.updates makeObjectsPerformSelector: #selector(invoke)];
How "idiomatic" that is will probably be situation-dependent...
EDIT: This depends on the fact that blocks are implemented in the runtime as Objective-C objects, and respond to the selector -invoke. In other words, the expression block(); can also be expressed as [block invoke];. I'm not aware of any more succinct way to execute an array of blocks.

For non-concurrent execution, for-in is the way to go. For concurrent execution, you could use NSArray's -enumerateUsing... methods and pass the concurrent flag, or use dispatch_apply() instead of a loop.

Related

GCD why to use dispatch_sync when I read shared resource

I have some question about use dispatch_sync when I read a shared resource.
I have searched several questions on Stack Overflow (such as: GCD dispatch_barrier or dispatch_sync?), but I didn't found an exact answer.
I don't understand that why to use
- (void)addPhoto:(Photo *)photo
{
if (photo) { // 1
dispatch_barrier_async(self.concurrentPhotoQueue, ^{ // 2
[_photosArray addObject:photo]; // 3
dispatch_async(dispatch_get_main_queue(), ^{ // 4
[self postContentAddedNotification];
});
});
}
}
- (NSArray *)photos
{
__block NSArray *array; // 1
dispatch_sync(self.concurrentPhotoQueue, ^{ // 2
array = [NSArray arrayWithArray:_photosArray];
});
return array;
}
I know why to use dispatch_barrier_async,but I don't know why to use dispatch_sync when I read _photosArray, I guess the write operation of _photosArray is in the self.concurrentPhotoQueue, so the read operation of _photosArray also need in the self.concurrentPhotoQueue or else use dispatch_sync in order to achieve multi-read?
What will happen if I don't use dispatch_sync when I do read operation? such as:
- (NSArray *)photos
{
__block NSArray *array;
array = [NSArray arrayWithArray:_photosArray];
return array;
}
Thank you very much!
Probably concurrentPhotoQueue is a serial queue. The main reason for concurrentPhotoQueue is to synchronize the access to the photos array.
Since it is serial all accesses from this queue are serialized, and no race conditions may occur if there are no accesses from other queues / threads in your app.
Writing access may be asynchronous because the writer needs no result of the writing operation in general. But reading must be done synchronously, because the caller has to wait for the result. If your photos method would use dispatch_async it would write the result to array after the photos method has returned. Thus, photos always would return nil.
Your unsynchronized version of photos might produce a race condition: _photosArray could be modified while it copies its contents, such that the number of copied items and the length of the array differ. This could lead to a crash inside of arrayWithArray:.

Methods that use completion blocks and return an object

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.

What is the parameter that #synchronized() takes

I know what #synchronized() does, but...
sometimes we have:
1- #synchronized(self)
2- #synchronized([MyClass class])
3- #synchrinized(myObj)
What is the difference, and what is the parameter I should pass to this block ?
From the documentation:
The object passed to the #synchronized directive is a unique
identifier used to distinguish the protected block. If you execute the
preceding method in two different threads, passing a different object
for the anObj parameter on each thread, each would take its lock and
continue processing without being blocked by the other. If you pass
the same object in both cases, however, one of the threads would
acquire the lock first and the other would block until the first
thread completed the critical section.
So it depends on what you want to protect from being executed simultaneously,
and there are applications for all three cases.
For example, in
-(void)addToMyArray1:(id)obj
{
#synchronized(self) {
[self.myArray1 addObject:obj];
}
}
-(void)addToMyArray2:(id)obj
{
#synchronized(self) {
[self.myArray2 addObject:obj];
}
}
both #synchronized blocks cannot be executed simultaneously by two threads calling
the method on the same instance (self), thus protecting simultaneous access to the
arrays from different threads.
But it also prevents the block from the first method
to be executed simultaneously executed with the block from the second method, because they
use the same lock self. Therefore, for more fine-grained locking, you could use
different locks:
-(void)addToMyArray1:(id)obj
{
#synchronized(self.myArray1) {
[self.myArray1 addObject:obj];
}
}
-(void)addToMyArray2:(id)obj
{
#synchronized(self.myArray2) {
[self.myArray2 addObject:obj];
}
}
Now the simultaneous access to self.myArray1 and self.myArray2 from different threads
is still protected, but independently of each other.
A lock on the class can be used to protect access to a global variable.
This is just a trivial example for demonstration purposes:
static int numberOfInstances = 0;
-(id)init
{
self = [super init];
if (self) {
#synchronized([self class]) {
numberOfInstances++;
}
}
}
#synchronized should have the same object passed each time. So #synchronized(self) would work best.

Write macros for GCD calls?

I'd like to create a macro for GCD calls like for example:
dispatch_async(dispatch_get_main_queue(), ^{
stuff....
});
the macro could look something like this:
main(^{...})?
Not sure how to write it. Any suggestion?
thank you
Suggestion: don't. Among other things, it'll screw up line numbers in debugging.
You can actually define a normal function that will do the same thing, if you want, something like
typedef void(^VoidBlock)();
void on_main(VoidBlock block) {
dispatch_async(dispatch_get_main_queue(), block);
}
This has the advantage that you don't lose line numbers for the block during debugging. I've even done things like
void on_main(VoidBlock block) {
if (dispatch_get_current_queue() == dispatch_get_main_queue()) {
block();
} else {
dispatch_sync(dispatch_get_main_queue(), block);
}
}
which I can call either from the main queue or not.
You really want to avoid putting code blocks in macro arguments. If you do and have to debug that code, you'll hate yourself. (A bit tongue in cheek, but it really is painful if you have to debug any macro that expands to multiple lines of code.)
You can define macros like this:
#define ASYNC(...) dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ __VA_ARGS__ })
#define ASYNC_MAIN(...) dispatch_async(dispatch_get_main_queue(), ^{ __VA_ARGS__ })
First one will invoke the code asynchronously on unspecified thread (do all long running tasks using it) and the second one will invoke the block asynchronously on the main thread (do all UI related stuff using it).
You can combine both. Let's say you want to grab something from the network and update UI, you can write:
ASYNC({
NSString *result = [[NSString alloc] initWithContentsOfURL: ...];
ASYNC_MAIN({
self.myTextField.string = result;
[result release];
});
});
You add curly braces to make Xcode indent the code properly.
Please note where retain/release calls are made. This is powerful technique which will make your code readable.

Timing loop results

I got a little stuck and I'm hoping someone can point me in the right direction. I have an NSMutableArray that stores a sequence. I created an enumerator so that a while loop can get the content of the array one by one.
Everything works fine however I want the methods to be called with a 10 second gap in between each call. Right now it plays all at once (or in very quick order). What should I look at to create a delay in between method calls?
Below is what I got so far. Thanks!
NSEnumerator * enumerator = [gameSequenceArray objectEnumerator];
id element;
while(element = [enumerator nextObject])
{
NSLog(element);
int elementInt = [element intValue];
[self.view showButton:elementInt];
}
You almost certainly don't want to stick a "delay" in your loop, which will block the thread until it's over (and, unless your app is multi-threaded, block the entire thing). You could use multiple threads, but instead I'd probably split the loop out over repeated timed calls of another selector. Store the enumerator (or current index), and then look at NSObject's performSelector:awithObject:afterDelay:
So something like
[NSObject performSelector:#selector(some:selector:name:) withObject:objInstance afterDelay: 10]
where the selector will pickup the current enumerator, use it, advance it and schedule another call. Make sure you don't allow changes to the collection whilst this set of timed methods is executing.
This is what NSTimer is for. Use NSTimer to get each element in the array sequentially.
As an aside: you might want to take a look at Objective-C 2.0's Fast Enumeration
if gameSequenceArray is an array, then you don't need to use an enumerator:
NSTimeInterval time = 10;
for (id elementId in gameSequenceArray) {
[self.view performSelector:#selector(showButton:) withObject:elementID afterDelay:time];
}
and then you declare showButton:
- (void)showButton:(id)anElement {
...
}
If you end up passing your object enumerator around with a timer, know that you are not allowed to modify your array's contents until you are finished enumerating it.
So here was the solution that I came up with based on everyones input.
NSEnumerator * enumerator = [gameSequenceArray objectEnumerator];
NSTimeInterval time = 5;
for (NSString *element in enumerator) {
id elementId = element;
time++;
[self.view performSelector:#selector(showButton:) withObject:elementId afterDelay:time];
}
Thanks again for pointing me in the right direction everyone.

Resources