|
|
# Haskell Objective-C FFI: Objective-C Classes
|
|
|
|
|
|
## Using classes
|
|
|
## Declaring classes
|
|
|
|
|
|
|
|
|
We might import ObjC classes like so:
|
|
|
Importing an ObjC class has two components: (1) we declare the class type, i.e., the Haskell type of objects of that class, and (2) we import the class object; i.e., the object on which we invoke class methods.
|
|
|
|
|
|
```wiki
|
|
|
foreign import objc "@class UIView" o'UIView :: Class
|
|
|
foreign class objc "UIKit/UIKit.h UIView" C'UIView -- declares the class type
|
|
|
foreign import objc "UIKit/UIKit.h UIView" o'UIView :: Class -- imports the class object
|
|
|
```
|
|
|
|
|
|
**Problem:** We also need to introduce the class type `C'UIView`. So far, the FFI has no support for the import of types. Maybe we need a `foreign type` declaration.
|
|
|
|
|
|
## Declaring classes
|
|
|
The class type is implemented as a newtype of a `ForeignPtr`. The type is abstract, but can be passed to and received from ObjC land; i.e., it is a *foreign type* in the sense of Section 3.2 of the Haskell FFI. The class object is looked up by the module initialisation code using `objc_lookUpClass` and `Class` is defined in `Foreign.ObjectiveC` and corresponds to the type `Class` of the ObjC runtime.
|
|
|
|
|
|
### Finaliser
|
|
|
|
|
|
One option might be something like
|
|
|
|
|
|
```wiki
|
|
|
foreign export "@class MyUIView : UIView" myUIView :: C'UIView
|
|
|
```
|
|
|
A `foreign class` declaration can optionally specify a finaliser for the objects of the declared class. By default, the finaliser sends a `release` message to the finalised object. If a custom finaliser is specified, it needs to be implemented in ObjC (as per the requirements of `Foreign.ForeignPtr`).
|
|
|
|
|
|
### Option 1: value definition
|
|
|
## Implementing ObjC classes in Haskell
|
|
|
|
|
|
|
|
|
The class definition value `myUIView` contains the details of the class definition. However, it is odd, because `C'UIView` should usually be the type of instances if of `UIView`.
|
|
|
Any ObjC class implemented in Haskell requires an `@interface` definition in a standard ObjC header file. Instead, of a `.m` file that contains the implementation, we provide the implementation in Haskell. In Haskell, we specify in the `foreign class` declaration that the curent Haskell module implements the class; thusly,
|
|
|
|
|
|
### Option 2: incremental definition
|
|
|
```wiki
|
|
|
foreign class objc "MyClass.h @implementation MyClass" MyClass
|
|
|
```
|
|
|
|
|
|
|
|
|
In addition to the `foreign export` declaring the class, the class is populated with methods in further `foreign export` declarations.
|
|
|
For any Haskell module that implements one or more class, we generate an ObjC stub file (not unlike the C stubs generated by GHC for foreign export wrappers in the C FFI). The stub file contains the actual class implementation.
|
|
|
|
|
|
### Class and instance methods
|
|
|
|
|
|
We have the following problems:
|
|
|
|
|
|
- The ObjC runtime uses two functions to initiate and finalise the declaration of a new class, namely `objc_allocateClassPair` and `objc_registerClassPair`. All ivars must be added between these two calls (except for the 64-bit runtime, I guess, but the runtime ref doesn't say that). Methods and protocols can still be added after registering.
|
|
|
- `objc_allocateClassPair` expects the superclass as a value of type `Class`. The documentation does not say whether the superclass must already be registered (or whether it is sufficient to have it allocated at that point).
|
|
|
For any class or instance method of a class implemented in Haskell, we have one `foreign export` statement; for example,
|
|
|
|
|
|
```wiki
|
|
|
foreign export objc "-[MyClass doSomethingCool:]" doSomethingCool
|
|
|
:: MyClass -> UIView -> IO BOOL
|
|
|
```
|
|
|
|
|
|
Problems arise because Haskell declarations are unordered, so we should make no assumption about the order of foreign declarations. Moreover, we need to have foreign imported all class that we subclass (and which are not locally defined) – or we just call `objc_getClass`. On the other hand, when you subclass an existing class, you almost certainly need to foreign import the superclass anyway, to implement the methods for the new class (so an implicit `objc_getClass` is probably not worth the effort).
|
|
|
|
|
|
\[**FIXME***Need to make sure that we can determine the ObjC signature from the Haskell signature in every case.*\]
|
|
|
|
|
|
What about ObjC classes that have been declared in other Haskell modules, do we have to foreign import them again or do we just import the Haskell representation?
|
|
|
### Ivars and properties
|
|
|
|
|
|
|
|
|
We may have to compute the class DAG and make sure we generate class initialisation code that at module load time allocates, registers, and subclasses all Objective-C classes in the right order.
|
|
|
Any ivars and properties of a class need to be defined in the header with the interface. We can import or export setters and getters of properties as any other selector. However, if the class is implemented in Haskell, we might want to have the ObjC compiler synthesize the setter and getter. We can achieve this via a `@synthesize` directive in the `foreign import` of the setter and getter.
|
|
|
|
|
|
## BIG Questions
|
|
|
```wiki
|
|
|
foreign import objc "@synthesize myProperty -[MyClass myProperty]" myProperty
|
|
|
:: MyClass -> IO CInt
|
|
|
foreign import objc "@synthesize myProperty -[MyClass setMyProperty:]" setMyProperty
|
|
|
:: MyClass -> CInt -> IO ()
|
|
|
```
|
|
|
|
|
|
### How do we represent ObjC classes in Haskell land?
|
|
|
|
|
|
The synthesize directive includes the property name, as the property declaration may have specified non-default names for the setter and getter. Moreover, the directive should be duplicated by specifying it in the setter and getter (except for a `readonly` property) – this is much like header file specifications are replicated across foreign declarations in the C FFI.
|
|
|
|
|
|
It could just be a pointer. The advantage of a pointer is that there is no issue of keeping ObjC land and Haskell land data in sync (BIG PLUS). However, what does it mean to export a class in that case? In fact, we would need to allocate, populate, and register the class in ObjC land and then **import** it into Haskell land. This clashes with that the Haskell identifier mentioned in a `foreign export` is at a usage, not a defining occurrence.
|
|
|
|
|
|
### How do we populate ObjC classes with ivars, methods & properties in Haskell land? |
|
|
\ No newline at end of file |
|
|
We provide no special support to access the ivars directly beyond the functionality already available in the C FFI for Haskell. |