Invoke any Method on any Thread

I previously wrote a blog post titled Performing any Selector on the Main Thread detailing a convenience category on NSInvoication for easily creating invocation objects that could be invoked on any thread.

This category has served me well, and even got traction in the iOS developer community, so I never bothered to stop and think if there exist an even better solution.

Especially now that GDC exists, and doing an inline block to invoke on the main thread or a background queue is easier than ever before.

Until Today

Traveling home by bus as usual I got a flash of genius; Why not use proxy objects? Calling a method on the main thread should be no harder then this;

The old NSInvocation solutions have a few drawbacks. First of all there is no code completion for the arguments, secondly there is not even basic type checking, and lastly the implementation is quite ABI specific.

The NSObject class already has support for method forwarding, and wrapping method calls up into NSInvocation instances. Why not use seriously battle proven code that Apple provides.

The Skeleton

The implementation for -[NSObject mainThreadProxy] will be childishly simple:

The CWMainProxy class is a simple private helper, that will perform all of the heavy lifting. The interface and life time code is very small:

The Meaty Parts

Now comes the tricky parts; actually act like a proxy. The proxy is a simple NSObject subclass, first it needs to be able to fake to any caller that it can respond to everything that the proxies target responds to.

Next up if the object claims it responds to a selector but do not have an actual implementation, then we must provide a NSMethodSignature so the run-time can create a proper NSInvocation for us.

And finally we need to actually forward the NSInvocation call to the main thread, as per the callers request:

Conclusions

Given some thought I regard this solution to be much more elegant than my old solution. And it even yields less and more readable code than using GCD on iOS 4 and later:

  • The implementation is much smaller, and relies on the Foundation framework functionality, not my own Objective-C ABI hacks.
  • It is less code to write to use the functionality.
  • Xcode can do proper code completion and basic type checking.
  • It is compatible all the way back to iPhone OS 2, or Mac OS X 10.0, if you should care.

The CWFoundation project on Github has a more complete implementation. With more support for optional blocking, delays, and more proxies not only for the main thread but also for background threads and operations queue.

You are seldom alone with great ideas, so a nod to Joachim Bengtsson who has written about an invocation grabber before.

3 Comments

  1. This is really an invaluable technique, and can be extended in interesting ways. For example, when Spotify starts up, it will deserialize the UI state of its last run, and start creating view controllers. Some of these might want to do things that only make sense when everything is done; this is done by the StateController exposing:
    -(id)invokeWhenDoneOn:(NSObject*)object;
    This returns an invocation grabber that has already been added to an array of things to do; and that that array is invoked in order once the state deserialization is completed. This is similar to “invoke on next runloop iteration” (by doing a performAfterDelay with a 0 delay), but with a more controlled execution point.

  2. If somebody wants to check out an other implementation of proxy based thread invoking then go to https://github.com/moszi/AOP-in-Objective-C

Trackbacks for this post

  1. iOS 개발 뉴스 : 8월 10일. « 김종령의 생각

Leave a Reply