Why does this Grand Central Dispatch code doesn't work? - objective-c

It's my first Grand Central Dispatch code but it doesn't work. Working on Mac OS X 10.8 and last Xcode version. I know it's too basic. Thanks.
#import <Foundation/Foundation.h>
#import <dispatch/dispatch.h>
void printResult(int r);
void printResult(int r)
{
NSLog(#"%i", r);
}
int main(int argc, const char * argv[])
{
#autoreleasepool {
dispatch_queue_t queue = dispatch_queue_create("myQueue", NULL);
dispatch_async(queue, ^{
int number = pow(2, 5);
dispatch_async(dispatch_get_main_queue(), ^{
printResult(number);
});
});
}
return 0;
}

First. Your application actually exits before the blocks you passed in GCD could finish.
To fix that, you could use GCD groups and synchronization tools that they give you.
#autoreleasepool {
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("myQueue", NULL);
dispatch_group_async(group, queue, ^{
int number = pow(2, 5);
dispatch_group_async(group, dispatch_get_main_queue(), ^{
printResult(number);
});
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
}
return 0;
Second. Here you will encounter another problem called deadlock. The Second block is added to the main thread queue. And main thread is actually waiting for this block to finish, so it could not be executed. Add the second block to the queue you've created before.
dispatch_group_async(group, queue, ^{
printResult(number);
});
Now you can see 32 in console, which is what you expect.

int main(int argc, const char * argv[])
{
#autoreleasepool {
dispatch_queue_t queue = dispatch_queue_create("com.myQueue", NULL);
dispatch_async(queue, ^{
int number = pow(2, 5);
dispatch_async(dispatch_get_main_queue(), ^{
printResult(number);
});
});
}
[[NSRunLoop currentRunLoop] run];
return 0;
}
I think you just need a runloop here.

Related

Calling a method every second doesn't work

I am trying to schedule a GNUStep Objective-C method call to run every second for a variable number of seconds. I am trying to use NSTimer to schedule the method call, but the handler method never gets called.
Here is my code:
Timer.m:
- (id) init {
self = [super init];
if(self) {
_ticks = 0;
}
return self;
}
- (void) startWithTicks: (unsigned int) ticks {
_ticks = ticks; //_ticks is an unsigned int instance variable
if(_ticks > 0) {
[NSTimer scheduledTimerWithTimeInterval: 1.0
target: self
selector: #selector(onTick:)
userInfo: nil
repeats: YES];
}
}
- (void) onTick: (NSTimer*) timer {
NSLog(#"tick");
_ticks--;
if(_ticks == 0) {
[timer invalidate];
timer = nil;
}
}
main.m:
int main(int argc, const char* argv[]) {
Timer* t = [[Timer alloc] init];
NSLog(#"Setting timer");
[t startWithTicks: 3];
usleep(5000);
NSLog(#"End of timer");
return 0;
}
I would expect the output to be
Setting timer
tick
tick
tick
End of timer
However, the output is
Setting timer
End of timer
Why is this and how can I fix it?
The timer won't run while your thread is sleeping.
Your timer class code works fine if you're using it from a ViewController.
If instead you'd like to use it within the main method, you'll want to explicitly run the mainRunLoop. Try adjusting your main method to this:
int main(int argc, char * argv[]) {
Timer *timer = [[Timer alloc] init];
NSLog(#"Setting Timer");
[timer startWithTicks:3];
[[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:3]];
NSLog(#"End of Timer");
return 0;
}
to run the mainRunLoop running for 3 seconds, which should produce your desired output.
https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html
https://developer.apple.com/documentation/foundation/nsrunloop

Implement a Job Scheduler using Dispatch Queues

Question: Implement a job scheduler which takes in function f and int n and calls f after n seconds.
Since im using Obj C to answer this question, I should probably use dispatch queues instead of selectors or function pointers.
Here's my attempt to implement the answer, but it's not returning a time stamp:
void jobSch(dispatch_queue_t queue, int64_t n, dispatch_block_t f)
{
NSLog(#"%d\n", n);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, n * NSEC_PER_SEC), dispatch_get_main_queue(), f);
}
void (^aBlock)(void) = ^(){
NSDateFormatter *dateFormatter=[[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
NSLog(#"%#",[dateFormatter stringFromDate:[NSDate date]]);
};
int main(int argc, const char * argv[])
{
#autoreleasepool {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
jobSch(queue, 3, aBlock);
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
}
return 0;
}
What could I be doing wrong?
Also my intended implementation would be able to call jobSch(queue, 10, aBlock) at runtime 0 and jobSch(queue, 3, aBlock) at runtime 2, so that would mean the second call would print before the first.
The problem is that main is exiting long before your enqueued block is run. And that is because your call to dispatch_group_wait isn't waiting. And that is because your dispatch group is empty.
In order for a dispatch group and a call to dispatch_group_wait to be useful, you need to make paired calls to dispatch_group_enter and dispatch_group_leave.
You need to call dispatch_group_enter before you start any async call and you need to call dispatch_group_leave after the block has finished (aBlock in this case).
Your code is not setup to make this easy. You would need to make group a global variable. Then you can call dispatch_group_enter and dispatch_group_leave where needed.
Something like this should work:
dispatch_group_t group;
void jobSch(dispatch_queue_t queue, int64_t n, dispatch_block_t f)
{
NSLog(#"%d\n", n);
dispatch_group_enter(group);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, n * NSEC_PER_SEC), queue, f);
}
void (^aBlock)(void) = ^(){
NSDateFormatter *dateFormatter=[[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
NSLog(#"%#",[dateFormatter stringFromDate:[NSDate date]]);
dispatch_group_leave(group);
};
int main(int argc, const char * argv[])
{
#autoreleasepool {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
group = dispatch_group_create();
// no need for dispatch_group_async here
jobSch(queue, 10, aBlock);
jobSch(queue, 3, aBlock);
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
}
return 0;
}
Also note that you never actually make use of queue inside jobSch.
You need to change:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, n * NSEC_PER_SEC), dispatch_get_main_queue(), f);
to:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, n * NSEC_PER_SEC), queue, f);

How to stop monitoring file paths

I am currently monitoring multiple file paths from an array with objective-c by using this function:
-(void)monitorPath:(NSString*)path{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
int fildes = open([path UTF8String], O_EVTONLY);
__block typeof(self) blockSelf = self;
__block dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fildes,
DISPATCH_VNODE_DELETE | DISPATCH_VNODE_WRITE | DISPATCH_VNODE_RENAME, queue);
dispatch_source_set_event_handler(source, ^{
unsigned long flags = dispatch_source_get_data(source);
NSLog(#"Path altered");
});
dispatch_source_set_cancel_handler(source, ^(void) {
close(fildes);
});
dispatch_resume(source);
}
Is there a method to stop all of the file paths from being monitored?

Obj-C synchronize an async method which using block callbacks

I have an async method called getCount: which goes to a web URL, counts some stuff, and invokes a callback with the count when it's done.
I have another method which is synchronous and needs to take those results, puts them into a message, and returns that message. Here are the two together:
- (NSString *)describe {
__block bool gotCount = NO;
[self getCount:^(int count) {
NSLog(#"Got the count: %i", count);
_count = count; // _count is an ivar of the object with this method.
gotCount = YES;
}];
// Pause here until the count has been fetched.
while (!gotCount) {
[NSThread sleepForTimeInterval:0.05];
}
return [NSString stringWithFormat:#"The count is %i", _count];
}
My callback is never called when I try this. It never prints
Got the count 0
or any other value for count in this scenario.
If I comment out the while loop, that message does get printed out. So I know that the getCount: method works, there's just something wrong with my loop waiting for it to arrive.
I need getCount: to remain asynchronous (there's other places it gets used where that's more important) and I need describe to remain synchronous. How can I handle this?
one possible thing: if your describe method is in the main thread then you call getCount method also from the main thread and all web callbacks are in the main thread. BUT you block the main thread with the thread sleep -> you can not get call back from the web to get a count.
Edited:
try to call getCount method from another thread. use e.g.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self getCount:^(int count) {
NSLog(#"Got the count: %i", count);
_count = count; // _count is an ivar of the object with this method.
gotCount = YES;
}];
});
Edited 2:
I tried this code and it works fine -> something probably wrong with the threads in your getCount method.
- (NSString *)describe {
__block bool gotCount = NO;
__block NSInteger _count;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[NSThread sleepForTimeInterval:5.00];
_count = 5;
gotCount = YES;
});
// Pause here until the count has been fetched.
while (!gotCount) {
[NSThread sleepForTimeInterval:0.05];
}
return [NSString stringWithFormat:#"The count is %li", _count];
}
a way that would work but is QUITE the hack (which apple employed in older APIs on the mac all the time) would be to run the runloop while waiting:
note: this relies on the callback being on the same queue as the describe method
see: JSON async request [SAME ISSUE]
a self contained WORKING example:
#import <Foundation/Foundation.h>
#interface T : NSObject
- (NSString *)describe;
#end
#implementation T {
int _count;
}
- (void)getCount:(void (^)(int c)) handler {
dispatch_async(dispatch_get_global_queue(0,0), ^ {
sleep(5);
dispatch_sync(dispatch_get_main_queue(), ^{
handler(55);
});
});
}
- (NSString *)describe {
__block bool gotCount = NO;
[self getCount:^(int count) {
NSLog(#"Got the count: %i", count);
_count = count; // _count is an ivar of the object with this method.
gotCount = YES;
}];
// Pause here until the count has been fetched.
while (!gotCount) {
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
}
return [NSString stringWithFormat:#"The count is %i", _count];
}
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
T *t = [T new];
NSLog(#"describe: %#", [t describe]);
}
}

Block code in a method does not get run

I have the code below to load a group of images and notify me via completionHandler when they are all done loading. However, I find that certain dispatch_group_leave won't be called at times and my guess is imageLoader is deallocated before the block gets to run. If I put a reference of imageLoader within the loadImageWithURL:completionHandler block, then everything works as intended.
Is my guess of the cause correct? What's the correct fix for this issue? I know ARC does block copy in most cases automatically, should I do a block copy in this case?
- (void)loadGroupImagesAsyncWithCompletion:(void(^)(NSError *))completionHandler {
dispatch_group_t group = dispatch_group_create();
int index = 0;
for (Item *item in items) {
char queueLabel[30] = {0};
sprintf(queueLabel, "loader%d", index);
dispatch_queue_t queue = dispatch_queue_create(queueLabel, NULL);
dispatch_group_enter(group);
dispatch_async(queue, ^{
ImageLoader *imageLoader = [[ImageLoader alloc] init];
[imageLoader loadImageWithURL:url completionHandler:^(UIImage *image, NSError *error) {
if (image) {
item.image = image;
}
//NOTE: if item object is referenced in this block,
//then there is no missed dispatch_group_leave call.
dispatch_group_leave(group);
}];
});
}
// Non-blocking wait
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// shouldn't take more than 5 secs to load all images
dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)));
dispatch_async(dispatch_get_main_queue(), ^{
completionHandler(nil);
});
});
}
Here's my guess. It's only a guess because you haven't posted the code of anything to do with ImageLoader.
If -loadImageWithURL:completionHandler: operates asynchronously i.e. it uses an async dispatch queue itself then you could be right that it is being deallocated before the load finishes. This is because its lifetime is just the scope of the for block it is declared in.
In fact, there is no reason why that method needs to do stuff asynchronously, because you have already got it in an asynchronous block. Just have a synchronous method and call dispatch_group_leave() after the method finishes.
EDIT
Given that you have no control over ImageLoader and -loadImageWithURL:completionHandler: operates asynchronously without any help from you, you should remove the dispatch_async wrapper around the call. You'll still have the problem of the ImageLoader being deallocated, but that can be avoided by putting each ImageLoader in a array when you create it.
The code would look something like this:
- (void)loadGroupImagesAsyncWithCompletion:(void(^)(NSError *))completionHandler
{
NSMutableArray* loaders = [[NSMutableArray alloc] init];
dispatch_group_t group = dispatch_group_create();
int index = 0;
for (Item *item in items) {
char queueLabel[30] = {0};
sprintf(queueLabel, "loader%d", index);
dispatch_queue_t queue = dispatch_queue_create(queueLabel, NULL);
dispatch_group_enter(group);
ImageLoader *imageLoader = [[ImageLoader alloc] init];
[imageLoader loadImageWithURL:url completionHandler:^(UIImage *image, NSError *error) {
if (image) {
item.image = image;
}
//NOTE: if item object is referenced in this block,
//then there is no missed dispatch_group_leave call.
dispatch_group_leave(group);
}];
[loaders addObject: imageLoader];
}
// Non-blocking wait
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// shouldn't take more than 5 secs to load all images
dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)));
dispatch_async(dispatch_get_main_queue(), ^{
completionHandler(nil);
});
[loaders removeAllObjects];
});
}