What if I have a function that returns an int and the return value of the int is taken from the block?
For example:
- (int) queryForKey:(NSString *)aKey view:(UIButton *)aView countView:(UIView *)aCountView counter:(int) count {
//some initialization
[query countObjectsInBackgroundWithBlock:^(int number, NSError * error) {
[aCountView addSubview:self.generateCountLabel];
if (number > 0){
[aView setUserInteractionEnabled:YES];
[aView setEnabled:YES];
}
//return number; //doing this will generate an error
}];
}
also another question is, what if I have an int passed in as an argument of the function above and I would like to assign a value to it. Is some thing like that even possible?
Well your block does not have a return value, it returns void.
To return a value you could use the __block modifier on a variable outside your block and store then answer there which can then be used by the rest of your method (or code).
The problem is that you have a synchronous method (one that wants to return the value immediately) that needs to return a value derived from an asynchronous method (one that goes about it's business in a different thread).
There are a couple of ways of fixing this:
wait for the countObjectsInBackgroundWithBlock: method to complete, use the __block pattern as #simonpie described.
replace the return number; with a call to something somewhere interested in the resulting number. This also means that queryForKey:view:countView: will likely return void.
The latter is the preferred solution as it will not block the thread calling the queryForKey:... method.
Note that you can't diddle UIKit classes on anything but the main thread. If that block is executed on a background thread, then doing what you are doing in the block is invalid.
I've found a better solution. Hopefully this will help someone else who stumbles across the question. I would implement a solution to your code like so:
- (int) queryForKey:(NSString *)aKey view:(UIButton *)aView countView:(UIView *)aCountView counter:(int) count {
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
__block int number;
//some initialization
[query countObjectsInBackgroundWithBlock:^(int number, NSError * error) {
dispatch_async(queue, ^{
[aCountView addSubview:self.generateCountLabel];
if (number > 0){
[aView setUserInteractionEnabled:YES];
[aView setEnabled:YES];
}
dispatch_semaphore_signal(sema);
});
}];
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
return number; //doing this will no longer generate an error
}
Then encapsulate your call with another dispatch_async so that your semaphore wait call doesn't block the main thread.
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
[self queryForKey:#"AKey" view:myView countView:myCountView counter:aCounter];
});
Related
what's is more prefered way to write multi threaded apps. I see two ways.
Implement method with GCD inside and then just simple call (myMethodA), or just implement method and then call it with GCD? Thanks in advance.
My point:
ClassA / method implementation
- (void)myMethodA
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// doSomething1
// doSomething2
});
}
- (void)myMethodB
{
// doSomething1
// doSomething2
}
ClassB / method call
{
[myClassA methodA];
// or
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[myClassA methodB];
};
}
IMHO, neither.
The preferred way should be having an object which knowns where to execute its actions:
completion_block_t completionHandler = ^(id result) { ... };
AsyncOperation* op = [AsyncOperation alloc] initWithCompletion:completionHandler];
[op start]; // executes its actions on a private execution context
Then, one can wrap those AsyncOperation objects into a convenient method:
- (void) fetchUsersWithCompletion:(completion_block_t)completionHandler
{
NSDictionary* params = ...;
self.currentOperation = [[HTTPOperation alloc] initWithParams:params
completion:completionHandler];
[self.currentOperation start];
}
The client may only be interested in specifying where its completionHandler should be executed. The API may be enhanced as follows:
- (void) fetchUsersWithQueue:(NSOperationQueue*)handlerQueue
withCompletion:(completion_block_t)completionHandler
{
NSDictionary* params = ...;
self.currentOperation = [[HTTPOperation alloc] initWithParams:params
completion:^(id result){
// As per the documentation of HTTPOperation, the handler will be executed
// on an _unspecified_ execution context.
// Ensure to execute the client's handler on the specified operation queue:
[handlerQueue:addOperationWithBlock:^{
completionHandler(result);
}];
}];
[self.currentOperation start];
}
The latter API can be used as this:
[self fetchUsersWithQueue:[NSOperation mainQueue] completion:^(id result){
self.users = result;
[self.tableView reloadData];
}];
Personal preference. Choose whichever makes the code more readable / understandable / obvious. Also, consideration of whether the code should be possible to run on the 'current' thread or whether it should always be run on a background thread. You need to design your threading configuration, describe it and then implement with that in mind. If you're calling methods between classes like in your example then I'd generally say that any threading should be handled inside that class, not inside the calling class. But that's about distribution of knowledge.
It doesn't make much of a difference - it just depends on what you want to do.
If you want to execute the method on different queues each time, then the myMethodB system is more appropriate. If, however, you always want to run the method on the same queue, then myMethodA will save you time writing code (you only have to write the GCD code once).
I'm executing an online fetch of data in a thread and I want to do something immediately after the block is executed.
Here's my code:
- (IBAction)refresh:(UIBarButtonItem *)sender {
NSLog(#"checking");
[self editToolbar];
dispatch_queue_t fetchQ = dispatch_queue_create("Refreshing", NULL);
dispatch_async(fetchQ, ^{
[self setupFetchedResultsController];
[self fetchImonggoItemsDataIntoDocument: self.itemDatabase];
});
dispatch_release(fetchQ);
NSLog(#"done checking");
//do something here
}
The thing is dispatch_async returns immediately and "done checking" prints immediately even before the block is done executing. How do I solve this?
I think it's an architectural issue. The tasks are something like:
edit toolbar
fetchImonggoItemsDataIntoDocument
do something else
If these must be done exactly in order then I don't quite understand the use of blocks or queues; just run the statements after each other and you'll be set.
Otherwise, alternative #1 would be to use dispatch_sync rather than dispatch_async. Again, I'm not quite sure what the benefit of using a queue would be but there it is.
Alternative #2 would be to use a callback from the block. Something like:
- (IBAction)refresh:(UIBarButtonItem *)sender {
NSLog(#"checking");
[self editToolbar];
dispatch_queue_t fetchQ = dispatch_queue_create("Refreshing", NULL);
dispatch_async(fetchQ, ^{
[self setupFetchedResultsController];
[self fetchImonggoItemsDataIntoDocument: self.itemDatabase];
[self doneChecking]; // <-- NOTE! call the callback
});
dispatch_release(fetchQ);
}
// NOTE! refresh: has been split up into two methods
- (void)doneChecking:
NSLog(#"done checking");
//do something here
}
As others have already suggested, this is probably what you need.
NSArray *items = [iMonggoFetcher fetchImonggoData:IMONGGO_GENERIC_URL_FOR_PRODUCTS withFormat:#"json" withDateRangeArgs:args];
[document.managedObjectContext performBlock:^{
for (NSDictionary *itemInfo in items){
[Product productWithImonggoInfo:itemInfo inManagedObjectContext:document.managedObjectContext];
}
// Put here what you need :)
}];
Is it possible to run a completion block on the main thread?
For example, I have one method which returns a value:
- (int)test
{
/* here one method is called with completion block with return type void */
[obj somemethodwithcompeltionblock:
{
/* here I am getting my Int which I want to return */
}
];
}
but I can't see how to return the integer value from within the completion block as the result of this method, because the completion block runs on a background thread.
How can I do this?
You're missing some basics about asynchronous development with blocks. You can't have a dispatched block return from anywhere but its own scope. Think of each block as its own method, instead of inline code.
I think what you're looking for is something similar to this...
- (void)testWithHandler:(void(^)(int result))handler
{
[obj somemethodwithcompeltionblock:^{
int someInt = 10;
dispatch_async(dispatch_get_main_queue(), ^{
handler(10);
});
}
];
}
- (void)callSite
{
[self testWithHandler:^(int testResult){
NSLog(#"Result was %d", testResult);
}];
}
I have the following dispatch queue my app :
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^ {
[activeModel freeUpMallocedData];
// UI Updates have to be made on the main thread, so request from GCD.
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^ {
[mainViewController removeTidyUpScreen];
[mainViewController showSceneList];
[activeModel release];
});
});
The freeUpMallocedData method updates a UI Progress View :
- (void) freeUpMallocedData {
// Calculate the percentage increase for each item in the pointerStorageArray as it is released so we can update the Progress Screen.
float arrayCount = [pointerStorageArray count];
float incrementCounter = 1 / arrayCount; // Caculates 1% of pointerStorageArray
float newValue = incrementCounter;
int stepCounter = 0;
NSString * message;
// Now iterate through the pointerStorageArray and free all malloced memory.
for (NSValue * value in pointerStorageArray) {
stepCounter ++;
newValue = newValue + incrementCounter;
message = [NSString stringWithFormat:#"Freeing Up Memory (%d of %d) ...", stepCounter, (int)arrayCount];
free(value);
[self tidyUpProgress:message amount:newValue];
}
}
The tidyUpProgress method then executes on the main thread.
- (void) tidyUpProgress: (NSString *) progressMsg amount: (float) amount {
if (tidyUpMonitorDelegate) {
tidyUpProgressMsg = progressMsg;
tidyUpProgressAmount = amount;
[tidyUpMonitorDelegate performSelectorOnMainThread:#selector(model3DTidyUpProgressUpdate) withObject:nil waitUntilDone:NO];
}
}
- (void) model3DTidyUpProgressUpdate {
progressView.progress = app.activeModel.tidyUpProgressAmount;
loadingStatus.text = app.activeModel.tidyUpProgressMsg;
}
The problem is that the app crashes when the freeUpMallocedData method completes. The reason for this is that my initial dispatch queue moves on to request the main queue which then releases activeView. This seems to hijack the thread from the tidyUpMonitorDelegate before it can perform its last update - when it gets the main thread back the activeView has been released and therefore the app crashes as the model3DTidyUpProgresUpdate method is requesting access to variable in a class which has now been dealloced.
Can anyone advise on how to fix this timing issue ?
Thank you in advance.
Just a thought - try renaming the variable inside the dispatch:
dispatch_queue_t mainqueue = dispatch_get_main_queue();
dispatch_async(mainqueue, ^ {
You use two different mechanisms to schedule tasks in the main thread : dispatch_asyc and performSelectorInMainThread:withObject:waitUntilDone:. Each mechanism uses its own queue, which is read by the main run loop.
The order in which those queue are read is undefined. Thus, a task scheduled by performSelectorInMainThread:withObject:waitUntilDone may be performed after (or before) a task scheduled with dispatch_async regardless of which was scheduled first.
You should update tidyUpProgress: to use dispatch_async. Then the order will be guaranteed.
Moreover, after releasing an object, you should always nullify the variable that holds the reference to that object.
this is wrong way:
float arrayCount = [pointerStorageArray count];
-correct way:
NSUinteger arrayCountInt = [pointerStorageArray count];
NSNumber *arrayCountNumber = [NSNumber numberWithUnsignedInteger:arrayCountInt]
float arrayCount = [arrayCountNumber floatValue];
I have recently, like a few people, discovered that [ALAssetsLibrary enumerateGroupsWithTypes] likes to run its blocks on another thread. What a shame that Apple didn't document that :-)
In my current circumstance I need to wait for the enumeration to complete, before the main thread returns any results. I clearly need some sort of thread synchronisation.
I've read about NSLock & NSConditionLock, but nothing yet seems to fit the requirement of 'signal a blocked thread that this worker thread has completed'. It seems like a simple enough need - can anyone point me in the right direction?
Your clue & boos, are most welcome as always,
M.
The framework doesn't run these blocks on a separate thread. It just runs them as additional events in the same run-loop. To prove it, try this
[library enumerateGroupsWithTypes:ALAssetsGroupAll
usingBlock:[^(ALAssetsGroup * group, BOOL * stop)
{
if([NSThread isMainThread])
{
NSLog(#"main");
}
else
{
NSLog(#"non-main");
}
} copy]
failureBlock:^(NSError * err)
{NSLog(#"Erorr: %#", [err localizedDescription] );}];
[library release];
if([NSThread isMainThread])
{
NSLog(#"main");
}
else
{
NSLog(#"non-main");
}
My output from this was
main
main
main
Meaning that the block was being called in the main thread. It's just a separate event.
To solve your problem, you just need to return your value somehow from within the block when you reach the last step. You can tell it's the last step because your block will be called with nil for the group object.
EDIT: for instance use this block
^(ALAssetsGroup * group, BOOL * stop)
{
if(group == nil)
{
// we've enumerated all the groups
// do something to return a value somehow (maybe send a selector to a delegate)
}
}
The answer is to use the NSConditionLock class thusly ...
typedef enum {
completed = 0,
running = 1
} threadState;
...
NSConditionLock *lock = [[NSConditionLock alloc] initWithCondition:running];
Then spin off your thread, or in my case a call to [ALAssetsLibrary enumerateGroupsWithTypes:]. Then block the parent thread with this ...
// Await completion of the worker threads
[lock lockWhenCondition:completed];
[lock unlockWithCondition:completed];
When all work is done in the child/worker thread, unblock the parent with this ...
// Signal the waiting thread
[lock lockWhenCondition:running];
[lock unlockWithCondition:completed];
Simply use this:
[library enumerateGroupsWithTypes:ALAssetsGroupAll
usingBlock:[^(ALAssetsGroup * group, BOOL * stop)
{
if(group == nil)
{
// this is end of enumeration
}
}
.
.
.