Bonjour,
Voici un exemple de création d'une fenêtre MacOSX avec instanciation des objets NSApplication, NSWindow et création des classes TApplicationDelegate et TWindowDelegate qui reçoivent les événements des deux précédents.
Il faut savoir que Objective-C est une surcouche à C qui se contente de faire les appels que j'ai mis ici sous Delphi XE2, c'est donc plus verveux sous Delphi XE2 mais au final c'est la même chose.
Le coeur du système est la fonction objc_msgSend qui permet d'invoquer la méthode d'un object. L'objet peut être une classe (objc_getClass) et dans ce cas on a une méthode de classe, ou une instance obtenue par un objc_msgSend justement. En Objective-C le code objc_msgSend(objc_getClass('NSObject'), sel_getUid('alloc')) s'écrira tout simplement [NSObject alloc]; de même l'appel d'init se fera dans la foulée [[NSObject alloc] init].
Notez cependant que Objective-C utilise des fichiers NIB qui sont l'équivalent des DFM de Delphi, donc généralement l'approche est tout de même différente.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236 program MacAPI; // (c)2012 Execute SARL http://www.execute.re {$APPTYPE CONSOLE} {$R *.res} const // les "DLL" nécessairse à l'application libdl = '/usr/lib/libdl.dylib'; libobjc = 'libobjc.A.dylib'; // pour dlopen équivalent à LoadLibrary RTLD_LAZY = 1; // NSWindow.styleMask NSTitledWindowMask = 1; NSClosableWindowMask = 2; NSMiniaturizableWindowMask = 4; NSResizableWindowMask = 8; // NSWindow.NSBackingStoreType NSBackingStoreBuffered = 2; type NSPoint = record x: Single; y: Single; end; NSSize = record width: Single; height: Single; end; NSRect = record origin: NSPoint; size: NSSize; end; function dlopen(Filename: PAnsiChar; Flag: Integer): NativeUInt; cdecl; external libdl name '_dlopen'; // Objective-C Runtime function objc_allocateClassPair(superclass: Pointer; name: PAnsiChar; ExtraBytes: Longword): Pointer; cdecl; external libobjc name '_objc_allocateClassPair'; function class_addMethod(Cls: Pointer; theSelector: Pointer; Impl: Pointer; types: PAnsiChar): Integer; cdecl; external libobjc name '_class_addMethod'; procedure objc_registerClassPair(Cls: Pointer); cdecl; external libobjc name '_objc_registerClassPair'; function objc_getClass(const name: PAnsiChar): Pointer; cdecl; external libobjc name '_objc_getClass'; function sel_getUid(const str: PAnsiChar): Pointer; cdecl; external libobjc name '_sel_getUid'; function objc_msgSend(theReceiver, theSelector: Pointer): Pointer; cdecl; varargs; external libobjc name '_objc_msgSend'; var // Selector (ou messages) de Objective-C msg_alloc : Pointer; msg_init : Pointer; msg_setDelegate : Pointer; msg_object : Pointer; msg_setTitle : Pointer; msg_stringWithCharacters: Pointer; // Classes Objective-C NSObject : Pointer; NSString : Pointer; // Classes utilisateur TAppDelegate : Pointer; TWindowDelegate : Pointer; // variables de l'application Pool : Pointer; App : Pointer; // allouer une classe d'après son nom équivalent de TObject.newInstance function Alloc(const AClass: PAnsiChar): Pointer; begin Result := objc_msgSend(objc_getClass(AClass), msg_alloc); end; // Appelle le constructor "init" d'une classe d'après son nom function Init(const AClass: PAnsiChar): Pointer; overload; begin Result := objc_msgSend(Alloc(AClass), msg_init); end; // Appelle le constructor "init" d'une classe d'après son Pointer (objc_getClass ou objc_allocateClassPair) function Init(const AClass: Pointer): Pointer; overload; begin Result := objc_msgSend(objc_msgSend(AClass, msg_alloc), msg_init); end; // Conversion d'un chaîne Delphi en NSString autodétruite function StrToNSString(const Str: string): Pointer; begin Result := objc_msgSend(NSString, msg_stringWithCharacters, PWideChar(Str), Length(Str)); end; // Evénement "OnLoad" de l'application function applicationDidFinishLaunching(id, sel, notification: Pointer): Boolean; cdecl; var Rect : NSRect; Flags : LongWord; Window: Pointer; begin WriteLn('Evénement applicationDidFinishLaunching'); Rect.origin.x := 100; Rect.origin.y := 100; Rect.size.width := 320; Rect.size.height := 200; Flags := NSTitledWindowMask or NSClosableWindowMask or NSMiniaturizableWindowMask or NSResizableWindowMask; WriteLn(' Création d''une NSWindow'); Window := objc_msgSend( Alloc('NSWindow'), sel_getUid('initWithContentRect:styleMask:backing:defer:'), Rect, Flags, NSBackingStoreBuffered, True ); WriteLn(' Association d''un TWindowDelegate'); objc_msgSend(Window, msg_setDelegate, Init(TWindowDelegate)); WriteLn(' Afficher la fenêtre'); objc_msgSend(Window, sel_getUid('makeKeyAndOrderFront:'), App); Result := True; end; // L'application doit-elle se terminer quand toutes les fenêtres sont fermées ? function applicationShouldTerminateAfterLastWindowClosed(id, sel, App: Pointer): Boolean; cdecl; begin WriteLn('Evénement applicationShouldTerminateAfterLastWindowClosed'); Result := True; end; // Evénements OnResize d'une fenêtre procedure windowDidResize(id, sel, notification: Pointer); cdecl; var Window: Pointer; begin WriteLn('Evénement windowDidResize'); // Lecture de l'objet "Sender" Window := objc_msgSend(notification, msg_object); // changement du titre objc_msgSend(Window, msg_setTitle, StrToNSString('Hello')); end; begin WriteLn('Chargement de AppKit'); dlopen('/System/Library/Frameworks/AppKit.framework/AppKit', RTLD_LAZY); WriteLn('Lecture des selecteurs'); msg_alloc := sel_getUid('alloc'); msg_init := sel_getUid('init'); msg_setDelegate := sel_getUid('setDelegate:'); msg_object := sel_getUid('object'); msg_setTitle := sel_getUid('setTitle:'); msg_stringWithCharacters := sel_getUid('stringWithCharacters:length:'); WriteLn('Lecture des classes'); NSObject := objc_getClass('NSObject'); NSString := objc_getClass('NSString'); WriteLn('Déclaration de la classe TAppDelegate'); TAppDelegate := objc_allocateClassPair(NSObject, 'TAppDelegate', 0); // Ajout des méthodes dont on a besoin AVANT le register class_addMethod( TAppDelegate, sel_getUid('applicationDidFinishLaunching:'), @applicationDidFinishLaunching, 'i12@0:4@8' // 'i' = retourne en Integer, '@' = NSObject, ':' = Selector ); class_addMethod( TAppDelegate, sel_getUid('applicationShouldTerminateAfterLastWindowClosed:'), @applicationShouldTerminateAfterLastWindowClosed, 'B12@0:4@8' // retourne un Boolean à l'offset 12, NSObject à l'offset 0, Selector à l'offset 4, NSObject à l'offset 8 ); // obligatoire AVANT d'instancier la classe objc_registerClassPair(TAppDelegate); WriteLn('Déclaration de la classe TWindowDelegate'); TWindowDelegate := objc_allocateClassPair(NSObject, 'TWindowDelegate', 0); // Ajout des méthodes dont on a besoin class_addMethod( TWindowDelegate, sel_getUid('windowDidResize:'), @windowDidResize, //'v12@0:4@8' // procedure (void) à l'offet 12, NSObject à l'offet 0, Selector à l'offet 4, NSObject à l'offset 8 //'v@:@' // fonctionne également sans préciser les offsets ! nil // fonctionne également sans rien précisier (voir le cas des overload...) ); objc_registerClassPair(TWindowDelegate); WriteLn('Allocation d''un NSAutoreleasePool'); Pool := Init('NSAutoreleasePool'); try WriteLn('Allocation d''un NSApplication'); App := objc_msgSend(objc_getClass('NSApplication'), sel_getUid('sharedApplication')); WriteLn('Associer l''AppDelegate'); objc_msgSend(App, msg_setDelegate, Init(TAppDelegate)); finally WriteLn('Libération du NSAutorealasePool (si plus utilisé)'); objc_msgSend(Pool, sel_getUId('release')); end; WriteLn('Lancement de l''application'); objc_msgSend(App, sel_getUid('run')); WriteLn('Jamais de retour ici.'); end.![]()
Partager