objc.selector and objc.signature

I’ve been trying to learn PyObjC by going through Aaron Hillegass’s excellent Cocoa Programming for Mac OS X, doing the exercises in Python instead of Objective C. For the most part the translation between Python and Objective C provided by PyObjC is remarkably seamless, but of course I immediately ran into one of the seams.

I was trying to bind an NSTableView to an array stored in a model object, by way of an NSArrayController. The array was named “employees”, and so I needed to implement collection accessors for inserting and removing items, following the patterns insertObject:in<Key>AtIndex: and removeObjectFrom<Key>AtIndex:, where <Key> is the capitalized name of the collection. So in Objective C these would look like:


- (void) insertObject:(id) obj inEmployeesAtIndex: (unsigned int) index;
- (void) removeObjectFromEmployeesAtIndex: (unsigned int) index;

The translation to Python is simple, just replace colons with underscores and add the self parameter:


def insertObject_inEmployeesAtIndex_(self, obj, index):
def removeObjectFromEmployeesAtIndex_(self, index):

I ran the app, and got an error as soon as I tried to remove an item from my table, causing the removeObjectFromEmployeesAtIndex_ method to be called. It seemed that the index parameter was being set to None (Python’s nil). How was that happening?

The problem was the type of the index parameter — Cocoa was passing an unsigned int, which happened to be zero. But PyObjC defaults to treating all parameters as objects, so that zero was treated as the nil object. PyObjC knows about the methods of lots of Cocoa classes, and can construct special cases so they all work automatically, but it couldn’t know about this call — it couldn’t know that I’d name my array employees, and therefore need those specific collection accessors. I had to find a way to tell PyObjC that my functions take an unsigned int as a parameter.

Unfortunately, the documentation for how to do this is scattered around. Examples are mentioned in passing in the introduction and How to wrap an Objective-C class library documents, and I got the idea that the selector and signature functions in the objc module were involved. So I fired up the Python interpreter and typed print objc.selector.__doc__ and print objc.signature.__doc__. But the most helpful reference was Geoff Wilson’s Showing a NSSavePanel as a sheet, a blog post that described solving a similar problem with an NSSavePanel callback.

The solution, was to follow the definition of insertObject_inEmployeesAtIndex_ with:

insertObject_inEmployeesAtIndex_ = objc.selector(
insertObject_inEmployeesAtIndex_,
signature = 'v@:@I'
)

and to follow the definition of removeObjectFromEmployeesAtIndex_ with:

removeObjectFromEmployeesAtIndex_ = objc.selector(
removeObjectFromEmployeesAtIndex_,
signature = 'v@:I'
)

In Python 2.4 it’s a bit simpler; you can preceed the definition of insertObject_inEmployeesAtIndex_ with:

@objc.signature('v@:I')

and the definition of removeObjectFromEmployeesAtIndex_ with:

@objc.signature('v@:I')

objc.selector or objc.signature tell PyObjC what sort of arguments to expect from Objective C. Figuring out the signature is the one tricky part, but fortunately these functions are simple. Apple documents the supported type encodings. For example, 'v@:I' indicates a function that returns void ('v'), is an object method ('@:'), and takes one unsigned integer argument ('I'). Things get more complicated with pointers, output parameters, etc. I do think it would be helpful if the PyObjC documentation included an extensive collection of example method signatures, along with a more explicit explanation for when objc.selector and objc.signature are required.

Advertisements

8 Responses to objc.selector and objc.signature

  1. I wish I could take credit for pseudofish.com, but I can’t. The author there (Geoff Wilson, who kind of looks like a more attractive version of me) deserves proper citation.

    Cheers,
    Jonathan Saggau

  2. Jim Matthews says:

    Thanks for the correction! I’ve edited the post. Your blog was one of the ones I came across as I tried to figure this problem out — thanks for posting your experiences with PyObjC.

  3. ian says:

    Thank you for this concise explanation! I’ve been struggling to get a struct back from ObjC for the longest time! This was the missing piece!
    Thank you,
    Ian

  4. […] can find more information about this bit of weirdness over at Jim Matthews Blog or, to a very limited extent, in the PyObjC class wrapping […]

  5. pepijndevos says:

    Hi, The link to the signature is not working, where can I find this information?

    Thanks!

  6. Jim Matthews says:

    I’m not finding the broken link — which one is it?

  7. rezoant says:

    Yeah the Apple link is gone. The information is presumably still in the document it redirects to, but I haven’t found it yet. This is what I’ve gathered so far:
    @ – object
    i – integer (4-byte?)
    : – selector (interned string?)
    @: – instance method (put after return type, before args)
    v – void
    f – float
    d – double
    I – unsigned integer? (https://jimmatthews.wordpress.com/2007/07/12/objcselector-and-objcsignature/)

    Hope it helps.

  8. rezoant says:

    Oh, the broken link is http://developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC/Articles/chapter_5_section_7.html#//apple_ref/doc/uid/TP30001163-CH9-TPXREF165

    It redirects to the index and it seems to have been reorganized. Sorry for double posting

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: