========================= An introduction to PyObjC ========================= .. :authors: Ronald Oussoren :contact: pyobjc-dev@lists.sourceforge.net :URL: http://pyobjc.sourceforge.net/ :copyright: 2003 The PyObjC Project .. contents:: Preface ------- 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. .. _`Apple`: http://www.apple.com/ Objective-C for PyObjC users ---------------------------- 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: * `The Objective-C Programming Language`_ at `Apple`_. .. _`The Objective-C Programming Language`: http://developer.apple.com/techpubs/macosx/Cocoa/ObjectiveC/index.html 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 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``. Methods and functions ..................... 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: * Concatenate all elements of the method name: ``someMethod:withFoo:andBar:`` * Then convert all colons to underscores: ``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). .. _`Notes on supported APIs and classes on MacOS X`: api-notes-macosx.html 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_, ...) Reference counting .................. 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`_. .. _`weak references`: http://www.python.org/doc/current/lib/module-weakref.html 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. (Informal) protocols .................... 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 for Python programmers ---------------------------- 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: * `Cocoa documentation at the Apple developer website`_ * `Cocoa examples at the Apple developer website`_ * `stepwise.com`_ * Your local bookstore or library .. _`PyObjC Example index`: ../Examples/00ReadMe.html .. _`Cocoa libraries`: http://developer.apple.com/techpubs/macosx/Cocoa/CocoaTopics.html .. _`Cocoa documentation at the Apple developer website`: http://developer.apple.com/techpubs/macosx/Cocoa/CocoaTopics.html .. _`Cocoa examples at the Apple developer website`: http://developer.apple.com/samplecode/Sample_Code/Cocoa.htm .. _`stepwise.com`: http://www.stepwise.com/ Notes on specific tasks ----------------------- Working with threads .................... 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). Finalizers .......... 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. Building applications --------------------- 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. "Pure Python" : buildapp.py ............................ 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. .. __: ../Examples/00ReadMe.txt "IDE approach" : Project builder ................................ 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. .. __: ProjectBuilder-Templates.html 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.