I am using the Racket Objective-C FFI to embed a WebKit WebView in an app.
In order to receive page-load notifications I am creating a new ObjC class in Racket that is set as the frame-load delegate of the web-view.
The class looks something like:
(define-objc-class MyWebFrameLoadDelegate NSObject
[]
(- _void (webView: [_id wv] didFinishLoadForFrame: [_id wf])
(send frame set-status-text "Page Loaded")))
When running the code within DrRacket it works great the first time. Further iterations cause the process to die:
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x0000000000000020
...
Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0 libobjc.A.dylib 0x00007fff8511e299 objc_registerClassPair + 63
1 Racket 0x00000001002d329c ffi_call_unix64 + 76
2 Racket 0x00000001002d3eb4 ffi_call + 644
3 Racket 0x00000001002c612f ffi_do_call + 1599
4 ??? 0x00000001004b50cc 0 + 4299903180
...
... which seems to indicate that it is the re-definition of the ObjC class that is the cause.
Is there a clean way using the FFI, or more general Racket features, to avoid re-defining the class if it already exists ?
I tried wrapping define-objc-class
in a conditional but it needs to be a top-level form.
I could drop down to the raw ObjC runtime functions and define the delegate class on the fly - but it would be nice to avoid that.
Solution - wrap the define-objc-class
in a let
to define the class in a nested scope inside a conditional:
(define MyWebFrameLoadDelegate
(or (objc_lookUpClass "MyWebFrameLoadDelegate")
(let ()
(define-objc-class MyWebFrameLoadDelegate NSObject
[]
(- _void (webView: [_id wv] didFinishLoadForFrame: [_id wf])
(send frame set-status-text "Page Loaded")))
MyWebFrameLoadDelegate)))
The ffi/unsafe library enables the direct use of C-based APIs within Racket programs— without writing any new C code. From the Racket perspective, functions and data with a C-based API are foreign, hence the term foreign interface.
From the Racket perspective, functions and data with a C-based API are foreign, hence the term foreign interface. Furthermore, since most APIs consist mostly of functions, the foreign interface is sometimes called a foreign function interface, abbreviated FFI.
In Objective-C, the class interface specifies exactly how a given type of object is intended to be used by other objects. In other words, it defines the public interface between instances of the class and the outside world.
Furthermore, since most APIs consist mostly of functions, the foreign interface is sometimes called a foreign function interface, abbreviated FFI.
I don't think define-obj-class
must be used at top level. It is a definition form, though, so you can't use it as an expression. You should be able to do something like the following:
(define MyClass
(or ___ ;; find the class, if it already exists
(let ()
(define-objc-class MyClass ___)
MyClass)))
BTW, it looks like a fix for the crashing behavior was committed on March 7.
Just in case you missed it:
https://github.com/shekari/racket-webkit/blob/master/webkit.rkt
Enjoy,
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With