Source

pyobjc-docs / pyobjc-core / Doc / general / misc.rst

Objective-C for PyObjC users

Object allocation and initialization are explicit and separate 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.

Overview of the bridge

Classes

Objective-C classes are visible as (new-style) Python classes and can be subclassed just like normal Python classes. All the usual introspection mechanisms work as well, as do __slots__ and descriptors. The major differences between normal Python classes and Objective-C classes are the way that instances are created and initialized, and the fact that Objective-C selectors look strange when translated to Python methods.

Multiple inheritance may be used when subclassing an Objective-C class, so long as the Objective-C class is the first base class and there is only one Objective-C base class. The Objective-C runtime does not support multiple inheritance. These mix-in classes should not contain different implementations for Objective-C methods. To achieve this behavior, Categories should be used instead.

Another thing to keep in mind is that the names of Objective-C classes must be globally unique per process, including across Python modules. That is, it is not possible to have two Python modules that define a class with the same name. It is conventional to choose class names with a short prefix that uniquely identify your project or company. For example, Apple uses NS as the prefix for all classes in the Cocoa libraries. Note that the NS prefix made much more sense when it was called NeXTstep, but persists to this day for compatibility reasons.

As described in Objective-C for PyObjC users the creation of Objective-C objects is a two-stage process. To initialize objects, first call a class method to allocate the memory (typically alloc), and then call an initializer (typically starts with init). Some classes have class methods which perform this behind the scenes, especially classes that create cached, immutable, or singleton instances.

Messages and Functions

Objective-C methods are bridged to Python methods. Because Objective-C message dispatch syntax can not be translated directly to Python, a few simple translations must take place. The rules for these translations are:

  1. Replace all colons in the selector with underscores:

    • someMethod:withFoo:andBar: translates to someMethod_withFoo_andBar_
  2. If the result class or raise (Python keywords), append two underscores:

    • class translates to class__
    • raise translates to raise__
  3. Use this translated selector as a normal Python method. The arguments must be passed in the same order, and the number of arguments passed will normally be equal to the number of underscores in the method name; exceptions to this rule and the behavior of "result" are mentioned below.

    result = [someObject someMethod:firstArg withFoo:foo andBar:bar];
    

    translates to

    result = someObject.someMethod_withFoo_andBar_(firstArg, foo, bar)
    

Note that it is currently not possible to support methods with a variable number of arguments from Python. These selectors must be wrapped by custom Objective-C code in order to be accessible by Python.

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 Mac OS X for Cocoa on Mac OS X).

Most methods or functions that take or return pointers to values will be an exception to this rule if it is callable from Python at all. In Objective-C terminology, there are three kinds of pointers that can be used in a method:

in:

Used to pass data by reference to the function. This is not a special case from Python.

Instead of a regular value you may also pass in the value objc.NULL, when you do that the Objective-C method will receive a NULL pointer instead of a pointer to your value.

out:

Used to pass data from the function (e.g. an additional return value).

Pass in either None or objc.NULL for output arguments. to the method. If the value is objc.NULL the Objective-C code will receive a NULL pointer for this argument, otherwise it will receive a valid pointer.

inout:

A combination of in and out (a value is passed by reference, and mutated upon return). Unlike out, these arguments remain in the argument list, and thus do not have an effect on the number of arguments a method expects. See below for notes on how inout arguments change the return value.

Instead of a regular value you may also pass in the value objc.NULL, when you do that the Objective-C method will receive a NULL pointer instead of a pointer to your value.

In order to determine what the return value of such an exceptional message will look like, you must "make a list" of the return values with the following rules:

  1. If the return type of the method or function is not void, add it to the list.
  2. For each argument in the method or function, add it to the list if it is out or inout. When objc.NULL was used as the argument value it will also be used as the result value.

After creating this list, you will have one of three cases:

Empty:
The return value of this call will always be None.
One element:
The return value of this call will correspond to the one element of the list.
More than one element:
The return value of this call will be a tuple in the same order as the list.

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 implements a selector named getNumberOfRows:columns: with the following signature:

-(void)getNumberOfRows:(int *)rowCount columns:(int *)columnCount

This method is used from Python like this:

rowCount, columnCount = matrix.getNumberOfRows_columns_(None, None)

When a function or method has an array of values and the length of that array as arguments, None may be passed as the length to specify that the length of the given sequence should be used.

Python's array.array type may be used to represent a C array if the typestr and size match what is expected by the selector. Numeric, numarray, and other third party array types are not supported at the moment.

When defining methods in an Objective-C subclass, the bridge must provide type signatures for each method to the Objective-C runtime. The default type signature is for all arguments as well as the return value to be objects (just like with normal Python methods). If there is no return statement in the implementation, then the return value will be void. The bridge will automatically pick a better signature when it has more information available. Specifically, a method overrides an existing method, the bridge will assume you want to use the same method signature. Furthermore, if the method is implemented 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 it is rarely necessary to explicitly add information about the signature of methods. For the two most common cases where this is necessary, we have provided convenience decorators (used like staticmethod or classmethod):

objc.accessor:
Use this to wrap a Key-Value Coding or Key-Value Observing compliant accessor.
PyObjCTools.AppHelper.endSheetMethod:
Use this to wrap the implementation of a sheet's "didEndSelector" callback.

For complete control of the mapping to Objective-C you can use the function objc.selector to create custom descriptors. 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):

            # -(void)someMethod:(float)arg
            def someMethod_(self, arg):
                    pass

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

In Python 2.4 or later there is a decorator for this purpose:

    class MyObject(NSObject):

            @objc.signature('v@:f')
            def someMethod_(self, arg):
                    pass

Cocoa Bindings

In Mac OS X 10.3 Apple introduced Cocoa Bindings, a method to make it easier to create and use Controller objects using Key-Value Observing and Key-Value Coding. In order to create accessors compatible with this, you must use objc.accessor to create an appropriate selector descriptor.

PyObjC automaticly emits the right Key-Value Observing notifications when you set attributes on an Objective-C class. This is however not supported for pure python objects. You should therefore use NSMutableArray instances instead of Python lists for instance variables that will be observed and contain a sequence of values (and simularly for NSMutableDictionary instead of dict).

NOTE: Key-Value Observing is not supported for "pure" python objects, that is instances of classes that don't inherit from NSObject. Adding such support is not possible adding a KVO-like interface to the Python interpreter.

Cocoa for Python programmers

Cocoa frameworks are mapped onto Python packages with the same name; that is the classes, constants and functions 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 useful 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: