wiki:CocoaBridgeTranslation

Version 1 (modified by pfeilgm, 4 years ago) (diff)

--

Here are a bunch of conversions between Obj-C code and the equivalent Clozure CL Cocoa Bridge code, to show how different idioms are encoded. Some of these things are part of the Clozure CL FFI, and not specific to the bridge, but they are included here to give an overall view.

literals

T and NIL are mapped to the boolean values YES and NO, respectively. All numbers are also portable. NSStrings need to be explicitly created:

@"some string"

becomes

#@"some string"

If you need to convert between lisp strings and NSStrings, the following functions are what you want.

(let ((a-lisp-string "foo"))
  (ccl::%make-nsstring a-lisp-string))

and you also need to convert NSStrings when you receive them:

(ccl::lisp-string-from-nsstring (#/title some-object))

Clozure CL used to autoconvert strings, but that caused issues with memory management. So make sure to retain/release any NSStrings you need to.

nil
NULL

Both of these are null pointers. Use

ccl:+null-ptr+

to represent them in Lisp.

types

You may have to use type (not class) names as the return values of methods you define and in some other cases.

NSInteger
BOOL

becomes

#>NSInteger
#>BOOL

constants, enumerations and variables

NSTitledWindowMask
NSUTF8StringEncoding
#$NSTitledWindowMask
#$NSUTF8StringEncoding

selectors

@selector(someSelector:withParams:)

becomes

(@selector "someSelector:withParams:")

class definition

@interface SomeClass : SuperClass {
    IBOutlet NSString *aString;
}
(defclass some-class (super-class)
  ((a-string :foreign-type :id))
  (:metaclass ns:+ns-object))

method definition

@implementation SomeClass // just included so we show the class name

- (id) initWithFrame:(NSRect)frame andStuff:(id)stuff {
    if ((self = [super initWithFrame:frame])) {
        // body
    }
    return self;
}

- (void) viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    // body
}
(objc:defmethod (#/initWithFrame:andStuff: :id)
                ((self some-class) (frame #>NSRect) (stuff :id))
  (let ((new-self (#/initWithFrame: self frame)))
    (when new-self
      ;; body
      )
    new-self))

(objc:defmethod (#/viewDidAppear: :void) ((self some-class) (animated #>BOOL))
  (call-next-method))
  ;; body
  )

There is the usual CALL-NEXT-METHOD (which works just as expected in Obj-C methods), but that doesn’t cover all the use cases you need in Obj-C. As in common in init methods, you sometimes need to call a super-method with a different name than the current method. This is where CALL-NEXT-METHOD breaks down. The easiest way to handle this yourself is to treat the call to super as a call to self, as in the above example.

instantiating objects

[[NSWindow alloc] initWithContentRect:NSRectMake(0, 0, 300, 300)
                            styleMask:NSTitledWindowMask
                              backing:NSBackingStoreBuffered
                                defer:YES];
(make-instance 'ns:ns-window
  :with-content-rect (ns:make-ns-rect 0 0 300 300)
  :style-mask #$NSTitledWindowMask
  :backing #$NSBackingStoreBuffered
  :defer t)

method call

[self doSomethingToObject:anObject];
[NSDate date];
(#/doSomethingToObject: self an-object)
(#/date ns:ns-date)

calling setters / setting properties

You can call setters the same way as any other method, of course, but to make them work more like properties or slots, you can also use SETF for them.

[self setName:aName];
self.name = aName; // provided there is a @property defined
(#/setName: self aName)

or

(setf (#/name self) a-name)

The SETF form will work regardless of whether there's a property defined, but a getter (IE (#/name self)) must exist. This is because users of SETF expect the stored value to be returned, but Cocoa setters generally have no return value.