An introduction to the Io/Objective-C bridge
Objective-C and Io are both languages that put an emphasis on dynamicity. They both share the same definition of objects : entities that communicate with messages, and implement this definition with objects that dynamically resolve how to react to a particular message.
Du to the dynamic and reflexive nature of both languages, it was possible to interface Io and Objective-C in such a way that objects can be written indifferently in Objective-C and Io, and use indifferently in both of these languages. This is pretty much what is possible with the couple Java/Jython, where Python code can be embedded in Java code and Python objects manipulated in Java, and where the whole Java API is manipulable in Python. The Io Objective-C bridge goes even further, because there is even no difference in the manipulation of an Objective-C and Io object with one of this language : the bridge is almost transparent.
This document will roughly introduce the bridge, and how it can be used. The first part describes how Io and Objective-C are interfaced, while the second part gives more concrete use cases, coming with source code.
Related documents:
Note: This document needs updating, because it was written with the old, non pure-OO VM.
1. The IoObjcBridge? object
The Io-Objc bridge offers the following functionnalities:
- Convert Io values to Objective-C objects and vice-versa
- Convert Io primitives to C/Objective-C variables and vice-versa
- Create new Objective-C classes in Io scripts
When the IoObjcBridge? was initialised into the Io VM, it becomes available in
Io under the ObjcBridge slot and can be accessed from C/Objective-C using
the IoObjcBridge?_sharedBridge() operation.
Converting an Objective-C object to a Io value
Converting an Objc object to a Io value allows to use the Objc object as any other Io object within Io scripts.To do so, you need an IoObjcBridge? instance and the Objc object you wish to convert, then call the following operation:
IoValue? io_value = (IoValue? *)IoObjcBridge?_proxyForId_(bridge, objc_obj);
The Objective-C bridge will simply create a Io proxy object for the given Objective-C object.
Converting a Io value to an Objective-C object
This allows to import a IoValue? into Objective-C and send messages to it. Once again, the bridge encapsulates the Io value within an Objective-C proxy. To get an Objective-C object from a Io value, use the following operation:
id objc_obj = (id)IoObjcBridge?_proxyForIoValue_(bridge, io_value);
Also, don't forget to retain the return object, as it is autoreleased. Note that if the proxy already existed, it is returned (there is only one proxy per Io value), otherwise it is created "on the fly".
Converting a C variable to a Io value
The C (and then Objective-C) languages have primitive types such as int, float, char, which can be used to create variables. The bridge allows to create a Io primitive from each C variable using the following call:
IoValue?* io_value = IoObjcBridge?_ioValueForCValue_ofType_(bridge, (void*)var_ptr, (char*)var_type )
The var_ptr is the address of the C variable and the var_type si a pointer to a character defining the type of this variable.
Table 1: Conversion from C variables to Io Objects
| Type | C variable type | Io object |
| c | char | IoNumber? |
| d | double | IoNumber? |
| i | int | IoNumber? |
| I | unsigned int | IoNumber? |
| l | long | IoNumber? |
| f | float | IoNumber? |
| r | char* | IoString? |
| * | char** | IoString? |
| @ | id | depends |
| # | id* | depends |
Table 2: Conversion from Objective-C classes to Io Objects
| Objective-C class | Io object |
| NSNumber? | IoNumber? |
| NSString? | IoString? |
| Objc2Io? | IoObject? |
| any | IoObject? |
The basic c types (c,d,i,I,l,f) are all converted to numbers, pointer-based types such as char* and char** are converted to strings. Now, the @ and # type get interesting because they describes an Objective-C objects. According to Table 2, only NSNumber? and NSString? are converted to corresponding primitives types, whille all other instances are encapsulated in an Objc2Io? Io value (which results in the generic IoObject? as a result type).
Converting a Io value to a C variable
The process of getting a C variable from a Io value is symetric to converting a C variable to a Io value. To do this, you simply have to call the following operation:
void *IoObjcBridge?_cValueForIoValue_ofType_error_(bridge,
io_value, (char*)cvar_type, (char **)error)
This operation will try to return the C value, which is contained in the
original Io value `o' field. This means that the C value will live as long as
its associated Io value. It is also interesting to note that this operation
handles NSPoint? to IoPoint? conversion.
Creating an Objective-C class from Io
Objective-C is an inheritance-based language, whereas Io is a prototype-based
language. The bridge offers the possibility to create a new class which
instanciation will simply clone the prototype associated to the class. This
can be done from Io using the newClassWithNameAndProto(aString, anObject) message from the ObjcBridge.
See the documentation [1] for more information on this.
2. Using the bridge
Adding an Objective-C objects into Io at runtime
If you wish to script your Objc application with Io, you will generally have to pass some objects from Objective-C to Io in order to make them accessible to Io scripts. To do so, the simplest thing is to set a slot in the Lobby, containing your Objc instance encapsulated in a Objc2Io? value.
The following code illustrates how to do such an operation:
//We initialise the Objective-C bridge IoObjcBridgeInit(ioState, io_embedded); IoObjcBridge* bridge = (IoObjcBridge*)IoState_doCString_(ioState,"ObjcBridge"); //We register Piranhas Kernel into Io //We first wrap the Kernel in a Io object IoValue* io_kernel = IoObjcBridge_proxyForId_(bridge,kernel); IoObject_setSlot_to_(ioState->lobby, IoState_stringWithCString_(ioState, "PiranhasKernel"), io_kernel);
In the above code, the `kernel' variable represents an instance of the PKernel? Objective-C class. It is first encapsulated in a Objc2Io? value thanks to the IoObjcBridge?_proxyForId_ operation, and the newly created value is then assigned to the "PiranhasKernel?" slot in the lobby. We can now do the following from Io:
Io> PiranhasKernel description() print <PKernel: 8123d50>
We see that the PKernel? instance is now accessible from Io scripts. In all your applications, simply pass in an object that encapsulates your application services/objects (the API) and offer them to be manipualted by script. It can be interesting to apply the Facad design pattern in this context.
Improvements
Here is a list of things that should be improved in the Objective-C bridge :
- Default methods for string representation (shortcut to the description method)
- Default methods for object comparisons (==, !=, <, >, etc)
- Integration with the Io garbage-collector
- Facililty that would replace the XXX alloc by something like new(), and manage the retain count in consequence
