PyObjC is a proxy between Objective-C and Python and allows access to Python classes from Objective-C and vice versa. PyObjC also allows Python to subclass Objective-C classes, and these subclasses will be natively visible to both runtimes.
All Python objects can be accessed from Objective-C through proxy objects.
Whenever a Python object crosses the line from Python to Objective-C a proxy
object is created (of class OC_PythonObject
, a subclass of NSProxy
).
This proxy object will forward all method calls from Objective-C to Python, and
will return the results back to Objective-C.
See the section 'Method protocol' for a description of how PyObjC translates between Python and Objective-C method calls.
A number of Python types/classes are treated specially:
int
, float
, long
) are translated into
NSNumber
instances. Their identity is not preserved across the bridge.str
is proxied using OC_PythonString
, a subclass of
NSString
. A Python str
may be used anywhere a NSString
is
expected, but unicode
should be used whenever possible.
OC_PythonString
will use the default encoding of NSString
, which is
normally MacRoman but could be something else.unicode
is proxied using OC_PythonUnicode
, a subclass of
NSString
. A Python unicode
may be used anywhere a NSString
is expected.dict
is proxied using OC_PythonDictionary
, a subclass of
NSMutableDictionary
. A Python dict
may be used anywhere
an NSDictionary
is expected.list
and tuple
are proxied using OC_PythonArray
, a
subclass of NSMutableArray
. Python list
or tuple
objects
may be used anywhere an NSArray
is expected.str
and unicode
, are proxied using OC_PythonData
, a NSData
subclass.
Objects that implement the Python buffer API such as buffer
,
array.array
, mmap.mmap
, etc. may be used anywhere a NSData
is
expected.These special cases allow for more transparent bridging between Python and Objective-C.
Objective-C objects are accessed through proxy objects that forward method
calls from Python to Objective-C. All proxy objects are instances of
objc.objc_object
, or a subclass of this class.
See the section 'Method protocol' for a description of how PyObjC translates between Python and Objective-C method calls.
Objective-C classes are also accessed through proxy objects, but those are
subclasses of objc.objc_class
. The proxies for Objective-C classes are
classes in Python.
Instances are created by calling allocator class methods (like +alloc
or
factory methods). Objective-C instances can not be created by using the class
as a factory function.
It is possible to create subclasses from Objective-C classes using the normal mechanism. There are some limitations though:
Limitations 3 and 4 can be worked around by creating a category using
objc.Category
.
PyObjC provides support for Objective-C protocols. The type
obc.informal_protocol
can be used to shadow protocol definitions in
Objective-C. Instances of this type can be used as a superclass when defining
a subclass of an existing Objective-C class. The information in an
'informal_protocol' object will be used to check if the new class does
implement the protocol, and to provide information to the bridge that is needed
to correctly forward methods.
Formal protocols are wrapped by the type objc.formal_protocol
and can be
accessed using the function objc.protocolNamed(...)
. Instances of this
type can be used as a superclass when defining a subclass of an existing
Objective-C class. The class will not be checked to see if it actually
does conform to the protocol. Formal protocols can be created by using
objc.formal_protocol
's constructor.
A subclass of an Objective-C (proxy) class is not only a Python class, but also
an Objective-C class. This means that it is possible to create instances of
these classes from Objective-C using normal Objective-C syntax, although the
class must be located using the function NSClassFromString
or equivalent
(e.g. defining``@class MyPythonClass`` will not work). One of the results of
this feature is that these classes can be used to implement classes that are
defined in Interface Builder NIB files.
Like Python classes, Objective-C classes can have instance variables. Normal
Python instance variables in a Python subclass will only be visible from Python,
and not Objective-C (except when using Key-Value Coding). This means that
normal Python instance variables can not be used as outlets in Interface Builder.
Objective-C visible instance variables can be defined using special properties:
objc.ivar
and objc.IBOutlet
.
The Objective-C class:
@interface MyClass : NSObject { IBOutlet id my_outlet1; IBOutlet id my_outlet2; id my_ivar; int my_int; } // ... @end // MyClass
The Python equivalent:
class MyClass(NSObject): my_outlet1 = objc.IBOutlet('my_outlet1') my_outlet2 = objc.IBOutlet('my_outlet2') my_ivar = objc.ivar('my_ivar') my_int = objc.ivar('my_int', 'i') # ...
There is a straightforward translation from Objective-C method names to Python method names: Concatenate all parts of the Objective-C method name (without any whitespace) and then replace all colons by underscores.
Examples:
(void)myAction:(id)sender <-> def myAction_(self, sender) method:(int)x andY:y <-> def method_andY_(self, x, y)
As can be seen in the examples above, Objective-C allows explicit specification of the types of arguments and the return value, while this is not possible in Python. This is not a problem when calling existing Objective-C methods, because PyObjC will automatically read the needed information from the Objective-C runtime, but it can be a problem when defining new Objective-C methods in Python.
PyObjC therefore provides a function to define the signature of a python method in subclasses of Objective-C classes. This function, objc.selector, should be used whenever defining a method that does not extend or override an existing Objective-C method in the superclass.
The following Objective-C class:
@interface MyClass : NSObject { } -(int)methodWithX:(int)x andY:(float)y; -(void)myAction:(id)sender;
can be defined in Python like this:
class MyClass(NSObject): def methodWithX_andY_(self, x, y): return 0 methodWithX_andY_ = objc.selector(methodWithX_andY_, signature='i@:if') def myAction_(self, sender): pass myAction_ = objc.selector(myAction_, signature='v@:')
In Python 2.4, it is also possible to write this example as such:
class MyClass(NSObject): @objc.signature('i@:if') def methodWithX_andY_(self, x, y): return 0 @objc.signature('v@:') def myAction_(self, sender): pass
The explicit selectors don't really help to increase readability, especially
given the cryptic type signature strings. It is therefore advisable to use other
methods to define the signature if possible, the most likely way to do this
is by using existing objc.informal_protocol
definitions (like
AppKit.NSOutlineViewDataSource
).
Unless explicitly specified as above, or defined by a superclass or protocol,
PyObjC creates a default signature with an object return value, and object
arguments. If no return statement is in the function, then the return value
is void
, such as the above myAction_
. Fortunately, these cases cover
most usage of PyObjC, so it is not often necessary to explicitly define selectors
as above.