Structure of the PyObjC package

Introduction

This document gives an overview of the PyObjC for developers (of the package).

One of the sections describes how all of it works, and some of the limitations.

This document is a incomplete, it should be updated.

Methods

Classes are scanned for methods when the Python wrapper for a class is created. We then create Python wrappers for those methods. This way users can use the normal Python introspection methods to check which methods are available.

There are several occasions when these method tables are rescanned, because classes can grow new methods when categories are loaded into the runtime. Additionally, it is known that some Cocoa frameworks in macOS change their method tables when the first instance is created.

Subclassing

It is possible to subclass Objective-C classes from Python. These classes end up in a structure containing both a Python type object and an Objective-C class. Instances of these classes also contain both a Python instance and an Objective-C object.

The first Python subclass of an Objective-C class introduces a new instance variable in the Objective-C object to store the pointer to the Python half of the cluster. This variable is always referenced by name. The Python half is a subclass of objc_object that already contains a pointer to an Objective-C object. This first subclass also introduces a number of class and instance methods that the PyObjC bridge uses to maintain the illusion of a single object on both sides. Check class-builder.m for details.

Directory structure

Doc/

Documentation

Examples/

Example scripts and applets.

Lib/

The pure Python parts of the packages that comprise PyObjC.

Modules/

Extension modules related to the packages in ‘Lib’.

libffi-src/

A local copy of libffi, the Foreign Function Interface library used by PyObjC.

Reference counts

The Objective-C rules for reference counts are pretty easy: A small number of class methods (alloc, allocWithZone:, copy, …) transfer object ownership to the caller. For all other objects you have to call retain if you want to keep a reference. This includes all factory methods, such as [NSString stringWithCString:"bla"]!

When programming Cocoa in Python, you rarely need to worry about reference counts: the objc module makes this completely transparent to user. This is mostly implemented in [de]pythonify_c_value. Additional code is needed when calling methods that transfer ownership of their return value (as described above) and when updating a instance variable in an Objective-C object (retain new and release old, in that order). Both are implemented.

Strings

Python unicode instances are proxied by the OC_PythonUnicode subclass of NSString. This is a proxy, and will maintain the identity of the original unicode instance.

NSString instances are represented in Python as a subtype of unicode: objc.pyobjc_unicode. This performs a conversion, because Python’s unicode type is immutable, but it also maintains a reference to the original NSString. NSString and NSMutableString methods are available from the objc.pyobjc_unicode object, though they do not show up via Python’s introspection mechanisms. In order to get the latest Python representation of a NSMutableString, use the return value of its self() method.

Python str instances are proxied by the OC_PythonString subclass of NSString. This is a proxy, and will maintain the identity of the original str instance. OC_PythonString will use the default encoding of NSString, so its results might be surprising if you are using non-ASCII text. It is recommended that you use unicode whenever possible. In order to help you determine where you are not using unicode, it is possible to trigger an objc.PyObjCStrBridgeWarning warning whenever a str instance crosses the bridge:

import objc
objc.setStrBridgeEnabled(False)

To promote these to an exception, do the following:

import objc
import warnings
warnings.filterwarnings('error', objc.PyObjCStrBridgeWarning)