Saturday, August 2, 2008

Cocoa Selectors

I'm more than a little out of my depth here, but I'm going to speak up anyway.

Method names in Cocoa may be referred to loosely as "selectors." For example, on p. 34 Hillegass refers to insertObject:atIndex: as a selector, and Bob Ippolito does the same here. But a selector is really a special thing, not just a string, although it is connected to a string (the method name) by a lookup table. The system also stores type information about the function arguments. This latter aspect makes everything a little bit more complicated. (Much / all of what I'm saying here has been said before e.g. here. But I don't think that's a bad thing. Google isn't perfect yet).

According to the docs

the messaging routine has access to method implementations only through selectors, so it treats all methods with the same selector alike. It discovers the return type of a method, and the data types of its arguments, from the selector.


I don't think they mean "all methods," but "all methods in the same class."

The @selector() directive lets Objective-C source code refer to the compiled selector. Usually, we don't need to do this. The PyObjC bridge takes care of everything "under the hood."

Why does it matter? Well, we do need to refer to a selector any time we have to tell Cocoa what method to call. For example, in Objective-C we might do

[myButtonCell setAction:@selector(reapTheWind:)];

or this in PyObjC:

oop = NSRunLoop.currentRunLoop()
loop.performSelector_target_argument_order_modes_(
"delayedResetFocus:",
self,
None,
9999,
NSArray.arrayWithObject_(NSDefaultRunLoopMode)
)

def delayedResetFocus_(self):
self.myWindow.makeFirstResponder_(self.itemDescription)

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

Recently in PyObjC we would reformulate this last part as


@objc.signature('v@:')
def delayedResetFocus_(self):
self.myWindow.makeFirstResponder_(self.itemDescription)

Or, we may want to run a process for the user that takes some time. Meanwhile, we could put up a sheet that is "modal" for the application, with a progress bar and a button giving the option to cancel. To do that, we will need to pass in a selector to the method we want to call when the sheet returns.

But the string passed to objc.signature is a shorthand for the types of the arguments and return value of the function, and it can get pretty complicated. Here is a little code to explore this. If you need a signature for a method, try to find a Cocoa method with the same arguments and / or return values and then ask it what its signature is.

import objc
from Foundation import *

sel = NSString.stringWithString_
print sel.selector
# stringWithString:
print sel.signature
# @12@0:4@8

sel = NSString.length
print sel.selector
# length
print sel.signature
# I8@0:4

class A(NSObject):
@objc.signature('i8@0:')
def anInt(self):
return 2

@objc.signature('v@:')
def notMuch(self):
print 'Hi'



a = A.alloc().init()
print a.anInt()
# 2
a.notMuch()
# Hi
sel = A.anInt
print sel.selector
# anInt
print sel.signature
# i8@0:
sel = A.notMuch
print sel.selector
# notMuch
print sel.signature
# v@: