PyObjC is a bridge between Python and Objective-C. It allows you to write Python scripts that use and extend existing Objective-C class libraries, most importantly the Cocoa libraries by Apple.
This document describes how to use Objective-C class libraries from Python scripts and how to interpret the documentation of those libraries, from the point of view of a Python programmer.
It is necessary to understand a little bit of Objective-C to use PyObjC, this helps you to better understand the class libraries and makes it easier to read (and translate) example code.
Objective-C is an object-oriented programming language that is an extension of C and borrows heavily from Smalltalk. It features single inheritance with (in theory) multiple root classes and dynamic dispatch of methods. This is basicly the same as Python with single inheritance.
An important difference between Python and Objective-C is that the latter is
not a pure object-oriented language. Some values are not objects, but values
of plain C types, such as int
and double
. These basic C types can also
be used as the types of arguments and the return value of methods.
Object allocation and initialization are explicit and seperate actions in
Objective-C. The former is done by the class-method alloc
, while the
latter is done by instance-methods whose name customarily starts with init
.
Objective-C code looks just like plain C code, with some easily recognizable
extensions for the Object-Oriented parts of the language. And example class
declaration (usually found in .h
files) and implementation (usually found
in .m
files) are listed below). Class declarations are easily recognized as
blocks of code between @interface
and @end
, and simularly the
implementation is between @implementation
and @end
. Calling methods
is done using expressions enclosed with brackets (name?), e.g.
[foo method]
. This is the same as foo.method()
in Python.
A class declaration:
@interface MYClass : MySuperClass { id anInstanceVariable; int anotherInstanceVariable; } +aClassMethod; -(int)anInstanceMethodWithArg1:arg1 andArg2:(BOOL)arg2; @end
A class implemenation:
@implementation MYClass +aClassMethod { id res = [[MYClass alloc] init]; return res; } -(int)anInstanceMethodWithArg1:arg1 andArg2:(BOOL)arg2 { int res; if (arg2) { res = [self fooWith:arg1]; } else { res = [arg1 bar]; } } @end
Objective-C also features exceptions, but as those are mostly used for disaster recovery and not for normal error handling you won't see them very often in example code. The The Objective-C Programming Language if you want to know more about exceptions in Objective-C.
One thing to keep in mind when translating Objective-C snippets to python is
that it is valid to call methods on nil
(that is the NULL pointer). Those
method calls are ignored by the runtime. The value nil
is represented in
Python by the None
, this means that calls to non-existing methods are
not ignored but will raise AttributeError
.
For more information about Objective-C see:
Objective-C classes are visible as (new-style) Python classes and can be subclassed just like normal Python classes. All the usual introspection mechanism work as well, as do __slots__ and descriptors. The major differences between normal Python classes and Objective-C classes are the way you create instances and the fact that Objective-C methods have odd names.
You can use multiple inheritance with Objective-C classes, as long as the Objetive-C is the first base-class and there is only one Objective-C base-class. E.g. it is not possible to subclass from the Objective-C classes at the same time. Multiple inheritance should also not be used to mix-in different implementations for Objective-C methods, that will not work and you won't get errors about this.
Another thing to keep in mind is that the names of Objective-C classes must
be unique, without taking modules into account. That is, it is not possible
to have two modules that define a class with the same name. If you write classes
that will be used outside of a single project it is customary to pick a
(short) prefix and stick that in front of all class names, e.g. Apple NS
as the prefix in the Cocoa libraries.
As described in Objective-C for PyObjC users the creation of Objective-C
objects is a two-stage process. You first call the class method alloc
, and
then call some variation of init
to initialize the objects. The newly
created object is the result of the call to init
. Most classes have
convienence class methods that combine the calls to alloc
and init
.
Objective-C methods are bridged to Python callables. Because Objective-C method names can contain colons it is necessary to translate methods names. The rules for translation are:
someMethod:withFoo:andBar:
someMethod_withFoo_andBar_
Wrapped/bridged methods (and functions) have the same number of arguments as the corresponding Objective-C method or function, unless otherwise noted in the documentation (Notes on supported APIs and classes on MacOS X for Cocoa on MacOS X).
One group of exceptions to this rule can be described in a global way. Some
methods and functions have pointers as arguments, specifically pointers to
a single value that is passed in and/or out of the function. These arguments
are sometimes called pass by reference arguments, and can be subdived into
three types of arguments: in
arguments are used to pass data to the
function, out
arguments are used to pass data from the function (e.g. and
additional return value) and inout
arguments are a combination of the two.
The in
and inout
arguments for a method are also present in the Python
interface for that method (or function). In python the value passed to the
function is a "normal" argument. Out
arguments are not present in the
argument list of the Python function.
If a function (or method) has one or more output arguments (out
or
inout
) the output values are returned as part of the return value of the
method. That is, the return value of the function is a tuple containing
the return value of the C function (or method), followed by the values of
the out
in inout
arguments in the order the are present in the argument
list. If the C function (or method) has return type void
, the tuple contains
only the output arguments. As a final complication, methods with a single output
argument and return type void
, have the value of the output argument as
the return value (e.g. not a tuple containing the return value).
The rules for pass by reference arguments may look quite complicated, but it turns out this is very straightforward when working with them.
As an example of a method with two output arguments, NSMatrix has a method
named getNumberOfRows_columns_
with the following signature:
(void)getNumberOfRows:(int *)rowCount columns:(int *)columnCount
You use this method in python like this:
rowCount, columnCount = matrix.getNumberOfRows_columns_()
When you define methods in a subclass of an Objective-C class, the bridge has to tell the Objective-C runtime what the signature of those methods is. The basic rule is that all arguments as well as the return value are objects (just like with normal Python methods). The bridge will automaticly pick a better signature when it has more information available. Specifically, if you overide an existing method the bridge will assume you want to use the same method signature. And furthermore, if you implement a method in an (informal) protocol known to the bridge it will use the signature from the corresponding method in that signature.
The end result is that you almost never have to add information about the
signature of methods. The only known case where you have to tell the bridge
about the signature of a method is the call-back method for sheets. You can
use the function PyObjCTools.AppHelper.endSheetMethod
to create an object
that contains the right information. This function is used like
staticmethod
and classmethod
(as introduced in Python 2.2).
For complete control of the mapping to Objective-C you can use the function
objc.selector
. See the documentation of the objc
module for the
arguments you can use with this function. It is normally used like this:
class MyObject (NSObject): def someMethod_(self, arg): pass someMethod_ = objc.selector(someMethod_, ...)
The Cocoa libraries, and most (if not all) other class libraries for
Objective-C use explicit reference counting to manage memory. The methods
retain
, release
and autorelease
are used to manage these
reference counts. You won't have to manage reference counts in Python, the
bridge does all that work for you.
The only reasons reference counts are mentioned at all are to tell you about ignoring them, and more importantly to introduce you to some issues w.r.t. reference counting.
It turns out that Cocoa uses a primitive form of weak references. Those are not true weak references as in Python, but use-cases where an object stores a reference to another object without increasing the reference count for that other object. The bridge cannot solve the issues this introduces for you, which means that you get hard crashes when you're not carefull when dealing with those weak references.
The basic rule to deal with weak references is: make sure objects stays alive as long as someone might have a weak reference to them. Due to the way the bridge works, this means that you must make sure that you don't create weak references from Objective-C to a plain Python object. The Python object stays alive, but the proxy object as seen by the Objective-C code is actually an autoreleased object that will be cleaned up unless the Objective-C code increases its reference count.
The document Notes on supported APIs and classes on MacOS X contains
information about classes that work with weak references. The most important
are notification centers and NSOutlineView
, to be exact: the outline view
stores weak references to the objects return by the method
outlineView:child:ofItem:
of its data source. The easiest way to avoid
crashes with outline views is to make sure that you model for the view uses
subclasses of NSObject
to represent the nodes in the outline view.
Another gotcha is when you're manually allocating and assigning delegate(-like)
objects: most of the time obj.setDelegate_()
will not retain the
delegate, so you must keep a reference manually.
Cocoa defines a number of formal and informal protocols that specify methods that should be implemented by a class if it is to be used in a specific role, such as the data source for an NSTableView.
Those protocols are represented by instances of objc.informal_protocol
. The
only ones that have to care about these objects are the maintainers of
wrappers around Objective-C frameworks: they have to keep these protocol
wrappers up-to-date.
PyObjC will automaticly use the information in the informal_protocol
objects to add the right method signatures to methods, and to warn about
classes that partially implement a protocol.
Cocoa frameworks are mapped onto Python packages with the same name, that is
the classes, constants and functioins from the AppKit framework are available
after you import AppKit
in your Python script.
These helper modules contain only functions, constants and classes that
wrap items in the corresponding framework. All utility functions and classes
are located in the PyObjCTools
package and objc
module. Note that it
is possible to use pydoc
(or the help()
) function with the framework
wrappers, but that this is not very usefull for the entire module due to the
size of these modules.
This makes it easier to find documentation for an item: if you import it from the wrapper module for an Objective-C framework the documentation for that item can be found in the documentation for the framework, otherwise the item is documented in the PyObjC documentation.
The module PyObjCTools.NibClassBuilder
can be used to make working with
NIB files more convenient. This module can be used to extract information
about classes from NIB files, both as a standalone tool generating source code
and during runtime. See the online documentation for this module for more
information.
PyObjC includes a number of examples that show how to use Cocoa from Python. The PyObjC Example index contains an overview of those examples.
More information on Cocoa programming can be found at:
When you create a thread and want to use PyObjC from that thread you will
have to create an NSAutoreleasePool
in that thread and clean it up when
you're done. The easiest way to that is to create an instance of that class
bound to a local variable. If the thread is long-lived you may want to arrange
for recycling the pool once in a while.
There are some limitiation w.r.t. threading. You cannot use NSThread
to
create new threads, but must use the python primitives instead.
You must also make sure that Objective-C only makes calls to Python from a
thread that owns the Python GIL (that's also the reason for not being able
to use NSThread
to create new threads). This restriction will be lifted
in a future version of PyObjC (at least when using Python 2.3).
In Python you can use the method __del__
to clean up resources when your
object is garbage collected. In Objective-C/Cocoa this is done with a method
named dealloc
.
In PyObjC you should always use the __del__
method, the dealloc
method
can safely be ignored and the bridge will complain when you try to override
this method.
There are two different ways to build applications with PyObjC. There are no major advantages to using either one of them, use the one that is most convenient to you.
PyObjC includes a copy of the bundlebuilder
module. This module will be
part of the Python 2.3 MacPython release and offers a way to build
distutils-style scripts for building (standalone) applications.
An example buildapp.py
script:
from bundlebuilder import buildapp buildapp( name = 'iClass', mainprogram = "main.py", resources = ["English.lproj", "datasource.py" ], nibname = "MainMenu", )
During development you typically invoke it from the command line like this:
python buildapp.py --link build
This will build an application bundle in a folder named build
in the
current folder. The --link
option tells bundlebuilder
to add symbolic
links to the application bundle instead of copies of your source and resource
files, allowing you to edit them without having to rebuild the application. To
build a standalone application, either use --standalone
or
--semi-standalone
. The latter will put all used modules that are not in
Python's standard library into the application bundle. The result will still
depend on an installed Python, but yields a relatively compact application.
--standalone
will cause bundlebuilder
to include everything needed
into the app bundle, including the entire Python runtime. This is useful if
you're using a different version of Python that the one that comes with MacOSX
10.2, or if you fear that a future version of OSX may come with an
incompatible Python version.
The online documentation for bundlebuilder
contains more information on
building buildapp.py
scripts and how to invoke them. There are plenty of
example buildapp.py
scripts in the various Examples subfolders.
PyObjC includes a number of Project Builder templates that can be used to build (standalone) applications. Those templates are used like any other Project Builder template. The only non-obvious detail is that you have to add your sources as resources, but Project Builder usually does the right thing when you add a new file.
The templates will build an application that makes use of the installed copy
/usr/bin/python
(e.g. the one shipped by Apple in MacOS X 10.2) and will
copy the PyObjC modules into the application bundle. This means that this
application bundle should be useable on any MacOS X 10.2 system.
See the documentation for the templates for more details.
MacOS X 10.3 seems to ship with Python 2.3 as /usr/bin/python. This means that
standalone applications build using the Project Builder templates will start
to complain about Python ABI versions in the Console
when you run them
on MacOS X 10.3. These are harmless messages caused by the extension modules
in PyObjC.