Now it's time to look at the workhorse of Tiny.m, the setup( ) function. We'll try to digest it in pieces. Here is the first part of the function:
NSWindow *myWindow = nil;
NSView *myView = nil;
NSRect graphicsRect;
// now create the window
graphicsRect = NSMakeRect(100.0, 350.0, 400.0, 400.0);
The first two lines set up local variables that will be used to hold the ids of the NSWindow and NSView objects that will be created. They are initialized to nil, which is a pointer to the empty object. (That is, it is a pointer to 0; messages sent to nil are ignored.) The third line creates a local variable that will hold the location on the screen where Tiny.m will draw its window.
The Cocoa Foundation provides three C typedefs for doing graphics (NSPoint, NSSize, and NSRect), which are defined in the following code. If you're interested, you can find their declarations in the file NSGeometry.h in the /System/Library/Frameworks/
Foundation.framework/Headers directory (we'll refer to this file as / Foundation/NSGeometry.h).
typedef struct _NSPoint { float x;
float y;
} NSPoint;
typedef struct _NSSize {
float width; /* should never be negative */
float height; /* should never be negative */
} NSSize;
typedef struct _NSRect { NSPoint origin;
NSSize size;
} NSRect;
The function NSMakeRect( ) is simply a convenient shorthand for creating a rectangle that has a particular origin and size. Instead of using this:
graphicsRect = NSMakeRect(100.0, 350.0, 400.0, 400.0);
we could have used:
graphicsRect.origin.x = 100.0;
graphicsRect.origin.y = 350.0;
graphicsRect.size.width = 400.0;
graphicsRect.size.height = 400.0;
The graphicsRect contains the details of where the new window will be located and how big it will be. The window itself gets created in the next Tiny.m program line, when the alloc message is sent to the NSWindow class (recall that alloc is a class method). The new instance object is then initialized within the nested initWithContentRect:styleMask:
backing:defer: message. The id of the new NSWindow object that is created is assigned to the variable myWindow:
myWindow = [ [NSWindow alloc]
initWithContentRect: graphicsRect
styleMask: NSTitledWindowMask |NSClosableWindowMask
|NSMiniaturizableWindowMask backing: NSBackingStoreBuffered
defer: NO ];
One of the many nice features of Cocoa's Objective-C interface is that arguments are labeled, which makes Objective-C programs easy to read. In the example above, the four arguments are initWithContentRect:, styleMask:, backing:, and defer:. After each colon are the arguments themselves.
Let's look at each of the arguments:
initWithContentRect: graphicsRect
Specifies where the window will be created and how large it will be. In this case, the location of the lower-left corner is at (100.0,350.0) and the size is 400 pixels square.
(The screen origin - the point (0.0,0.0) - is the pixel at the lower-left corner of the Mac OS X screen.)
style: NSTitledWindowMask|NSClosableWindowMask| NSMiniaturizableWindowMask Tells the Window Server to display the window with a title bar, a close button, and a miniaturize button. (The vertical bar is the Objective-C bitwise OR operator, which causes the bits within the numerical constants to be OR-ed together.) Most Mac OS X windows have title bars that contain titles. To set up a window without a title bar, omit the NSTitledWindowMask argument. These and other window attributes are defined in the file /Appkit/NSWindow.h.
backing: NSBackingStoreBuffered
Specifies which kind of backing to use. Windows can have three kinds of backing:
retained, buffered, or none. Retained backing means that visible portions of the window that a program draws are written directly to screen memory, but that an off- screen buffer is set up to retain nonvisible portions that are obscured by other windows. Thus, if the window is covered by another window and then exposed, the Window Server can redraw it without any work on the part of your program.
Buffered windows use the off-screen buffer as an input buffer, and the buffer's contents are transferred to the screen when the window is flushed. Windows with no backing have no off-screen memory; if they are covered and then exposed, they must be redrawn, and might momentarily flash white while that redrawing takes place.
Buffered windows are most common in Cocoa.
defer: NO
Tells the Window Server that we want our window created right away, rather than later.
Pass Small Structures, Not Pointers to Structures
Cocoa frequently passes entire structures on the stack as arguments to functions and methods, whereas other frameworks more often will pass pointers to
structures. In the previous example, for instance, the entire graphicsRect structure, rather than a pointer, is passed.
Even though it's faster to push a pointer on to the stack than to push the entire structure, once the structure is on the stack, the called subroutine can access the structure's element very quickly. By contrast, if a pointer is pushed onto the stack, referencing each element requires a pointer de-reference.
If the called function is going to access every element of the structure, it is
considerably faster to push the entire structure onto the stack. And as added benefits, passing complete structures on to the stack results in cleaner code, eases memory management, and improves threading.
Remember, all of these arguments make up a single Objective-C method, whose proper name is initWithContentRect:styleMask:backing:defer:.
Unlike in C++, you cannot leave off an argument and get a default value!
After the long myWindow statement executes, the myWindow variable contains the id of the window created with the attributes provided. We can then send messages to the window by sending messages to that id, as we do in the next statement. The following message sets the window's title to the string "Tiny Application Window". The at-sign directive, @"", tells the compiler to create an NSString object with the text "Tiny Application Window", rather than creating a char * string:
[myWindow setTitle: @"Tiny Application Window"];
The next four statements in Tiny.m create an object of the NSView class and set up the window for drawing. We need to describe the NSView class before we can discuss these statements thoroughly.