Question

Is it possible to use the AppName-Prefix.pch file to import a given header file in all source files except one?

Problem: I have followed the approach described here: https://stackoverflow.com/a/617559/1062572 to overwrite a C function call, namely the GCD dispatch_async function.

Now I need to import the header file intercept.h in all my source files and for that I tried to use the AppName-Prefix.pch file. However this also imports the header file in my implementation file intercept.m. This cause an endless call loop because I try to call the original dispatch_async in there.

Heres my header file intercept.h:

#ifdef INTERCEPT
#define dispatch_async(queue, block) my_dispatch_async(queue, block)
#endif

And heres is my implementation file intercept.m:

void my_dispatch_async(dispatch_queue_t queue, dispatch_block_t block) {
NSLog(@"\nBlock is enqueued!\n");
dispatch_async(queue, ^{
    NSLog(@"\nBlock is dequeued!\n");
    block();
    NSLog(@"\nBlock has executed!\n");
});
}

Here is my Prefix.pch file:

#ifdef INTERCEPT
#import "Intercept.h"
#endif

How can I import the header file in all my sources, with the implementation file as the only exception? I hope it can be done without manually having to insert an import statement in every source file. And without writing a script to do it. ;)

One thing that confuses me even more is: Actually I have the implementation file in a compiled library (Testing.a), so why is the header file imported in it?

Even more information: I am writing a test framework that waits until all async tasks has completed before checking the results. That why I override dispatch_async. Any other suggestion is welcome. :)

Also I have noticed this answer: https://stackoverflow.com/a/617606/1062572 However it seems that this will not work on OSX, hence not iOS which is my target.

All these approaches will only overwrite the function call in my own source code. Actually I want it to overwrite it everywhere. However for this question I am satisfied if it works for my own source code.

Was it helpful?

Solution

Unfortunately, its not possible. Prefix headers are compiled, cached and included in every file during compilation. You can't tell which files to ignore. However you can ignore it if you already included the Intercept.h. Here's how:

1- Remove ifdef INTERCEPT condition around #import "Intercept.h" from Prefix.pch. You don't need it there.

2- Update your Intercept.h to:

#ifndef INTERCEPT_H
#define INTERCEPT_H
#define dispatch_async(queue, block) my_dispatch_async(queue, block)
#endif

What happens here is that you first checked whether INTERCEPT_H is already included/defined in current definition, if not, you defined it in the next line and then defined your macro. Now, the #ifndef INTERCEPT_H condition will return false if it has already included its content in the same context.

Hope it helps.

OTHER TIPS

Even more information: I am writing a test framework that waits until all async tasks has completed before checking the results. That why I override dispatch_async. Any other suggestion is welcome. :)

Depending on your situation, this is likely solvable in better ways. As long as you have access to which queues are being used, it's pretty simple. Consider this API, where you are passing the queue to be used:

[object doSomethingAsyncWithCompletion:block1 queue:myQueue];
[object doSomethingElseAsyncWithCompletion:block2 queue:myQueue];
[object doMoreAsyncWithCompletion:block3 queue:myQueue];

Now, you want to wait until all those finish. Assuming this is a custom concurrent queue (not one of the global queues), just use a barrier:

dispatch_barrier_sync(myQueue, ^{
  NSLog(@"This will not run until everything else before it on the queue finishes.");
}

But what if you don't know what queue is being used? Well, as long as you control the completion blocks, that's fine, too. (See Waiting on Groups of Queued Tasks.)

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();

dispatch_block_t doneBlock = ^{
  dispatch_group_leave(group);
}

dispatch_group_enter(group);
[object doSomethingAsyncWithCompletion:doneBlock queue:myQueue];
dispatch_group_enter(group);
[object doSomethingElseAsyncWithCompletion:doneBlock queue:myQueue];
dispatch_group_enter(group);
[object doMoreAsyncWithCompletion:doneBlock queue:myQueue];

// Wait for all the doneBlocks to fire
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_release(group);

Of course you could also do this with a semaphore. That's sometimes easier if you just want to convert a single operation from asynchronous to synchronous.

I'd recommend these kinds of approaches rather than trying to hijack dispatch_async itself.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top