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.
Nom : MacAPI.jpg
Affichages : 320
Taille : 51,5 Ko