345 lines
12 KiB
Racket
345 lines
12 KiB
Racket
#lang scribble/doc
|
|
@(require scribble/manual
|
|
scribble/eval
|
|
(for-label racket/base
|
|
racket/contract
|
|
ffi/unsafe/objc
|
|
(except-in ffi/unsafe ->)
|
|
(only-in ffi/objc objc-unsafe!)
|
|
(only-in scheme/foreign unsafe!)))
|
|
|
|
@(define objc-eval (make-base-eval))
|
|
@(interaction-eval #:eval objc-eval (define-struct cpointer:id ()))
|
|
|
|
@(define seeCtype
|
|
@elem{see @secref["ctype"]})
|
|
|
|
@title{Objective-C FFI}
|
|
|
|
@defmodule[ffi/unsafe/objc]{The
|
|
@racketmodname[ffi/unsafe/objc] library builds on
|
|
@racketmodname[ffi/unsafe] to support interaction with
|
|
@link["http://developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC/"]{Objective-C}.}
|
|
|
|
The library supports Objective-C interaction in two layers. The upper
|
|
layer provides syntactic forms for sending messages and deriving
|
|
subclasses. The lower layer is a think wrapper on the
|
|
@link["http://developer.apple.com/DOCUMENTATION/Cocoa/Reference/ObjCRuntimeRef/index.html"]{Objective-C
|
|
runtime library} functions. Even the upper layer is unsafe and
|
|
relatively low-level compared to normal Racket libraries, because
|
|
argument and result types must be declared in terms of FFI C types
|
|
(@seeCtype).
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
@section{FFI Types and Constants}
|
|
|
|
@defthing[_id ctype?]{
|
|
|
|
The type of an Objective-C object, an opaque pointer.}
|
|
|
|
@defthing[_Class ctype?]{
|
|
|
|
The type of an Objective-C class, which is also an @racket[_id].}
|
|
|
|
@defthing[_Protocol ctype?]{
|
|
|
|
The type of an Objective-C protocol, which is also an @racket[_id].}
|
|
|
|
@defthing[_SEL ctype?]{
|
|
|
|
The type of an Objective-C selector, an opaque pointer.}
|
|
|
|
@defthing[_BOOL ctype?]{
|
|
|
|
The Objective-C boolean type. Racket values are converted for C in the
|
|
usual way: @racket[#f] is false and any other value is true. C values
|
|
are converted to Racket booleans.}
|
|
|
|
@defthing[YES boolean?]{
|
|
|
|
Synonym for @racket[#t]}
|
|
|
|
@defthing[NO boolean?]{
|
|
|
|
Synonym for @racket[#f]}
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
@section{Syntactic Forms and Procedures}
|
|
|
|
@defform*/subs[[(tell result-type obj-expr method-id)
|
|
(tell result-type obj-expr arg ...)]
|
|
([result-type code:blank
|
|
(code:line #:type ctype-expr)]
|
|
[arg (code:line method-id arg-expr)
|
|
(code:line method-id #:type ctype-expr arg-expr)])]{
|
|
|
|
Sends a message to the Objective-C object produced by
|
|
@racket[obj-expr]. When a type is omitted for either the result or an
|
|
argument, the type is assumed to be @racket[_id], otherwise it must
|
|
be specified as an FFI C type (@seeCtype).
|
|
|
|
If a single @racket[method-id] is provided with no arguments, then
|
|
@racket[method-id] must not end with @litchar{:}; otherwise, each
|
|
@racket[method-id] must end with @litchar{:}.
|
|
|
|
@examples[
|
|
#:eval objc-eval
|
|
(eval:alts (tell NSString alloc) (make-cpointer:id))
|
|
(eval:alts (tell (tell NSString alloc)
|
|
initWithUTF8String: #:type _string "Hello")
|
|
(make-cpointer:id))
|
|
]}
|
|
|
|
@defform*[[(tellv obj-expr method-id)
|
|
(tellv obj-expr arg ...)]]{
|
|
|
|
Like @racket[tell], but with a result type @racket[_void].}
|
|
|
|
@defform[(import-class class-id ...)]{
|
|
|
|
Defines each @racket[class-id] to the class (a value with FFI type
|
|
@racket[_Class]) that is registered with the string form of
|
|
@racket[class-id]. The registered class is obtained via
|
|
@racket[objc_lookUpClass].
|
|
|
|
@examples[
|
|
#:eval objc-eval
|
|
(eval:alts (import-class NSString) (void))
|
|
]}
|
|
|
|
@defform[(import-protocol protocol-id ...)]{
|
|
|
|
Defines each @racket[protocol-id] to the protocol (a value with FFI type
|
|
@racket[_Protocol]) that is registered with the string form of
|
|
@racket[protocol-id]. The registered class is obtained via
|
|
@racket[objc_getProtocol].
|
|
|
|
@examples[
|
|
#:eval objc-eval
|
|
(eval:alts (import-protocol NSCoding) (void))
|
|
]}
|
|
|
|
@defform/subs[#:literals (+ - +a -a)
|
|
(define-objc-class class-id superclass-expr
|
|
maybe-mixins
|
|
maybe-protocols
|
|
[field-id ...]
|
|
method)
|
|
([maybe-mixins code:blank
|
|
(code:line #:mixins (mixin-expr ...))]
|
|
[maybe-protocols code:blank
|
|
(code:line #:protocols (protocol-expr ...))]
|
|
[method (mode result-ctype-expr (method-id) body ...+)
|
|
(mode result-ctype-expr (arg ...+) body ...+)]
|
|
[mode + - +a -a]
|
|
[arg (code:line method-id [ctype-expr arg-id])])]{
|
|
|
|
Defines @racket[class-id] as a new, registered Objective-C class (of
|
|
FFI type @racket[_Class]). The @racket[superclass-expr] should produce
|
|
an Objective-C class or @racket[#f] for the superclass. An optional
|
|
@racket[#:mixins] clause can specify mixins defined with
|
|
@racket[define-objc-mixin]. An optional @racket[#:protocols] clause
|
|
can specify Objective-C protocols to be implemented by the class.
|
|
|
|
Each @racket[field-id] is an instance field that holds a Racket value
|
|
and that is initialized to @racket[#f] when the object is
|
|
allocated. The @racket[field-id]s can be referenced and @racket[set!]
|
|
directly when the method @racket[body]s. Outside the object, they can
|
|
be referenced and set with @racket[get-ivar] and @racket[set-ivar!].
|
|
|
|
Each @racket[method] adds or overrides a method to the class (when
|
|
@racket[mode] is @racket[-] or @racket[-a]) to be called on instances,
|
|
or it adds a method to the meta-class (when @racket[mode] is
|
|
@racket[+] or @racket[+a]) to be called on the class itself. All
|
|
result and argument types must be declared using FFI C types
|
|
(@seeCtype). When @racket[mode] is @racket[+a] or @racket[-a], the
|
|
method is called in atomic mode (see @racket[_cprocedure]).
|
|
|
|
If a @racket[method] is declared with a single @racket[method-id] and
|
|
no arguments, then @racket[method-id] must not end with
|
|
@litchar{:}. Otherwise, each @racket[method-id] must end with
|
|
@litchar{:}.
|
|
|
|
If the special method @racket[dealloc] is declared for mode
|
|
@racket[-], it must not call the superclass method, because a
|
|
@racket[(super-tell dealloc)] is added to the end of the method
|
|
automatically. In addition, before @racket[(super-tell dealloc)],
|
|
space for each @racket[field-id] within the instance is deallocated.
|
|
|
|
@examples[
|
|
#:eval objc-eval
|
|
(eval:alts
|
|
(define-objc-class MyView NSView
|
|
[bm] (code:comment @#,elem{<- one field})
|
|
(- _racket (swapBitwmap: [_racket new-bm])
|
|
(begin0 bm (set! bm new-bm)))
|
|
(- _void (drawRect: [@#,racketidfont{_NSRect} exposed-rect])
|
|
(super-tell drawRect: exposed-rect)
|
|
(draw-bitmap-region bm exposed-rect))
|
|
(- _void (dealloc)
|
|
(when bm (done-with-bm bm))))
|
|
(void))
|
|
]}
|
|
|
|
@defform[(define-objc-mixin (class-id superclass-id)
|
|
maybe-mixins
|
|
maybe-protocols
|
|
[field-id ...]
|
|
method)]{
|
|
|
|
Like @racket[define-objc-class], but defines a mixin to be combined
|
|
with other method definitions through either
|
|
@racket[define-objc-class] or @racket[define-objc-mixin]. The
|
|
specified @racket[field-id]s are not added by the mixin, but must be a
|
|
subset of the @racket[field-id]s declared for the class to which the
|
|
methods are added.}
|
|
|
|
|
|
@defidform[self]{
|
|
|
|
When used within the body of a @racket[define-objc-class] or
|
|
@racket[define-objc-mixin] method, refers to the object whose method
|
|
was called. This form cannot be used outside of a
|
|
@racket[define-objc-class] or @racket[define-objc-mixin] method.}
|
|
|
|
@defform*[[(super-tell result-type method-id)
|
|
(super-tell result-type arg ...)]]{
|
|
|
|
When used within the body of a @racket[define-objc-class] or
|
|
@racket[define-objc-mixin] method, calls a superclass method. The
|
|
@racket[result-type] and @racket[arg] sub-forms have the same syntax
|
|
as in @racket[tell]. This form cannot be used outside of a
|
|
@racket[define-objc-class] or @racket[define-objc-mixin] method.}
|
|
|
|
|
|
@defform[(get-ivar obj-expr field-id)]{
|
|
|
|
Extracts the Racket value of a field in a class created with
|
|
@racket[define-objc-class].}
|
|
|
|
@defform[(set-ivar! obj-expr field-id value-expr)]{
|
|
|
|
Sets the Racket value of a field in a class created with
|
|
@racket[define-objc-class].}
|
|
|
|
@defform[(selector method-id)]{
|
|
|
|
Returns a selector (of FFI type @racket[_SEL]) for the string form of
|
|
@racket[method-id].
|
|
|
|
@examples[
|
|
(eval:alts (tellv button setAction: #:type _SEL (selector terminate:)) (void))
|
|
]}
|
|
|
|
@defproc[(objc-is-a? [obj _id] [cls _Class]) boolean?]{
|
|
|
|
Check whether @racket[obj] is an instance of the Objective-C class
|
|
@racket[cls].}
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
@section{Raw Runtime Functions}
|
|
|
|
@defproc[(objc_lookUpClass [s string?]) (or/c _Class #f)]{
|
|
|
|
Finds a registered class by name.}
|
|
|
|
@defproc[(objc_getProtocol [s string?]) (or/c _Protocol #f)]{
|
|
|
|
Finds a registered protocol by name.}
|
|
|
|
@defproc[(sel_registerName [s string?]) _SEL]{
|
|
|
|
Interns a selector given its name in string form.}
|
|
|
|
@defproc[(objc_allocateClassPair [cls _Class] [s string?] [extra integer?])
|
|
_Class]{
|
|
|
|
Allocates a new Objective-C class.}
|
|
|
|
@defproc[(objc_registerClassPair [cls _Class]) void?]{
|
|
|
|
Registers an Objective-C class.}
|
|
|
|
@defproc[(object_getClass [obj _id]) _Class]{
|
|
|
|
Returns the class of an object (or the meta-class of a class).}
|
|
|
|
@defproc[(class_addMethod [cls _Class] [sel _SEL]
|
|
[imp procedure?]
|
|
[type ctype?]
|
|
[type-encoding string?])
|
|
boolean?]{
|
|
|
|
Adds a method to a class. The @racket[type] argument must be a FFI C
|
|
type (@seeCtype) that matches both @racket[imp] and the not
|
|
Objective-C type string @racket[type-encoding].}
|
|
|
|
@defproc[(class_addIvar [cls _Class] [name string?] [size exact-nonnegative-integer?]
|
|
[log-alignment exact-nonnegative-integer?] [type-encoding string?])
|
|
boolean?]{
|
|
|
|
Adds an instance variable to an Objective-C class.}
|
|
|
|
@defproc[(object_getInstanceVariable [obj _id]
|
|
[name string?])
|
|
(values _Ivar any/c)]{
|
|
|
|
Gets the value of an instance variable whose type is @racket[_pointer].}
|
|
|
|
@defproc[(object_setInstanceVariable [obj _id]
|
|
[name string?]
|
|
[val any/c])
|
|
_Ivar]{
|
|
|
|
Sets the value of an instance variable whose type is @racket[_pointer].}
|
|
|
|
@defthing[_Ivar ctype?]{
|
|
|
|
The type of an Objective-C instance variable, an opaque pointer.}
|
|
|
|
@defproc[((objc_msgSend/typed [types (vector/c result-ctype arg-ctype ...)])
|
|
[obj _id]
|
|
[sel _SEL]
|
|
[arg any/c])
|
|
any/c]{
|
|
|
|
Calls the Objective-C method on @racket[_id] named by @racket[sel].
|
|
The @racket[types] vector must contain one more than the number of
|
|
supplied @racket[arg]s; the first FFI C type in @racket[type] is used
|
|
as the result type.}
|
|
|
|
@defproc[((objc_msgSendSuper/typed [types (vector/c result-ctype arg-ctype ...)])
|
|
[super _objc_super]
|
|
[sel _SEL]
|
|
[arg any/c])
|
|
any/c]{
|
|
|
|
Like @racket[objc_msgSend/typed], but for a super call.}
|
|
|
|
@deftogether[(
|
|
@defproc[(make-objc_super [id _id] [super _Class]) _objc_super]
|
|
@defthing[_objc_super ctype?]
|
|
)]{
|
|
|
|
Constructor and FFI C type use for super calls.}
|
|
|
|
@table-of-contents[]
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
@section{Legacy Library}
|
|
|
|
@defmodule[ffi/objc]{The @racketmodname[ffi/objc] library is a
|
|
deprecated entry point to @racketmodname[ffi/unsafe/objc]. It
|
|
exports only safe operations directly, and unsafe operations are
|
|
imported using @racket[objc-unsafe!].}
|
|
|
|
@defform[(objc-unsafe!)]{
|
|
|
|
Analogous to @racket[(unsafe!)], makes unsafe bindings of
|
|
@racketmodname[ffi/unsafe/objc] available in the importing
|
|
module.}
|