Sign in Join
Wiki copytemporary

PasCocoa: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: PasCocoa

OldNewLine
1+[![Warning-icon.png](/wiki-images/b/b2/Warning-icon.png)](</File:Warning-icon.png>)
2+
3+**Warning:** The information on this page is legacy and only available for historical purposes. Please see [[FPC PasCocoa]] for current information.
4+
5+
6+
7+**PasCocoa** is the project to build object oriented bindings to use Cocoa in Pascal.
8+
9+## Objective-C to Pascal Bindings
10+
11+There are 2 main versions of Objective-C, and thus of Objective-C headers: 1.0 and 2.0
12+
13+Version 2.0 is only available on Leopard 10.5 or later, making it too restrictive at the moment, and thus the available headers are a translation of 1.0 headers from a Tiger 10.4.9 machine.
14+
15+### Objective-C 1.0 Bindings
16+
17+ * Reference page: <http://developer.apple.com/documentation/Cocoa/Reference/ObjCRuntimeRef1/Reference/reference.html>
18+
19+ * Available on: 10.0 and later
20+
21+### Objective-C 2.0 Bindings
22+
23+ * Reference Pages:
24+ * Main Page: <http://developer.apple.com/documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html>
25+ * Type Encodings: <http://developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC/Articles/chapter_14_section_9.html>
26+ * Available on: Leopard 10.5 and later
27+
28+## Example
29+```
30+{
31+ simpleform.pas
32+
33+ This example shows how to use the PasCocoa bindings to create a
34+ NSAutoreleasePool, initialize the application global variable, create
35+ a simple window without contents and attach a close handler to it that
36+ exits the application.
37+
38+ Compilation of this example requires the following options:
39+ -k-framework -kcocoa -k-lobjc
40+
41+ This example project is released under public domain
42+
43+ AUTHORS: Felipe Monteiro de Carvalho
44+ }
45+ program simplewindow;
46+
47+ {$ifdef fpc}{$mode delphi}{$endif}
48+
49+ uses
50+ objc, ctypes, FPCMacOSAll, AppKit, Foundation;
51+
52+ const
53+ Str_Window_Title = 'This is the title';
54+ Str_Window_Message = 'This is the message';
55+ var
56+ { classes }
57+ pool: NSAutoreleasePool;
58+ MainWindow: NSWindow;
59+ MainWindowView: NSView;
60+ TextField: NSTextField;
61+ { strings }
62+ CFTitle, CFMessage: CFStringRef;
63+ { sizes }
64+ MainWindowRect, TextFieldRect: NSRect;
65+ begin
66+ { Creates a AutoreleasePool for this thread. Every thread must have one }
67+ pool := NSAutoreleasePool.Create;
68+
69+ { Creates the application NSApp object }
70+ NSApp := NSApplication.sharedApplication;
71+
72+ { Creates a simple window }
73+ MainWindowRect.origin.x := 300.0;
74+ MainWindowRect.origin.y := 300.0;
75+ MainWindowRect.size.width := 300.0;
76+ MainWindowRect.size.height := 500.0;
77+
78+ MainWindow := NSWindow.initWithContentRect(MainWindowRect,
79+ NSTitledWindowMask or NSClosableWindowMask or NSMiniaturizableWindowMask or NSResizableWindowMask,
80+ NSBackingStoreBuffered, NO);
81+
82+ { Initializes the title of the window }
83+
84+ CFTitle := CFStringCreateWithPascalString(nil, Str_Window_Title, kCFStringEncodingUTF8);
85+ MainWindow.setTitle(CFTitle);
86+
87+ { Adds a NSTextField with a string }
88+
89+ CFMessage := CFStringCreateWithPascalString(nil, Str_Window_Message, kCFStringEncodingUTF8);
90+ TextFieldRect.origin.x := 0.0;
91+ TextFieldRect.origin.y := 200.0;
92+ TextFieldRect.size.width := 300.0;
93+ TextFieldRect.size.height := 100.0;
94+ TextField := NSTextField.initWithFrame(TextFieldRect);
95+ TextField.setStringValue(CFMessage);
96+ MainWindowView := NSView.CreateWithHandle(MainWindow.contentView);
97+ MainWindowView.addSubview(TextField);
98+
99+ { Put's the window on the front z-order }
100+
101+ MainWindow.orderFrontRegardless;
102+
103+ { Enters main message loop }
104+
105+ NSApp.run;
106+
107+ { Releases the AutoreleasePool for this thread }
108+ pool.Free;
109+ end.
110+```
111+
112+## Subversion
113+
114+New repository:
115+```
116+svn co http://svn.freepascal.org/svn/lazarus/trunk/lcl/interfaces/carbon/objc objc
117+ svn co http://svn.freepascal.org/svn/lazarus/trunk/lcl/interfaces/carbon/pascocoa pascocoa
118+```
119+
120+Old repository:
121+```
122+svn co https://lazarus-ccr.svn.sourceforge.net/svnroot/lazarus-ccr/bindings/objc objc
123+ svn co https://lazarus-ccr.svn.sourceforge.net/svnroot/lazarus-ccr/bindings/pascocoa pascocoa
124+```
125+
126+## Documentation
127+
128+### Creating a class which inherits from a Cocoa class
129+
130+To create a class which inherits from a Cocoa class, one needs to create a normal class which inherits from any of the classes on the PasCocoa bindings and use the NSObject.CreateClassDefinition method to register that class on the Objective-C runtime before calling the NSObject constructor.
131+
132+Example:
133+```
134+uses foundation;
135+
136+ type
137+
138+ { TMyController }
139+
140+ TMyController = class(NSObject)
141+ public
142+ { Extra binding functions }
143+ constructor Create; override;
144+ function getClass: objc.id; override;
145+ ...
146+ end;
147+
148+ const
149+ Str_TMyController = 'TMyController';
150+ ...
151+
152+ implementation
153+
154+ constructor TMyController.Create;
155+ begin
156+ { The class is registered on the Objective-C runtime before the NSObject constructor is called }
157+ if not CreateClassDefinition(Str_TMyController, Str_NSObject) then WriteLn('Failed to create objc class');
158+
159+ inherited Create;
160+
161+ { Insert other desired initialization here }
162+ end;
163+
164+ { Called by the NSObject constructor to get the definition of our class.
165+
166+ At this point the class is simply identifyed by its name and must be already registered
167+ on the Objective-C runtime. }
168+ function TMyController.getClass: objc.id;
169+ begin
170+ Result := objc_getClass(Str_TMyController);
171+ end;
172+```
173+
174+### Registering Pascal methods as Objective-c methods
175+
176+Pascal methods aren't automatically available to Objective-C. They need to be first registered on the Objective-C runtime by overriding the AddMethods method and calling NSObject.AddMethod method for every method to be registered. This is very common practice for assigning delegate and controller methods in Cocoa.
177+
178+When calling NSObject.AddMethod you must convert the parameter types to single characters, each representing a specific type (table can be found below). The process contains 3 steps:
179+
180+ * Locate the Objective-C method you wish to override in the Cocoa headers (it may not exist in the Pascal interfaces).
181+ * Make the Pascal wrapper method which conforms to the Objective-C syntax. Note the first 2 paratemters are always "_self: objc.id; _cmd: SEL".
182+ * Add method to Objective-C runtime using NSObject.AddMethod and convert the parameter types to single character representation.
183+
184+Example for outlineView:isItemExpandable: method of NSOutlineView:
185+
186+ * \- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item;
187+ * function isItemExpandable (_self: objc.id; _cmd: SEL; item: objc.id): BOOL; cdecl;
188+ * AddMethod('outlineView:isItemExpandable:', 'B@:@', Pointer(isItemExpandable));
189+
190+Note the string "B@:@" which is the parameters of the method converted to single characters as explained in the table below.
191+
192+Full Example:
193+```
194+type
195+ TMyController = class(NSObject)
196+ public
197+ { Extra binding functions }
198+ constructor Create; override;
199+ procedure AddMethods; override;
200+ { Objective-c Methods }
201+ class procedure doClose(_self: objc.id; _cmd: SEL; sender: objc.id); cdecl; {$ifndef VER2_2_2}static;{$endif}
202+ class function applicationShouldTerminateAfterLastWindowClosed(_self: objc.id;
203+ _cmd: SEL; theApplication: objc.id): cbool; cdecl; {$ifndef VER2_2_2}static;{$endif}
204+ end;
205+
206+ implementation
207+
208+ procedure TMyController.AddMethods;
209+ begin
210+ AddMethod('doClose:', 'v@:@', Pointer(doClose));
211+ AddMethod('applicationShouldTerminateAfterLastWindowClosed:', 'B@:@',
212+ Pointer(applicationShouldTerminateAfterLastWindowClosed));
213+ end;
214+
215+ constructor TMyController.Create;
216+ begin
217+ { The class is registered on the Objective-C runtime before the NSObject constructor is called }
218+ if not CreateClassDefinition(ClassName(), Str_NSObject) then WriteLn('Failed to create objc class ' + ClassName());
219+
220+ inherited Create;
221+ end;
222+
223+ { Objective-c Methods }
224+
225+ class procedure TMyController.doClose(_self: objc.id; _cmd: SEL; sender: objc.id); cdecl;
226+ begin
227+ {close the window}
228+ end;
229+
230+ class function TMyController.applicationShouldTerminateAfterLastWindowClosed(_self: objc.id;
231+ _cmd: SEL; theApplication: objc.id): cbool; cdecl;
232+ begin
233+ Result := objc.YES;
234+ end;
235+```
236+
237+
238+
239+### Getting objects from Cocoa classes
240+
241+Cocoa classes often contain methods which return an object which was allocated internally for use by that class. For example NSTextView.textStorage will return the NSTextStorage object which the NSTextView uses internally to manage the text. However the object returned belongs to Objective-C and therefore you must explicitly create a wrapper (which you must eventually release).
242+
243+Example:
244+```
245+var
246+ textView: NSTextView;
247+ textStorage: NSTextStorage;
248+ bounds: NSRect;
249+
250+ textView := NSTextView.initWithFrame(bounds);
251+ textView.insertText(CFSTR('Foo'));
252+
253+ textStorage := NSTextStorage.CreateWithHandle(textView.textStorage);
254+ writeln(textStorage.length);
255+ textStorage.Free;
256+```
257+
258+### Using Selectors with the Target/Action Paradigm
259+
260+Selectors are used to represent a message in Objective-C and can be used in conjunction with a class (the target) to produce an "action". However, with the PasCocoa you must first create a NSObject class to handle the selector and register a method to accept it.
261+
262+For example we create the controller class TMyController and register the handleButtonAction method along with our Pascal implementation.
263+```
264+type
265+ TMyController = class (NSObject)
266+
267+ class procedure handleButtonAction (_self: objc.id; _cmd: objc.SEL; sender: objc.id); cdecl;
268+
269+ procedure AddMethods; override;
270+ constructor Create; override;
271+ end;
272+
273+ class procedure TMyController.handleButtonAction (_self: objc.id; _cmd: objc.SEL; sender: objc.id); cdecl;
274+ begin
275+ writeln('Hello World');
276+ end;
277+
278+ procedure TMyController.AddMethods;
279+ begin
280+ AddMethod('myController:handleButtonAction:', 'v@:@', Pointer(handleButtonAction));
281+ end;
282+
283+ constructor TMyController.Create;
284+ begin
285+ if not CreateClassDefinition(ClassName, 'NSObject') then
286+ writeln('Failed to create class ' + ClassName);
287+
288+ inherited Create;
289+ end;
290+```
291+
292+Next we need to create the class and a test subject: NSButton.
293+```
294+var
295+ Controller: TMyController;
296+ Button: NSButton;
297+
298+ {create the NSObject controller class}
299+ Controller := TMyController.Create;
300+
301+ {create a new NSButton}
302+ CFButtonText := CFStringCreateWithPascalString(nil, 'Hello World', kCFStringEncodingUTF8);
303+ ButtonRect.origin.x := 30;
304+ ButtonRect.origin.y := 30;
305+ ButtonRect.size.width := 128;
306+ ButtonRect.size.height := 25;
307+ Button := NSButton.initWithFrame(ButtonRect);
308+ Button.setTitle(CFButtonText);
309+ Button.setBezelStyle(NSRoundedBezelStyle);
310+```
311+
312+Finally we can set the buttons target (which is our controller class) with setTarget. Remember to use the classes actual Objective-C reference using Controller.Handle (not the wrapper Controller). Then call the Objective-C runtime function sel_registerName to register the selector, which is the name of our method that we registered before. If the selector has already been registered it will just return the handle, not leak memory. Then, set the action with the selector using setAction.
313+```
314+Button.setAction(sel_registerName(PChar('myController:handleButtonAction:')));
315+ Button.setTarget(Controller.Handle);
316+```
317+
318+The methods setAction and setTarget happen to belong to NSControl but this paradigm of action/target is used commonly throughout the API, where the action is a selector (message string which is the name of a method) and the target which is an object.
319+
320+
321+
322+## Roadmap
323+
324+### General Roadmap
325+
326+Component | Status | Details
327+---|---|---
328+Objective-C 1.0 Runtime Headers | Working | Cheetah 10.0 or superior
329+Objective-C 2.0 Runtime Headers | Not Implemented | Leopard 10.5 or superior only
330+Automatic headers Parser | Partially Implemented
331+Foundation | Partially Implemented
332+AppKit | Partially Implemented
333+
334+### Foundation Classes Roadmap
335+
336+Component | Status
337+---|---
338+NSArray | Working
339+NSAutoreleasePool | Working
340+NSBundle | Working
341+NSDate | Working
342+NSGeometry | Partially Implemented
343+NSObjcRuntime | Working
344+NSObject | Partially Implemented
345+NSRange | Partially Implemented
346+NSString | Working
347+NSValue | Working
348+NSZone | Partially Implemented
349+
350+### AppKit Classes Roadmap
351+
352+Component | Status
353+---|---
354+NSActionCell | Partially Implemented
355+NSAlert | Partially Implemented
356+NSApplication | Working
357+NSBitmapImageRep | Working
358+NSBox | Working
359+NSButton | Working
360+NSButtonCell | Partially Implemented
361+NSCell | Partially Implemented
362+NSControl | Working
363+NSGraphics | Partially Implemented
364+NSGraphicsContext | Working
365+NSImage | Working
366+NSImageRep | Working
367+NSMenu | Working
368+NSMenuItem | Partially Implemented
369+NSNibDeclarations | Partially Implemented
370+NSNibLoading | Partially Implemented
371+NSOpenPanel | Working
372+NSPanel | Partially Implemented
373+NSResponder | Partially Implemented
374+NSSavePanel | Working
375+NSStatusBar | Working
376+NSStatusItem | Working
377+NSText | Working
378+NSTextField | Working
379+NSTextFieldCell | Working
380+NSToolbar | Working
381+NSToolbarItem | Working
382+NSView | Working
383+NSWindow | Working
384+
385+## Implementation Details
386+
387+### Overview
388+
389+The Cocoa headers contain several peculiarities that lead us to need a special structure for the bindings. First, several forward declarations of classes are done in a very unordently way. Pascal only accepts forward declarations of classes if they are all declared on the same **type** clause, and therefore a special session **FORWARD** is created to have a single forward declaration for each file.
390+
391+The include file has a total of 4 separate sessions: HEADER, FORWARD, CLASSES and IMPLEMENTATION
392+
393+A tipical include file should have this structure:
394+```
395+{%mainunit appkit.pas}
396+ {
397+ NSStatusBar.h
398+ Application Kit
399+ Copyright (c) 1997-2005, Apple Computer, Inc.
400+ All rights reserved.
401+ }
402+
403+ {$ifdef HEADER}
404+ {$ifndef NSSTATUSBAR_PAS_H}
405+ {$define NSSTATUSBAR_PAS_H}
406+
407+ //insert constants, records and other interface declarations with the exception of classes
408+
409+ {$endif}
410+ {$endif}
411+
412+ {$ifdef FORWARD}
413+
414+ TNSClass = class;
415+
416+ {$endif}
417+
418+ {$ifdef CLASSES}
419+ {$ifndef NSSTATUSBAR_PAS_H}
420+ {$define NSSTATUSBAR_PAS_H}
421+
422+ //declaration of classes
423+
424+ TNSClass = class(NSObject)
425+ ...
426+ end;
427+
428+ {$endif}
429+ {$endif}
430+ {$ifdef IMPLEMENTATION}
431+
432+ //insert implementation declarations here. The order is not important on the implementation, so don't use extra ifdefs.
433+
434+ {$endif}
435+```
436+
437+### HEADERS
438+
439+**Guidelines:**
440+
441+ * Inclusion of headers in other frameworks should be removed, because the each framework is mapped to a pascal unit, and therefore all include files of a used framework are already accessible. Inclusion of headers of the same file framework should become include clauses which will used a C-styled ifdef mechanism to resolve in which order the include files will be added. This mechanism works fairly well and keeps the structure of the include files similar to the original one.
442+
443+### CLASSES
444+
445+**Guidelines:**
446+
447+ * Should include the same include files as the HEADERS section
448+
449+ * All private members of classes should be removed
450+
451+### IMPLEMENTATION
452+
453+ * Method should send message using one of the functions:
454+
455+objc_msgSend
456+
457+objc_msgSend_frep (float and double types for i386, not for PPC)
458+
459+objc_msgSend_stret (records)
460+
461+Reference Page: <http://developer.apple.com/documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html>
462+
463+ * Class methods must be called for ClassID
464+ * Instance methods must be called for Handle
465+
466+### Type Encodings
467+
468+ * Reference page: <http://developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC/Articles/chapter_14_section_9.html#//apple_ref/doc/uid/TP30001163-CH9-TPXREF165>
469+
470+Table of codes used for types when registering methods in the Objective-C Run-time Library:
471+
472+c | Char | A character
473+---|---|---
474+i | cint (ctypes unit) | A C compatible integer
475+s | cshort (ctypes unit) |
476+l | clong (ctypes unit) |
477+q | clonglong (ctypes unit) | A long long
478+C | cuchar (ctypes unit) |
479+I | cuint (ctypes unit) |
480+S | cushort |
481+L | culong |
482+Q | culonglong | An unsigned long long
483+f | Float |
484+d | Double |
485+B | ??? | A C++ bool or a C99 _Bool
486+v | None | Indicates the absense of the parameter
487+* | PChar | A C-style string
488+@ | id (objc unit) | An Objective-C class instance
489+# | Class (objc unit) |
490+: | Sel (objc unit) | An Objective-C selector
491+[array type] | | An array
492+^type | P<type> | A pointer to type
493+? | Pointer | A generic pointer
494+
495+## Notes
496+
497+### Objects allocation
498+
499+Objective-C language does not provide any special syntax to define, if class method is construtor method or not. However, Apple provides some kind of methods naming convention, you can learn more about it here: <http://developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC/Articles/chapter_13_section_3.html>
500+
501+Following Objective-C methods, should be constructors for Pascal objects:
502+
503+ * class methods named +(id) alloc... (Obj-C construct methods)
504+ * class methods +(NSClassName *) anyMethodName... (Obj-C convenience constructors)
505+ * class methods +(id) anyMethodName (Obj-C convenience constructors)
506+ * instance methods name - init... (Obj-C initialization methods).
507+
508+Though object allocation and initialization are separated in time for Objective-C. Pascal rarely uses the same style, providing larger number of constructors, so - init... methods should become constructors, though it's questionable.
509+
510+### Categories
511+
512+Objective-C provides mechanism to extend class methods dynamicly without need of class iheritance. These are known as categories and extensions. Reference page: <http://developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC/Articles/chapter_4_section_1.html> This feature is not supported by Pascal, there's no way to extend methods of the class without inheritance.
513+
514+Categories are used very often in Cocoa headers. They are used to group methods logically. It's common, that Base class is extended with several categories within a single header file. The good example is NSString.h, there base NSString class has only 2 method, while it's categories declares dozens of new methods.
515+
516+In this case (if base class and it's categories are declared in the single header) all methods of the categories must be declared the base class. So convertede Pascal class NSString, would contain all methods that were declared in NSString.h categories.
517+
518+The same way, might be used to generate Pascal classes, if base Obj-C class and it's categories are declared in different headers, but discouraged.
519+
520+### Carbon-Cocoa integration
521+
522+Some Obj-C classes can be cast to Carbon types, such types are also known as toll-free bridged. It's recommended to use Carbon types (all of them declared in the MacOSALL unit) for parameters and method result types.
523+```
524+function GetName: NSString;
525+ procedure SetStrParam(value: NSString);
526+```
527+
528+should be
529+```
530+function GetName: CGStringRef;
531+ procedure SetStrParam(value: CGStringRef);
532+```
533+
534+List of toll-free bridged types, add additional information on Carbon-Cocoa integration can be found at this page: <http://developer.apple.com/documentation/Cocoa/Conceptual/CarbonCocoaDoc/Articles/InterchangeableDataTypes.html>
535+
536+### Unnamed enums
537+
538+There're great number of unnamed enums declared in Cocoa headers. These enums are used to describe the constant values for methods parameters. They must be converted as Pascal constants, rather than enumeration, to avoid parameter size conflicts.
539+
540+## Cocoa related external links
541+
542+ * NSToolbar tutorial: <http://www.dikant.de/2007/09/05/nstoolbar-basics/>
543+ * Working without a nib file
544+
545+ 1. <http://lapcatsoftware.com/blog/2007/05/16/working-without-a-nib-part-1/>
546+ 2. <http://lapcatsoftware.com/blog/2007/06/04/working-without-a-nib-part-2-also-also-wik/>
547+ 3. <http://lapcatsoftware.com/blog/2007/06/10/working-without-a-nib-part-5-no-3/>
548+ 4. <http://lapcatsoftware.com/blog/2007/06/17/working-without-a-nib-part-4-setapplemenu/>
549+ 5. <http://lapcatsoftware.com/blog/2007/07/10/working-without-a-nib-part-5-open-recent-menu/>
550+ 6. <http://lapcatsoftware.com/blog/2007/11/25/working-without-a-nib-part-6-working-without-a-xib/>
551+
552+ * Code to create a menu without a nib file: <http://trixul.cvs.sourceforge.net/viewvc/*checkout*/trixul/trixul/layout/cocoa/cocoamenubarimpl.mm?revision=1.6>
553+
554+## Articles about PasCocoa
555+
556+ * Toolbox Magazine 2008, editions 5 and 6 (german language): <http://www.toolbox-mag.de/archiv2008.html>
557+
558+---
559+*From the Free Pascal wiki, page "PasCocoa", as the Internet Archive kept it on 2025-03-24, last changed on 2021-06-06. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history