rubicon.objc.api - The high-level Rubicon API¶
This module contains Rubicon's main high-level APIs, which allow easy interaction with Objective-C classes and objects using Pythonic syntax.
Nearly all attributes of this module are also available on the main rubicon.objc module, and if possible that module should be used instead of importing rubicon.objc.api directly.
Objective-C objects¶
rubicon.objc.api.ObjCInstance
¶
Python wrapper for an Objective-C instance.
The constructor accepts an objc_id or
anything that can be cast to one, such as a c_void_p,
or an existing ObjCInstance.
ObjCInstance objects are cached — this
means that for every Objective-C object there can be at most on
ObjCInstance object at any time. Rubicon
will automatically create new
ObjCInstances or return existing ones as
needed.
The returned object's Python class is not always exactly
ObjCInstance. For example, if the passed
pointer refers to a class or a metaclass, an instance o
ObjCClass or
ObjCMetaClass is returned as
appropriate. Additional custom
ObjCInstance subclasses may be defined
and registered using
register_type_for_objcclass.
Creating an ObjCInstance from a nil
pointer returns None.
Rubicon retains an Objective-C object when it is wrapped in an
ObjCInstance and autoreleases it when the
ObjCInstance is garbage collected.
The only exception to this are objects returned by methods which create an object (starting with "alloc", "new", "copy", or "mutableCopy"). We do not explicitly retain them because we already own objects created by us, but we do autorelease them on garbage collection of the Python wrapper.
This ensures that the ObjCInstance can
always be used from Python without segfaults while preventing Rubicon from
leaking memory.
ptr
instance-attribute
¶
ptr: objc_id
The wrapped object pointer as an objc_id.
This attribute is also available as _as_parameter_ to allow
ObjCInstances to be passed into ctypes
functions.
__getattr__
¶
__getattr__(name)
Allows accessing Objective-C properties and methods using Python attribute syntax.
If self has a Python attribute with the given name, its value is
returned.
If there is an Objective-C property with the given name, its value is returned using its getter method. An attribute is considered a property if any of the following are true:
- A property with the name is present on the class (i.e. declared using
@propertyin the source code) - There is both a getter and setter method for the name
- The name has been declared as a property using
ObjCClass.declare_property()
Otherwise, a method matching the given name is looked up.
ObjCInstance understands two syntaxes for
calling Objective-C methods:
- "Flat" syntax: the Objective-C method name is spelled out in the
attribute name, with all colons replaced with underscores, and all
arguments are passed as positional arguments. For example, the
Objective-C method call
[self initWithWidth:w height:h]translates toself.initWithWidth_height_(w, h). - "Interleaved" syntax: the Objective-C method name is split up between
the attribute name and the keyword arguments passed to the returned
method. For example, the Objective-C method call
[self initWithRed:r green:g blue:b]translates toself.initWithRed(r, green=g, blue=b).
The "interleaved" syntax is usually preferred, since it looks more
similar to normal Objective-C syntax. However, the "flat" syntax is also
fully supported. If two arguments have the same name (e.g.
performSelector:withObject:withObject:), you can use __ in the
keywords to disambiguate (e.g., performSelector(..., withObject__1=...,
withObject__2=...). Any content after and including the __ in an argument
will be ignored.
__repr__
¶
__repr__()
Get a debugging representation of self, which includes the Objective-C
object's class and debugDescription.
__setattr__
¶
__setattr__(name, value)
Allows modifying Objective-C properties using Python syntax.
If self has a Python attribute with the given name, it is set. Otherwise, the
name should refer to an Objective-C property, whose setter method is called with
value.
__str__
¶
__str__()
Get a human-readable representation of self.
By default, self.description converted to a Python string is returned. If
self.description is nil, self.debugDescription converted to a Python is
returned instead. If that is also nil, repr(self) is returned as a fallback.
rubicon.objc.api.objc_const
¶
objc_const(dll, name)
Create an ObjCInstance from a global pointer
variable in a CDLL.
This function is most commonly used to access constant object pointers defined by a library/framework, such as NSCocoaErrorDomain.
Objective-C classes¶
rubicon.objc.api.ObjCClass
¶
ObjCClass(*args, **kwargs)
Bases: ObjCInstance, type
Python wrapper for an Objective-C class.
ObjCClass is a subclass of
ObjCInstance and supports the
same syntaxes for calling methods and accessing properties.
The constructor accepts either the name of an Objective-C class to look up
(as str or bytes), or a pointer to an existing class object
(in any form accepted by ObjCInstance).
If given a pointer, it must refer to an Objective-C class; pointers to
other objects are not accepted. (Use
ObjCInstance to wrap a
pointer that might also refer to other kinds of objects.) If the pointer
refers to a metaclass, an instance of
ObjCMetaClass is returned
instead. Creating an ObjCClass from a Nil
pointer returns None.
ObjCClass can also be called like
type, with three
arguments (name, bases list, namespace mapping). This form is called
implicitly by Python's class syntax, and is used to create a new
Objective-C class from Python (see
Creating custom Objective-C classes and protocols).
The bases list must contain exactly one
ObjCClass to be
extended by the new class. An optional protocols keyword argument is
also accepted, which must be a sequence of
ObjCProtocols for
the new class to adopt.
If the name of the class has already registered with the Objective-C
runtime, the auto_rename option can be used to ensure that the
Objective-C name for the new class will be unique. A numeric suffix will
be appended to the Objective-C name to ensure uniqueness (for example,
MyClass will be renamed to MyClass_2, MyClass_3 etc. until a
unique name is found). By default, classes will not be renamed, unless
ObjCClass.auto_rename is set at
the class level.
auto_rename
class-attribute
instance-attribute
¶
auto_rename = False
A bool value describing whether a defined class should be renamed
automatically if a class with the same name already exists in the Objective-C
runtime.
superclass
property
¶
superclass
The superclass of this class, or None if this is a root class (such as
NSObject).
__instancecheck__
¶
__instancecheck__(instance)
Check whether the given object is an instance of this class.
If the given object is not an Objective-C object, False is returned.
This method allows using ObjCClasses as the
second argument of isinstance: isinstance(obj, NSString) is equivalent
to obj.isKindOfClass(NSString).
__subclasscheck__
¶
__subclasscheck__(subclass)
Check whether the given class is a subclass of this class.
If the given object is not an Objective-C class, TypeError is raised.
This method allows using ObjCClasses as the
second argument of issubclass: issubclass(cls, NSValue) is equivalent to
obj.isSubclassOfClass(NSValue).
declare_class_property
¶
declare_class_property(name)
Declare the class method name to be a property getter.
This is equivalent to self.objc_class.declare_property(name).
declare_property
¶
declare_property(name)
Declare the instance method name to be a property getter.
This causes the attribute named name on instances of this class to be
treated as a property rather than a method — accessing it returns the
property's value, without requiring an explicit method call. See
ObjCInstance.__getattr__ for a
full description of how attribute access
behaves for properties.
Most properties do not need to be declared explicitly using this method, as they
are detected automatically by
ObjCInstance.__getattr__. This
method only needs to be used for properties that are read-only and don't have a
@property declaration in the source code, because Rubicon cannot tell such
properties apart from normal zero-argument methods.
Note
In the standard Apple SDKs, some properties are introduced as regular
methods in one system version, and then declared as properties in a later
system version. For example, the description method/property of
NSObject was declared as a regular method up to
OS X 10.9,
but changed to a property as of OS X
10.10.
Such properties cause compatibility issues when accessed from Rubicon:
obj.description() works on 10.9 but is a TypeError on 10.10,
whereas obj.description works on 10.10 but returns a method object on
10.9. To solve this issue, the property can be declared explicitly using
NSObject.declare_property('description'), so that it can always be
accessed using obj.description.
rubicon.objc.api.ObjCMetaClass
¶
ObjCMetaClass(*args, **kwargs)
Bases: ObjCClass
Python wrapper for an Objective-C metaclass.
ObjCMetaClass is a subclass of
ObjCClass and supports almost exactly the same
operations and methods. However, there is usually no need to look up a metaclass
manually. The main reason why ObjCMetaClass is a
separate class is to differentiate it from ObjCClass
in the repr. (Otherwise there would be no way to tell classes and metaclasses
apart, since metaclasses are also classes, and have exactly the same name as their
corresponding class.)
The constructor accepts either the name of an Objective-C metaclass to look up (as
str or bytes), or a pointer to an existing metaclass object (in any form
accepted by ObjCInstance).
If given a pointer, it must refer to an Objective-C metaclass; pointers to other
objects are not accepted. (Use ObjCInstance to
wrap a pointer that might also refer to other kinds of objects.) Creating an
ObjCMetaClass from a Nil pointer returns
None.
Standard Objective-C and Foundation classes¶
The following classes from the Objective-C runtime and the Foundation framework are provided as ObjCClasses for convenience. (Other classes not listed here can be looked up by passing a class name to the ObjCClass constructor.)
Note
None of the following classes have a usable Python-style constructor - for example, you cannot call NSString("hello") to create an Objective-C string from a Python string. To create instances of these classes, you should use ns_from_py (also called at): ns_from_py("hello") returns a NSString instance with the value hello.
rubicon.objc.api.NSObject
¶
The
NSObject
class from <objc/NSObject.h>.
See the ObjCInstance
documentation for a list of operations that Rubicon supports on all objects.
debugDescription
property
¶
debugDescription: str
Exposes the Objective-C
debugDescription
property.
rubicon.objc.api.Protocol
¶
The
Protocol
class from <objc/Protocol.h>.
This class has no (non-deprecated) Objective-C methods; protocol objects
can only be manipulated using Objective-C runtime functions. Rubicon
automatically wraps all Protocol
objects using ObjCProtocol, which
provides an easier interface for working with protocols.
rubicon.objc.api.NSNumber
¶
The
NSNumber
class from <Foundation/NSValue.h>.
This class can be converted to and from standard Python primitives
(bool, int, float) using py_from_ns and
ns_from_py.
rubicon.objc.api.NSDecimalNumber
¶
The
NSDecimalNumber
class from <Foundation/NSDecimalNumber.h>.
This class can be converted to and from Python decimal.Decimal using
py_from_ns and
ns_from_py.
rubicon.objc.api.NSString
¶
The
NSString
class from <Foundation/NSString.h>.
This class also supports all methods that str does.
This class can be converted to and from Python str using
py_from_ns and
ns_from_py. You can also call
str(nsstring) to convert a NSString to str.
NSString objects consist of UTF-16
code units, unlike str, which consists
of Unicode code points. All NSString
indices and iteration are based on UTF-16, even when using the
Python-style operations/methods. If indexing or iteration based on code
points is required, convert the NSString to
str first.
UTF8String
property
¶
UTF8String: str
This Objective-C property has been declared using
ObjCClass.declare_property()
and can always be accessed using attribute syntax.
rubicon.objc.api.NSData
¶
The
NSData
class from <Foundation/NSData.h>.
This class can be converted to and from Python bytes using
py_from_ns and
ns_from_py.
rubicon.objc.api.NSArray
¶
The
NSArray
class from <Foundation/NSArray.h>.
This class can be converted to and from Python list using
py_from_ns and
ns_from_py.
py_from_ns(nsarray) will recursively convert nsarray's elements to
Python objects, where possible. To avoid this recursive conversion, use
list(nsarray) instead.
ns_from_py(pylist) will recursively convert pylist's elements to
Objective-C. As there is no way to store Python object references as
Objective-C objects yet, this recursive conversion cannot be avoided. If
any of pylist's elements cannot be converted to Objective-C, an error
is raised.
Supports
Python-style sequence operations
including: __getitem__(), __len__(), __iter__(), __contains__(), __eq__(),
__ne__(), index(), count(), and copy().
rubicon.objc.api.NSMutableArray
¶
The
NSMutableArray
class from <Foundation/NSArray.h>.
This class can be converted to and from Python exactly like its
superclass NSArray.
Supports
Python-style mutable sequence operations
including: __setitem__(), __delitem__(), append(), clear(), extend(),
insert(), pop(), remove(), and reverse().
rubicon.objc.api.NSDictionary
¶
The
NSDictionary
class from <Foundation/NSDictionary.h>.
This class can be converted to and from Python dict using
py_from_ns and
ns_from_py.
py_from_ns(nsdict) will recursively convert nsdict's keys and values
to Python objects, where possible. To avoid the recursive conversion of
the values, use {py_from_ns(k): v for k, v in nsdict.items()}. The
conversion of the keys cannot be avoided, because Python
dict keys need to be hashable, which
ObjCInstance is not. If any of the
keys convert to a Python object that is not hashable, an error is raised
(regardless of which conversion method you use).
ns_from_py(pydict) will recursively convert pydict's keys and values
to Objective-C. As there is no way to store Python object references as
Objective-C objects yet, this recursive conversion cannot be avoided. If
any of pydict's keys or values cannot be converted to Objective-C, an
error is raised.
Supports
Python-style mapping operations
including __getitem__(), __len__(), __iter__(), __contains__(), __eq__(),
__ne__(), copy(), get(), keys(), items(), and values().
Unlike most Python mappings, NSDictionary's
keys,
values, and
items
methods don't return dynamic views of the dictionary's
keys, values, and items.
keys and
values return
lists that are created each time the methods are
called, which can have an effect on performance and memory usage for
large dictionaries. To avoid this, you can cache the return values of
keys and
values, or convert
the NSDictionary to a Python dict
beforehand.
items is currently
implemented as a generator, meaning that it returns a single-use iterator. If you
need to iterate over
items more than
once or perform other operations on it, you should convert it to a Python
set or list first.
rubicon.objc.api.NSMutableDictionary
¶
The
NSMutableDictionary
class from <Foundation/NSDictionary.h>.
This class can be converted to and from Python exactly like its
superclass NSDictionary.
Supports
Python-style mutable mapping operations
including setitem(), delitem(), clear(), pop(), popitem(), setdefault(),
and update().
Objective-C protocols¶
rubicon.objc.api.ObjCProtocol
¶
Bases: ObjCInstance
Python wrapper for an Objective-C protocol.
The constructor accepts either the name of an Objective-C protocol to look up
(as str or bytes), or a pointer to an existing protocol object
(in any form accepted by ObjCInstance).
If given a pointer, it must refer to an Objective-C protocol; pointers
to other objects are not accepted. (Use
ObjCInstance to wrap a
pointer that might also refer to other kinds of objects.) Creating an
ObjCProtocol from a nil pointer returns
None.
ObjCProtocol can also be called like type,
with three
arguments (name, bases list, namespace mapping). This form is called
implicitly by Python's class syntax, and is used to create a new
Objective-C protocol from Python (see
Creating custom Objective-C classes and protocols).
The bases list can contain any
number of ObjCProtocol objects to be extended by
the new
protocol.
If the name of the protocol has already registered with the Objective-C
runtime, the auto_rename option can be used to ensure that the
Objective-C name for the new protocol will be unique. A numeric suffix
will be appended to the Objective-C name to ensure uniqueness (for
example, MyProtocol will be renamed to MyProtocol_2,
MyProtocol_3 etc. until a unique name is found). By default,
protocols will not be renamed, unless
ObjCProtocol.auto_rename is set at
the class level.
auto_rename
class-attribute
instance-attribute
¶
auto_rename = False
A bool value whether a defined protocol should be renamed automatically if
a protocol with the same name is already exists.
__instancecheck__
¶
__instancecheck__(instance)
Check whether the given object conforms to this protocol.
If the given object is not an Objective-C object, False is returned.
This method allows using ObjCProtocols as the
second argument of isinstance: isinstance(obj, NSCopying) is equivalent
to obj.conformsToProtocol(NSCopying).
__subclasscheck__
¶
__subclasscheck__(subclass)
Check whether the given class or protocol conforms to this protocol.
If the given object is not an Objective-C class or protocol, TypeError is
raised.
This method allows using ObjCProtocols as the
second argument of issubclass: issubclass(cls, NSCopying) is equivalent
to cls.conformsToProtocol(NSCopying), and issubclass(proto, NSCopying) is
equivalent to protocol_conformsToProtocol(proto, NSCopying)).
Standard Objective-C and Foundation protocols¶
The following protocols from the Objective-C runtime and the Foundation framework are provided as ObjCProtocols for convenience. (Other protocols not listed here can be looked up by passing a protocol name to the ObjCProtocol constructor.)
rubicon.objc.api.NSObjectProtocol
¶
The
NSObject
protocol from <objc/NSObject.h>. The protocol is exported as
NSObjectProtocol in Python because it
would otherwise clash with the NSObject class.
Converting objects between Objective-C and Python¶
rubicon.objc.api.py_from_ns
¶
py_from_ns(nsobj)
Convert a Foundation object into an equivalent Python object if possible.
Currently supported types:
objc_id: Wrapped in anObjCInstanceand converted as belowNSString: Converted tostrNSData: Converted tobytesNSDecimalNumber: Converted todecimal.DecimalNSDictionary: Converted todict, with all keys and values converted recursivelyNSArray: Converted tolist, with all elements converted recursivelyNSNumber: Converted to abool,intorfloatbased on the type of its contents
Other objects are returned unmodified as an
ObjCInstance.
rubicon.objc.api.ns_from_py
¶
ns_from_py(pyobj)
Convert a Python object into an equivalent Foundation object.
The returned object is autoreleased.
This function is also available under the name at, because
its functionality is very similar to that of the Objective-C @ operator and
literals.
Currently supported types:
None,ObjCInstance: Returned as-isenum.Enum: Replaced by theirvalueand converted as belowstr: Converted toNSStringbytes: Converted toNSDatadecimal.Decimal: Converted toNSDecimalNumberdict: Converted toNSDictionary, with all keys and values converted recursivelylist: Converted toNSArray, with all elements converted recursivelybool,int,float: Converted toNSNumber
Other types cause a TypeError.
Creating custom Objective-C classes and protocols¶
Custom Objective-C classes are defined using Python class syntax, by subclassing an existing ObjCClass object:
class MySubclass(NSObject):
# method, property, etc. definitions go here
A custom Objective-C class can only have a single superclass, since Objective-C does not support multiple inheritance. However, the class can conform to any number of protocols, which are specified by adding the protocols keyword argument to the base class list:
class MySubclass(NSObject, protocols=[NSCopying, NSMutableCopying]):
# method, property, etc. definitions go here
Note
Rubicon requires specifying a superclass when defining a custom Objective-C class. If you don't need to extend any specific class, use NSObject as the superclass.
Although Objective-C technically allows defining classes without a base class (so-called root classes), this is almost never the desired behavior (attempting to do so causes a compiler error by default). In practice, this feature is only used in the definitions of core Objective-C classes like NSObject. Because of this, Rubicon does not support defining Objective-C root classes.
Similar syntax is used to define custom Objective-C protocols. Unlike classes, protocols can extend multiple other protocols:
class MyProtocol(NSCopying, NSMutableCopying):
# method, property, etc. definitions go here
A custom protocol might not need to extend any other protocol at all. In this case, we need to explicitly tell Python to define an ObjCProtocol. Normally Python detects the metaclass automatically by examining the base classes, but in this case there are none, so we need to specify the metaclass manually.
class MyProtocol(metaclass=ObjCProtocol):
# method, property, etc. definitions go here
Defining methods¶
rubicon.objc.api.objc_method
¶
objc_method(py_method)
Exposes the decorated method as an Objective-C instance method in a custom class or protocol.
In a custom Objective-C class, decorating a method with
@objc_method makes it available to
Objective-C: a corresponding Objective-C method is created in the new Objective-C
class, whose implementation calls the decorated Python method. The Python method
receives all arguments (including self) from the Objective-C method call, and
its return value is passed back to Objective-C.
In a custom Objective-C protocol, the behavior is similar, but the method
body is ignored, since Objective-C protocol methods have no implementations.
By convention, the method body in this case should be empty (pass).
(Since the method is never called, you could put any other code there as
well, but doing so is misleading and discouraged.)
rubicon.objc.api.objc_classmethod
¶
objc_classmethod(py_method)
Exposes the decorated method as an Objective-C class method in a custom class or protocol.
This decorator behaves exactly like @objc_method,
except that the decorated method becomes a class method, so it is exposed on the
Objective-C class rather than its instances.
Method naming¶
The name of a Python-defined Objective-C method is same as the Python method's name, but with all underscores (_) replaced with colons (:) — for example, initWithWidth_height_ becomes initWithWidth:height:.
Warning
The Objective-C language imposes certain requirements on the usage of colons in method names: a method's name must contain exactly as many colons as the method has arguments (excluding the implicit self and _cmd parameters), and the name of a method with arguments must end with a colon. For example, a method called init takes no arguments, initWithSize: takes a single argument, initWithWidth:height: takes two, etc. initWithSize:spam is an invalid method name.
These requirements are not enforced by the Objective-C runtime, but methods that do not follow them cannot easily be used from regular Objective-C code.
In addition, although the Objective-C language allows method names with multiple consecutive colons or a colon at the start of the name, such names are considered bad style and never used in practice. For example, spam::, :ham:, and : are unusual, but valid method names.
Future versions of Rubicon may warn about or disallow such nonstandard method names.
Parameter and return types¶
The argument and return types of a Python-created Objective-C method are determined based on the Python method's type annotations. The annotations may contain any ctypes type, as well as any of the Python types accepted by ctype_for_type. If a parameter or the return type is not specified, it defaults to ObjCInstance. The self parameter is special-cased — its type is always ObjCInstance, even if annotated otherwise. To annotate a method as returning void, set its return type to None.
Before being passed to the Python method, any object parameters (objc_id) are automatically converted to ObjCInstance. If the method returns an Objective-C object, it is converted using ns_from_py before being returned to Objective-C. These automatic conversions can be disabled by using objc_rawmethod instead of objc_method.
The implicit _cmd parameter is not passed to the Python method, as it is normally redundant and not needed. If needed, the _cmd parameter can be accessed by using objc_rawmethod instead of objc_method.
rubicon.objc.api.objc_rawmethod
¶
objc_rawmethod(py_method)
Exposes the decorated method as an Objective-C instance method in a custom class,
with fewer convenience features than objc_method.
This decorator behaves similarly to
@objc_method.
However, unlike with objc_method, no automatic
conversions are
performed (aside from those by ctypes). This means that all parameter
and return types must be provided as ctypes types (no
ctype_for_type conversion is performed), all
arguments are passed in their raw form as received from ctypes, and
the return value must be understood by ctypes.
In addition, the implicit _cmd parameter is exposed to the Python
method, which is not the case when using
objc_method. This means
that the decorated Python method must always have an additional _cmd
parameter after self; if it is missing, there will be errors at runtime
due to mismatched argument counts. Like self, _cmd never needs to be
annotated, and any annotations on it are ignored.
Defining properties and ivars¶
rubicon.objc.api.objc_property
¶
objc_property(vartype=objc_id, weak=False)
Defines a property in a custom Objective-C class or protocol.
This class should be called in the body of an Objective-C subclass or protocol, for example:
class MySubclass(NSObject):
counter = objc_property(NSInteger)
The property type may be any ctypes type, as well as any of the
Python types accepted by ctype_for_type.
Defining a property automatically defines a corresponding getter and setter.
Following standard Objective-C naming conventions, for a property name
the getter is called name and the setter is called setName:.
In a custom Objective-C class, implementations for the getter and setter are
also generated, which store the property's value in an ivar called
_name. If the property has an object type, the generated setter keeps
the stored object retained, and releases it when it is replaced.
In a custom Objective-C protocol, only the metadata for the property is generated.
If weak is True, the property will be created as a weak property.
When assigning an object to it, the reference count of the object will not
be increased. When the object is deallocated, the property value is set to
None. Weak properties are only supported for Objective-C or Python object
types.
rubicon.objc.api.objc_ivar
¶
objc_ivar(vartype)
Defines an ivar in a custom Objective-C class.
If you want to store additional data on a custom Objective-C class, it is
recommended to use properties (objc_property)
instead of ivars. Properties are a more modern and high-level Objective-C feature,
which automatically deal with reference counting for objects, and creation of
getters and setters.
The ivar type may be any ctypes type.
Unlike properties, the contents of an ivar cannot be accessed or modified using
Python attribute syntax. Instead, the get_ivar and
set_ivar functions need to be used.
rubicon.objc.api.get_ivar
¶
get_ivar(obj, varname, weak=False)
Get the value of obj's ivar named varname.
The returned object is a ctypes data object.
For non-object types (everything except objc_id
and subclasses), the returned data object is backed by the ivar's actual memory.
This means that the data object is only usable as long as the "owner" object is
alive, and writes to it will directly change the ivar's value.
For object types, the returned data object is independent of the ivar's memory.
This is because object ivars may be weak, and thus cannot always be accessed
directly by their address.
rubicon.objc.api.set_ivar
¶
set_ivar(obj, varname, value, weak=False)
Set obj's ivar varname to value.
If weak is True, only a weak reference to the value is stored.
value must be a ctypes data object whose type matches that of the ivar.
Objective-C blocks¶
Blocks are the Objective-C equivalent of function objects, so Rubicon provides ways to call Objective-C blocks from Python and to pass Python callables to Objective-C as blocks.
Automatic conversion¶
If an Objective-C method returns a block (according to its type encoding), Rubicon will convert the return value to a special ObjCInstance that can be called in Python:
block = an_objc_instance.methodReturningABlock()
res = block(arg, ...)
Similarly, if an Objective-C method has a parameter that expects a block, you can pass in a Python callable object, and it will be converted to an Objective-C block. In this case, the callable object needs to have parameter and return type annotations, so that Rubicon can expose this type information to the Objective-C runtime:
def result_handler(res: objc_id) -> None:
print(ObjCInstance(res))
an_objc_instance.doSomethingWithResultHandler(result_handler)
If you are writing a custom Objective-C method (see Creating custom Objective-C classes and protocols), you can annotate parameter or return types using objc_block so that Rubicon converts them appropriately:
class AnObjCClass(NSObject):
@objc_method
def methodReturningABlock() -> objc_block:
def the_block(arg: NSInteger) -> NSUInteger:
return abs(arg)
return the_block
@objc_method
def doSomethingWithResultHandler_(result_handler: objc_block) -> None:
res = SomeClass.someMethod()
result_handler(res)
Note
These automatic conversions are mostly equivalent to the manual conversions described in the next section. There are internal technical differences between automatic and manual conversions, but they are not noticeable to most users.
The internals of automatic conversion and objc_block handling may change in the future, so if you need more control over the block conversion process, you should use the manual conversions described in the next section.
Manual conversion¶
These classes are used to manually convert blocks to Python callables and vice versa. You may need to use them to perform these conversions outside of Objective-C method calls, or if you need more control over the block's type signature.
rubicon.objc.api.ObjCBlock
¶
ObjCBlock(pointer, restype=AUTO, *argtypes)
Python wrapper for an Objective-C block object.
This class is used to manually wrap an Objective-C block so that it can be called
from Python. Usually Rubicon will do this automatically, if the block object was
returned from an Objective-C method whose return type is declared to be a block
type. If this automatic detection fails, for example if the method's return type is
generic id, Rubicon has no way to tell that the object in question is a block
rather than a regular Objective-C object. In that case, the object needs to be
manually wrapped using ObjCBlock.
The constructor takes a block object, which can be either an
ObjCInstance, or a raw
objc_id pointer.
Note
objc_block is also accepted, because it is
a subclass of objc_id). Normally you do not
need to make use of this, because in most cases Rubicon will automatically
convert objc_blocks to a callable object.
In most cases, Rubicon can automatically determine the block's return type and
parameter types. If a block object doesn't have return/parameter type
information at runtime, Rubicon will raise an error when attempting to convert
it. In that case, you need to explicitly pass the correct return type and
parameter types to ObjCBlock using the restype
and argtypes parameters.
__call__
¶
__call__(*args)
Invoke the block object with the given arguments.
The arguments and return value are converted from/to Python objects according to
the default ctypes rules, based on the block's return and parameter types.
rubicon.objc.api.Block
¶
Block(func, restype=NOTHING, *argtypes)
A wrapper that exposes a Python callable object to Objective-C as a block.
The constructor accepts any Python callable object.
If the callable has parameter and return type annotations, they are used
as the block's parameter and return types. This allows using
Block as a decorator:
@Block
def the_block(arg: NSInteger) -> NSUInteger:
return abs(arg)
For callables without type annotations, the parameter and return types
need to be passed to the Block constructor in the
restype and argtypes arguments:
the_block = Block(abs, NSUInteger, NSInteger)
Defining custom subclasses of ObjCInstance¶
The following functions can be used to register custom subclasses of ObjCInstance to be used when wrapping instances of a certain Objective-C class. This mechanism is for example used by Rubicon to provide Python-style operators and methods on standard Foundation classes, such as NSString and NSDictionary.
rubicon.objc.api.register_type_for_objcclass
¶
register_type_for_objcclass(pytype, objcclass)
Register a conversion from an Objective-C class to an
ObjCInstance subclass.
After a call of this function, when Rubicon wraps an Objective-C object that
is an instance of objcclass (or a subclass), the Python object will have
the class pytype rather than ObjCInstance.
See type_for_objcclass for a full
description of the lookup process.
Warning
This function should only be called if no instances of objcclass (or
a subclass) have been wrapped by Rubicon yet. If the function is called
later, it will not fully take effect: the types of existing instances do
not change, and mappings for subclasses of objcclass are not
updated.
rubicon.objc.api.for_objcclass
¶
for_objcclass(objcclass)
Decorator for registering a conversion from an Objective-C class to an
ObjCInstance subclass.
This is equivalent to calling
register_type_for_objcclass on
the decorated class.
rubicon.objc.api.type_for_objcclass
¶
type_for_objcclass(objcclass)
Look up the ObjCInstance subclass used to
represent instances of the given Objective-C class in Python.
If the exact Objective-C class is not registered, each superclass is also checked,
defaulting to ObjCInstance if none of the classes
in the superclass chain is registered. Afterward, all searched superclasses are
registered for the ObjCInstance subclass that was
found. (This speeds up future lookups, and ensures that previously computed mappings
are not changed by unrelated registrations.)
This method is mainly intended for internal use by Rubicon, but is exposed in the public API for completeness.
rubicon.objc.api.unregister_type_for_objcclass
¶
unregister_type_for_objcclass(objcclass)
Unregister a conversion from an Objective-C class to an
ObjCInstance subclass.
Warning
This function should only be called if no instances of objcclass (or a subclass)
have been wrapped by Rubicon yet. If the function is called later, it will not fully
take effect: the types of existing instances do not change, and mappings for
subclasses of objcclass are not removed.
rubicon.objc.api.get_type_for_objcclass_map
¶
get_type_for_objcclass_map()
Get a copy of all currently registered
ObjCInstance subclasses as a mapping.
Keys are Objective-C class addresses as ints.