OpenSTEP Developers Tutorial 4.0 Mach 1996
OpenSTEP Developers Tutorial 4.0 Mach 1996
A DEVELOPER TUTORIAL
Object-Oriented Software
DISCOVERING OPENSTEP:
A Developer Tutorial
f·
NeXT Software, Inc.
900 Chesapeake Drive
Redwood City, CA 94063
U.S.A.
We at NeXT have tried to make the information contained in this publication as accurate and reliable as
possible. Nevertheless, NeXT disclaims any warranty of any kind, whether express or implied, as to any
matter whatsoever relating to this publication, including without limitation the merchantability or fitness
for any particular purpose. NeXT will from time to time revise the software described in this publication
and reserves the right to make such changes without the obligation to notify the purchaser. In no event
shall NeXT be liable for any indirect, special, incidental, or consequential damages arising out of purchase
or use of this publication or the information contained herein.
Restricted Rights Legend: Use, duplication, or disclosure by the Government is subject to restrictions as
set forth in subparagraph (c)(1 )(ij) of the Rights in Technical Data and Computer Software clause at DFARS
252.227-7013 (or, if applicable, similar clauses at FAR 52.227-19 or NASA FAR Supp. 52.227-86).
NeXT, the NeXT logo, NEXTSTEp, Netlnfo, and Objective-C are registered trademarks of NeXT Software,
Inc. The NEXTSTEP logo, Application Kit, Enterprise Object, Enterprise Objects Framework, Interface
Builder, OPENSTEp, the OPENSTEP logo, PDO, Portable Distributed Objects, WebObjects, and
Workspace Manager are trademarks of NeXT Software, Inc. Use in commerce other than as "fair use" is
prohibited by law except by express license from NeXT Software, Inc.
Address inquiries concerning usage of NeXT trademarks, designs, or patents to General Counsel, NeXT
Software, Inc., 900 Chesapeake Drive, Redwood City, CA 94063 USA.}
28 Aligning on a Grid
97 Getting in on the Action: Delegation and Notification
iii
Table of Contents
iv
tjciono Projects :b;1j
Delete
ry
Jill
~24-Hour
05,.:,5.2,; ,~~tJ
~
Chapter 1
Introduction
Sections
What is OPENSTEP?
3
Introduction
4
When you begin any enterprise, you must find a starting
point. You set out from that starting point and acquire a basic
vocabulary, a notion of boundaries and techniques, a sense
of how things fit together and what is possible. For those
who want to learn how to create OPENSTEP applications,
this book provides a starting point.
With this book you become familiar with OPENSTEP
application development not merely by reading but by
doing. The book guides you through the creation of three
applications of increasing complexity. Along the way it
explains related concepts and issues. The techniques and
concepts you learn in one tutorial lay the foundation for the
more advanced techniques and concepts in the next
tutorial.
The final chapter of the book tells you where to go for
further information and where and how to find things, such
as tools and documentation. It also tells you how to get
NeXT products and services.
This book covers a lot of ground, although sometimes at
only a summary level. Finishing this book makes you much
better prepared to take on serious application development
with OPENSTEP in general and the Enterprise Object
Framework in particular.
Although the aim is primarily to educate, this book is also
intended-for those interested in programming-to be fun.
5
Chapter 1 Introduction to OPENSTEP
The two core components ofthe product are OPENSTEP User and
OPENSTEP Developer.
. Applications
Application Kit
OPENSTEP User is a user environment Display Foundation
acclaimed for its intuitively navigable desktop Postscript Kit
0IifHSfBi and file manager. On it you can easily deploy
your own OPENSTEP applications as well as those OpenStep
supplied by NeXT and third-party vendors. Intelligent networking,
particularly Netlnfo, makes it possible to install and upgrade
OPENSTEP in a fraction of the time it takes other systems.
The OpenStep object layer allows corporate customers to create, evolve
OPENSTEP Developer, NeXT's software- and deploy multi-tier; client/server business applications in a fraction of
development environment, provides time it takes other methods.
seamlessly integrated set of tools for building
• Application Kit: APls for user-interface objects and for essential
0IifHSfBi complex applications that can be deployed on application behavior, such as event handling
heterogeneous client/server networks running not only
OPENSTEP, but Portable Distributed Objects, Enterprise Objects • Foundation Kit APls that define basic object behavior, that support
Framework, and OpenStep-based software developed by other object persistence and distribution, and that "objectify" collections,
vendors. Unicode strings, and many other programmatic entities
• Display PostScript: APls for PostScript drawing
Whats in a Name?
"0PENSTEP" refers to the software product. "0penStep" refers to the standard or specification on which the product is based, and by extension to the
concepts expressed by the specification.
The OpenStep specification is available via anonymous ftp at ftp. next. com.
6
The OPENSTEP user
environment includes File
!newer (a file-system browser),
Mail, Preferences, Edit, and
other applications in/NextApps.
• With WebObjects you can easily create dynamic websites. application to be distributed among a heterogeneous network
WebObjects applications provide a smart, interactive of OpenStep clients and a broad range of servers.
connection between corporate databases and customers on
"The Web." • D'OLE brings the PD~ object model to the Windows platform,
giving Windows application developers the ability to make use
• Enterprise Objects Framework enables you to construct of distributed-object technology.
applications that use (and reuse) enterprise business objects,
storing them in relational databases such as those from Oracle In addition to software products, NeXT provides developer
and Sybase. support and professional services, in particular the Object
Experts program, an innovative on-site support and training
• Portable Distributed Objects (PDO) allow objects in a single program.
CORBA NeXT has licensed SunSoft's implementation of the CORBA standard (from the Object Management
Group) and is committed to CORBA interoperability.
7
Chapter 1 Introduction to OPENSTEP
OPENSTEP Developer 4.0 is a programming environment ideally Interface Builder makes it easy to create application
suited for the rapid development of custom object-oriented interfaces by dragging objects from palettes.
applications deployable on a variety of computer architectures. It Standard palettes hold an assortment of Application
comprises an integrated set of development software, libraries, Kit objects. Custom palettes can include third-party
header files, tools, documentation, and other resources. objects as well as the developer's own objects. Interface Builder
archives and restores elements of a user interface as objects-it
doesn't "hardwire" them into the interface. Interface Builder
helps to connect objects for messaging, and it assists in the
::::::'::-I1!IiIL~
Project Builder is an application that manages
definition of custom classes.
software-development projects, and that
orchestrates and streamlines the development
process. It integrates a project browser, a full-
featured code editor, language-savvy symbol recognition,
sophisticated project search capabilities, header file and
documentation access, build and debugging support, and a host
of other features.
@implementation Controller
8
, .
• OpenStep Class Libraries. Includes NeXT's implementation of • Compiler and Library Technology. Two important features of
the Application Kit, the Foundation Kit, and Display PostScript, OPENSTEP Developer 4.0 for Mach are dynamic shared
with some extensions. libraries and frameworks. Programs linked with a dynamic
shared library share one copy ofthat library's routines, and are
• Objective-C. An object-oriented programming language that is linked with only those modules they currently need.
an extension of standard ANSI C, Dbjective-C is a simple and Frameworks assemble all library components in one place:
powerful language. It is easy to learn, yet elegant in its executable code, header files, resources, and documentation.
application to the problem domain. OPENSTEP projects can The executable code is in the form of a dynamic shared library.
include Objective-C, C, and C++ source code. The Application Kit, Foundation, and Display PostScript are
In addition, OPENSTEP Developer 4.0 offers a new version of the installed as frameworks.
GNU Ccompiler, enhancements to C++ compilation, and GNU
make technology.
9
Chapter 1 Introduction to OPENSTEP
--+- Messages
10
The Advantage of OPEN STEP
Proven Technology. NeXT Software's technology has been Don't Take Our Word For It
evolving through 10 years and four major releases. During that
time, it has been rigorously tested and iteratively refined. NeXT Here are a few comments on OPENSTEP and its predecessor,
has an established track record in object technology, while it will NEXTSTEP:
be years before its major competitors can offer comparable • Booz Allen & Hamilton's study of OPENSTEP development
technology of comparable maturity. suggests that experienced developers could increase their
True Objects. OPENSTEP objects are truly objects-modular, productivity five to ten times.
autonomous, persistent, and distributable. They are not static • "Information is our business. That's why OPENSTEP succeeds
entities, but can be bound dynamically atrun time. When you drag an here. The most important product we have is the quality of the
object from an Interface Builder palette, you're getting a real object service we provide: our timeliness, the effectiveness of our
and not an area painted on the screen with some code attached. analysis and planning."
Portability. OPENSTEP is designed to foster both hardware Director, Software Engineering
portability and operating-system portability. Fannie Mae
Simplified Client/Server Development. OPENSTEP Developer's • "We would never have be able to do what we did on time or on
integrated tool-set simplifies the complex process of building budget if we had chosen any other solution but NeXT."
distributed client/server applications. Manager, IS Branch Automation
Chrysler Financial Corporation
Substantial Business Benefits. OPENSTEP's object-orientation
helps managers to accelerate the introduction of new products • "The greatthing about object-oriented programming is thatthe
and services that depend on new software. With OPENSTEP longer you're at the game, the more benefits you derive. You
programmers can modify software quickly and assuredly to take can reuse objects you've created or add to objects to make
advantage of evolving business opportunities. Through reusable them more robust. And NEXTSTEP is the best integrated
object libraries, systems integrators can quickly customize a computer platform on the market."
generic productto produce an individualized software solution for MIS Manager,
each client. UBS Securities
11
Chapter 1 Introduction to OPENSTEP
12
Rle Vievn~r
r;y Converter
ToDo
Exchange R ate per $1: 3.45~
Convert (.J'l
Chapter 2
Currency Converter Tutorial
Sections Concepts
15
Currency Converter Tutorial
16
The application that you are going to create in this tutorial is called Currency
Converter. It is a simple application, yet it exemplifies much of what software
development with OpenStep is about. As you'll discover, Currency Converter is
amazingly easy to create, but it's equally amazing how many features you get
"for free" - as with all OpenStep applications.
Exchange R ate per $1 :I.~.~~_. ".~ . ~ ... #.: Enter an exchange rate and a dollar amount
Instead of clicking the button, you can also press the Return key. You can
double-click the converted amount, copy it (with the Edit menu's Copy
command) and paste it in another application that takes text. You can tab
between the first two fields. You can do many other things common to OpenStep
applications.
In this tutorial you 'Illearn the basic things you must do to create a OpenStep
application. You will discover how to:
• Create a project.
• Create an interface.
• Create a custom subclass.
• Connect an instance of the custom subclass to the interface.
• Design an application using a common object-oriented design paradigm.
17
Chapter 2 Currency Converter Tutorial
By following the steps of this chapter, you will become more familiar with the
two most important OpenStep applications for program development: Interface
Builder and Project Builder. You will also learn the typical work flow of
OpenStep application development:
18
Creating the Currency Converter Project
Every OpenStep application starts out as aproject. A project is a repository for all
the elements that go into the application, such as source code files, makefiles,
frameworks, libraries, the application's user interface, sounds, and images. You
use the Project Builder application to create and manage projects.
2 Make a new project. [-- -- - ---- - ------ - - - ------- -- - --- --- -- ---------
Project Builder creates a project directory named after the project-in this case
CurrencyConverter-and populates this directory with an assortment of ready-
made files and directories. It then displays its main window.
Note: Here's a variation on project creation: Create a project directory using File
Viewer and then, in the New Project panel, navigate to that directory, type
"PB.project" in the Name field, and click OK.
19
Chapter 2 Currency Converter Tutorial
Go ahead and click an item in the left column of the project browser (a grouping
of project resources sometimes called a "suitcase"); see what some of these
A makefile specifies file suitcases contain already:
dependency relations and
compiler and linker instructions • Other Sources: This suitcase contains CurrencyConvertecmain.m, the mainO routine
for building the project. See that loads the initial set of resources and runs the application. (Do not modify
OPENSTEP Development: Tips this file!)
and Techniques for common
changes to Makefile.preamble and • Interfaces: This suitcase contains CurrencyConverter.nib, the file that contains the
Makefile.postamble. application's user interface. More on this file in the next step.
• Supporting Files: This suitcase contains the project's default makefiles and
template source-code files. You can modify the preamble and postamble
makefiles, but you must leave Makefile unchanged.
Project Indexing
When you create or open a project, after Usually indexing happens automatically
some seconds you may notice triangular when you create or open a project. You can
"branch" buttons appearing after source turn off this option if you wish. Choose
code files in the browser. Project Builder has Preferences from the Info menu and then
indexed these files. choose the Indexing display. Turn off the
"Index when project is opened" switch.
During indexing Project Builder stores all
symbols of the project (classes, methods,
globals, etc.) in virtual memory. This allows You can also index a project at any time by
Project Builder to access project-wide choosing Index Source Code from the
information quickly. Indexing is Project menu. If you want to do without
indispensable to such features as name indexing (maybe you have memory
completion and Project Find. (More on these constraints), choose Purge Indices from the
features later.) Project menu.
20
Creating the Currency Converter Interface
When you create an application project, Project Builder puts the maillllib jile in
the Interfaces suitcase. A nib file is primarily a description of a user interface (or
part of a user interface). The main nib file contains the main menu and any
windows and panels you want to appear when your application starts up; at start-
up time, each application loads the main nib file.
At the beginning of a project, the main nib file is like a blank canvas, ready for
you to craft the interface. Look in the Interfaces suitcase for nib files.
21
Chapter 2 Currency Converter Tutorial
A window in Open Step looks very similarto windows in other user its windows and tracks the current status of each. Each window,
environments such as Windows or Macintosh. It is a rectangular on the other hand, manages a hierarchy of views in addition to its
area on the screen in which an application displays controls, PostScript window.
fields, text, and graphics. Windows can be moved around the
At the "top" of this hierarchy is the content view, which fits just
screen and stacked on top of each other.like pieces of paper. A
within the window's content rectangle. The content view
typical OpenStep window has a title bar, a content area, and
encloses all other view (its subviews), which come below itinthe
several control objects.
hierarchy. The NSWindow distributes events to views in the
hierarchy and regulates coordinate transformations among them.
resize bar
The window that the Window Server creates is paired with an Windows have numerous characteristics. They can be on-screen
object supplied by the Application Kit: an instance of the or off-screen. On-screen windows are "Iayered" on the screen in
NSWindow class. Each physical window in an object-oriented tiers managed by the Window Server. On-screen windows also
program is managed by an instance of NSWindow (or subclass). can carry a status: keyor main.
When you create an NSWindow object, the Window Server Key windows respond to key presses for an application and are
creates the physical window that the NSWindow object will the primary recipient of action messages from menus and panels.
manage. The Window Server references the window by its Usually a window is made key when the user clicks it Key
window number, the NSWindow by its own identifier. windows have black title bars. Each application can have only
one key window.
Application, Window, View An application has one main window, which can often have key
status as well. The main window is the principal focus of user
In a running OpenStep application, NSWindow objects occupy a
actions for an application. Often user actions in a modal key
middle position between an instance of NSApplication and the
window (typically a panel such as the Font panel or an inspector)
views of the application. (A view is an object that can draw itself
have a direct effect on the main window. In this case, the title bar
and detect user events.) The NSApplication object keeps a list of
of the main window (when it is not key) is a dark gray.
22
Creating the Currency Converter Interface
Most objects on an interface have attributes that you can set in the Inspector
panel's Attributes display.
C
The title of the major window in
an application is often the
Selectthe Attributes display from
application name.
the pop-up list.
Backlng - Controls -
Enter the window title. C Nonretained Miniaturize.:kl
C Retained Close .:J When this option is turned off,
Turn off the resize option. []
C Buffered R~e~si~ze~b~a~lf=lt::tt_ _ _- the windows's resize bar
'""" , c " w' " ""
23
Chapter 2 Currency Converter Tutorial
Put palette objects on the window using the "drag and drop" technique.
To initialize the text field, double- You must get rid of the word "Text" in this field; otherwise, that's what the field
click "Text" and press Delete. will show when the nib file is loaded.
The text field should be longer so it can hold more digits (you're dealing with
millions here):
Currency Converter needs two more text fields, both the same size as the first.
You have two options: you can drag another object from the palette and make it
the same size; or you can duplicate the first object
24
Creating the Currency Converter Interface
5 Duplicate an object.
Select the text field. ..II The new text field appears slightly offset
from the original field. Reposition it under the
.Q
Choose Edit ~ Copy. first text field.
Choose Edit ~ Paste.
Get the third field from the palette and make it the same size as the first field.
field.
Choose Format ~ Size ~ Same
Size
You're not done yet with these text fields. The bottom text field displays the
result of the computation. It should not be editable and therefore should, by
convention, have a gray background.
The Views palette provides a "Title" object that you can easily adapt to be a
text-field label. (The title object is actually a text field, set to have a gray
background and no border, and to be non-editable and non-selectable.) Text in
25
Chapter 2 Currency Converter Tutorial
the title object is centered by default, but labels are usually aligned from the
right.
When you cut and paste objects that contain text, like these labels, the object
should be selected and not the text the object contains; if the text is selected,
de-select it by clicking outside the text, then click the object again to select it.
26
Creating the Currency Converter Interface
If you check the attributes of the button in the Inspector panel, you'll notice two
things have been added: NSReturnSign is now listed as the button's icon, and the
Key field contains the escape sequence for a carriage return (\r).
You've probably noticed that the final interface for Currency Converter (shown
on the first page of this chapter) has a decorative line between the text fields and
the button. This line is easy to make.
27
Chapter 2 Currency Converter Tutorial
As you might have noticed, the Currency Converter has a main menu that holds,
by default, the commands Info, Hide, and Quit, and the Edit, Services, and
Windows menus. The menus contain ready-made sets of commands. The Edit
menu includes commands for cutting, copying, and pasting text. The Windows
menu lists the titles of open windows as well as common window commands.
The Services menu allows your application to communicate with other
applications, often with no work on the part of your application. For example, if
your application handles text, you can use the Services menu to transfer
information to other applications that accept text.
Aligning on a Grid
You can align objects on a window by nearest grid intersection like nails to a
imposing a grid on the window. When you magnet. You set the edges of alignment and
move objects in this grid, they "snap" to the the spacing of the grid (in pixels) in the
Alignment panel. Choose Format ~Align ~
Alignment to display this panel.
Be sure the grid is turned on before you move
objects (Format ~Align ~ Turn Grid On).
You can move selected user-interface
objects in Interface Builder by pressing an
arrow key. When the grid is turned onthe unit
of movement is whatever the grid is set to (in
pixels). When the grid is turned off, the unit of
movement is one pixel.
28
Creating the Currency Converter Interface
The nextKeyView variable is an The final step in composing the Currency Converter interface has more to do
outlet. An outlet is the identifier of with behavior than appearance. You want the user to be able to tab from the first
an object that another object
editable field to the second, and back again to the first. Many objects on
stores as an instance variable.
Outlets enable communication
Interface Builder's palettes have an instance variable named nextKeyView. This
between objects. See page 40 for variable identifies the next object to receive keyboard events when the user
more information on outlets. presses the Tab key (or the previous object if Shift-Tab is pressed). If you want
inter-field tabbing you must connect fields through the nextKeyView variable.
29
Chapter2 Currency Converter Tutorial
Don't connect the nextKeyView outlet of the "Amount in Other Currency" field;
this field is not supposed to be editable.
30
Creating the Currency Converter Interface
13 Testthe interface. The CurrencyConverter interface is now complete. Interface Builder lets you
test an interface without having to write one line of code.
Choose Document ~ Save to
save the interface to the nib file.
Note: You can also exit from test mode by double-clicking the Interface Buildfer
Choose Document ~ Test icon, which changes to the following image to represent test mode:
Interface.
Try various operations in the
interface (see suggestions on the
following pagel.
When finished, choose Quit from
the main menu.
31
Chapter 2 Currency Converter Tutorial
The simplest OpenStep application, even one without a line of could also have setthe auto-resizing attributes of the window and
code added to it, includes a wealth of features that you get "for its views so thatthe window's objects would resize proportionally
free": You do not have to program these features yourself. You can to the resized window or would retain their initial size (see
see this when you test an interface in Interface Builder. OPENSTEP Programming: Tools and Techniques for details on
auto-resizing).
To enter test mode, choose Test Interface from the Document
menu. Interface Builder simulates how your application (in this
case, Currency Converter) would run, minus the behavior added Controls and Text
by custom classes. Go ahead and try things out: move your
windows, type in fields, click buttons. The buttons and textfields of Currency Converter come with many
built-in behaviors. Click the Convert button. Notice how the button
is highlighted momentarily.
Application and Window Behavior
In test mode Currency Converter behaves almost like any other
application on the screen. Click elsewhere on the screen, and
Currency Converter is deactivated, becoming totally or partially
obscured by the windows of other applications. If you had buttons of a different style, such as radio buttons, they
would also respond in characteristic ways to mouse clicks.
Now click in one of the text fields. See how the cursor blinks in
place. Type some text and select it. Use the commands in the Edit
menu to copy it and paste it in the other text field.
32
Creating the Currency Converter Interface
An OpenStep application can do an impressive range of things management), the Color panel (and color management), and,
without a formidable programming effort on your part. although it's not a panel, the text ruler and the tabbing and
indentation capabilities it provides.
Doc·ument Management
Formatter classes enable your application to format numbers,
Many applications create and manage semi-autonomous objects dates, and other types of field values. Support for validating the
called documents. Documents contain discrete sets of
contents of fields is also available.
information and support the entry and maintenance of that
information. A word-processing document is a typical example.
The application coordinates with the user and communicates Printing and Faxing
with its documents to create, open, save, close and otherwise
manage them. With just a simple Interface Builder procedure, OpenStep
The final tutorial in this book describes how to create an automates simple printing and faxing of views that contain text or
application based on a multi-document architecture. graphics. When a user clicks the control, an appropriate panel
helps to configure the print or fax process. The output is
File and Account Management WYSIWYG.
An application can use the Open panel of the Application Kit to Several Application Kit classes give you greater control over the
help the user locate files in the file system and open them. It can printing of documents and forms, including features such as
also make the Save panel available for saving information in files. pagination and page orientation.
NeXT's version of OpenStep also provides classes for managing
files in the file system (creating, comparing, copying, moving, and
so forth) and for managing system-account information and user Help
defaults.
You can create a help system for your application using Interface
Communicating With Other Applications Builder, Project Builder, and an RTF text editor (such as Edit). The
Application Kit includes an class for context-sensitive help. If the
OpenStep gives an application several ways of communicating
user clicks an object on the application's interface while pressing
information to and from other applications:
a Help key, a small window is displayed containing concise
• Pasteboard: The pasteboard is a global facility for sharing information on the object.
information among applications. Applications can use the
pasteboard to hold data that the user has cut or copied and
may paste into another application. Custom Drawing and Animation
• Services: Any application can avail itself ofthe services OpenStep lets you create your own custom views that draw their
provided by another application, based on the type of the own content and respond to user actions. To assist you in this,
selected data (such as text). An application can also provide OpenStep provides image-compositing and event-handling API
services to other applications such as encryption, language as well as PostScript operators, operator functions, and client
translation, or record-fetching. library functions.
• Drag-and-drop: If your application implements the proper
protocol, users can drag objects to and from the interfaces of
Plug and Play
other applications.
33
Chapter 2 Currency Converter Tutorial
Note: This design for Currency Converter is intended to illustrate a few points,
and so is perhaps overly designed for something so simple. It is quite possible to
have the application's controller class, ConverterController, do the computation
and do without the Converter class.
This book depicts objects as filled and segmented "donuts." Why and may return data to the requesting object. As the symbol
this unlikely shape? suggests, an object's methods do the encapsulating, in effect
mediating access to the object's data. An object's methods are
also its interface, articulating the ways in which the object
communicates with the world outside it.
34
Designing the Currency Converter Application
You can divide responsibility within Currency Converter among two custom
objects and the user interface, taken as a collection of ready-made Application
Kit objects. The Converter object is responsible for computing a currency
amount and returning that value. Between the user interface'and the Converter
object is a cOlltrol/erobject, ConverterController. ConverterControl1er coordinates
the activity between the Converter object and the ur objects.
tn\ ConverierConlrolier
/.:::sJ
·~dnve;i"
I/'''''-·~''''''''' "
r
\
yI Converter
The ConverterController class assumes a central role. Like all controller objects,
it communicates with the interface and with model objects, and it handles tasks
specific to the application, such as managing the cursor. ConverterController
gets the values users enter into fields, passes these values to the Converter
object, gets the result back from Converter, and puts this result in a field in the
interface.
The Converter class merely computes a value from two arguments passed into
it and returns the result. As with any model object, it could also hold data as well
as provide computational services. Thus, objects that represent customer
records (for example) are akin to Converter. By insulating the Converter class
from application-specific details, the design for Currency Converter makes it
more reusable, as you'll see in the Travel Advisor tutorial.
35
Chapter 2 Currency Converter Tutorial
This type of object represents special knowledge and expertise. Hybrid Models
Model objects hold a company's data and define the logic
that manipulates that data. For example, a Customer object, MVC, strictly observed, is not advisable in all circumstances.
common in business applications, is a Model object. It holds data
Sometimes its bestto combine roles. For instance, in a graphics-
describing the salient facts of a customer and has access to
algorithms that access and calculate new data from those facts. intensive application, such as an arcade game, you might have
A more specialized Model class might be one in a meteorological several View objects that merge the roles of View and Model.
system called Front; objects of this class would contain the data In some applications, especially simple ones, you can combine
and intelligence to represent weather fronts. Model objects are the roles of Controller and Model; these objects join the special
not displayable. They often are reusable, distributed, persistent, data structures and logic of Model objects with the Controller's
and portable to a variety of platforms.
hooks to the interface.
View Objects
A Note on Terminology
A View object in the paradigam represents something visible on
the user interface (a window, for example, or a button). A View The Application Kit and Enterprise Objects Framework reserve
object is "ignorant" of the data it displays. The Application Kit special meanings for "vew object" and "model." A view object in
usually provides all the View objects you need: windows, text the Application Kit denotes a user-interface object that inherits
fields, scroll views, buttons, browsers, and so on. But you might from NSView. In the Enterprise Objects Framework, a model
wantto create your own View objects to show or represent your
establishes and maintains a correspondence between an
data in a novel way (for example, a graph view). You can also
group View objects within a window in novel ways specific to an enterprise object class and data stored in a relational database.
application. View objects, especially those in kits, tend to be very This book uses "model object" only within the context ofthe
reusable and so provide consistency between applications. ModelNiew-Controlier paradigm.
36
Defining the Classes of Currency Converter
Interface Builder is a versatile tool for application developers. It enables you not
only to compose the application's graphical user interface, but it gives you a way
to define much of the programmatic interface of the application's classes and to
connect the objects eventually created from those classes.
You must go to the Classes display of the nib file window to define a class. Once
there, the first thing you must do is select the superclass, the class your new
subclass will inherit from. Let's start with the ConverterController class.
Specify a subclass.
Click to select the Classes display.
Go to the Classes display ofthe
nib file window.
Select NSObject, the superclass
of your custom classes. NSObject the root class, is the class that
Choose Subclass from the pull- ConverterController will inherit from.
down Operations menu.
The Subclass command in this pull-down
menu generates a new subclass.
To newcomers to the subject, explanations of object-oriented What especially differentiates a class from its instance is data. A
programming might seem to use the terms "object" and "class" instance has its own unique set of data but its class, strictly
interchangeably. Are an object and a class the same thing? And if speaking, does not. The class defines the structure of the data its
not, how are they different? How are they related? instances will have, but only instances can hold data.
An object and a class are both programmatic units. They are A class, on the other hand, implements the behavior of all of its
closely related, but serve quite different purposes in a program. instances in a running program. The donut symbol used to
represent objects is a bit misleading here, because it suggests
First, classes provide a taxonomy of objects, a useful way of
that each object contains its own copy of code. This is fortunately
categorizing them. Just as you can say a particular tree is a pine
not the case; instead of being duplicated, this code is shared
tree, you can identify a particular object by its class. You can among all current instances in the program.
thereby know its purpose and what messages you can send it. In
other words, a class describes the type of an object. Implicit in the notion of a taxonomy is inheritance, a key property
of classes. Classes exist in a hierarchical relationship to one
Second, you use classes to generate instances-or objects.
another, with a subclass inheriting behavior and data structures
Classes define the data structures and behavior of their
from its superclass, which in turn inherits from its superclass.
instances, and at run time create and initialize these instances.
In a sense, a class is like a factory, stamping out instances of itself See the appendix, "Object-Oriented Programming," for more on
when requested. these and other aspects of classes.
37
Chapter 2 Currency Converter Tutorial
Now your class is established in the hierarchy of classes within the nib file. Next,
specify the paths for messages travelling between the ConverterController
object and other objects. In Interface Builder you specify these paths as outlets
and actions.
Before You Go On - - - - - - - - - - - - - - - - - - - - -
Here's some basic terminology:
Action Refers both to a message sent to an object when the user clicks a button
or manipulates some other control object and to the method that is invoked.
Control object A user-interface object (a device) with which users can interact to
affect events in the application. Control objects include buttons, text fields,
forms, sliders, and browsers. All control objects inherit from NSControl.
38
Defining the Classes of Currency Converter
In the nib file window, click the -~~~~~~:"""'lT~~~~ ---- Click here to begin specifying outlets.
electrical-outlet icon to the right
ofthe class. "Outlets" appears indented underneath,
highlighted (not shown).
Choose Add Outlet from the
Operations pull-down menu Instead of choosing Add Outlets from the
Operations menu, you can press Return
Type the name of the outlet over when "Outlets" is highlighted to add an
the highlighted "myOutlet." Name outlet.
the first outlet rateField.
Press Return.
Repeat the last three steps to
define two other outlets:
doliarField
totalField
ConverterController has one action method, convert:. When the user clicks the
Convert button, a convert: message is sent to the target object, an instance of
ConverterController.
39
Chapter 2 Currency Converter Tutorial
source destination
id anObjecti
You can use id as the type for any object; objects with id as their
type are dynamically typed, meaning thatthe class ofthe object is
determined at run time. You can statically type an object as a
pointer to a class name; you can declare these objects as
instance variables, butthey are not outlets. What distinguishes
outlets is their relationship to Interface Builder.
40
Defining the Classes of Currency Converter
One or more cell objects are always associated with a control • To make an outlet connection, draw a line from the custom
object (that is, an object inheriting from NSControl, such as a instance to another object in the application.
button). Control objects "drive" the invocation of action methods,
Another way to clarify connections is to consider who needs to
but they get the target and action from a cell. NSActionCell
find whom. With outlets, the custom object needs to find some
defines the target and action outlets, and most kinds of cells in the
other object, so the connection is from the custom objectto the
Application Kit inherit these outlets.
other object With actions, the control object needs to find the
custom object, so the connection is from the control object
These are only rules of thumb for the common case, and do not
.MiOg. apply in all circumstances. For instance, many OpenStep objects
have a delegate outlet; to connect these, you draw a connection
line from the OpenStep object to your custom object
i
I
action r.::'~------
.. :I Convert ~ ¢!ll
_________ .J, / . ---
", ....,.--_.... ,.............. ". ..,. ............... -.. - .. ".-... .. NSActionCell
ConnecUons
myController
1I Ir'" ............
' ......•.1....
·Rcvc,t ..
",. . . . . . . . . . . . . . . . . . . .11II
·r··lr···conneci··~~····-
41
Chapter 2 Currency Converter Tutorial
When you instantiate a class (that is, create an instance of it), Interface Builder
switches to the Instances display and highlights the new instance, which is
named after the class.
Now you can connect this ConverterController object to the user interface. By
connecting it to specific objects in the interface, you initialize your outlets.
ConverterController will use these outlets to get and set values in the interface.
42
Defining the Classes of Currency Converter
Exch an 9e Rate per $1 :1F==t==:=+-~--- When a black line encloses an object it will
be selected as the destination object of the
connection if you release the mouse
button.
Amount in Other Currency:
Convet1 'PI
Interface Builder brings up the Connections display of the Inspector panel. This
display shows the outlets you have defined for ConverterController.
To receive action messages from the user interface-to be notified, for example,
when users click a button-you must connect the control objects that emit those
messages to CurrencyConverter. The procedure for connecting actions is similar
to that for outlets, but with one major difference. When you connect an action,
always start the connection line from a control object (such as a button, text field,
43
Chapter 2 Currency Converter Tutorial
or form) that sends an action message; you usually end the connection at an
instance of your custom class. That instance is the target outlet of the control
object.
The Connections display of the Inspector panel shows the action methods you
have specified for ConverterController.
44
Defining the Classes of Currency Converter
Before You Go On - - - - - - - - - - - - - - - - - - - - - -
Define the Converter Class: While connecting ConverterController's outlets, you
probably noticed that one outlet remains unconnected: converter. This outlet
identifies the instance of the Converter class in the Currency Converter
application, which doesn't exist yet.
Define the Converter class. This should be pretty easy because Converter, as
you might recall, is a model class within the lVlodel-View-Controller paradigm.
Since instances of this type of class don't communicate directly with the
interface, there is no need for outlets or actions. Here are the steps to be
completed:
Optional Exercise - - - - - - - - - - - - - - - - - - - - - - -
Text fields and action messages: The NSReturnsign image that you embedded earlier in
the Convert button indicates that users can activate this button by pressing the
Return key. In Currency Converter this key event occurs when the cursor is in
a text field. Text fields are control objects just as buttons are; when the user
presses the Return key and the cursor is in a text field, an action message is sent
to a target object if the action is defined and the proper connection is made.
Connect the second text field (that is, the one with the "Dollars to Convert"
label) to the convert: action method of ConverterController. You won't be
disconnecting the prior action connection because multiple control objects in an
interface can invoke the same action method.
45
Chapter 2 Currency Converter Tutorial
Interface Builder generates source code files from the (partial) class definitions
you've made. These files are "skeletal," in the sense that they contain little
more than essential Objective-C directives and the class-definition information.
You'll usually need to supplement these files with your own code.
Interface Builder then displays two attention panels, one after the other:
When a Create Files panel is Click Yes to confirm that you want the
displayed, click Yes. header and implementation files for the
class created. Interface Builder files have
A second Create Files panel is an extension of.h and implementation files
displayed; click Yes again. an extension of.m.
Repeat for the Converter class. Create
INeVseaporVhomes/delawareMonolProjects/Currenc
Save the nib file. yConverterfConverterController,Ihm]? '
> <' .
d , ~; "
Now we leave Interface Builder for this application. You'll compl~te the
application using Project Builder.
46
Implementing the Classes of Currency Converter
You can add instance variables or method declarations to a header file generated
by Interface Builder. This is commonly done, but it isn't necessary in
ConverterController's case. But we do need to add a method to the Converter
class that the ConverterController object can invoke to get the result of the
computation. Let's start with by declaring the method in Converter.h.
@end
This declaration states that convertAmount:byRate: takes two arguments of type float,
and returns a float value. When parts of a method name have colons, such as
convertAmount: and byRate:, they are keywords which introduce arguments. (These
are keywords in a sense different from keywords in the C language.) Most
method declarations begin with a dash (-), followed by a space.
Now you need to update both implementation files. First examine Converter.m.
47
Chapter 2 Currency Converter Tutorial
Select Converter.m.
The method simply multiplies the two arguments and returns the result. Simple
enough. Next update the "empty" implementation of the convert: method that
Interface Builder generated.
Select ConverterController.m in
- (void) convert: (id) sender
the project browser.
Update the convert: method as float rate, amt, totali
shown by the example.
Import Converter.h. amt = [dollarField floatValueli /* 1 */
rate = [rateField floatValueli
total = [converter convertAmount:amt byRate:rateli /* 2 */
[totalField setFloatValue:totall; /* 3 */
[rateField selectText:selfli /* 4 */
1. Gets the floating-point values typed into the rate and dollar-amount fields
48
Implementing the Classes of Currency Converter
4. Sends selectText: to the rate field; this puts the cursor in the rate field so the
user begin another calculation.
Before You Go On - - - - - - - - - - - - - - - - - - - - - -
Each line of the convert: method shown above, excluding the declaration offloats,
is a message. The "word" on the left side of a message expression identifies the
object receiving the message ( called the "receiver"). These objects are
identified by the outlets you defined and connected. After the receiver comes
the name of the method that the sending object (called the "sender") wants to
invoke. Messages often result in values being returned; in the above example,
the local variables rate, amt, and total hold these values.
Before you build the project, add a small bit of code to ConverterController.m that
will make life a little easier for your users. When the application starts up, you
want Currency Converter's window to be selected and the cursor to be in the
Exchange Rate per $1 field. We can do this only after the nib file is unarchived,
which establishes the connection to the text field rateField. To enable set-up
operations like this, awakeFromNib is sent to all objects when unarchiving
concludes. Implement this method to take appropriate action.
49
Chapter 2 Currency Converter Tutorial
The Objective-C language is a superset of ANSI Cwith special Messages and Method Implementations
syntax and run-time extensions that make object-oriented
programming possibie. Objective-C syntax is uncomplicated, but • Methods are procedures implemented by a class for its objects
powerful in its simplicity. You can mix standard Cand even C++ (or, in the case of class methods, to provide functionality not
code with Objective-C code. tied to a particular instance). Methods can be public or private;
public methods are declared in the class's header file (see
The following summarizes some of the more basic aspects of the above). Messages are invocations of an object's method that
language. See Object-Oriented Programming and the Objective-C identify the method by name.
Language for complete details. Also, see "Object-Oriented
Programming" in the appendix for explanations of terms that are • Message expressions consist of a variable identifying the
italicized. receiving objectfollowed by the name ofthe method you want
to invoke; enclose the expression in brackets.
Declarations [anObject doSomethingWithArg:this]i
• Dynamically type objects by declaring them as id: receiver method to invoke
- (NSString *)countryNamei • Use nil to specify a null object; this is analogous to a null
+ (NSDate *)calendarDatei pointer. Note that some OpenStep methods do not accept nil
objects as arguments.
• Put the type of value returned by a method in parentheses
between the minus sign (or plus sign) and the beginning ofthe • A method can usefully refer to two implicit identifiers: self and
method name. (See above example.) Methods returning no super. Both identify the object receiving a message, but they
explicit type are assumed to return id. affect differently how the method implementation is located:
self starts the search in the receiver's class whereas super
• Method argument types are in parentheses and go between starts the search in the receiver's superclass. Thus
the argument's keyword and the argument itself:
[super init] i
- initWithName: (NSString *)name
andType: (int)typei causes the init method of the superclass to be invoked.
Be sure to terminate all declarations with a semicolon. • In methods you can directly access the instance variables of
your class's instances. However, accessor methods are
• By default, the scope of an instance variable is protected,
recommended instead of direct access, except in cases where
making that variable directly accessible only to objects of the
performance is of paramount importance. Chapter 4, "Travel
class that declares it or of a subclass of that class. To make an
Advisor Tutorial," describes accessor methods in greater
instance variable private (accessible only within the declaring
detail.
class), insert the @private directive before the declaration.
50
Building the Currency Converter Project
II
-------------~.;jI II.I+--
. Build, Clean, and Build
Options buttons.
Jdynamlc':-obYms8IVculTencyConverter_main.o
Iblnlcc -9 ·Wall-plpe -0 -dynamic .fno-coMmon·llProJectHeader.... ---H+-- Detailed build results.
-lJdel1vscUrc -arch m6sk -ObJC -$ectcreafe _ICON _header
CurrencyConver1erJconheader-segpro1_ICON t r ·sectcreate ...:-,ICON app
applicatlon,t!1f-o JCurrencyConverler.applCurrencyConverter.m68k.
r" Jdynamlc_obyms8IVConverter.o Jdynamlc_obyms8k!Convertercontroller.o
t JdynamIC_obYmS8!rJCurrencyConvertecmaln.o ·framewott AppKlt
I -framewort Foundation
;. Iblnlln JCurrencyconvsrter.appICurrencyConverle':MSSk
1: CurrencyConverter.appICurrenClICOnYsrter
;y
51
Chapter2 Currency Converter Tutorial
An application wrapper is a file package with an extension of the Workspace Managerthatthe application wrapper
".app". A file package is a directorythatthe Workspace contains an executable that can be run ("Iaunched") by
Manager presents to users as a simple file; in other words, it double-clicking.
hides the contents of the directory. The ".app" extension tells
52
Building the Currency Converter Project
gnumalte(I): ••
Converter.m:8:
gnumaka:'" (
!!~~~~~~~~~~~~!!!!!!!!!!!!!!!!!
illegal statement: missing ';' after ')'
1111lport. "Converter .h"
(l1mplcmentot1al ~verter
:1
i (tloot)convertAmcult:(tloot)o!llt byRote:(tloot)rote I
~~::!2m'm::I(i!.!!o~t~!Jr]]ilte~?I:!================::1,/
(lend
~ I
Project Builder highlights thslins that
contains the error.
53
Chapter 2 Currency Converter Tutorial
Help-c~cka contro~field,menu
app~cation. A small window appears that briefly describes the
selected object. command,or other areas of the I~~~~~~~~~~~~~~~~~~~~~~~~~~~
The Help key varies by computer architecture. Consult user
documentation for the Help key on your machine.
54
Building the Currency Converter Project
Frameworks: Under Frameworks in the project browser, you can • Object-Oriented Programming and the Objective-C Language
browse the header files related to OpenStep frameworks within • Topics in OPENSTEP Programming (concepts and
Project Builder. The Application Kit and Foundation frameworks programming procedures)
always are included by default for application projects. See
chapter 5, "Where to Go From Here," for a fuller description • OPENSTEP Development: Tools and Techniques (a task-
oriented approach to using the development tools)
• OPENSTEP Conversion Guide (step-by-step instructions for
converting 3.x NEXTSTEP applications to run on OPENSTEP 4.0.
The /NextDev directory also includes release notes. It also
contains documentation on the following products, if they're
installed: Enterprise Objects Framework, Distributed Objects
(DO), Portable Distributed Objects (PDO).
See chapter 5, "Where to Go From Here," for more information on
NeXT's technical publications.
55
Chapter 2 Currency Converter Tutorial
You can use Project Builder's Of course, the more complex an application is, the more thoroughly you will
graphical debugger or gdb to track need to test it. You might discover errors or shortcomings that necessitate a
bugs down. See "Using the change in overall design, in the interface, in a custom class definition, or in the
Graphical Debugger" on page
104 for an overview of the
implementation of methods and functions.
graphical debugger.
Although it's a simple application, Currency Converter still introduced you to
many of the concepts, tools, and skills you'll need to develop OpenStep
applications. Let's review what you've learned:
Optional Exercise - - - - - - - - - - - - - - - - - - - - - -
Nesting Messages:You can nest message expressions; in other words, you can use
the value returned by a message as the receiver of another message or as a
message argument. It is thus possible to rewrite the first three messages of the
ConverterController's convert: method as one statement:
56
y: ~.ustralia
'alia
::e ,-------..,----- logistics -------:--1
Sections Concepts
59
Travel Advisor Tutorial
60
In this chapter you create Travel Advisor, an application that is considerably
more complex than the Currency Converter application you built in the first
tutorial. Travel Advisor is a forms-based application used for entering, viewing,
and maintaining records on countries that the user travels to. Users enter a
country name and information associated with that country. When they click
Add, the country appears in the table below the country name. They can select
countries in the table, and the information on that country appears in the forms.
The application also performs temperature and currency conversions.
Country: 1.tI~str<l:li<l:
I·.··· Australia
I France . - - - - - - - Logistics ----:-:---:--,--'-"-:"
1 Germa.ny Airp orts: I.~. Y~~? yl~tern<ltig~<ll
Airlines: 19~<l~t<l~
Tra.nsp ortati on :1!<P<:ig~,II,~~~9\':1~t9\':1~§Y~~~Y
Molels: 1.~.~~n~Y}'ilt9~$76~~S{QLqDt .~.w~,
Notes and Itinerary for Australia , - - - - - , - - - - - Other -,-------,--.....,
Lv 6/26/95 11:36 SFO Quantas 'currencY:I~Q~l Rate:~! per$1
I:,. Arr Sydney 6/274: 14 AM
I.' Meeting John Crotten, Sr. VP, Languages:I~~.~I}i~M'c,.. ,' '. "'U '"
61
Chapter 3 Travel Advisor Tutorial
Create the application project. You should be familiar with many of the objects on the Travel Advisor interface
because you've encountered them in the Currency Converter tutorial. The
Start Project Builder.
following illustration points out the objects that are new to you in this tutorial.
Choose New from the Project
menu.
Name the application
"TraveIAdvisor."
The following pages describe the purpose of each new object found on
Interface Builder's palettes and explain how to set these objects up for Travel
Advisor. Before getting to these new objects, start with the familiar ones:
buttons and text fields.
62
Creating the Travel Advisor Interface
Languages:1
Add
You might think the "English widely spoken" object is a new kind of object.
It's actually a button, a special style of button called a switch.
Set up the switch. -+-_ _ _ Double-click to select text, then type new label.
Click to set the initial state of this toggled button (no checkmark).
Varieties of Buttons
63
Chapter 3 Travel Advisor Tutorial
64
Creating the Travel Advisor Interface
To make titled sections of the fields, forms, and buttons on the Travel Advisor
interface, group selected objects. By grouping them, you put them in a box.
The scroll view on the DataViews palette encloses a text object (an instance of
NSText). This object allows users to enter, edit, and format text with minimal
programmatic involvement on your part.
65
Chapter 3 Travel Advisor Tutorial
You don't need to change any of the default attributes of the scroll view (but you
might want to look at the attributes you can set, if you're curious).
A table view is an objectfor displaying and editing tabular data. Later in this tutorial you will learn some basic techniques for
Often that data consists of a set of related records, with rows for accessing and managing the data in a table view. Here's a quick
individual records and columns for the common fields (attributes) preview of the essential pieces:
ofthose records, Table views are ideal for applications that have
• Data source. The data source is any object in your application
a database component, such as Enterprise Objects Framework
that supplies the NSTableView with data. The elements of data
applications.
(usually records) must be identifiable through zero-based
The table view on Interface Builder's TabulationViews palette is indexing. The data source must implement some or all of the
actually several objects, bound together in a scroll view. Inside methods of the NSTableDataSources informal protocol.
the scroll view is an instance of NSTableView in which data is
• Column identifier. Each column (NSTableColumn) of a table
displayed and edited. At the top of the table view is an
view has an identifier associated with it, which can be either
NSTableHeaderView object, which contains one or more column
an NSString or a number. You use the identifier as a key to
headers (instance of NSTableColumn).
obtain the value of a record field .
66
Creating the Tra"~1 Advisor Interface
. . , - - - - - - - Conversions ----:;--"-:;--"---;
Dollars: 1_.~J Local:j 2: :;:" '/ /co'nv~rt'l
Farenheit:C ;\coC:n\;e~n '
To configure the table view, you must set attributes of two component objects:
the NSTabieView object and the NSTableColumn object.
67
Chapter 3 Travel Advisor Tutorial
The Attributes display for NSTableView is the same as that for NSScrollView.
The Travel Advisor window is nearly complete. For a decorative touch, you're
next going to add an image to the interface.
68
Creating the Travel Advisor Interface
9 Add an image to the interface. Click here to get the Data Views palette.
r-T ra-n sp-: -: ;~:- ~.-!:-· _~: LD:_~;'.~; C;-~ -~~~_=__. ._~~J~..ll~11 ~Ic"",=
project browser.
In the Open panel, select the file
Airline.eps from the __ .- __ . _ _=
. __ __
=....
_
/AppKit/TraveIAdvisor
subdirectory of
/NextDeveloper/Examples Hotels:
Before You Go On - - - - - - - - - - - - - - - - - - - - - - -
Sometimes buttons are the preferred objects for holding images-for instance
when you want a different image for either state of a button. But when buttons
are disabled, any image they display is dimmed. So for decorative images, use
image views (NSImageView) instead of buttons.
When you drop a sound or image over a button or image view, it is added to the
nib file. When you add an image or a sound to a nib file, Interface Builder asks
if you also want to add the resource to the project. Nib files are localized and
their resources are only accessible when the nib file has been loaded. Resources
that are associated with a project can be localized and are always accessible.
~~~:-:-----ll---- Since the image is larger than the image view, have it
scale proportionally.
69
Chapter 3 Travel Advisor Tutorial
Tip: To make the "velocity" line behind the airplane, make a title-less black box
with a vertical offset of zero, and run the top and bottom lines together.
Travel Advisor's main menu has a submenu and a command that do not come
ready-made on the Menus palette. You use the Submenu and the Item cells to
create customized submenus and menu commands, respectively.
Three dots after a menu command indicates that the command opens a panel:
"Print Notes ... " means that clicking this command displays the Print panel.
You can now connect many of the objects on the Travel Advisor interface
through outlets and actions defined by the Application Kit. As you might recall,
text fields have a nextKeyView outlet that you connect so that users can tab from
field to field. Forms also have a nextKeyView outlet for tabbing. (The fields within
a form are already interconnected, so you don't need to connect them.)
70
Creating the Travel Advisor Interface
The Application Kit also has "pre-set" actions that you can connect your
application to. The NSText object in the scroll view can print its contents as can
all objects that inherit from NSView. To take advantage of this capability, "hook
up" the menu command with the NSText action method for printing.
The final step in crafting the Travel Advisor interface has nothing to do with the
main window, but with what users see of your application when they encounter
it in the File Manager: the application's icon. '
71
Chapter 3 Travel Advisor Tutorial
In Project Builder:
Open the Project Inspector.
Go to the Project Attributes
display of the inspector.
Click in the Application Icon field. )JJ~lrlUilL~~~~ed.4-- Make sure the cursor is in
this field before dragging.
In File Manager
After you drag the image
Locate TravelAdvisor.tiff in the into the well, the icon is
/AppKit/TraveIAdvisor displayed in the well and
subdirectory of the image file is
automatically added to the
/NextDeveloper/Examples.
project.
Drag TravelAdvisor.tiff into the
icon well in the Project Attributes
display.
13 Test the interface. You're finished with the Travel Advisor interface. Test it by choosing Test
Interface from Interface Builder's Document menu.Try the following:
• Press the Tab key repeatedly. Notice how the cursor jumps between the
fields of the form, and how it loops from the Languages field to the Country
field. Press Shift-Tab to make the cursor go in the reverse direction .
• Enter some text in the scroll view, then click the Print Notes menu item. The
print panel is displayed. Print the text object's contents.
• Also in the scroll view, press the Return key repeatedly until a slider appears
in the scroBer.
72
The Design of Travel Advisor
Converter
Key
Value
Model Obiects
Travel Advisor's design is more interesting and dynamic than Currency
Converter's because it must display a unique set of data depending on the
country the user selects. To make this possible, the data for each country is
stored in a Country object. These objects encapsulate data on a country (in a
sense, they're like records in a relational database). The application can manage
potentially hundreds of these objects, tracking each without recourse to a
"hardwired" connection.
Another model object in the application is the instance of the Converter class.
This instance does not hold any data, but does provide some specialized
behavior.
73
Chapter 3 Travel Advisor Tutorial
Controller
The controller object for the application is TAController. Like all controller
objects, TAController is responsible for mediating the flow of data between the
user interface (the View part of the paradigm) and the model objects that
encapsulate that data: the Country objects. Based on user choices in the
interface, TAController can find and display the requested Country object; it
can also save changes made by users to the appropriate Country'object.
Several classes in OpenStep's Foundation Framework create created (see "Abstract Classes and Class Clusters" on page 101).
objects whose purpose is to hold other objects. These col.lection
Collection objects also provide a valuable way to store data.
classes are very useful. Instances of them can store and locate
their contents through a number of mechanisms. When you store (or archive) a collection object in the file system,
its constituent objects are also stored.
• Arrays (NSArray) store and retrieve objects in an ordered
fashion through zero-based indexing.
• Dictionaries (NSDictionary) store and quickly retrieve objects NSObject
using key/value pairs. For example, the key "red" might be
associated with an NSCoior object representing red. I
NSArray NSDictionary NSSet
• Sets (NSSet) are unordered collections of distinct elements.
Counted sets (NSCountedSet) are sets that can contain I I
duplicate (non-distinct) elements; these duplicates are tracked NSMutableArray NSMutableSet
through a counter. Use sets when the speed of membership-
NSMutableDictionary
testing is important.
The mutable versions of these classes allow you to add and NSCountedSet
remove objects programmatically after the collection object is
74
The Design of Travel Advisor
75
Chapter 3 Travel Advisor Tutorial
l~ri)9uagesFjeld >
,logistic
76
. Defining the Classes of Travel Advisor
In OpenStep there are many ways to reuse objects through their classes. For
example, subclassing an existing class to obtain slightly different behavior is one
way to reuse the functionality of the superclass. Another way is to integrate an
existing class-like the Converter class-into your project.
In Interface Builder:
Open CurrencyConverter.nib in n£J~~E[=======r.::;~3E+-- Make sure to select the
superclass before pasting.
the English.lproj subdirectory of
the CurrencyConverter project
directory.
In the Classes display of the nib
file window, select the Converter
class.
Choose Edit ~ Copy.
Select the nib file window for
TraveIAdvisor.nib.
In the Classes display, select the
Headers
superclass (NSObject). When you add a
~ g~~s__~_~ CCmonwV9rert;ea~h~~~~========~~========~- class to a project
Choose Edit ~ Paste. Headers t> Project BuNder
OtT,erSOllfces"',;:' adds the
In Project Builder: Interfaces r-
Images ,..
associated header
Launch Project Builder. Other Resource r- file too.
77
Chapter3 Travel Advisor Tutorial
" NSDictionary
NSFontManager
(j NSlmage
" NSRespondef
NSTableCoJumn
Instantiate
'Edit Cla~s
Read File
, Create Files
You don't need to instantiate the Country class in the nib file because it is not
involved in any outlet or action connections. TAController interacts behind the
scenes with users as they manipulate the application's interface. It therefore
needs access to interface objects and to be made the target of action messages.
logisticsForm Form in group (box) labelled "Logistics"; the form is selected when a
gray line borders it.
78
Defining the Classes of Travel Advisor
handleTVClick: The table view(the area beneath the "Countries" column header)
The nib file window of Interface Builder gives you two modes in Outline mode, as the phrase suggests, represents objects in a
which to view the objects in a nib file and to make connections hierarchical list: an outline. The advantages of outline mode are
between those objects. So far you've been working in the icon that it represents all objects and graphically indicates the
mode of the Instances display, which pictorially represents connections between them. You can connect objects through
objects such as windows and custom objects. their outlets and actions in outline mode, as well as disconnect
them by Control-clicking a connection line.
l
~~~~~~~~~~~;;;~;~~rr-- Atypeconnection
connection line between
objects. (electricalisoutlet
identified by name
for outlet, and iconfor
cross-hairs for
action)
79
Chapter 3 Travel Advisor Tutorial
TravelAdlllsor
File's Owner
Every nib file has one owner, represented by Nib files other than the main nib file-
the File's Owner icon in a nib file window. The auxiliary nib files-contain objects and
owner is an object, external to the nib file, resources that an application may load only
that relays messages between the objects when it needs them (for example, an Info
unarchived from the nib file and the other panel). You must specify the owner of
objects in your application. auxiliary nib files.
You can specify a file's owner in Interface
Builder or programmatically, with You can determine or set the class of the
NSBundle's loadNibNamed:owner:. The current nib file's owner in Interface Builder
File's Owner icon for the main nib file always by selecting the File's Owner icon in the nib
represents NSApp, the global NSApplication file window and then displaying the Custom
constant. The main nib file is automatically Class inspector view. You'll get to practice
created when you create an application this technique when you learn howto create
project; it is loaded in main() when an multi-document applications in the next
application is launched. tutorial.
80
Defining the Classes of Travel Advisor
A palette is a display on the Palettes window that holds one or • Select objects singly or in groups on the interface or in the nib
more reusable objects. You can add these objects to your file window (either icon or outline mode)
application's interface using the drag-and-drop technique. There
are two types of palettes: dynamic and compiled (also called • Alternate-drag these objects and drop them on the blank
"static palettes"). To the user, they seem identical, butthe palette.
differences are many.
Alternate-drag to
Static palettes are built as a project and have code defining their move objects
objects; dynamic palettes include no special code-they're onto palettes, to
unique configurations of objects found on static palettes. move objects
Consequently, static palettes must be compiled, but you can around palettes,
create dynamic palettes on the fly, without writing and compiling and to remove
objects from
code. Objects on static palettes can have inspectors and editors, palettes.
which dynamic-palette objects cannot.
81
Chapter 3 Travel Advisor Tutorial
NSString objects represent character strings. They're behind character set. Hence they can represent words in Chinese,
almost all text in an application, from labels to spreadsheet entries Japanese, Arabic,and many other languages,
to word-processing documents. NSStrings (or string objects)
supplant that familiar C programming data type, char *, The N'SString and NSMutableString classes provide APlto create
static and dynamic strings, respectively, and to perform string
"But why?" you might be saying. "Why not stick with the tried and
operations such as substring searching, string comparison, and
true?" By representing strings as objects, you confer on them all
concatenation.
the advantages that belong to objects, such as persistency and
distributability. Moreover, thanks to data encapsulation, string
objects can use whatever encoding is needed and can choose the None of this prevents you from using char * strings, and there are
most efficient storage for themselves. occasions where for performance or other reasons you should.
However, the public interfaces of OpenStep classes now use
The most important rationale for string objects is the role they play string objects almost exclusively. A number of NSString methods
in internationalization. String objects contain Unicode characters enable you to convert string objects to char * strings and back
rather than the narrow range of characters afforded by the ASCII again.
82
Implementing the Country Class
Country.h also declares a dozen or more methods. Most of these are accessor
methods. Accessor methods fetch and set the values of instance variables. They
are a critical part of an object's interface.
3. Accessor methods. The declaration for accessor methods that return val ues is, by
convention, the name of the instance variable preceded by the type of the
returned value in parentheses. Accessor methods that set the value of instance
variables begin with "set" prepended to the name of the instance variable
(initial letter capitalized). The "set" method's argument takes the type of the
instance variable and the method itself returns void.
83
Chapter 3 Travel Advisor Tutorial
The Foundation Framework consists of a base layer of classes methods for searching, combining, and comparing strings.
that specify fundamental object behavior plus a number of utility NSCharacterSet represents various groupings of characters
classes. It also introduces several paradigms that define which are used by NSString. An NSScanner object scans
functionality not covered by the Objective-C language. Notably, numbers and words from an NSString object. For more
the Foundation Framework: information, see "NSString: A String for All Countries" on page 82.
• Makes software development easier by introducing consistent You use NSBundle objects to load code and localized resources
conventions for things such as object deallocation dynamically (see "Only When Needed: Dynamically Loading
Resources and Code" on page 118). The NSUserDefaults class
• Supports Unicode strings, object persistence, and object
enables you to store and access default values based on locale.
distribution
• Provides a level of operating-system independence, Object Persistence and Distribution
enhancing application portability
NSSerializer makes it possible to representthe data that an object
contains in an architecture-dependent way. NSCoder and its
Root Class subclasses take this process a step further by storing class
NSObject, the principal root class, provides the fundamental information along with the data, thereby enabling archiving and
behavior and interface for objects. It includes methods for distribution. Archiving (NSArchiver) stores encoded objects and
creating, initializing, deallocating, copying, comparing, and other data in files. Distribution denotes the transmission of
querying objects. Almost all OpenStep objects inherit ultimately encoded object data between different processes and threads
from NSObject. (NSPortCoder, NSConnection, NSDistantObject, and others).
The Foundation Framework introduces a mechanism for ensuring Date and time. The NSDate, NSCalendarDate, and NSlimeZone
that objects are properly deallocated when they're no longer classes generate objects that represent dates and times. They
needed. This mechanism, which depends on general offer methods for calculating temporal differences, for displaying
conformance to a policy of object ownership, automatically dates and times in any desired format, and for adjusting times and
tracks objects that are marked for release within a loop and dates based on location in the world.
deallocates them atthe close ofthe loop. See "Object Ownership,
Application coordination. NSNotification, NSNotificationCenter,
Retention, and Disposal" on page 88 for more information.
and NSNotificationQueue implement a system for broadcasting
notifications of changes within an application. Any object can
Data Storage and Access specify and post a notification, and any other object can register
The Foundation Framework provides object-oriented storage for itself as an observer of that notification. You can use an NSlimer
object to send a message to another object at specific intervals.
• Arrays of raw bytes (NSData) and characters (NSString)
Operating system services. Many Foundation classes help to
• Simple Cdata values (NSValue and NSNumber) insulate your code from the peculiarities of disparate operating
systems.
• Objective-C objects of any class (NSArray, NSDictionary,
NSSet, and NSPPL) • NSFileManager provides a consistent interface for file-system
operations such as creating files and directories, enumerating
NSArray, NSDictionary, and NSSet (and related mutable classes)
directory contents, and moving, copying, and deleting files.
are collection classes that also allow you to organize and access
objects in certain ways (see "The Collection Classes" on page 74). • NSThread and NSProcesslnfo let you create multi-threaded
applications and query the environment in which an
Text and Internationalization application runs.
NSString internally represents text in various encodings, most • NSUserDefaults allows applications to query, update, and
importantly Unicode, making applications inherently capable of manipulate a user's default settings across several domains:
expressing a variety of written languages. NSString also provides globally, per application, and per language.
84
Implementing the Country Class
Before You Go On - - - - - - - - - - - - - - - - - - - - - - -
If you don't want to allow an instance variable's value to be changed by anyone outside of your
class, dOll 't provide a set method for the instance variable. If you do provide a set method, make
sure objects of your own class use it when specifying a value for the instance variables. This has
important implications for subclasses of your class.
Exercise: The previous example shows the declarations for only a few accessor
methods. Every instance variable of the Country class should have an accessor
method that returns a value and one that sets a value. Complete the remaining
declarations.
Now that you've declared the Country class's accessor methods, implement
them.
1. For "get" accessor methods (at least when the instance variables, like Travel
Advisor's, hold immutable objects) simply return the instance variable.
2. For accessor methods that set objec/values, first send autorelease to the current
instance variable, then copy (or retain) the passed-in value to the variable. The
autorelease message causes the previously assigned object to be released at the
In many situations you can send end of the current event loop, keeping current references to the object valid
retain instead of copy to keep an until then.
object around. But for "value"
type objects, such as Country's
If the instance variable has a non-object value (such as an integer or float
instance variables, copy is better.
For the reason why, and for more
value), you don't need to autorelease and copy; just assign the new value.
on autorelease, retain, copy, and
related messages for object Before You Go On - - - - - - - - - - - - - - - - - - - - - - -
disposal and object retention, see
"Object Ownership, Retention, Exercise: The example above shows the implementation of the accessor methods
and Disposal" on page 88. for the name instance variable. Implement the remaining accessor methods.
85
Chapter 3 Travel Advisor Tutorial
return self; /* 3 */
Don't substitute nil when empty 2. Initializes an NSString instance variable to an empty string. @"" is a compiler-
objects are expected, and vice supported construction that creates an immutable NSString object from the
versa.The Objective-C keyword
text enclosed by the quotes. You could have just as well typed:
nil represents an "object" with an
id (value) ofzero. An empty
object (such as @'''') is a true name = @"Howdy Doody";
object; it just has no content of its
given type. To learn more about But that wouldn't have been practical as an initial value. You don't need to
Objective-C keywords, see
Object-OrieJlted ProgrammiJlg aJld
initialize instance variables to null values because the run-time system does
the Objective-C Lallguage. it for you; it assigns nil to objects, zeroes to integers and floats, and NULL to
char *'s if they're not explicitly initialized. However, you should initialize
instance variables that take other starting values.
3. By returning self you're returning a true instance of your object; up until this
point, the instance is considered undefined.
Before You Go On - - - - - - - - - - - - - - - - - - - - - - -
Note that release itself doesn't Implement the dealloc method. In this method you release (that is, send release
deallocate objects, but it leads to or autorelease to) objects that you've created, copied, or retained (which don't
their deallocation. For more on
have an impending autorelease). For the Country class, release all objects held as
release and autorelease, see
"Object Ownership, Retention,
instance variables. If you had other retained objects, you would release them,
and Disposal" on page 88. and if you had dynamically allocated data, you would free it. 'Vhen this method
completes, the Country object is deallocated. The dealloc method should send
dealloc to super as the last thing it does, so that the Country object isn't released
by its superclass before it's had the chance to release all objects it owns.
86
Implementing the Country Class
You want the Country objects created by the Travel Advisor application to be
persistellt. That is, you want them to "remember" their state between sessions.
Archiving lets you do this by encoding the state of application objects in a file
along with their class membership. The NSCoding protocol defines two
methods that enable archiving for a class: encodeWithCoder: and initWithCoder:.
[coder encodeObject:currencyName]i
[coder encodeValueOfObjCType: f" at:¤cyRate]i
l
[coder encodeObject:comments]i
2. For both object and non-object types, you can use encodeValueOfObjCType:at:.
87
Chapter 3 Travel Advisor Tutorial
The problem of object ownership and disposal is a natural allocating or copying them. You also own (or share ownership in)
concern in object-oriented programming. When an object is objects that you retain, since retain increments an object's
created and passed around various "consumer' objects in an reference count (see facing page). The flip side of this rule is: If
application, which object is responsible for disposing of it? And you don't own an object, you need not worry about releasing it.
when? If the object is not deallocated, memory leaks. If the object
is deallocated too soon, problems may occur in other objects that OK, but now another question arises. If the owner of an object
assume its existence, and the application may crash. must release the object within its programmatic scope, how can
it give that object to other objects? The short answer is: the
The Foundation Framework introduces a mechanism and a policy autorelease method, which marks the receiver for later release,
that helps to ensure that objects are deallocated when-and only enabling it to live beyond the scope of the owning object so that
when-they are no longer needed. other objects can use it.
Who Owns Which Object? The autorelease method must be understood in a larger context
ofthe autorelease mechanism for object deallocation. Through
The policy is quite simple: You are responsible for disposing of all this programmatic mechanism, you implementthe policy of object
objects that you own. You own objects that you create, either by ownership and disposal.
r--
.J
retention count
88
Implementing the Country Class
Each object in the Foundation Framework has an associated When you retain an object you're sharing it with its owner and
reference count. When you allocate or copy an object, its other objects that have retained it. While this might be what you
reference count is set at 1. You send release to an object to want, it can lead to some undesirable consequences. Ifthe owner
decrement its reference count. When the reference count is released, any object you received from it and retained is usually
reaches zero, NSObject invokes the object's dealloc method, and invalid. If you had retained an instance variable of the owning
the object is destroyed. However, successive consumers of the object, and that instance variable is reassigned, your reference
object can delay its destruction by sending it retain, which would also become invalid.
increments the reference count. You retain objects to ensure that
they won't be deallocated until you're done with them.
copy Versus retain
Each application has an autorelease pool. An autorelease pool When deciding whether to retain or copy objects, it helps to
tracks objects marked for eventual release and releases them at categorize them as value objects or entity objects. Value objects
the appropriate time. You put an object in the pool by sending the are objects such as NSNumbers or NSStrings that encapsulate a
object an autorelease message. When your code finishes discrete, limited set of data. Entity objects, such as NSViews and
executing and control returns to the application object (typically NSWindows, tend to be larger objects that manage and
atthe end ofthe event cycle), the application object sends coordinate subordinate objects. Forvalue objects, use copy when
release to the autorelease pool, and the pool releases each you want your own "snapshot" ofthe object; use retain when you
object it contains. If afterwards the reference count of an object intend to share it. Always retain entity objects.
in the pool is zero, the object is deallocated.
In accessor methods that set value-object instance variables, you
Putting the Policy Into Practice usually (but not always) wantto make your own copy ofthe object
and not share it. (Otherwise it might change without your
When an object is used solely within the scope of the method that knowing.) Send autorelease to the old object and then send
creates it, you can deallocate it immediately by sending it release. copy-not retain-to the new one:
Otherwise, send autorelease to all created objects that you no
longer need but will return or pass to other objects. - (void)setTitle: (NSString *)newTitle
You shouldn't release objects that you receive from other objects
[title autorelease]i
(unless you precede the release or autorelease with aretain). You
don't own these objects, and can assume that their owner has title = [newTitle copy];
seen to their eventual deallocation. You can also assume that
(with some exceptions, described below) a received object
remains valid within the method it was received in. That method OpenStep framework classes can, for reasons of efficiency,
can also safely return the object to its invoker. return objects cast as immutable when to the owner (the
framework class) they are mutable. Thus there is no guarantee
You should send release or autorelease to an object only as many that a vended framework object won't change, even if it is of an
times as are allowed by its creation (one) plus the number of immutable type. The precaution you should take is evident: copy
retain messages you have sent it. You should never send free to a objects obtained from framework classes if it's important the
OpenStep object. object shouldn't change from under you.
89
Chapter 3 Travel Advisor Tutorial
The TAController class plays a central role in the Travel Advisor application. As
the application's controller object, it transfers data from the model objects
(Country instances) to the fields ofthe interface and, when users enter or modify
data, back to the correct Country object. The TAController must also coordinate
the data displayed in the table view with the current object, and it must do the
right thing when users select an item in the table view or click the Add or Delete
button. All custom code specific to the user interface resides in TAController.
Key Value
o _~~fJl~~9___ _
1 --_
France
.. ---_ .. _--
2 _G~!~~!)y___
3
4 I
----------~----------
5'---_ _ _---'
Array TAControlier Dictionary
The dictionary contains Country objects (values) that are identified by the
names of countries (keys). The dictionary is the source of data for the fields of
Travel Advisor. The array derives from the dictionary and is sorted. It is the
source of data for the table view.
After describing what other instance variables you must add to TAController,
this section covers the following implementation tasks:
• Getting the data from Country objects to the interface and back
• Getting the table view to work, including updating Country records
• Adding and deleting "records" (Country objects)
• Formatting and validating field values
• "Housekeeping" tasks (application management)
90
Implementing the TAControlier Class
Update TAController.h.
NSMutableDictionary *countryDict;
Import Country.h. NSMutableArray *countryKeys;
Add the instance-variable BOOL recordNeedssaving;
declarations shown at right.
The variables countryDict and countryKevs identify the array and the dictionary
discussed on the previous page. The boolean recordNeedsSaving flags that record
if the user modifies the information in any field.
This declaration is not essential, but the enum constants provide a clear and
convenient way to identify the cells in the Logistics form. Methods such as
cellAtlndex: identify the editable cells in a form through zero-based indexing.
This declaration gives each cell in the Logistics form a meaningful designation.
When you write code with Project Builder you have a set of by pressing Escape (or Tab, ifthat key is bound in Preferences).
"workbench" tools at your disposal, among them: You can use name completion in the code editor andin all panels
where you are finding information or searching for files to open.
Indentation As an example: you know there's a certain constant to use with
In Preferences you can set the characters at which indentation fonts, but you cannot remember it. In your code, type NSFont.
automatically occurs, the number of spaces per indentation, and Then press the Escape key several times. These symbols appear
other global indentation characteristics. The Edit menu includes in succession (the found portion is underlined):
the Indentation submenu, which allows you to indent lines or NSFontldentityMatrix
blocks of code on a case-by-case basis. NSFontManager
NSFontPanel
Brace and Bracket Checking
Emacs Bindings
Double-click a brace (left or right, it doesn't matter) to locate the
matching brace;the code in-between the braces is highlighted. In You can issue the most common Emacs commands in Project
an identical fashion, double-click a square bracket in a message Builder's code editor. (Emacs is a popular editor for writing code.)
expression to locate the matching bracket. For example, there are the commands page-forward (Control-v),
word-forward (Meta-f), delete-word (Meta-d), kill-forward
Name completion (Control-k), and yank from kill ring (Control-y). You can also
perform an incremental search by pressing Control-s; this
Name completion is a facility that, given a partial name, command displays a small search panel and takes you to the next
completes itfrom all symbols known by the project. You activate it occurrence of whatever you type.
91
Chapter 3 Travel Advisor Tutorial
Data Mediation
TAController acts as the mediator of data exchanged between a source of data
and the display of that data. Data mediation involves taking data from fields,
storing it somewhere, and putting it back into the fields later. TAController has
two methods related to data mediation: populateFields: puts Country instance data
into the fields of Travel Advisor and extractFields: updates a Country object with
the information in the fields.
[countryField selectText:self]i /* 3 */
1. Causes the Country field to display the value of the name instance variable of
the Country record (aRec) passed into the method. Since [aRec name] is nested,
the object it returns is used as the argument of setStringValue:, which sets the
textual content of the receiver (in this case, an NSTextFieldCell).
2. The cellAtlndex: message is sent to the form and returns the cell identified by
the enum constant LGairports.
3. Selects the text in the Country field or, if there is no text, inserts the cursor.
92
Implementing the TAController Class
[currencyNameField setStringValue:@III1];
[currencyRateField setFloatValue:O.OOO];
[languagesField setStringValue:@"I];
[englishSpokenSwitch setState:NO]; /* 1 */
[currencyDollarsField setFloatValue:O.OO];
[currencyLocalField setFloatValue:O.OO];
[celsius setlntValue:O];
[commentsField setString:@"" ]; /* 2 */
[countryField selectText:self];
2. The setString: message sets the textual contents ofNSText objects (such as the
one enclosed by the scroll view).
Before You Go On - - - - - - - - - - - - - - - - - - - - - - -
Exercise: Implement the extractFields: method. In this method set the values of the
passed-in Country record's instance variables with the contents of the associated
fields.
Tip: Use the stringValue method to get field contents and use Country's accessor
methods to set the values of instance variables.
93
Chapter 3 Travel Advisor Tutorial
Search results.
94
Implementing the TAControlier Class
Table views get the data they display from a data source. A data source is an
object that implements the informal NSTableDataSource protocol to respond to
NSTableView requests for data. Since the NSTableView organizes records by
zero-based indexing, it is essential that the data source organizes the data it
provides to the NSTabie View similarly: in an array.
95
Chapter 3 Travel Advisor Tutorial
To fulfill its role as data source, TAController must implement two methods of
the NSTableDataSource informal protocol.
If you had an application with multiple table views, each would invoke this
NSTableView delegation method (as well as the others). By evaluating the
theTableView argument, you could distinguish which table view was involved.
2. This method first evaluates the column identifier to determine if it's the right
column (it should always return "Countries"). If it is, the method returns the
country name from the countryKeys array that is associated with rowlndex. This
name is then displayed at rowlndex of the column. (Remember, the array and
the cells of the column are synchronized in terms of their indexing.)
96
Implementing the TAControlier Class
posts
Many OpenStep framework objects hold a delegate as an
OddBallDidActSillyNotification
instance variable. A delegate is a object that receives messages
from the framework object when specific events occur.
Delegation messages are of several types, depending on the oddBall
expected role of the delegate:
97
Chapter 3 Travel Advisor Tutorial
The final thing you need to do to get the table view working is to respond to
mouse clicks in it. As you recall, you defined in Interface Builder the
handleTVClick: action for this purpose. This method must do a number of things:
4 Update records.
- (void)handleTVClick: (id) sender
Implement the method that
responds to user selections in the Country *aRec, *newRec, *newerRecj
table view. int index;
98
Implementing the TAControlier Class
1. When any Country-object data is added or altered, Travel Advisor sets the
recordNeedsSaving flag to YES (you'll learn how to do this on later on). If
recordNeedsSaving is YES, first delete any existing Country record for that
country from the dictionary and also remove the country name from the table
view's array. (Upon removal, the objects are automatically released by the
array.) Then create a new Country instance and initialize it with the values
currently on the screen; add the instance to the dictionary, add the country
name to the table view's array, sort the array, and reset the recordNeedsSaving
flag. At the end, invoke thetile method, which (among other things) causes
the table view to request data from its data source.
2. The selectedRow message queries the table view for the row index of the cell
that was clicked. If this index is within expected bounds, use it to get the
country name from the array, and then use the country name as the key to get
the associated Country instance. Write the instance-variable values of this
instance to the fields of the application, update the "Notes and Itinerary for"
label.
Optional Exercise - - - - - - - - - - - - - - - - - - - - - - -
Application developers often like to have key alternatives to mouse actions such
as clicking a table view. One way of acquiring a key alternative is to add a menu
cell in Interface Builder, specify a key as an attribute of the cell, define an action
method that will be invoked, and then implement that method.
The methods nextRecord: and prevRecord: should be invoked when users chose
N..ext Record and Prev Record or type the key equivalents Command-n and
Command-r. In TAController.m, implement these methods, keeping the following
hints in mind:
3. If the start or end of the table view is encountered, "wrap" the selection.
(Hint: Use the index of the last object in the countryKeys array.)
4. Using the index, select the new row, but don't extend the selection.
99
Chapter 3 Travel Advisor Tutorial
1. This section of code verifies that a country name has been entered and sees
if there is a Country object in the dictionary. If there's no object for the key,
objectForKey: returns nil. If the object exists and it's flagged as modified, the
code removes it from the dictionary and removes the country name from the
100
Implementing the TAControlier Class
countryKeys array. Note that removing an object from a dictionary or array also
releases it, so the code sets aCountry to nil. It then tests aCountry and, if it's nil,
creates a new object; otherwise it just returns, because an object already exists
for this country and it hasn't been modified.
2. After updating the new Country object with the information on the
application's fields (extractFields:), this code adds the Country object to the
dictionary and the country name to the countryKeys array.
3. This section of code performs some things that have to be done, such as
resetting the recordNeedsSaving flag and updating the label over the scroll view
to reflect the just-added country.
4. The tile message forces the table view to update its contents. The
selectRow:byExtendingSelection: message highlights the new record in the table
VIew.
Before You Go On - - - - - - - - - - - - - - - - - - - - -
Exercise: Implement the deleteRecord: method. Although similar in structure to
addRecord: this method is much simpler, because you don't need to worry about
whether a Country record has been modified. Once you've deleted the record,
remember to update the table view and clear the fields of the application.
101
Chapter 3 Travel Advisor Tutorial
The NSCell class provides methods for specifying how cell values are
formatted. In this instance, setEntryType: sets the type of value as a float and
setFloatingPointFormat:left:right: specifies the number of digits on each side of the
decimal point.
The NSControrclass gives you an API for validating the contents of cells.
Validation verifies that the values of cells fall within certain limits or meet
certain criteria. In Travel Advisor, we want to make sure that the user does not
enter a negative value in the Rate field.
In awakeFromNib, make
[currencyRateField setDelegate:self];
TAControlier a delegate of the
field to be validated.
Implementthe - (BOOL) control: (NSControl *)control isValidObject: (id)obj
control:isValidObject: method to
validate the value of the field. if (control == currencyRateField) { /* 1 */
if ([obj floatValue] < 0.0) {
NSRunAlertPanel(@"Travel Advisor", /* 2 */
@"Rate cannot be negative.", nil, nil, nil);
return NO;
return YES;
102
Implementing the TAControlier Class
- - - - - - control
cell (NSTextField)
(NSTextFieldCel/}
tracking messages
The Foundation Framework provides the NSDateFormatter class
to generate date formatters and will release other specialized
formatter classes in the future. You can make a custom subclass
Cell of NSFormatter to derive your own formatters.
drawing messages
103
Chapter 3 Travel Advisor Tutorial
1. Because you might have more than one field's value to validate, this example
first determines which field is sending the message. It then checks the field's
value (passed in as the second object); if it is negative, it displays an attention
panel and returns NO, blocking the entry of the value. Otherwise, it returns
YES and the field accepts the value.
For more information on
2. The NSRunAlertPanelO function allows you to display a modal attention panel
NSRunAlertPanel(), see the
"Functions" section of the from any point in your code. The above example calls this function simply to
Application Kit (framework) inform the user why the value cannot be accepted.
reference documentation.
Rate cannotbenegatlve.
104
Implementing the TAControlier Class
Application Management
By now you've finished the major coding tasks for Travel Advisor. All that
remains to implement are a half dozen or so methods. Some of these methods
perform tasks that every application should do. Others provide bits of
functionality that Travel Advisor requires. In this section you'll:
The data that users enter into Travel Advisor should be saved in the file system,
or archived. The best time to initiate archiving in Travel Advisor is when the
application is about to terminate. Earlier you made TAController the delegate
of the application object (NSApp). Now respond to the delegate message
applicationShouldTerminate:, which is sent just before the application terminates.
return YESj
105
Chapter 3 Travel Advisor Tutorial
8 Implement TAControlier's
- (id)init
methods for initializing and
{
deallocating itself.
/* 1 */
Implementthe init method, as NSString *storePath = [[NSBundle mainBundle]
shown at right. pathForResouree:@ITravelData" ofType:nilli
[super init];
Implement the dealloc method to
/* 2 */
release object instance variables.
eountryDiet = [NSUnarehiver unarehiveObjeetWithFile:storePath];
/* 3 */
if (!eountryDiet)
eountryDiet [[NSMutableDietionaryalloe] init];
eountryKeys [[NSMutableArrayalloe] initWithCapaeity:l0];
else
eountryDiet [eountryDiet retain];
reeordNeedsSaving=NOj
return self;
1. Using NSBundle methods, locates the archive file "TraveIData" in the applicati l
2. The unarchiveObjectWithFile: message unarchives (that is, restores) the object whos(
attributes are encoded in the specified file. The object that is unarchived and
returned is the NSDictionary of Country objects (countryDict).
106
Implementing the TAControlier Class
When users modify data in fields of Travel Advisor, you want to mark the current
record as modified so later you'll know to save it. The Application Kit broadcasts
a notification whenever text in the application is altered. To receive this
notification, add TAController to the list of the notification's observers.
Optional Exercise - - - - - - - - - - - - - - - - - - - - - - - -
Convert Celsius to Fahrenheit: Implement the convertCelsius: method. You've already
specified and connected the necessary outlets (celsius, fahrenheit) and action
(convertCelsius:), so all that remains is the method implementation. The formula
you'll need is:
107
Chapter 3 Travel Advisor Tutorial
(sel f=Elx14848c ,
108
Building and Running Travel Advisor
• Click the items in the table view and notice how the selected records are
displayed. Press Command-n and Command-r and observe what happens.
• Quit the application and then start it up again. Notice how the application
displays the same records that you entered.
Problems in object deallocation are not unusual in OpenStep • The AnalyzeAllocation tool compiles statistics on memory
applications under development. You might release an objecttoo allocation during the time a program executes.
many times or you might not release an object as many times as
is needed to deallocate it. Both situations lead to nasty problems See the man pages on these tools for more information.
-in the first case, to run-time errors when your code references
non-existent objects; the second case leads to memory leaks. Avoiding Deallocation Errors
If you're releasing an object too many times, you'll get run-time Here's a fewthings to remember that might help you avoid
error messages telling you that a message was sent to a freed deallocation bugs in OpenStep code:
object. To find which methods were releasing the object, in gdb or
the graphical debugger:: • Make sure there's an alloc, copy, mutableCopy, or retain
message sent to an object for each release or autorelease
Send enableFreedObjectCheck: to NSAutoreleasePool with sentto it.
an argument of YES.
• When you release a collection object (such as an NSArray),
2 Set a breakpoint on _NSAutoreleaseFreedObject. you release all objects stored in it as well. When you request
an object stored in a collection object, it's returned to you
3 Run the program under the debugger.
autoreleased.
4 When the program hits the breakpoint, do a backtrace and
check the stack to find the method releasing the object. • Superviews retain subviews as you add them to the view
hierarchy and release subviews as you release them. If you
Other tools help you track down problems related to release and want to keep swapped-out views, you should retain them.
autorelease: Similarly, when you replace a window's or box's content view,
the old view is released and the new view is retained.
• The oh command records allocation and deallocation events
related to a specific process. It produces a report showing the • To avoid retain cycles, objects should not retain their
stack frame for an object each time the object is allocated, delegates. Objects also should not retain their outlets, since
copied, retained or released. they do not own them.
109
Chapter 3 Travel Advisor Tutorial
110
(Juliets
calenciaJ February 1996
da~,,'Label
ite rn t',,1 atri;:.~
lielions
If~~;~~i~~Ef~~~c~i~!~~l~~~~l_-..-.t1 ~-----~-
_~l.""""o""","O""o",oOO"OO"
4
Chapter 4
To Do Tutorial
Sections Concepts
113
To Do Tutorial
114
Niany kinds of applications-word processors and spreadsheets, to name a
couple-are designed with the notion of a document in mind. A document is a
body of information, usually contained by a window, that is self-contained and
repeatable. Users can create, modify, store, and access a document as a discrete
unit. Multi-document applications (as these programs are called) can generate
an almost unlimited number of documents.
~L:J~...:J~~
.-J~ ~~_~=-L~:J 9 Inspector [X
115
Chapter 4 To Do Tutorial
• Event handling
• The core program framework
• Drawing and image composition
When you complete this tutorial, you should be ready to tackle OpenStep
programming on your own.
Every OpenStep application project created through Project 2 Gets the Class object for NSApplication and invokes its
Builder has the same mainO function (in the file sharedApplication class method, creating an instance of
ApplicationName_main.m). When users double-click an NSApplication, which is stored in the global variable, NSApp.
application or document icon in the File Viewer, mainO (the entry Creating the NSApplication object connects the application to
point) is called first; mainO, in turn, calls NSApplicationMain()- the window system and the Display PostScript server, and
and that's all it does. initializes its PostScript environment.
116
The Design of To Do
The Design of To Do
The To Do application vaults past Travel Advisor in terms of complexity.
Instead of Travel Advisor's one nib file, To Do has three nib files. Instead of
three custom classes, To Do has seven. This diagram shows the
interrelationships among instances of some of those classes and the nib files that
they load:
ToDoDoc.nib
~=-_.a ToDolnspector.nib
---II~~ Creates
ToDoltem
(Model)
q ToDo.nib
Some of the objects in this diagram are familiar, fitting as they do into the
Model-View-Controller paradigm. The ToOoltem class provides the model
objects for the application; instances of this class encapsulate the data associated
with the items appearing in documents. They also offer functions for computing
subsets of that data. And then there's the controller object... actually, there is
The ToDoInspector instance in
the above diagram is an offshoot more than one controller object.
of the application controller,
ToDoController. By breaking To Do's Multi-Document Design
down a problem domain into
distinct areas of responsibility, Two types of controller objects are at the heart of multi-document application
and assigning cenain types of design. They claim different areas of responsibility within an application.
objects to each area, you increase ToOoController is the application cOlltroller, it manages events that affect the
the modularity and reusability of application as a whole. Each ToOoDoc object is a document cOlltroller, and
the object, and make
manages a single document, including all the ToOoItems that belong to the
maintenance and trouble-
shooting easier. See "Object-
document. Naturally, it's essential that the application controller be able to
Oriented Programming" in the communicate with its (potentially) numerous document controllers, and they
appendix for more on this. with it.
117
Chapter 4 To Do Tutorial
As any developer knows well, performance is a key consideration nib files. The File's Owner object is not really appearing twice; it's
in program design. One factor is the timing of resource allocation. created in one file and referenced in the other.
If an application loads all code and resources that it might use
The File's Owner object dynamically loads a nib file and makes
when it starts up, it will probably be a sluggish, bloated
itself the owner of that file by sending loadNibNamed:owner: to
application-and one that takes awhile to launch.
NSBundle, specifying self as the second argument.
You can strategically store the resources of an application
(including user-interface objects) in several nib files. You can also NSBundle and Bundles
put code that might be used among one or more loadable bundles.
When the application needs a resource or piece of code, it loads A bundle is a location in the file system that stores code and the
the nib file or loadable bundle that contains it. This technique of resources that go with that code, including images, sounds, and
deferred allocation benefits an application greatly. By conserving archived objects. A bundle is also identified with an instance of
memory, it improves program efficiency.ltalso speeds upthetime NSBundle, which makes the contents of the bundle available to
it takes to launch the application. other objects that request it.
The generic notion of bundles is pervasive throughout OpenStep.
Auxiliary Nib Files Applications are bundles, as are frameworks and palettes. Every
application has at least one bundle-its main bundle-which is
When more sophisticated applications start up, they load only a
the ".app" directory (or application wrappen where its
minimum of resources in the main nib file-the main menu and
executable file is located. This file is loaded into memory when
perhaps a window. They display other windows (and load other
the application is launched.
nib files) only when users request it or when conditions warrant it.
Nib files other than an application's main nib file are sometimes Loadable Bundles
called auxiliary nib files. There are two general types of auxiliary
nib files: special-use and document. You can organize an application into any number of other bundles
in addition to the main bundle and the bundles of linked-in
Special-use nib files contain objects (and other resources) frameworks. Although these loadable bundles usually reside
that might be used in the normal operation ofthe application. inside the application wrapper, they can be anywhere in the file
Examples of special-use nib files are those containing inspector system. Project Builder allows you to build Loadable Bundle
panels and Info panels. projects.
Document nib files contain objects that represent some Loadable bundles differ from nib files in thatthey don't require you
repeatable entity, such as a word-processor document. A to use Interface Builderto build them. Instead of containing
document nib file is a template for documents: it contains the UI mostly archived objects, they usually contain mostly code.
objects and other resources needed to make a document. Loadable bundles are especially useful for incorporating extra
behavior into an application upon demand. An economic-forecast
The Owner of an Auxiliary Nib File application, for example, might load a bundle containing the code
defining an economic model, but only when users request that
The object that loads a nib file is usually the object that owns
model. You could also use loadable bundlesto integrate "plug and
it. A nib file's owner must be external to the file. Objects play" components into an existing framework.
unarchived from the nib file communicate with other objects in
the application only through the owner. Loadable bundles usually have an extension of ".bundle"
(although that's a convention, not a requirement). Each loadable
In Interface Builder, the File's Owner icon represents this external bundle must have a principal class that mediates between bundle
object. The File's Owner is typically the application controller for
objects and external objects.
special-use nib files, and the document controller for document
118
The Design of To Do
The rationale behind, and The controller objects of To Do respond to a variety of delegation messages sent
process of, constructing when certain events occur-primarily from windows and NSApp-in order to
multi-document applications is save and store object state. One example of such an event is when the user
discussed in "The Structure of
Multi-Document Applications"
closes a document window; another is when data is entered into a document.
on page 141. Often when these events happen, one controller sends a message to the other
controller to keep it informed.
119
Chapter 4 To Do Tutorial
The array of ToDoItems is associated with a particular day. Thus the data for a
document consists of a (mutable) dictionary with arrays of To Do Items for values
and dates for keys.
NSMutableDictionary
15 Nov 1996 16 Nov 1996 17 Nov 1996
Key
ToDoltem ToDoltem ToDoltem
ToDoltem ToDoltem
ToDoltem ToDoltem ToDoltem
Value ToDoltem
ToDoltem
When users select a day in the calendar, the application computes the date,
which it then uses as the key to locate an array of ToDoltems in the dictionary.
SelectionNotifMatrix:
__-+___ modifies NSMatrix to
notify observers when a
ToDoCell: a subclass of selection in a text field
NSButtonCell, this is a tri- occurs.
state control with --+--~':""':""':""--:-:i:::====':':""::============; I
different images for each
state. It also displays
times items are due.
You'll learn much more about these custom subclasses in the pages that follow.
120
Setting up the To Do Project
Langua.ge: L~_~g~~~__ _
; __~;;';P;:;;;P1;:; ;_iC;';'~';';'_ig;';'; _~_;__';'";;;:;';;;;";;;;';;";;;;"
APpile ali 0 n Clas s: N;;'__S- r.;;,
Click Add.
Double-click the new cell under
the Extension column.
Type the extension of To Do
documents: "td". Document types specify the kinds of files the
application can open and "understand." They
Drag into the image well the file lIUt"'t:N~It:t' for ~1acti appear in the workspace with the assigned icon
calendar.tiff from the ToDo and may be opened by double-clicking.
project in Ii Help File
/NextDeveloper/Examples/
As with the application icon, when you drag
AppKit. the document icon into the image well, the image
file is added to the project.
.
".,. ..
'"
-"'
IV ·,,1 If the document type is well-known (for example,
".c"), just drag a document of that type into the well.
121
Chapter 4 To Do Tutorial
The ToDoItem class provides the model objects for the To Do application. Its
instance variables hold the data that defines tasks that should be done or
appointments that have to be kept. Its methods allow access to this data. In
addition, it provides functions that perform helpful calculations with that data.
ToDoItem thus encapsulates both data andbehavior that goes beyond accessing
data.
You are adopting the NSCopying protocol in addition to the NSCoding protocol
because you are going to implement a method that makes "snapshot" copies of
ToDoItem instances.
122
Creating the Model Class (ToDoltem)
day The day (a date resolved to 12:00 AM) ofthe to-do item
itemName The name of the to-do item (the content's of a document text field)
notes The contents of the inspector's Notes display; this could be any
information related to the to-do item, such as an agenda to discuss at
a meeting.
secsUntilDue The seconds after day at which the item comes due
enum {
minlnSecs = 60,
hrlnSecs = (minlnSecs * 60),
daylnSecs = (hrlnSecs * 24),
weeklnSecs = (daylnSecs * 7)
}i
The first set of constants are values for the itemStatus instance variable. The
second set of constants are for convenience and clarity in the methods that deal
with temporal values.
4 Declare two time-conversion BOOL ConvertSecondsToTime(long sees, int *hour, int *minute);
functions.
long ConvertTimeToSeconds(int hr, int min, BOOL flag);
123
Chapter 4 To Do Tutorial
Type the method declarations - (id)initWithName: (NSString *)name andDate: (NSCalendarDate *)date;
shown at right.
- (void)dealloc;
- (BOOL)isEqual: (id)anObjecti
- (id)copyWithZone: (NSZone *)zone;
- (id)initWithCoder: (NSCoder *)coder;
- (void)encodeWithCoder: (NSCoder *)coderi
- (void)setDay: (NSCalendarDate *)newDaYi
- (NSCalendarDate *)daYi
- (void)setltemName: (NSString *)newNamei
- (NSString *)itemNamei
- (void)setNotes: (NSString *)notesi
- (NSString *)notesi
- (void)setltemTimer: (NSTimer *)aTimeri
- (NSTimer *)itemTimeri
- (void)setSecsUntilDue:(long)secs;
- (long)secsUntilDue;
- (void)setSecsuntilNotif: (long)secsi
- (long)secsUntilNotif;
- (void)setltemStatus: (ToDoltemStatus)newStatus;
- (ToDoltemStatus)itemStatus;
Most of these declarations are for accessor methods. You know what to do.
The setltemTimer: method is slightly different from the other "set" accessor
methods. It sends invalidate to itemTimer to disable the timer before it autoreleases
Timers (instances of NSTImer)
are always associated with a run it.
loop (an instance of
NSRunLoop). See "Tick Tock In this application, you want client objects to be able to copy your ToDoltem
Brrrring: Run Loops and Timer" objects and test them for equality. You must define this behavior yourself.
on page 190 for more on timers
and run loops.
124
Creating the Model Class (ToDoltem)
Before You Go On - - - - - - - - - - - - - - - - - - - - - -
There is a specific as well as a general need for the isEqual: override. In the To
Do application, an NSArray contains a day's ToDoItems. To access them, other
objects in the application invoke several NSArray methods that, in turn, invoke
the isEqual: method of each object in the array.
return newobji
125
Chapter 4 To Do Tutorial
[self secsUntilDue],
[self secsUntilNotif]]i
return (desc) i
9 ImplementToDoltem's initializing Here are some things to remember as you implement initWithName:andDate: and
and deallocation methods. dealloc:
• The instance variables to initialize are day, itemName, notes, and itemStatus (to
"incomplete").
10 Implement ToDoltem's archiving When you implement encodeWithCoder: and initWithCoder:, keep the following in
and unarchiving methods. mind:
• You don't need to archive the itemTimer instance variable since timers are re-
set when a document is opened.
The final step in creating the ToDoltem class is to implement the functions that
furnish "value-added" behavior.
126
Creating the Model Class (ToDoltem)
11 Implement ToDoltem's time- long ConvertTimeToSeeonds(int hr, int min, BaaL flag) /* 1 */
conversion functions. {
if (flag) { /* PM * /
if (hr >= 1 && hr < 12)
hr += 12;
else
if (hr 12)
hr = 0;
if (sees) {
hr = sees / hrlnSees;
if (hr > 12) {
*hour = (hr -= 12);
pm YES;
else
pm NO;
if (hr == 0)
hr = 12;
*hour = hr;
return pm;
1. This expression, as well as others in these two methods, uses the enum constants
for time-values-as seconds that you defined earlier.
127
Chapter 4 To Do Tutorial
Creating a subclass of a class that is farther down the inheritance tree poses more
of a challenge for a developer than a simple sublcass of NSObject. A class such
as NSMatrix is more specialized than NSObject and carries with it more
baggage: It inherits from NSResponder, NSView, and NSControl, all fairly
complex Application Kit classes. And since CalendarMatrix inherits from
NSView, it appears on the user interface; it is an example of a view object in the
Model-View-Controller paradigm, and as such it is highly reusable.
Why NSMatrix?
When you select a specialized superclass as the basis for your subclass, it is
important to consider what your requirements are and to understand what the
superclass has to offer. To Do's dynamic calendar should:
NSMatrix is a class usedfor creating groups ofNSCells that work together in various
ways. It includes methods for arranging NSCells in rows and columns.... An
NSMatrix adds to NSControl's targetlaction paradigm by allowing a separate target
and action for each of its NSCells in addition to its own target and action.
128
Subclass Example: Adding Data and Behavior (CalendarMatrix)
So NSMatrix has an inherent capability for the first of the requirements listed
above, and part of the second (responding to selections). Our CalendarMatrix
subclass thus does not need to alter anything in its superclass. It just needs to
supplement NSMatrix with additional data and behavior so it can understand
dates (and update itself appropriately), navigate between months, and notify a
delegate that a selection was made.
choseDay:
monthChang ed:
fax:
When you created subclasses ofNSObject in the previous two tutorials, the next
step was to instantiate the subclass. Because CalendarMatrix is a view (that is, it
inherits from NSView), the procedure for generating an instance for making
connections is different.
129
Chapter 4 To Do Tutorial
The selection of the class for the CustomView creates an instance of it that you
can connect to other objects in the nib file. Now put the controls and fields
associated with CalendarMatrix on the window.
130
Subclass Example: Adding Data and Behavior ICalendarMatrix)
3 Put the objects related to ,.-,---,-----:-----,-".,--...,...-,--:---:-:----:--:--..,..-~=;:=;:=i'--- This label contains the month and year:. Initialize by
CalendarMatrix on the window. typing "September 9999" (the longest possible string).
set text to Helvetica 18, center it, then delete it.
131
Chapter 4 To Do Tutorial
/ * ... * /
- (void)refreshCalendar;
- (id)initWithFrame: (NSRect)frameRecti
- (void)dealloc;
- (void)setSelectedDay: (NSCalendarDate *)newDaYi
- (NSCalendarDate *)selectedDaYi
@end
@interface NSObject(CalendarMatrixDelegate) /* 2 */
- (void)calendarMatrix: (CalendarMatrix *)obj
didChangeToDate: (NSDate *)datei
- (void)calendarMatrix: (CalendarMatrix *)obj
didChangeToMonth: (int)mo year: (int)yr;
@end
1. The cells in CalendarMatrix are sequentially ordered by tag number, left to right,
going downward. startOffset marks the cell (by its tag) on which the first day of the
month falls.
132
Subclass Example: Adding Data and Behavior /CalendarMatrix)
[cell release];
selectedDay = [[NSCalendarDate dateWithYear: [now yearOfCommonEra]
month: [now monthOfYear] 1* 4 *1
day: [now dayOfMonth]
hour:O minute:O second:O
timeZone: [NSTimeZone locaITimeZone]] copy];
return self;
1. This invocation of date, a class method declared by NSDate, returns the current
date ("today") as an NSCalendarDate. (NSCalendarDate is a subclass ofNSDate.)
2. This message to super (NSMatrix) sets the physical and cell dimensions of the
matrix, identifies the type of cell using a prototype (an NSButtonCell), and
specifies the general behavior of the matrix: radio mode, which means that
only one button can be selected at any time.
3. Set the tag number of each cell sequentially left to right and down. Tags are
the mechanism by which CalendarMatrix sets and retrieves the day numbers
of cells.
133
Chapter 4 To Do Tutorial
The refreshCalendar method is fairly long and complex-it is the workhorse of the
class-so you'll approach it in sections.
In Open Step you represent dates and times as objects that inherit The NSCalendarDate class, which inherits from NSDate,
from NSDate. The major advantage of dates and times as objects generates objects that represent dates conforming to western
is common to all objects that represent basic values: they yield calendrical systems. NSCalendarDate objects also adjust the
functionality that, although commonly found in most operating representations of dates to reflect their associated time zones.
systems, is not tied to the internals of any particular operating-
Because of this, you can track an NSCalendarDate object across
system.
differenttime zones. You can also present date information from
NSDates hold dates and times as values of type NSlimelnterval time-zone viewpoints other than the one for the current locale.
and express these values as seconds. The NSlimelnterval type
makes possible a wide and fine-grained range of date and time Each NSCalendarDate object also has a calendar format string
values, giving accuracy within milliseconds for dates 10,000 years bound to it. This format string contains date-conversion specifiers
apart.
that are very similar to those used in the standard Clibrary
NSDate and its subclasses compute time as seconds relative to function strftimeO. NSCalendarDate can interpret user-entered
an absolute reference date (the first instant of January 1, 2001). dates that conform to this format string.
NSDate converts all date and time representations to and from
NSlimelnterval values that are relative to this reference date. NSCalendar has methods for creating NSCalendarDate objects
from formatted strings and from component time values (such as
NSDate provides methods for obtaining NSDate objects
(including date, which returns the current date and time as an minutes, hours, day of week, and year). It also supplements
NSDate), for comparing dates, for computing relative time values, NSDate with methods for accessing componenttime values and
and for representing dates as strings. for representing dates in various formats, locales, and time zones.
134
Subclass Example: Adding Data and Behavior ICalendarMatrix)
Before it can start writing day numbers to the calendar for a given month,
CalendarMatrix must know what cell to start with and how many cells to fill with
numbers. The refreshCalendar method begins by calculating these values.
1. Creates an NSCalendarDate for the first day of the currently selected month and
year (computed from the selectedDay instance variable).
2. Writes the month and year (for example, "February 1997") to the label above
the calendar.
3. Gets from the MonthDays static array the number of days for that month; if the
month is February and it is a leap year, this number is adjusted.
4. Gets the day of the week for the first day of the month and stores this in the
startOffset instance variable.
135
Chapter 4 To Do Tutorial
for (ii<42ii++) {
cell = [self cellWithTag:i] i
[cell setBordered:NO] i
[cell setEnabled:NO] i
[cell setTitle:@""]i
[cell setCellAttribute:NSCellHighlighted to:NOJi
The first and third for-loops in this section of code clear the leading and trailing
cells that aren't part of the month's days. Because the current day is indicated by
highlighting, they also turn off the highlighted attribute. The second for-loop
writes the day numbers of the month, starting at startOffset and continuing until
dayslnMonth, and resets the font (since the selected day is in bold face) and other
cell attributes.
This final section of refreshCalendar determines if the newly selected month and
year are the same as today's, and if so highlights the cell corresponding to today.
136
Subclass Example: Adding Data and Behavior (CalendarMatrix)
if (sender == rightButton) { /* 1 */
if (currentMonth == 12) {
currentMonth = 1i
currentYear++i
else {
currentMonth++i
else
if (currentMonth == 1)
currentMonth 12i
currentYear--i
else {
currentMonth--i
/* 2 */
[self setSelectedDay: [NSCalendarDate dateWithYear:currentYear
month:currentMonth
day:1 hour:O minute:O second:O
timeZone: [NSTimeZone localTimeZone]]]i
[self refreshCalendar]i
[[self delegate] calendarMatrix:self /* 3 */
didChangeToMonth:currentMonth year:currentYear]i
2. Resets the selectedDay instance variable with the new month (and perhaps
year) numbers and invokes refreshCalendar to display the new month.
137
Chapter 4 To Do Tutorial
1. Gets the tag number of the selected cell and subtracts the offset from it (plus one
to adjust for zero-based indexing) to find the number of the selected day.
3. Sets the font of the previously selected cell to the normal system font
(removing the bold attribute) and puts the number of the currently selected
cell in bold face.
4. Sets the selectedDay instance variable to the new date and sends the
11 Implement accessor methods for calendarMatrix:didChangeToDate: message to the delegate.
the selectedDay instance
variable. You are finished with CalendarMatrix. If you loaded ToDoDoc.nib right now, the
calendar would work, up to a point. If you clicked the arrow buttons,
CalendarMatrix would display the next or previous months. The days of the
month would be properly set out on the window, and the current day would be
highlighted.
But not much else would happen. That's because CalendarMatrix has not yet
been hooked up to its delegate.
138
The Basics of a Multi-Document Application
Create ToDoControlier as a
subclass of NSObject. o To Do Controller
OUtlets
Add the outlet and actions (listed inspector
at right) to the class.
Make the action connections
from the appropriate Document openDoc:
menu commands. saveDoc:
. showlnfo:
showlnspector;
showPreferences:
Now that you've defined the application-controller class, define the document-
controller class, ToDoDoc. Remember, since the ToDoDoc controller must own
the nib file containing the document, it must be external to it; although it is
defined in the main nib file (ToDo.nib) and in ToDoDoc.nib, it's instantiated before
its nib file is loaded.
139
Chapter 4 To Do Tutorial
Open ToDoDoc.nib.
Add the matrices oftext fields.
Add the labels above the
matrices.
Make the labels 14 points in the
user's application font.
Make the item text 12 points in the
Set the text color of this
user's application font. label to dark gray.
Add padding to this label,
Save ToDoDoc.nib. extending it acrss the
column.
II';ill!~:::::~~~~~~r Remember,
cells are text fields of the
same size as the cells of the
other matrix. However, you create a matrix
by Alternate-dragging a
will at run time substitute handle of a suitable object.
your own custom cell Before Alternate-dragging,
(ToDoCell). make the initial text field
scrollable.
140
The Basics of a Multi-Document Application
From a user's perspective, a document is a unique body of controller delegates ofthe application (NSApp) and the document
information usually contained by its own window. Users can window, they can receive messages sent at critical moments of a
create an unlimited number of documents and save each to a file. running application. These moments include the closure of
Common documents are word-processing documents and windows (windowShouldClose:), window selection
spreadsheets. (windowDidResignMain:), application start-up
(applicationWiIIFinishLaunching:) and application termination
From a programming perspective, a document comprises the
(applicationShouldTerminate:). In the methods handling these
objects and resources unarchived from an auxiliary nib file and
messages, the controllers can then do the appropriate thing, such
the controller object that loads and manages these things. This
as saving a document's data or displaying an empty document.
document controller is the owner ofthe auxiliary nib file
containing the document interface and related resources.To Several NSViews also have delegation messages that facilitate
manage a document, the document controller makes itselfthe document management, particularly text fields, forms, and other
delegate of its window and its "content" objects. Ittracks edited controls with editable text (controIText... ) and NSText objects
status, handles window-close events, and responds to other (text... ). One important such message is textDidChange: (or
conditions. controITextDidChange:), which signals that the document's
textual content was modified. In responding to this message,
When users choose the New (or equivalent) command, a method
controllers can setthe window's close button to have a "broken"
is invoked in the application's controller object. In this method, the
X with the setDocumentEdited: message; later, they can
application controller creates a document-controller object,
determine whether the document needs to be saved by sending
which loads the document nib file in the course of initializing itself.
isDocumentEdited to the window.
A documentthus remains independent of the application's" core"
objects, storing state data in the document controller. If the Document controllers often need to communicate with the
application needs information about a document's state, it can application controller or other objects in the application. One way
query the document controller. to do this is by posting notifications. Another way is to use the key
relationships within the core program framework (see page 149)
When users chose the Save command, the application displays a
to find the other object (assuming it's a delegate of an Application
Save panel and enables users to save the document in the file
Kit object). For example, the application controller can send the
system. When users chose the Open command, the application
following message to locate the current document controller:
displays an Open panel, allowing users to select a document file
and open it. [[NSApp mainWindow] delegate]
The document controller can find the application controller with:
Document Management Techniques
[NSApp delegate]
When you make the application controller and the document
creates loads
Sav,fAS':,
C!ose~ AppController DocController Doc.nib
141
Chapter 4 To Do Tutorial
Text fields in a matrix, just like a form's cells, are connected for inter-field
tabbing when you create the matrix. But you must also connect ToDoDoc and
ToDoController to the delegate outlets of other objects in the application-this
step is critical to the multi-document design.
delegate From the document window's title bar to File's Owner (ToDoDoc)
6 Create source-code files for The ToDoDoc class needs supplemental data and behavior to get the multi-
ToDoDoc and ToDoControlier. document mechanism working right.
In Project Builder:
@interface ToDoDoc:NSObject
7 Add declarations of methods and
instance variables to the /* ... * /
ToDoDoc class. NSMutableDictionary *activeDays;
NSMutableArray *currentltemsi
. SelectToDoDoc.h in the project
browser. /* ... * /
Add the declarations at right. - (NSMutableArray *)currentltems;
- (void)setCurrentltems: {NSMutableArray *)newltems;
(Ellipses indicate existing {NSMatrix *)itemMatrix;
declarations.)
- (NSMatrix *)markMatrixi
- (NSMutableDictionary *)activeDaYSi
- (void)saveDoCi
- (id)initwithFile: (NSString *)aFilei
- (void)dealloc;
- (void)activateDoCi
- (void)selectltem: (int) item;
@end
The active Days and currentltems instance variables hold the collection objects that
store and organize the data of the application. (You'll deal with these instance
variables much more in the next section of this tutorial.) Many of the methods
declared are accessor methods that set or return these instance variables or one
of the matrices of the document.
You'11 be ~witching between ToDoDoc.m and ToDoController.m in the next few tasks.
The intent is not to confuse, but to show the close interaction between these
two clas~es.
142
The Basics of a Multi-Document Application
The screen's coordinate system is the basis for all other The reference coordinate system for a window is known as the
coordinate systems used for positioning, sizing, drawing, and base coordinate system. It differs from the screen coordinate
event handling. You can think of the entire screen as occupying system in only two ways:
the upper-right quadrant of a two-dimensional coordinate grid.
• It applies only to a particular window, each window has its own
The other three quadrants, which are invisible to users, take
base coordinate system.
negative values along their x-axis, their y-axis, or both axes. The
screen's quadrant has its origin in the lower left corner; the • Its origin is at the lower left corner of the window, rather than
positive x-axis extends horizontally to the right and the positive y- the lower left comer of the screen. If the window moves, the
axis extends vertically upward. A unit along either axis is origin and the entire coordinate system move with it
expressed as a pixel.
For drawing, each NSView uses a coordinate system transformed
from the base coordinate system or from the coordinate system of
The screen coordinate system has just one function: to position its superview. This coordinate system also has itorigin pointatthe
windows on the screen. When your application creates a new lower-left corner of the NSView, making it more convenient for
window, it must specify the window's initial size and location in drawing operations. NSView has several methods for converting
screen coordinates.You can "hide" windows by specifying their between base and local coordinate systems. When you draw,
origin points well within one of the invisible quadrants. This coordinates are expressed in the application's currentcoordinate
technique is often used in off-screen rendering in buffered system, the system reflecting the last coordinate transformations
windows. to have taken place within the current window.
·::• ,:•
this point.
The location of the window is
expressed relative to the
··•
=
=
• x-axis
..
::
•
:
•
screen's origin, and its coord-
inate system begins here too.
143
Chapter 4 To Do Tutorial
[super init];
if (aFile) { /* 1 */
activeDays = [NSUnarchiver unarchiveObjectWithFile:aFile];
if (activeDays)
activeDays = [activeDays retain];
else
NSRunAlertPanel(@"To Do", @"Couldn't unarchive file %@",
nil, nil, nil, aFile);
else /* 2 */
activeDays = [[NSMutableDictionary alloc] init];
[self setCurrentltems:nil];
This method, which initializes and loads the document, has the following steps:
1. Restores the document's archived objects if the aFile argument is the pathname of
a file containing the archived objects (that is, the document is opened). If objects
are unarchived, it retains the activeDays dictionary; otherwise it displays an
attention panel.
3. Loads the nib file containing the document interface, specifying self as owner.
4. Sets the title of the window; this is either the file name on the left of the title
bar and the pathname on the right, or "UNTITLED" if the document is new.
Be/ore You Go On - - - - - - - - - - - - - - - - - - - - - -
Note the [itemMatrix window] message nested in the last message. Every object
that inherits from NSView "knows" its window and will return that NSWindow
object if you send it a window message.
144
The Basics of a Multi-Document Application
[oPanel setAllowsMultipleSelection:YES]i
if ([[[NSApp keyWindow] delegate] isKindOfClass: [ToDoDoc class]])
startDir = [[[NSApp keyWindow] representedFilename] /* 2 */
stringByDeletingLastPathComponent];
else
startDir = NSHomeDirectory();
resul t = [,oPanel runModalForDirectory: startDir file: nil /* 3 */
types:fileTypes] ;
if (result == NSOKButton) {
NSArray *filesToOpen = [oPanel filenames];
int i, count = [filesToOpen count];
for (i=O; i<count; i++) { /* 4 */
NSString *aFile = [filesToOpen objectAtIndex:i];
id currentDoc = [[ToDoDoc alloc] initWithFile:aFile];
[currentDoc activateDoc];
The open Doc: method displays the modal Open panel, gets the user's response
(which can be multiple selections) and opens the file (or files) selected.
1. Creates or gets the NSOpenPanel instance (an instance shared among objects of
an application). The previous message specifies the file types (that is, the
extensions) of the files that will appear in the Open panel browser. The next
message enables selection of multiple file in the panel's browser.
2. Sets the directory at which the NSOpenPanel starts displaying files either to
the directory of any document window currently key or , if there is none, to
the user's home directory.
4. If the key is NSOKButton, cycles through the selected files and, for each,
creates a document by allocating and initializing a ToDoDoc instance,
passing in a file name.
The methods invoked by the Document menu's Close and Save commands
both simply send a message to another object. How they locate these objects
exemplify important techniques using the core program framework.
145
Chapter 4 To Do Tutorial
In ToDoController.m, implement
id currentDoc =[[NSApp mainWindow] delegate];
if (currentDoc)
the saveDoc: method.
[currentDoc saveDoc];
As did closeDoc:, this method sends mainWindow to NSApp to get the main
window, but then it sends delegate to the returned window to get its delegate, the
ToDoDoc instance that is managing the document. It then sends the ToDoDoc-
defined message saveDoc to this instance.
Note: You could implement closeDoc: and saveDoc: in the ToDoDoc class, but the
ToDoController approach was chosen to make the division of responsibility
clearer.
146
The Basics of a Multi-Document Application
1. The title method returns the text that appears in the window's title bar. If the title
doesn't begin with "UNTITLED" (what new document windows are initialized
with), then a file name and directory location has already been chosen, and is
stored as the represented Filename.
2. If the window title begins with "UNTITLED" then the document needs to
be saved under a user-specified file name and directory location. This part of
the code creates or gets the shared NSSavePanel instance and sets the file
type, which is the extension that's automatically appended. Then it runs the
Save panel, specifying the user's home directory as the starting location.
3. Archives the document under the chosen directory path and file name and,
with the setDocumentEdited: message, changes the window's close button to an
"unbroken X" image (more on this in the next section).
12 Implementthe accessor methods Don't implement setCurrentltems: yet. This method does something special for
for ToDoController and ToDoDoc. the application that will be covered in "Managing the Data and Coordinating its
Display (ToDoDoc)" on page 154.
147
Chapter 4 To Do Tutorial
SAP~lic,ati~n~~~\Nin~~YI';an~ .~;S,Vie"!i;'.~1
t.--h"""''''''~_''_.~~_._._' ' • ..,...""...~_,,<,",._._.,..,,,~,J:,~,,,,,."»,,,,J..._,,,~~-.~/-:'~,. __,,,,,,,;.;;L~~,~:~•. ~_ __
Many classes ofthe Application Kit stand out in terms of relative NSResponder
importance. NSControl, for example, is the superclass of all user-
interface devices, NSText underlies all text operations, and NSResponder is an abstract class, but it enables event handling
NSMenu has obvious significance. But four classes are at the in all classes that inherit from it. It defines the set of messages
core of a running application: NSResponder, NSApplication, invoked when different mouse and keyboard events occur. It also
NSWindow, and NSView. Each of these classes plays a critical defines the mechanics of event processing among objects in an
role in the two primary activities of an application: drawing the application, especially the passing of events up the responder
user interface and responding to events. The structure of their chain to each next responder until the event is handled. See the
interaction is sometimes called the core program framework. "Events and the Event Cycle" on page 163 for more on the
responder chain and a description of first responder.
NSWindow
NSApplication
An NSWindow object manages each physical window (that is,
each window created by the Window Server) on the screen. It Every application must have one NSApplication object to act as
draws the title bar and window frame and responds to user its interface with the Window Server and to supervise and
actions that close, move, resize, and otherwise manipulate the coordinate the overall behavior of the application. This object
window. receives events from the Window Server and dispatches them to
the appropriate NSWindows (which, in turn, distribute them to
The main purpose of an NSWindow is to display an application's their NSViews). The NSApplication object manages its windows
user interface (or part of it) in its content area: that space below and detects and handles changes in their status as well as in its
the title bar and within the window frame. A window's content is own status: hidden and unhidden, active and inactive. The
the NSViews it encloses, and at the root of this view hierarchy is NSApplication object is represented in each application by the
the content view, which fills the content area. Based on the global variable NSApp. To coordinate your own code with NSApp,
location of a user event, NSWindows assigns an NSView in its you can assign your own custom object as its delegate.
content area to act as first responder.
An NSWindow allows you to assign a custom object as its NSView
delegate and so participate in its activities. Any object you see in a window's content area is an NSView.
(Actually, since NSView is an abstract class, these objects are
instances of NSView subclasses.) NSView objects are
responsible for drawing and for responding to mouse and
keyboard events Each NSView owns a rectangular region
The NSEvent class is also
involved in event processing. For associated with a particular window; it produces images within
more about NSEvent and the this region and responds to events occurring within the rectangle.
NSObject
event cycle, see "Events and the
NSViews in a window are logically arranged in a view hierarchy,
Event Cycle" on page 163.
with t~e content viewatthe top of the hierarchy (see facing page
for more information). An NSView references its window, its
superview, and its subviews. It can be the first responder for
events orthe next responder in the responder chain. An NSView's
NSResponder frame and bounds are rectangles that define its location on the
screen, its dimension, and its coordinate system for drawing.
I
NSWindow NSApplication NSView
148
The Basics of a Multi-Document Application
NSApp
NSApplication
windows .. r+ NSView(B)
~
NSWindow
delegate
~
.... window
contentView ..
po NSView(A) ...
~
superview
delegate subviews
.... window
superview (nil)
subviews
~
delegate
subviews
149
Chapter 4 To Do Tutorial
• Edit a document.
• Close a window.
• Quit the application.
• Hide the application.
• Switch to another application or window.
Several classes of the Application Kit send messages to their delegates when
these events occur, givi~g the delegate the opportunity to do the appropriate
thing, whether that be saving a document to the file system or marking a
document as edited.
Open ToDoDoc.m.
[[itemMatrix window] setDocumentEdited:YES];
Implement the
controlTextDidChange: method
to mark the document.
When a control that contains editable text-such as a text field or a matrix of text
fields-detects editing in a field, it posts the controlTextDidChange: notification
which, like all notifications, is sent to the control's delegate as well as to all
observers. The setDocumentEdited: message causes the document's window to
change the image in its close button to a broken X.
~ [window setDocumentEdited:NO] i
~ [window setDocumentEdited:YES];
150
Managing Documents Through Delegation
case NSAlertAlternateReturn:
return YES;
case NSAlertOtherReturn:
return NO;
return NO;
When users click a window's close button, the window sends windowShouldClose:
to its delegate. It expects a response directing it either to close the window or
leave it open.
1. Returns YES (meaning: go ahead, close the window) if the document hasn't been
edited.
2. Makes the window its own first responder. This has the effect of forcing the
validation of cells, flushing currently entered text to the method that handles
it (more on this in the next section).
Note: Do you recall the performClose: method that ToDoController sends the
document window when the user chooses the Close command? This method
151
Chapter 4 To Do Tutorial
else
[[NSApp keyWindow] close];
return YES;
152
Managing the Data and Coordinating its Display (ToDoDoc)
To lend clarity to this design's implementation, this section follows the process
from start to finish through which the ToDoDoc class handles entered data, and
organizes, displays, and stores it. It also shows how the display and manipulation
of data is driven by the selections made in the CalendarMatrix object.
Start by revisiting a portion of code you wrote earlier for ToDoDoc's initWithFile:
method.
/ * ... * /
if (aFile)
activeDays = [NSUnarchiver unarchiveObjectWithFile:aFilel i
if (activeDays)
activeDays = [activeDays retainJ;
else
NSRunAlertPanel{@"To Do", @IICouldn't unarchive file %@",
nil, nil, nil, aFile)i
else
activeDays = [[NSMutableDictionary allocl initJi
[self setCurrentItems:nilJi
/* */
Assume the user has chosen the New command from the Document menu.
Since there is no archive file (aFile is nil), the active Days dictionary is created but
is left empty. Then initWithFile: invokes its own setCurrentltems: method, passing in
nil.
153
Chapter 4 To Do Tutorial
Implement setCurrentltems:.
if (newItems)
currentItems = [newItems mutableCopy] i
else {
int numRows = [[itemMatrix cells] count]i
currentltems = [[NSMutableArray alloc]
initWithCapacity:numRows] i
while (--numRows >= 0)
[currentItems addObject:@""]i
This "set" accessor method is like other such methods, except in how it handles
a nil argument. In this case, nil signifies that the array does not exist, and so it
must be created. Not only does setCurrentltems: create the array, but it "initializes"
it with empty s.tring objects. It does this because NSMutableArray's methods
cannot tolerate nil objects within the bounds of the array.
154
Managing the Data and Coordinating its Display (ToDoDocl
/* 5 */
[self updateMatrix] ;
A control sends controlTextDidEndEditing: to its delegate when the cursor leaves a text
field. In addition to creating new ToDoItems, this implementation of
controlTextDidEndEditing: removes ToDoltems from arrays and modifies item text.
What it does is appropriate to what the user does.
1. If the document hasn't been edited (see controITextDidChange:) or if the selected row
exceeds the array bounds, it returns because there's no reason to proceed. It
initializes a currentltems array if one doesn't exist.
2. If the user deletes the text of an existing item, it removes the ToDoltem that
positionally corresponds to the row of that deleted text.
3. It changes the name of an item if the text entered in a field doesn't match the
name of the corresponding item in the currentltems array.
155
Chapter 4 To Do Tutorial
4. If either of the two previous conditions don't apply, and text has been
entered, it creates a new ToDoltem and inserts it in the currentltems array.
Implement updateMatrix:.
int it cnt =
[currentltems count], rows [[itemMatrix cells] count];
ToDoltem *thisltem;
else /* 2 */
[[itemMatrix cellAtRow:i column:O] setStringValue:@IIII]i
[[markMatrix cellAtRow:i column:O] setTitle:@"I)i
[[markMatrix cellAtRow:i column:O] setlmage:nil);
The update Matrix method writes the names of the items (ToDoItems) in the
currentltems array to the text fields of itemMatrix. It also updates the visual
appearance of the cells in the matrix (markMatrix) next to itemMatrix. These cells
are instances of a custom subclass of NSButtonCell that you will create later in
this tutorial. For now, just type all the code above; later, when you create the cell
class, ToDoCell, you can refer back to this example to see what is happening.
Basically, this method cycles through the array of items, doing the following:
1. If an object in the array is a ToDoltem, it writes the item name to the text field
corresponding to the array slot and updates the button cell next to the field.
2. If an object isn't a ToDoltem, it blanks the corresponding text field and cell.
156
Managing the Data and Coordinating its Display (ToDoDoc)
[self saveDocltems]i
[self setCurrentltems:nil]i
[self updateMatrix]i
157
Chapter 4 To Do Tutorial
This method inspects the currentltems array and, if it contains at least one
ToDoltem, puts the array in the activeDays dictionary with a key corresponding
to the date.
Now that you've completed the methods for saving and archiving the collection
objects holding ToDoltems, assume that the user has saved his document and
then opens it.
158
Managing the Data and Coordinating its Display lToDoDocl
159
Chapter 4 To Do Tutorial
The need for this class is this: An instance of NSMatrix is a control and thus can
send action messages to its cell's targets; but when it contains
NSTextFieldCells, action messages are sent only when users press the Return
key in a cell. You want the inspector to synchronize its displays when the user
selects a new item by clicking a text field. To do this, you will override the
method in NSMatrix that is invoked when users click the matrix; in your
implcmc~tation, you'll invoke the superclass method, detect the selected row,
and then post a notification to interested observers.
In SelectionNotifMatrix.m,
int row;
implement mouseD own: as
[super mouseDown:theEvent]; /* 1 */
shown here.
160
Subclass Example: Overriding Behavior (SelectionNotifMatrix)
2. Gets the row of the cell clicked and, if it's a valid row, creates a userlnfo
dictionary containing the clicked row, and posts the
SelectionInMatrixNotification.
Now that you've created the SelectionNotifMatrix class, you must re-assign the
class membership of the object in the interface. You can do this easily in
Interface Builder.
161
Chapter 4 To Do Tutorial
You can depictthe interaction between a user and an OpenStep codes identifying characters and keys, and various other kinds of
application as a cyclical process, with the Window Server playing information.
an intermediary role (see illustration below). This cycle-the
event cycle-usually starts at launch time when the application An NSEvent also divulges the type of event it represents. There
(which includes all the OpenStep frameworks it's linked to) sends are many event types (NSEventType); theyfall into five categories:
a stream of PostScript code to the Window Server to have it draw
the application interface. • Keyboard events Generated when a key is pressed down, a
pressed key is released, or a modifier key changes. Of these,
Then the application begins its main event loop and begins
key-down events are the most useful. When you handle a key-
accepting inputfrom the user (see facing page). When users click
down event, you often determine the character or characters
or drag the mouse or type on the keyboard, the Window Server
associated with the event by sending the NSEvent a
detects these actions and processes them, passing them to the
characters message.
application as events. Often the application, in response to these
events, returns another stream of PostScript code to the Window
• Mouse events Mouse events are generated by changes in
Server to have it redraw the interface.
the state of the mouse buttons (that is, down and up) for both
In addition to events, applications can respond to other kinds of left and right mouse buttons and during mouse dragging.
input, particularly timers, data received at a port, and data waiting Events are also generated when the mouse simply moves,
at a file descriptor. But events are the most important kind of input. without any button pressed.
Events
Other
Applications
Window
Server
User
Monitored Port
or File
PostScript
Code
Entries
162
Subclass Example: Overriding Behavior (SelectionNotifMatrixl
The Event Queue and Event Dispatching First Responder and the Responder Chain
When an application starts up, the NSApplication object (NSAppl Each NSWindow in an application keeps track ofthe object in its
starts the main event loop and begins receiving events from the view hierarchy that has first responderstatus. This is the NSView
Window Server (see page 1161. As NSEvents arrive, they're put in that currently receives keyboard events for the window. By
the event queue in the order they're received. On each cycle of default, an NSWindow is its own first responder, but any NSView
the loop, NSApp gets the topmost event, analyzes it, and sends an within the window can become first responder when the user
event message to the appropriate object. (Event messages are clicks it with the mouse.
defined by NSResponder and correspond to particular events.l
When NSApp finishes processing the event, it gets the next event, You can also set the first responder programmatically with the
and repeats the process again and again until the application NSWindow's makeFirstResponder: method. Moreover, the first-
terminates. responder object can be a target of an action message sent by an
NSControl, such as a button or a matrix. Programmatically, you do
The objectthat is "appropriate" for an event depends on the type this by sending setTarget: to the NSControl (or its cellI with an
of event. NSApp sends most event messages to the NSWindow in argument of nil. You can do the same thing in Interface Builder by
which the user action occurred. If the event is a keyboard or making a target/action connection between the NSControl and
mouse event, the NSWindow forwards the message to one ofthe the First Responder icon in the Instances display of the nib file
objects in its view hierarchy: the NSView within which the mouse window.
was clicked or the key was pressed. If the NSView can respond
to the event-that is, it accepts first responder status and defines Recall that all NSViews of the application, as well as all
an NSResponder method corresponding to the event message- NSWindows and the application object itself, inheritfrom
it handles the event. NSResponder, which defines the default message-handling
behavior: events are passed up the responder chain. Many
Application Kit objects, of course, override this behavior, so
events are passed up the chain until they reach an objectthat
does respond.
163
Chapter 4 To Do Tutorial
The ToDo application has an inspector panel that allows users to inspect and set
the attributes of the currently selected ToDoItem. The inspector panel has its
own controller: ToDolnspector. While showing you how to create the inspector
panel and ToDolnspector, this section focuses on four things:
In Interface Builder
164
Creating and Managing an Inspector (ToDolnspector)
165
Chapter 4 To Do Tutorial
Instantiate ToDolnspector. notiNiew The box in the off-screen panel containing the fields and controls
Connect the ToDolnspector related to notification of impending items
object to its outlets and as the
target of action messages (see reschedView The box in the off-screen panel containing the fields and controls
tables at right). related to rescheduling items
Connect ToDolnspector and the inspPopUp The pop-up button on the inspector panel
inspector panel via the panel's
delegate outlet. inspDate The uneditable text field next to the "Date" label
Close both panels. inspltem The uneditable text field next to the "Item" label
Save ToDolnspector.nib.
inspNotifHour The first field after the "lime" label
Create source-code files for
ToDolnspector and add them to inspNotifMinute The second field after the "lime" label
the project.
inspNotifAMPM The matrix holding the "AM" and "PM" radio buttons
switchChecked: The matrix of switches inthe "When to Notify" box, the AM-PM matrix,
the "Task Completed" switch, and the matrix of switches in the "When
to Reschedule" switches.
166
Creating and Managing an Inspector (ToDolnspector)
Open ToDolnspector.h. /* */
Type the declarations shown at - (void)setCurrentltem: (ToDoltem *)newltem;
right (ellipses indicate existing - (ToDoltem *)currentltem;
declarations). - (void)updatelnspector: (ToDoItem *)item;
@end
Import ToDoltem.h and
ToDoDoc.h.
The ToDoInspector class has a utility function for clearing switches set in a
matrix and defines constants for the tags assigned to the pop-up buttons.
1. Instead of copying the new value, this implementation retains it. By retaining, it
shares the current ToDoltem with the document controller (ToDoDoc) that has
sent the setCurrentltem: message, enabling both objects to update the same
ToDoltem simultaneously.
2. Updates the current display of the inspector with the appropriate values of
the new ToDoltem.
167
Chapter 4 To Do Tutorial
This method switches the current inspector display according to the pop-up
button users select; it does this switching by replacing the dummyView's content
view. Toward this end, the method:
1. Gets the panel's content view and the tag of the selected pop-up button.
2. Assigns to the newView local variable the off-screen box object corresponding
to the tag of the selected pop-up button.
3. Returns if the selected display is already on the inspector panel. The subviews
message returns an array of all su bviews of the inspector panel's control view,
and the containsObject: message determines if the chosen display is among
these subviews.
5. Updates the inspector with the current item; this item hasn't changed, but the
display is new and so the set of instance variables to be displayed is different.
The display message forces a re-draw of the inspector panel's views.
168
Creating and Managing an Inspector (ToDolnspector)
break;
case reschedTag:
break;
169
Chapter 4 To Do Tutorial
The updatelnspector: method is a long one, so we'll approach it in stages. This first part updat(
the common data elements (item name and date) and, if the selected display is for notification
updates that display.
3. If the tag of the selected pop-up button is notifTag, updates the associated
inspector display. This task starts by converting the due time from seconds to
hour, minute, and PM boolean values and then setting the appropriate fields
and button matrix with these values.
4. Sets the appropriate switch in the "When to Notify" matrix. It starts with the
difference (in seconds) between the time the item is due and the time the
item notification is sent. It calls clearButtonMatrixO to turn all switches off and
then, in a switch statement, sets the switch corresponding to the difference in
value between seconds from midnight before due and before notification.
Before You Go On - - - - - - - - - - - - - - - - - - - - -
Update the Notes display: Add code to update the inspector's Notes display from the
information in the ToDoltem passed into updatelnspector:. (Check the
documentation on NSText to see what method is suitable for this.) The selected
pop-up button must have notesTag assigned to it. Also put the cursor at the start
of the text object by selecting a "null" range.
Note that tutorial omits the rescheduling logic of the ToDo application,
including the code in this method that would update the "Reschedule" display.
Rescheduling of ToDoltems is reserved as an optional exercise for you at the
end of this tutorial.
170
Creating and Managing an Inspector IToDolnspector)
As you've most likely noticed, the updatelnspector: method calls the function
clearButtonMatrixO, which resets the states of all button cells in a switch matrix to
NO. This function has a counterpart, indexOfSetCellO, that returns the index of the
currently selected switch.
The cells message returns the cells of the matrix as an array; the count message
determines the number of cells.
171
Chapter 4 To Do Tutorial
172
Creating and Managing an Inspector (ToDolnspector)
When users click a switch button on any inspector display, or when they click
one of the AM-PM radio buttons, the switchChecked: method is invoked. This
method works by evaluating the sender argument: the sending object.
1. If sender is the radio-button matrix (AM-PM), gets the new time due by calling the
utility function ConvertTimeToSeconds(j, sets the current item to have this new value,
marks the document as edited, and then sends updateMatrix to the document
controller to have it display this new time.
2. If sender is the "When to Notify" matrix, gets the index of the selected cell
and the seconds until the item is due. It evaluates the first value in a switch
statement and uses the second value to set the current item's new secsUntilNotif
value. It also sets the window to indicate an edited document.
3. If sender is the "Task Completed" switch, sets the status of the current item
to "complete," sets the window to indicate an edited document, and has the
document controller update its matrices.
Since text fields are controls that send target/action messages, you could also
have switchChecked: respond when data is entered in the fields. However, users
might not press Return in a text field so you can't assume the action message will
be sent. Therefore, it's better to rely upon delegation messages.
173
Chapter 4 To Do Tutorial
long tmpSecs=O;
if ([notif object] == inspNotifHour I I /* 2 */
[notif object] == inspNotifMinute)
tmpSecs = ConvertTimeToSeconds([inspNotifHour intValue],
[inspNotifMinute intValue],
[[inspNotifAMPM cellAtRow:O column: 1] state]);
[currentltem setSecsUntilDue:tmpSecs];
[[[NSApp mainWindow] delegate] updateMatrix];
[[NSApp mainWindow] setDocumentEdited:YES];
else if ([notif object] == inspNotifOtherHours) /* 3 */
if ([inspNotifSwitchMatrix selectedRow] == 4)
[currentltem setSecsUntilNotif: ([inspNotifOtherHours
intValue] * hrlnSecs)];
[[NSApp mainWindow] setDocumentEdited:YES];
1. After editing takes place in the "Notes" text object, this method is invoked, and it
responds by resetting the notes instance variable of the ToDoItem with the
contents of the text object.
2. If the object behind the notification is the hour or minute field of the
"Notifications" display, controlTextDidEndEditing: computes the new due time,
sets the current item to have this new value, and then sends update Matrix to the
document controller to have it display this new time. (This code is almost the
same as that for the AM-PM matrix in the switch Checked: method.)
174
Creating and Managing an Inspector lToDolnspectorl
3. If the object behind the notification is the "Other... hours" text field in the
"When to Notify" box, the method verifies that the "Other" switch is
checked and, if it is, sets the ToDoltem with the new value.
4. Here is another empty rescheduling block of code that you can fill out in a
later exercise.
Now it's time to address two related problems in synchronizing displays of data.
The first is the requirement for the inspector to display the ToDoItem currently
selected in the document. In ToDoDoc.m write code that communicates this object
to ToDoInspector through notification.
calendarMatrix:didChangeToDate: nil
calendarMatrix:didChangeToMonth:year: nil
175
Chapter 4 To Do Tutorial
The selectltem: method selects the text field identified in the argument and posts
a notification to the inspector with the associated ToDoltem as argument (or nil
if the text field is empty). Next, invoke selectltem: in these methods:
Method Comment
The use of notifications to open Doc: Invoke after opening a document, with an argument of 0
communicate changes in one (ToDoController.m)
object to another object in an
application is a good design showl nspector: Invoke afer opening the inspector panel, passing in the index of the
strategy because it removes the selected row in the document. (ToDoController.m). Hint: Get the
current document by querying for the delegate of the main window,
need for the objects to have
then obtain the selected row from this object.
specific knowledge of each other.
It also makes the application
more extensible, because any
number of objects can also
become observers of the changes. Before You Go On - - - - - - - - - - - - - - - - - - - - -
However, there is a way for
ToDoDoc to locate Make ToDolnspector respond to the notification. Declare a notification method
ToDolnspector reliably using the named currentltemChanged: and implement it to set the current item with the object
various relationships established
value of the notification. Then, in init or awakeFromNib, add ToDolnspector as an
within the program framework.
See page 189 to see how this is observer of the ToDoltemChangedNotification, identifying currentltemChanged:
done. as the method to be invoked.
176
Creating and Managing an Inspector (ToDolnspector)
1. Sets the hour and minute fields to accept only positive integer values.
3. Makes the Notification display the start-up default, using the index of the
"Notification" cell rather than its title to improve localization. Then it sets self
to be the delegate of the text object.
177
Chapter 4 To Do Tutorial
Besides responding to events, all objects that inherit from programmatically move, scale, and rotate the NSView by
NSView can render themselves on the screen. They do this reference to its frame (setFrameOrigin:, setFrameSize:, and so
rendering through image composition and PostScript drawing. on).
NSViews draw themselves as an indirect result of receiving the
display message (or a variant of display); this message is sent To draw efficiently, the NSView must have its frame rectangle
explicitly or through conditions that cause automatic display. The translated into its own coordinate system. This translated
display message leads to the invocation of an NSView's rectangle, suitable for drawing, is called the bounds. The bounds
drawRect: method and the drawRect: methods of all subviews of rectangle usually specifies exactly the same area as the frame
that NSView. The drawRect: method should contain all code rectangle, but it specifies that area in a different coordinate
needed to redraw the NSView completely. system. In the default coordinate system, an NSView's bounds is
An NSView can be automatically displayed when: the same as its frame, except that the point locating the frame
becomes the origin of the bounds (x = 0.0, y = 0.0). The x- and y-
• Users scroll it (assuming it supports scrolling). axes of the default coordinate system run parallel to the sides of
• Users resize or expose the NSView's window. the frame so, for example, if you rotate the frame the default
coordinate system rotates with it.
• The window receives a display message or is automatically
updated. This relationship between frame and bounds has several
• For some Application Kit objects, when an attribute changes. implications important in drawing and compositing.
An NSView represents a context within which PostScript drawing • Each NSView's coordinate system is a transformation of its
can take place. This context has three components:
superview's.
• A rectangular frame within a window to which drawing is
clipped. • Drawing instructions don't have to account for an NSView's
location on the screen or its orientation.
• A coordinate system
• The current PostScript graphics state • Changes in a superview's coordinate system are propagated to
its subviews.
Frame and Bounds
NSView allows you to flip coordinate systems (so the positive y-
An NSView's frame specifies the location and dimensions of the
axis runs downward) and to otherwise alter coordinate systems.
NSView in terms of the coordinate system of the NSView's
superview. It is a rectangle that encloses the NSView. You can
~t:::.:/<··';:·:::·:::·:::·:::·:::·:::·:::··';:·:::·-':'·· Focusing
Frame ,:otated within its l~..····· . . . ..
178
Creating and Managing an Inspector !ToDolnspector)
PostScript Drawing NSViews can simply display an image within their frame. You
usually composite an image using NSlmage's
In OpenStep, NSViews draw themselves by sending binary- compositeToPoint:operation: (or a related method).
encoded PostScript code to the Window Server. The Application
Kit and the Display PostScript frameworks provide a number of C- NSlmage allows you to copy images into your user interface. It
language functions that send PostScript code to perform common uses various subclasses of NSlmageRep to store the multiple
drawing tasks. You can use these functions in combinations to representations of the same image-color, grayscale, TIFF, EPS,
accomplish fairly elaborate drawing. and so on-and chosing the representation appropriate for a
given type or display. NSlmage can read image data from a
The Application Kit has functions and constants, declared in
bundle (including the application's main bundle), from the
NSGraphics.h, for (among other things):
pasteboard, or from an NSData object.
• Drawing, filling, highlighting, clipping and erasing rectangles
Compositing allows you to do more than simply copy images.
• Drawing buttons, bezels, and bitmaps Compositing builds a new image by overlaying images that were
• Computing window depth and related display information previously drawn. It's like a photographer printing a picture from
two negatives, one placed on top of the other. Various
You also call OpenStep-compliant drawing routines defined in compositing operators (NSCompositingOperation, defined in
dpsOpenStep.h. These routines (such as DPSDoUserPath()) draw dpsOpenStep.h) determine how the source and destination
a specified path. In addition, you can call the functions declared images merge.
in psops.h. These functions correspond to single PostScript
operators, such as PSsetgrayO and PSfilIO.
Source Image Destination Image
You can also write and send your own custom PostScript code.
pswrap is a program (in /usrlbin) that converts PostScript code
into C-Ianguage functions that you can call within your
applications. It is an efficient way to send PostScript code to the
Window Server. The following pswrap function draws grid lines:
00
0
Operation Destination After
defineps DrawGrid(float width, height, every)
5 6 div setgray
o every width { Copy Source image overlays
o move to 0 height rlineto stroke
} for
o every height {
O
o exch move to width 0 rlineto stroke Source image wherever
} for Source it is opaque, and
endps Over destination image
elsewhere.
Compose the function in a file with a .psw extension and add itto
Destination image
the Other Source project "suitcase" in Project Builder. When you
next build your project, Project Builder runs the pswrap program,
generating an object file and a header file (matching the file name
of the .psw) file, and links these into the application. To use the
code, import the header file and call the function when you want
D
Out
l' r
es Ina Ion
O wherever it is opaque but
source image is
transparent, and
transparent elsewhere.
to do the drawing: You can achieve interesting effects with compositing when the
initial images are drawn with partially transparent paint.
DrawGrid(5.0, 5.0, 1.0);
(Transparency is specified by coverage, a PostScript indicator of
paint opacity.lln a typical compositing operation, paint that's
Compositing Images
partiallytransparentwon'tcompletely coverthe image it's placed
The other technique NSViews use to render their appearance is on top of; some of the other image will show through. The more
image compositing. By compositing (with the SOVER operator) transparent the paint is, the more of the other image you'll see.
179
Chapter 4 To Do Tutorial
If you want an object that draws itself differently than any other Handling Events
Application Kit object, or responds to events in a special way, you
should make a custom subclass of NSView. Your custom subclass In the next section, you'll make a subclass of NSButtonCell that
should complete at least the steps outlined below. uniquely responds to mouse clicks. The way custom NSViews
handle events is different. If you intend your custom NSView to
Note: If you make a custom subclass of any class that inherits from respond to user actions you must do a couple of things:
NSView, and you wantto do custom drawing or event handling, 4 Override acceptsFirstResponderto return YES ifthe NSView is
the basic procedure presented here still applies. to handle selections. (The default NSView behavior is to return
NO.)
Interface Builder 5 Override the desired NSResponder event methods
(mouseDown:, mouseD ragged:, keyDown:, etc.)
Define a subclass of NSView in Interface Builder. Then
generate header and implementation files. - (void)mouseDown: (NSEvent *)event
if (([event modifierFlags] &
2 Drag a CustomView object from the Views palette onto a NSControlKeyMask){
window and resize it. Then, with the CustomView object still
doSomething();
selected, choose the Custom Class display ofthe Inspector
panel and select the custom class. Connect any outlets and
actions.
You can query the NSEvent argument for the location of the user
action in the window, modifier keys pressed, character and key
codes, and other information.
Drawing
• Composite an NSlmage.
180
Subclass Example: Overriding and Adding Behavior (ToDoCell)
The ToOoCell class, which you will implement in this section, generates cells
that behave as three-state buttons. These buttons also display the time an item
is due.
181
Chapter 4 To Do Tutorial
Add header and implementation enum _ToDoButtonState {notDone=O, done, deferred} ToDoButtonState;
files to the project.
@interfaee ToDoCe11 NSButtonCel1
Chose New in Project from the
File menu. ToDoButtonState triState;
In the New File In ToDo panel, NSlmage *doneImage, *deferredImage;
select the Class suitcase, click NSDate *timeDue;
Create header, type "ToDoCeil"
after Name, and click OK. - (void)setTriState: (ToDoButtonState)newState;
- (ToDoButtonState) triState;
2 Complete ToDoCell.h. - (void)setTimeDue: (NSDate *)newTime;
- (NSDate *)timeDue;
Make the superclass @end
NSButtonCel1.
Add the instance-variable and
method declarations shown at The triState instance variable will be assigned ToDoButtonState constants as
right. values. The NSlmage variables hold the "X" and check mark images that
Add the enum constants for state represent statuses of completed and deferred (that is, rescheduled for the next
values (as shown). day). The timeDue instance variable carries the time the item is due as an
NSDate; for display, this object will be converted to a string.
return self;
1. Sets some superc1ass (NSButtonCell) attributes, such as button type, image and
text position, font of text, and border.
2. Through NSBundle's pathForlmageResource:, gets the path name for the cell
images and creates and stores the images using the pathname.
182
Subclass Example: Overriding and Adding Behavior (ToDoeelll
- (void)setState: (int)val /* 2 */
- (int)state /* 3 */
if (triState == deferred)
return (int)done;
else
return (int)triState;
1. If the new value for triState is one greater than the limit (deferred), reset it to zero
(notDone); otherwise, assign the value. The reason behind this logic is that (as
you'll soon learn) when users click a ToDoCell, setTriState: is invoked with an
argument one more than the current value. This way users can cycle through the
three states of ToDoCell.
2. Overrides setState: to be a null method. The reason for this override is that
NSCell intervenes when a button is clicked, resetting state to zero (NO).
This override nullifies that effect.
3. Overrides state to return a reasonable value to client objects that invoke this
accessor method.
183
Chapter 4 To Do Tutorial
case done:
[self setlmage:donelmage]i
break;
case deferred:
[self setlmage:deferredlmage];
break;
This portion of code handles the display of the cell's image by doing the
following:
2. In a switch statement, evaluates the tri-state argument and sets the cell's
image appropriately (setlmage: is an NSButtonCell method).
3. Sends updateCell: to the control view of the cell's control (a matrix) to force a
re-draw of the cell.
184
Subclass Example: Overriding and Adding Behavior IloDoCell1
if (flag == YES) {
[self setTriState: ([self triState] +1) ] ;
When you create your own cell subclass, you might want to override some
methods that are intrinsic to the behavior of the cell. Mouse-tracking methods,
inherited from NSCell, are among these. You can override these methods to
incorporate specialized behavior when the mouse clicks the cell or drags over it.
ToDoCell overrides these methods to increment the value of triState.
else
timeDue = nil;
[self setTitle:@"-->"];
The setlimeDue: method is similar to other "set" accessor methods, except that it
handles interpretation and display of the NSDate instance variable it stores. If
newlime is a valid object, it uses NSDate's
descriptionWithCalendarFormat:timeZone:locale: method to interpret and format the
185
Chapter 4 To Do Tutorial
date object, then displays the result with setTitle:. If newTime is nil, no due time has
been specified, and so the method sets the title to "-->".
You've now completed all code required for ToDoCell. However, you must now
"install" instances of this class in the To Do interface.
This block of code substitutes a ToDoCell for each cell in the left matrix
(markMatrix) you created for the To Do interface. It creates a ToDoCell, sets its
target and action message, then inserts it into the markMatrix by invoking
NSMatrix's putCe":atRow:column: method.
Finally, you must implement the action message sent when the matrix of
ToDoCells is clicked. (This response to mouse-down is for objects external to
To DoC ell, while the mouse-tracking response sets state internally.)
This method gets the ToDoCell that was clicked and the object in the
corresponding text field. If that object is a ToDoltem, the method updates its
status to reflect the state of the ToDoCell. It then marks the window as
containing an edited document.
186
Setting Up limers for Notification Messages
Here's how it works: Each ToDoltem with a "When to Notify" switch (other
than "Do not notify") selected in the inspector panel-and hence has a positive
secsUntilNotif value-has a timer set for it. If a user cancels a notification by
selecting "Do not notify," the document controller invalidates the timer. When
a timer fires, it invokes a method that displays the attention panel, selects the
"Do not notify" switch, and sets secsUntilNotif to zero.
Implementing the timer feature takes place entirely in Project Builder, but
extends across several classes.
1. Tests the ToDoltem to see if it has a positive secsUntilNotif value and, if it has,
composes the time the notification should be sent.
2. Creates a timer and schedules it to fire at the notification time, and instructs
it to invoke itemTimerFired: when it fires. It also sets the ~imer in the ToDoltem.
187
Chapter 4 To Do Tutorial
Before You Go On - - - - - - - - - - - - - - - - - - - - -
Implement resetNotifSwitch: You haven't written ToDoInspector's resetNotifSwitch
method yet, so do it now as an exercise. It should select the "Do not Notify"
switch after turning off all switches in the matrix, and then force a redisplay of
the switch matrix~
Next you must send setTim~rForltem: at the right place and time, which is
ToDoInspector, when the user alters a "When to Notify" value.
188
Setting Up Timers for Notification Messages
4 Send the message that sets the [[[NSApp mainWindow] delegate] setTimerForltem:currentltem];
timer at the right times
Open ToDolnspector.m.
Instead of archiving an item's NSTimer, To Do re-creates and resets it when the
In switch Checked:, insertthe
application is launched.
setTimerForltem: message at
right afterthe switch statement
evaluating which "When to if ([self activeDays])
Notify" switch was checked. dayenum = [[self activeDays] keyEnumerator];
In controITextDidEndEditing:, while (itemDate = [dayenum nextObject])
insert the same message atthe NSEnumerator *itemenum;
end of the block related to the ToDoltem *anltem=nil;
inspNotifOtherHours variable. NSArray *itemArray = [[self activeDays]
objectForKey:itemDate] ;
5 When the application is itemenum = [itemArray objectEnumerator];
launched, reset item timers. while ((anltem = [itemenum nextObject]) &&
[anltem isKindOfClass: [ToDoltem class]] &&
Add the code at right, below, to [anltem secsUntilNotif]) {
ToDoDoc's initWithFile: method. [self setTimerForltem:anltem];
This block of code traverses the activeDays dictionary, evaluating each ToOoItem
within the dictionary. If the ToOoItem has a positive secsUntilNotif value, it
invokes setTimerForltem: to have a timer set for it.
A run loop-an instance of NSRunLoop- elapsed and then fires, sending a specified
manages and processes sources of input. message to a specified object. For example,
These sources include mouse and keyboard you could create an NSTimer that
events from the window system, file periodically sends messages to an object,
descriptor, inter-thread connections asking it to respond if an attribute changes.
(NSConnection), and timers (NSTimer).
Applications typically won't need to either NSTimer objects work in conjunction with
create or explicitly manage NSRunLoop NSRunLoop objects. NSRunLoops control
objects. When a thread is created, an loops that wait for input, and they use
NSRunLoop object is automatically created NSTimers to help determine the maximum
for it. The NSApplication object creates a amount of time they should wait. When the
. default thread and therefore creates a NSTimer's time limit has elapsed, the
default run loop.
NSRunLoop fires the NSTimer (causing its
NSTimer creates timer objects. A timer message to be sent), then checks for new
object waits until a certain time interval has input.
189
Chapter 4 To Do Tutorial
1. When you choose New from the Document menu, the application creates a new
To Do document and selects the current day.
2. Enter a few items. Click a new day on the calendar and enter a few more
items. Click the previous day and notice how the items you entered reappear.
3. Choose Inspector from the main menu. When the inspector appears, click an
item and notice how the name and date of the item appears in the top part of
the inspector. Enter due times for a couple items, and some associated notes.
Note how the times, as you enter them, appear in the Status/Due column of
the To Do document. Click among a few items again and note how the
Notifications and Notes displays change.
4. Click a Status/Due button; the image toggles among the three states. Then,
with an item that has a due time, select a notification time- that has already
passed. The application immediately displays an attention panel with a
notification message. When you dismiss this panel, To Do sets the
notification option to "Do not notify."
s. Click the document window and respond to the attention panel by clicking
Save. In the Save panel, give the document a location and name. When the
window has closed, chose Open from the Document menu and open the
same document. Observe how the items you entered are redisplayed.
Optional Exercises
You should be able now to supplement the To Do application with other
features and behaviors. Try some of the following suggestions.
190
Build, Run, and Extend the Application
Implement Rescheduling
ToDo's Inspector pane has a Rescheduling display that does almost nothing
now. Implement the capability for rescheduling items by the period specified.
191
Chapter 4 To Do Tutorial
192
~
,:"'
ll",,~
'." ....,"',',"'.' .. ',:,",'"
,'I, II'"
Chapter 5
Where To Go From Here
Sections
Information
Professional Services
195
Where To Go From Here
196
If you've completed the tutorials in this book, you're no longer a novice in
OpenStep development. You should be able to attempt an OpenStep
application on your own, and should expect to carry it off successfully. However,
there is much more for you to learn (or to learn in greater detail). There will
probably be times when you'll need information or help. You might want to
pursue a training course, you might want to order a NeXT product, or you might
have questions you'll need answered, or problems you'll need resolved.
This section points you toward these sources of information and help. It also
includes descriptions of tools, resources, and documentation frequently used in
application development.
197
Chapter 5 Where To Go From Here
Name Description
FileMerge Visually compares the contents of two files or two directories. You can use
FileMerge, for example, to determine the differences between versions of the
same source code file or between two project directories. You can also use it
to merge changes.
Icon Builder A simple graphics program for creating application and document icons.
Sampler Analyzes performance problems with your application by sampling the call
stack of your program over a period. (In /NextDeveloper/Demos)
Name Description
InterfaceBuilder Creation of custom static (compiled) palletes for use in Interface Builder
198
Programming Tools and Resources
fixPrecomps Creates a precompiled header file for each of the major /usr/bin
frameworks.
199
ChapterS Where To Go From Here
The TOPS scripts you run to perform the conversion process, along with 3.3
header files and intermediate frameworks, are located at
/NextDeveloper/OpenStepConversion. The OPENSTEP Conversion Guide provides
instructions on using the scripts as well as summaries of API changes and
conversion tips.
Name Comments
Colors Bundles containing the default set of color binaries for the Colors panel
Fonts Default set of system fonts"including AFM, bitmap, and outline versions
PS2Resources PostScriptfiles containing calibrated color space and color rendering, printing
halftones, and gray-shading patterns
Sounds Default sound files (".snd") such as Cricket, Ping, and Rooster
200
Information
Information
NeXT publishes documentation for users, developers, and system
administrators. It also, through its Professional Services, offers customers access
to NeXTanswers, an automated document retrieval system.
Accessing Documentation
On OPENSTEP for Mach, several tools help you access documentation:
Developer Documentation
The core set of documentation for OPENSTEP is the reference specifications
describing OpenStep classes, protocols, methods, functions, types, and
constants.The reference documentation for the two major OpenStep
frameworks is stored within the frameworks:
/NextLibrary/Frameworks/AppKit.framework!Resources/English.1proj/Documentation
/NextLibrary/Frameworks/Foundation.framework/Resources/English.lproj/Documentation
201
Chapter 5 Where To Go From Here
OPENSTEP Development: Using Project Builder, Interface Builder, and other TasksAndConcepts
Tools and Techniques tools in program development (as DevEnvGuide)
202
Professional Services
Professional Services
NeXT provides training, consulting, and technical support for its customers. For
more information on the programs described below, call1-800-955-NEXT
(U.S.), +1415-780-2922 (elsewhere in North America), +44 181-565-0005
(Europe). You can also visit the Professional Services section of the NeXT
website at http://www.next.com/Services for up-to-date information on current
programs in technical education, consulting, and support.
Education
Courses offered by NeXT's Training department give developers of all
backgrounds a strong foundation in the fundamentals of OpenStep application
development. This background is critical to the successful implementation of
OpenStep programs by development teams.
203
Chapter 5 Where To Go From Here
NeXTanswers
NeXTanswers is an automated retrieval system that gives customers access to
the latest product information, technical documents, drivers, and other software.
You can access NeXTanswers through NeXT's website (http://www.next.com) and
by:
• BBS: Call 415-780-Z965, log in as "guest", and go to the Files section. From
there you can download NeXTanswer documents.
Requests sent to NeXTanswers are answered electronically, and are not read or
handled by a person. It does not answer your questions or forward your requests.
204
Ordering NeXT Products and Services
4. Contact one of the sales offices below, which can also furnish you with
product brochures, data sheets, and other information.
Worldwide Headquarters
900 Chesapeake Drive
Redwood City, CA 94063
Tel: (415) 366-0900
Fax: (415) 780-3714
205
Chapter 5 Where To Go From Here
Chicago Office
311 South Wacker Drive, 22nd Floor
Suite 2250
Chicago, IL 60606
Tel: (312) 697-4500
Fax: (312) 697-4501
Canadian Office
4370 Dominion Street
Suite 400
Burnaby, British Columbia
Canada V5G - 4L7
Tel: (604) 451-1877
Fax: (604) 451-1819
Mexico (MeXT)
Tel: 011 525-530-7278
206
Ordering NeXT Products and Services
140 Japan
Tel: +81-3-5461-7161
Fax: +81-3-5461-7170
207
Chapter 5 Where To Go From Here
208
Appendix A
Obiect-Oriented Programming
Objects
Classes
211
Object-Oriented Programming
212
"Object-oriented programming" has become one of the premier buzzwords in
the computer industry. To understand why, it's important to cut through the
hype and focus on the problem that engendered the object-oriented approach.
procedure' data
data
data
data
data
data
213
Appendix A Object-Oriented Programming
Obiects
An object is a self-contained programmatic unit that combines data and the
procedures that operate on that data. In the Objective-C language, an object's
data comprises its instance variables, and its procedures, the functions that affect
or make use of the data, are known as methods.
Encapsulation
Just as procedures compartmentalize code, objects compartmentalize both code
and data. This results in data encapsulation, effectively surrounding data with the
procedures for manipulating that data.
214
Using this modularity, object-oriented programs can be divided into distinct
objects for specific data and specific tasks. Programming teams can easily parcel
out areas of responsibility among them, agreeing on interfaces to the distinct
objects while implementing data structures and code in the most efficient way
for their specific area of functionality.
Messages
To invoke one of the object's methods you send it a message. A message requests
an object to perform some functionality or to return a value. In Objective-C, a
message expression is enclosed in square brackets, like this:
In this example converter is the object that receives the message, the receiver.
Everything to the right of this term is the message itself; it consists of a method
name and any arguments the method requires. The message received by
converter tells it to convert a temperature in Fahrenheit to Celsius and return that
value .
Often, but not always, messages return values to the sender of the message.
Returned values must be received in a variable of an appropriate type. In the
above example, the variable range must be of type NSRange. Messages that
return values can be nested, especially if those returned values are objects. By
enclosing one message expression within another, you can use a returned value
as an argument or as a receiver without having to declare a variable for it.
The above message nests two other messages, each of which returns a value
used as an argument. The inmost message expressions is resolved first, then the
next nested message expression is resolved, then the third message is sent and
a value is returned to newString.
215
Appendix A Object-Oriented Programming
An Obiect-Oriented Program
Object-oriented programming is more than just another way of organizing data
and functions. It permits application programmers to conceive and construct
solutions to complex programs using a model that resembles-much more so
than traditional programs-the way we organize the world around us. The
object-oriented model for program structure simplifies problem resolution by
clarifying roles and relationships.
-+- Messages
Applications also assign to objects functionality that isn't directly apparent in the
interface, giving each object a different area of responsibility. Some of these
objects might perform very specific computational tasks while others might
manage the display and transfer of data, mediating the interaction between
user-interface objects and computational objects.
216
Polymorphism and Dynamic Binding
Although the purpose of a message is to invoke a method, a message isn't the
same as a function call. An object "knows about" only those methods that were
defined for it or that it inherits. It can't confuse its methods with another object's
methods, even if the methods are identically named.
Each object is a self-contained unit, with its own name space (an name space
being an area of the program where it is uniquely recognized by name). Just as
local variables within a C function are isolated from other parts of a program, so
too are the variables and methods of an object. Thus if two different kinds of
objects have the same names for their methods, both objects could receive the
same message, but each would respond to it differently. The ability of one
message to cause different behavior in different receivers is referred to as
polymorphism.
Polymorphism
217
Appendix A Object-Oriented Programming
Dynamic Binding
~""....
........
........
........
........ ,,
...... :1..
< _________ :,
Dynamic binding even enables applications to deal with new kinds of objects,
ones that were not envisioned when the application itself was built. For
example, it lets Interface Builder send messages to objects such as EOModeler
when it is loaded into the application by means of custom palettes.
Introspection means that an object, even one typed as id, can reveal its class and
divulge other characteristics at run time. Several introspection methods allow
you to ascertain the inheritance relationships of an object, the methods it
responds to, and the protocols that it conforms to.
218
Classes
Some of the objects networked together in an applications are of different kinds,
and some might be of the same kind. Objects of the same kind belong to the
same class. A class is a programmatic entity that creates instances of itself-
objects. A class defines the structure and interface of its instances and specifies
their behavior.
When you want a new kind of object, you define a new class. You can think of a
class definition as a type definition for a kind of object. It specifies the data
structure that all objects belonging to the class will have and the methods they
will use to respond to messages. Any number of objects can be created from a
single class definition. In this sense, a class is like a factory for a particular kind
of object.
Obiect Creation
One of the primary functions of a class is to create new objects of the type the
class defines. For example, the NSButton class creates new NSButton objects
and the NSArray class creates new NSArrays. Objects are created at run time in
a two-step process that first allocates memory for the instance variables of the
new object and then initializes those variables. The following code creates a
new Country object:
219
Appendix A Object-Oriented Programming
instance variables, and do all the other things a Country object does. If you need
other Country objects, you create them in the same way from the same class
definition.
Objects can be typed as id, as in the above example, or can be more restrictively
typed, based on their class. Here, newCountry is typed as a Country object:
The more restrictive typing by class enables the compiler to perform type-
checking in assignment statements.
Inheritance
Inheritance is one of the most powerful aspects of object-oriented programming.
Just as people inherit traits from their forbearers, instances of a class inherit
attributes and behavior from that class's "ancestors." An object's total
complement of instance variables and methods derives not only from the class
that creates it, but from all the classes that class inherits from.
Inheritance . . New
. . Inherited
- - - - - - - - Instance of superclass
, . - - - - - Instance of subclass
Creating a new class is often a matter of specialization. Since the new class
inherits all its superclass's behavior, you don't need to reimplement the things
that work as you want them to. The subclass merely extends the inherited
behavior by adding new methods and any variables needed to support the
additional methods. All the methods and variables defined for-or inherited
by-the superclass are inherited by the subclass. A subclass can also alter
220
superclass behavior by overriding an inherited method, reimplementing the
method to achieve a behavior different from the superclass's implementation.
(The technique for doing this is discussed later.)
NSArray NSString
NSWindow NSApplication
NSTextField NSButton
Other root classes are possible. In NSObject is the root class of this hierarchy, as it is of most Objective-C class
fact, OPENSlEP's Distributed hierarchies. From NSObject, other classes inherit the basic functionality that
Objects makes use of another root
makes messaging work, enables objects to work together, and otherwise invests
class, NSProxy.
objects with the ability to behave as objects. Among other things, the root class
creates a framework for the creation, initialization, de allocation, introspection,
and storage of objects.
As noted earlier, you often create a subclass of another class because that
superclass provides most, but not all, the behavior you require. But a subclass
can have its own unique purpose that does not build on the role of an existing
class. To define a new class that doesn't need to inherit any special behavior
other than the default behavior of objects, you make it a subclass of the
NSObject class. Subclasses of NSObject, because of their general-purpose
nature as objects, are very common in OpenStep applications. They often
perform computational or application-specific functions.
221
Appendix A Object-Oriented Programming
Advantages of Inheritance
Inheritance makes it easy to bundle functionality common to a group of classes
into a single class definition. For example, every object that draws on the
screen-whether it draws an image of a button, a slider, a text display, or a graph
of points-must keep track of which window it draws in and where in the
window it draws. It must also know when it's appropriate to draw and when to
respond to a user action. The code that handles all these details is part of a single
class definition (the NSView class in the Application Kit). The specific work of
drawing a button, a slider, or a text display can then be entrusted to a subclass.
This bundling of functionality both simplifies the organization of the code that
needs to be written for an application and makes it easier to define objects that
do complicated things. Each subclass need only implement the things it does
differently from its superclass; there's no need to reimplement anything that's
already been done.
Any class can be the superclass for a new subclass. Thus inheritance makes
every class easily extensible-those provided by OpenStep, those you create,
and those offered by third party vendors.
Defining a Class
You define classes in two parts: One part declares the instance variables and the
interface, principally the methods that can be invoked by messages sent to
objects belonging to the class, and the other part actually implements those
methods. The interface is public. The implementation is private, and can
change without affecting the interface or the way the class is used.
The basic procedure for defining a class (using Interface Builder) is covered in
the Currency Converter tutorial. However, here is a supplemental list of
conventions and other points to remember when you define a class:
• The public interface for a class is usually declared in a header file (.h
extension), the name of which is the name of the class. This header file can
be imported into any program that makes use of the class.
• The code implementing a class is usually in a file taking the name of the class
and having an extension of .m. This code must be present-in the form of a
framework, dynamic shared library, static library, or the implementation file
itself-when the project containing the class is compiled.
222
• ~lethod declarations and implementations must begin with and - sign or a +
sign. The dash indicates that these methods are used by instances of the class;
a + sign precedes methods that the class object itself uses.
• ~lethod definitions are much like function definitions. Note that methods not
only respond to messages, they often initiate messages of their own-just as
one function might call another.
• A method can also refer to the receiving object as self. This variable makes it
possible for an object, in its method definitions, to send messages to itself.
Overriding a Method
A subclass can not only add new methods to the ones it inherits, it can also
replace an inherited method with a new implementation. No special syntax is
required; all you do is reimplement the method.
Overriding methods doesn't alter the set of messages that an object can receive;
it alters the method implementations that will be used to respond to those
messages. As mentioned earlier, this ability of each class to implement its own
version of a method is referred to as polymorphism.
It's also possible to extend an inherited method, rather than replace it outright.
To do this you override the method but invoke the superclass's same method in
the new implementation. This invocation occurs with a message to super, which
is a special receiver in the Objective-C language. The term super indicates that
an inherited method should be performed, rather than one defined in the
current class.
223
Appendix A Object-Oriented Programming
224
Index
Index
226
Index
227
Index
228
Index
229
Index
230
Index
231
Index
232
Prillted 011 recycled paper
6863.00