Using Objective-C from Clozure CL R. Matthew Emerson rme@clozure.com
Messaging id dict =... [dict addobject:@"tang" forkey:@"beverage"]; printf("%d\n", [dict count]); receiver message
Messaging id dict =... [dict addobject:@"tang" forkey:@"beverage"]; addobject:forkey:
Messaging id dict = [[NSMutableArray alloc] init];
Low-level Messaging [dict addobject:@"tang" forkey:@"beverage"]; /* The compiler would emit something like this */ SEL selector = sel_getuid("addobject:forkey:"); obj_msgsend(dict, selector, @"tang", @"beverage"); ;; We can call C from Lisp, but it's not exactly nice... (with-cstrs ((s "addobject:forkey:")) (let ((sel (#_sel_getuid s))) (#_objc_msgsend dict sel :id #@"tang" :id @#"beverage")))
Higher-level Messaging from Lisp [dict addobject:@"tang" forkey:@"beverage"]; ;deprecated old way (send dict "addobject:forkey:" #@"tang" #@"beverage") ;or (send dict :add-object #@"tang" :for-key #@"beverage") ;new way (#/addobject:forkey: dict #@"tang" #@"beverage")
FFI Notation and Reader Macro Zoo (#_getpid) (with-cstrs ((s "/dev/null")) (let ((fd (#_open s #$O_RDWR))) (#_close fd))) #&NSZeroRect #&sys_errlist (rlet ((tv (:struct :timeval))) (#_gettimeofday tv +null-ptr+) (format t "~d.~6,'0d" (pref tv :timeval.tv_sec) (pref tv :timeval.tv_usec))) (rlet ((r :<NS>Rect)) (setf (pref r :<NS>Rect.origin.x) 0d0)...) (rlet ((r #>NSRect)) (setf (pref r #>NSRect.origin.x) 0d0)...)
Semi-Real-Life Example (defun make-standard-window (x y w h) (ns:with-ns-rect (rect x y w h) (#/initwithcontentrect:stylemask:backing:defer: (#/alloc (objc:@class "NSWindow")) rect (logior #$NSTitledWindowMask! #$NSClosableWindowMask! #$NSMiniaturizableWindowMask! #$NSResizableWindowMask) #$NSBackingStoreBuffered nil)))
Demo
Semi-Real-Life Example (defun browse-url (string) (with-cfstring (s string) (let ((url (#/URLWithString: ns:ns-url s))) (if (%null-ptr-p url) (error "~s is not a valid URL" string) (gui:execute-in-gui #'(lambda () (let* ((w (make-standard-window 100 100 600 700)) (content-view (#/contentview w)) (web-view (#/initwithframe:framename:groupname: (#/alloc ns:web-view) (#/bounds content-view) +null-ptr+ +null-ptr+)) (request (#/requestwithurl: ns:ns-url-request url))) (#/setautoresizingmask: web-view (logior #$NSViewWidthSizable #$NSViewHeightSizable)) (#/addsubview: content-view web-view) (#/release web-view) (#/settitle: w (#_CFURLGetString url)) (#/orderfront: w +null-ptr+) (#/loadrequest: (#/mainframe web-view) request))))))))
Demo
Semi-Real-Life Example (defun browse-url (string) (with-cfstring (s string) (let ((url (#/URLWithString: ns:ns-url s))) (if (%null-ptr-p url) (error "~s is not a valid URL" string) (gui:execute-in-gui #'(lambda () (let* ((w (make-standard-window 100 100 600 700)) (content-view (#/contentview w)) (web-view (#/initwithframe:framename:groupname: (#/alloc ns:web-view) (#/bounds content-view) +null-ptr+ +null-ptr+)) (request (#/requestwithurl: ns:ns-url-request url))) (#/setautoresizingmask: web-view (logior #$NSViewWidthSizable #$NSViewHeightSizable)) (#/addsubview: content-view web-view) (#/release web-view) (#/settitle: w (#_CFURLGetString url)) (#/orderfront: w +null-ptr+) (#/loadrequest: (#/mainframe web-view) request))))))))
NSURL ns-array NSURLRequest ns-url-request WebView web-view Name Translation (compute-lisp-name "MIDIView") m-id-i-view (define-special-objc-word "MIDI") (compute-lisp-name "MIDIView") midi-view (compute-objc-classname 'ns-mutable-dictionary) "NSMutableDictionary"
More about #/ '#/addobject:forkey: NEXTSTEP-FUNCTIONS: addobject:forkey: (objc:@selector #/addobject:forkey:)
Bonus #/ Side Effect! '#/addobject:forkey: NEXTSTEP-FUNCTIONS: addobject:forkey: (objc:@selector #/addobject:forkey:) Interning a symbol into this package triggers an interface database lookup for Objective-C methods of the same name. A successful lookup results in the creation of a special type of dispatching function. The symbol is then given that dispatching function as its function definition. #'#/dealloc #<OBJC-DISPATCH-FUNCTION NSFUN: dealloc #x3020014369cf>
Very nice, but so? (objc-message-send view "bounds")? (#/bounds view) #<NS-RECT 600 X 700 @ 0,0 [gcable] (#x290a4e40) #x302002b5d37d>
Typed Foreign Pointers (objc-message-send view "bounds")? (#/bounds view) #<NS-RECT 600 X 700 @ 0,0 [gcable] (#x290a4e40) #x302002b5d37d> Certain foreign types can be treated as instances of built-in classes. (class-of (ns:make-ns-rect 0 0 10 10)) #<BUILT-IN-CLASS NS:NS-RECT> (class-of (ns:make-ns-point 0 10)) #<BUILT-IN-CLASS NS:NS-POINT> (class-of (ns:make-ns-size 300 110)) #<BUILT-IN-CLASS NS:NS-SIZE> (class-of (ns:make-ns-range 11 17)) #<BUILT-IN-CLASS NS:NS-RANGE> Objective-C objects can also be recognized as instances of built-in classes and printed, used with typep, and so forth.
Defining an Objective-C Class @interface Wand : NSObject { id wood; NSString *core; float lengthininches; } - (id)initwithwood:(id)w core:(nsstring *)c length:(float)inches; - (id)wood; - (void)setwood:(id)w; @end
@implementation Wand - (id)wood { return wood; } - (void)setwood:(id)w { [wood release]; wood = [w copy]; } - (void)dealloc { [wood release]; [core release]; [super dealloc]; } @end Defining Objective-C methods
And in Lisp Style... (defclass wand (ns:ns-object) ((wood :foreign-type :id) (core :foreign-type :id) (length-in-inches :foreign-type :float)) (:metaclass ns:+ns-object)) (objc:defmethod #/wood ((self wand)) (slot-value self 'wood)) (objc:defmethod (#/setwood: :void) ((self wand) w) (#/release (slot-value self 'wood)) (setf (slot-value self 'wood) (#/copy w))) (objc:defmethod (#/dealloc :void) ((self wand)) (#/release (slot-value self 'wood)) (#/release (slot-value self 'core)) (call-next-method))
A Custom NSView (defclass sample-view (ns:ns-view) () (:metaclass ns:+ns-object)) (objc:defmethod (#/drawrect: :void) ((self sample-view) (dirty #>NSRect)) (#/set (#/redcolor ns:ns-color)) (#_NSRectFill (#/bounds self))) (defun sample-view-window () (gui:execute-in-gui (lambda () (let* ((w (make-standard-window 100 100 300 300)) (content-view (#/contentview w)) (v (#/initwithframe: (#/alloc sample-view) (#/bounds content-view)))) (#/setautoresizingmask: v (logior #$NSViewWidthSizable #$NSViewHeightSizable)) (#/addsubview: content-view v) (#/release v) (#/orderfront: w +null-ptr+) w))))
Demo
Hybrid Classes (defclass card-pile-view (ns:ns-view) ((deck :accessor card-deck)) (:metaclass ns:+ns-object)) (defmethod initialize-instance :after ((v card-pile-view) &rest initargs) (declare (ignore initargs)) (setf (slot-value v 'deck) (make-shuffled-deck)) (unless *images* (make-card-images))) (objc:defmethod (#/dealloc :void) ((self card-pile-view)) (objc:remove-lisp-slots self) (call-next-method)) (objc:defmethod (#/drawrect: :void) ((self card-pile-view) (dirty #>NSRect))...)
Demo
See it in action! In the next talk, all of the user interface that you see was made in Lisp using the Objective-C interface just described.