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:.
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 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.
I have this method with a block in it, I want it to send the userID to another method as soon as it exists. userID is a value that is parsed from the internet, so it usually takes about 2 seconds to load up and 'exist'. Is there any way I can do a 'when userID exists, send it to another method?
Here's all my code:
- (void)parseForUserID {
//Get the Data you need to parse for (i.e. user main page returned as a block of NSData.
TClient *client = [[TClient alloc] init];
[client loginToMistarWithPin:#"20014204" password:#"yuiop" success:^{
[client getUserID:^(NSString *result) {
NSString *userIDWithHTML = [self userIDRegex:result];
NSString *userID = [self onlyNumbersRegex:userIDWithHTML];
//if userID exists, send it to another method in a different class
}];
} failure:^{
NSLog(#"login failed from controller");
}];
}
I see that this is the third question you ask related to the same issue, so I guess you're having some trouble understanding blocks.
First you have to understand that the block, in a certain sense, can be seen as a function. The difference is that, unlike a function, the block has no name, and instead of using function's name you just place the code inline where you need it.
Second thing to understand is that a block is usually used as a callback. Other callback mechanisms are function pointers and delegates. When you pass a block as a parameter to a function you're basically telling the function: "Hey, when certain conditions are met, execute this little code for me, please"
Third think to understand is if the block (or any callback) will be called synchronously. Actually this has nothing to do with the block itself, per se, but rather with the function being called. If the function is asynchronous, the function will create another thread and return immediately to execute the next line after the one that invoked the asynchronous function. Meanwhile the new thread will execute some code (the body of the async function) and, eventually execute the block passed as parameter, and finally the thread is killed and doesn't exist any more. (Note: There's no way to know if a function is synchronous or asynchronous other that reading the documentation for such a function).
Now let's go back to your code.
[client loginToMistarWithPin:#"20014204" password:#"yuiop" success:^{
[client getUserID:^(NSString *result) {
NSString *userIDWithHTML = [self userIDRegex:result];
NSString *userID = [self onlyNumbersRegex:userIDWithHTML];
// PLACE HERE THE CODE TO EXECUTE WHEN SUCCESSFULLY LOGGED IN
[anotherClassInstance someMethod:userID];
}];
} failure:^{
NSLog(#"login failed from controller");
}];
Everything that should be executed once the user logged in should be placed inside the block (if the function is synchronous you could place it after the block). To send the userID to another class, just call that class' method as you would in any other part of your code.
In my opinion using a delegate is not necessary (although only you would know, since you're the architect of your app).
As #santhu said, use either the delegate pattern or notification pattern. It's also a common practice to use both of them. Usually a delegate is the correct approach but sometimes you need a notification. Using both covers all your bases.
Look them up before deciding which and for full details on how they work, but basically:
[client getUserID:^(NSString *result) {
NSString *userIDWithHTML = [self userIDRegex:result];
NSString *userID = [self onlyNumbersRegex:userIDWithHTML];
// delegate pattern:
if ([self userIdIsValid:userID]) {
if (self.delegate && [self.delegate respondsToSelector:#selector(foundValidUserID:)]) {
[self.delegate foundValidUserID:userID];
}
} else {
if (self.delegate && [self.delegate respondsToSelector:#selector(foundInvalidUserID:)]) {
[self.delegate foundInvalidUserID:userID];
}
}
// notification pattern:
if ([self userIdIsValid:userID]) {
[[NSNotificationCenter defaultCenter] postNotificationName:MyFoundValidUserIDNotification object:self userInfo:#{#"userID": userID}];
}
} else {
[[NSNotificationCenter defaultCenter] postNotificationName:MyFoundInvalidUserIDNotification object:self userInfo:#{#"userID": userID}];
}
}];
There is a third option, which is you could use a block callback... this is how the new kids on the block do it... there's no well defined pattern here, blocks are brand new and delegates/notifications are 20 years old. But here's how I'd use a block to define a callback:
typedef void (^UserIdCallbackBlock)(NSString *userID);
- (void)parseForUserIDOnSuccess:(UserIdCallbackBlock)successCallback onFailure:(UserIdCallbackBlock)failureCallback {
...
NSString *userID = [self onlyNumbersRegex:userIDWithHTML];
if ([self userIdIsValid:userID]) {
successCallback(userID);
} else {
failureCallback(userID);
}
...
}
I would like to give a hint regarding your comment:
for code readability, it's not that I just have one more task to do, the thing I put inside this block will also have a block and another block and another.
This is a typical asynchronous pattern - called "continuation".
Given, that you should also implement proper error handling and that you should also provide a means to cancel that whole "chain" of asynchronous tasks at any point, the typical solutions with NSOperationQueues and NSOperations, dispatch_queue and blocks, NSNotifications or delegates will inevitable become unduly elaborate, complex and difficult to comprehend by others. (There's already an answer here that demonstrates this grandiose ;) )
So, whenever problems become more complex and the "built-in frameworks" don't provide a comfortable solution, third party libraries come into play to help you.
But first, lets have a non-trivial example, based on your comment:
it's not that I just have one more task to do, the thing I put inside this block will also have a block and another block and another
OK, lets suppose your objective is actually:
Asynchronously perform a Login for a web service.
Then, if that succeeded, asynchronously fetch a list of objects as JSON.
Then, if that succeeded, parse the JSON response.
Then, if that succeeded, insert the objects into a managed object context and asynchronously save the chain of managed object contexts and make it persistent.
When this all above succeeded, update the UI on the main thread
If anything fails, report the error of the task that failed
I will show how a solution utilizing a library implementing "promises" (see wiki Future and promises) may look like:
Without further ado, and without thorough explanation what that "Promise" is about, suppose we have a method defined in your View Controller, which is declared:
- (RXPromise*) loginToMistarWithPin:(NSString*)pin
password:(NSString*)password;
Note: The above method is asynchronous and it is functional equivalent to the form:
typedef void (^completion_t)(id result, NSError*error);
- (void) loginToMistarWithPin:(NSString*)pin
password:(NSString*)password
completion:(completion_t)completion;
then suppose we have another method in your View Controller, fetching objects from a remote server (asynchronous as well):
- (RXPromise*) fetchObjects;
Then, suppose we have a class CoreDataStack which consists of a "root context" saving to the persistent store having a child managed object context, the "main context", which is associated to the main thread.
The class CoreDataStack defines this method, which saves a chain of managed object contexts, which is basically setup: childContext -> main_context -> root_context:
- (RXPromise*) saveWithChildContext:(NSManagedObjectContext*)childContext;
Then, the whole task as stated in the steps 1. through 5. can be expressed as follows:
[client loginToMistarWithPin:#"20014204" password:#"yuiop"]
.then(^id(id result){
// login succeed, ignore result which is #"OK"
// Now fetch the objects with an asynchronous network request,
// returning JSON data as a NSData object when it succeeds:
return [client fetchAllUsers];
}, nil)
.then(^id(NSData* json){
// The network request succeeded, and we obtain the JSON as NSData.
// Parse it and get a Foundation representation:
NSError* error;
id jsonArray = [NSJSONSerialization JSONObjectWithData:json
options:0
error:&error];
if (jsonArray) {
return jsonArray; // handler succeeded
}
else {
return error; // handler failed
}
})
.then(^id(NSArray* objects){
// Parsing succeeded. Parameter objects is an array containing
// NSDictionaries representing a type "object".
// Save into Core Data:
// Create a managed object context, which is a child of the
// "main context" of a Core Data stack:
NSManagedObjectContext* moc = [[NSManagedObjectContext alloc]
initWithConcurrencyType:NSPrivateQueueConcurrencyType];
moc.parentContext = self.coreDataStack.managedObjectContext;
// Create managed objects and initialize them with the given
// NSDictionary:
for (NSDictionary* object in objects) {
// note: `createWithParameters:inManagedObjectContext` executes on
// the context's queue
[Object createWithParameters:object inManagedObjectContext:moc];
}
// Finally, asynchronously save into the persistent store and
// return the result (a RXPromise):
return [self.coreDataStack saveWithChildContext:moc];
}, nil)
.thenOn(dispatch_get_main_queue(), ^id(id result){
// Saving to the backing store succeeded. Now, we possibly want to
// update some UI on the main thread. We are executing on the main
// thread already (see thenOn(dispatch_get_main_queue())
...
[self.tableView reloadData];
return nil;
}, nil)
.then(nil, ^id(NSError* error){
// If something went wrong in any of the above four steps, the error
// will be propagated down and "cought" in this error handler:
NSLog(#"Error: %#", error);
});
Disclaimer: I'm the author of the library RXPromise available at GitHub. There are a few more Objective-C libraries which implement Promises.
I am transitioning some thread safety code from #synchronized to NSRecursiveLock.
Consider this code in which myItemsArray is an NSMutableArray:
- (NSUInteger) numberOfItems {
#synchronized(self.myItemsArray) {
return self.myItemsArray.count;
}
}
I believe the following code is incorrect because the lock would never get unlocked:
- (NSUInteger) numberOfItems {
[self.myRecursiveLock lock];
return self.myItemsArray.count;
[self.myRecursiveLock unlock];
}
So I'm using this approach instead:
- (NSUInteger) numberOfItems {
[self.myRecursiveLock lock];
NSUInteger itemCount = self.myItemsArray.count;
[self.myRecursiveLock unlock];
return itemCount;
}
However, I think this approach would break the thread safety, since another thread could add or remove an item after -unlock is called, but before itemCount is returned.
I'm not sure if I'm correct that the last approach isn't thread-safe, because I see this pattern in many widely used third-party libraries (for example, [AFHTTPRequestOperation -responseObject])
What is the correct way to return a value from a method synchronized using NSRecursiveLock?
Protecting numberOfItems can never ensure that the count is up-to-date. The array might be modified by another thread immediately after the method returns. It only prevents that two threads call the count method simultaneously.