Tracing Objective-c Message Calls
Developing native modules for extending Titanium Mobile is fun, but at times it can be quite frustrating, because of the lack of adequate documentation. The module development guides available in the Appcelerator documentation wiki only show the tip of the iceberg, and while exposing functionality through simple proxy classes is quite straightforward, things can become quickly complicated when implementing ViewProxies, i.e. those that represent view objects to be shown onscreen.
Actually, I’ve been mostly successful creating self contained native views, however problems started arising when I wanted to add Titanium intrinsic ViewProxies (e.g. Ti.UI.View) to my custom native views in iOS. In particular, something I’m still struggling to understand is the exact life cycle of TiViewProxy and their companion TiUIView objects, especially for what concerns the management of layout properties: since Titanium view objects are rendered lazily, there is a quite convoluted (at least at my eyes) mechanism in place for rearraging the layout of whole view hierarchies, which is managed by the TiViewProxy class.
Since Titanium is an open source project, we can always take a look at how things are implemented. Indeed, I think that reading the code of the SDK is a good way to really understand how Titanium Mobile works under the hood, and I plan to share some of my findings in some future post. However, reading source code can be a highly time consuming practice. Also stepping through the code with the debugger can help in some cases, but the risk is to lose a higher level view.
Since I wanted a more “dynamic” look at what happens to my viewproxy objects I thought that tracing the exact sequence of message calls involved between the creation and the onscreen rendering of a simple view could help. By googling around I’ve found some useful resources on the topic here and in the Mac OS X Debugging Magic guide from Apple. It tuns out that this is a feature that can be enabled in any objective-c development environment (i.e. either for MacOS, or iOS targets) by setting the
NSObjCMessageLoggingEnabled environment variable to
YES in XCode before running the project. In XCode 4 this can be easily done through the
"Product->Edit Scheme" menu, by selecting the
"Arguments" tab and adding a new environment variable (under the
"Environmnet Variables" section), as shown in the following picture:
Then we can run the project, and all the message calls handled by objective-c objects (and classes, for static methods) are logged into a file named
/tmp/msgSends-<pid>, where pid is the process ID.
The result is quite overwhelming and looks like an immense sequence of lines like the followings:
+ NSObject NSObject initialize + NSRecursiveLock NSObject initialize + NSRecursiveLock NSObject new + NSRecursiveLock NSObject alloc + NSRecursiveLock NSRecursiveLock allocWithZone: - NSRecursiveLock NSRecursiveLock init + NSRecursiveLock NSObject new + NSRecursiveLock NSObject alloc + NSRecursiveLock NSRecursiveLock allocWithZone: - NSRecursiveLock NSRecursiveLock init + NSLock NSObject initialize + NSLock NSObject new + NSLock NSObject alloc + NSLock NSLock allocWithZone: - NSLock NSLock init + NSLock NSObject new + NSLock NSObject alloc + NSLock NSLock allocWithZone: - NSLock NSLock init + NSLock NSObject new [...]
Now, what remains to do is to filter this file, searching for instances of TiWindowProxy, TiViewProxy and TiUIView, for later inspection:
$ grep 'TiWindowProxy\|TiViewProxy\|TiUIView' /tmp/msgSends-31489 > call-seq.txt
and the gems begin to surface:
+ TiViewProxy NSObject initialize + TiWindowProxy NSObject initialize - TiUIWindowProxy TiViewProxy init - TiUIWindowProxy TiWindowProxy _configure - TiViewProxy TiProxy _configure - TiUIWindowProxy TiViewProxy _initWithProperties: […] - TiUIWindowProxy TiWindowProxy open: - TiUIWindowProxy TiWindowProxy open: - TiUIWindowProxy TiWindowProxy isModal: - TiUIWindowProxy TiWindowProxy argOrWindowProperty:args: - TiUIWindowProxy TiWindowProxy isFullscreen: - TiUIWindowProxy TiWindowProxy argOrWindowProperty:args: - TiUIWindowProxy TiWindowProxy openOnUIThread: - TiUIWindowProxy TiWindowProxy isRootViewAttached - TiUIWindowProxy TiViewProxy view - TiUIWindowProxy TiViewProxy viewWillAttach - TiUIWindowProxy TiWindowProxy newView - TiUIWindowProxy TiViewProxy appFrame + TiUIView NSObject initialize - TiUIWindow TiUIView touchesBegan:withEvent: - TiUIWindow TiUIView setFrame: - TiUIWindow TiUIView proxy + TiViewProxy NSObject class - TiUIWindow TiUIView setProxy:
I need to say that extracting useful information from this sequence is not that easy. However my plan is to follow it as a “map” while stepping through the code with the debugger.
Unfortunately I have no results to show yet. Just stay tuned for a follow up post where I hope I’ll be able to layout at least a portion of the TiViewProxy object lifecycle.