Introduction
I think everyone heard about Mac OS X as an operating system for designers and housewives. But I want to tell you about the development tools for OS X, otherwise they write good programs, and nobody knows what.
I’ll say right away that I’m not going to talk about cross-platform frameworks and toolkits (such as Qt) or about creating console applications, and I’ll tell you about what distinguishes Mac OS X from other operating systems in terms of application development, namely Cocoa. I will make a reservation right away that I will try to avoid comparisons with other frameworks, I just want to tell you about Cocoa.
Let's look a little into history. Mac OS X - further development of NextSTEP. NextSTEP was the first OS in which the Objective-C language was very widely used, a large library of ready-made objects was written on it, and like the usual data types - strings, arrays, dictionaries, and objects used to build GUI applications. Therefore, most applications under NextSTEP were written in Objective-C using ready-made objects. This very library has grown into the Cocoa framework.
But it would be an extremely bad decision to include an API almost unknown to anyone in the new OS, so they added 2 more: Classic and Carbon.
')
Classic was created to run Mac OS 9 applications, at the moment it is pointless to consider it, because after the transition to Intel processors, for obvious reasons, Classic was thrown out of the system.
Carbon was created to easily transfer applications from Mac OS 9 to OS X, with the ability to add to the already ready program code new functionality, available only in the top ten. Strangely enough, many applications are still written on Carbon (for example, MS Office for Mac and some Adobe products).
Currently, the Carbon and Cocoa frameworks are being developed in parallel, but from the next Mac OS X release only Cocoa will be developed.
Objective-C is the main language of development for Cocoa, and since in the future all the examples will be in this language, I will tell you about it in the first part. But if you already own Python or Ruby, then you don’t need to study Objective-C, in XCode 3.0 (the development is happy, about it in the next part) the bindings for these languages ​​are distorted.
Objective-C programming language
In addition to the well-known and widespread object extension of the C language - the C ++ language - there is another extension of it - the Objective-C language, which has tremendous simplicity, full compatibility with the C language and a very powerful and expressive object model borrowed from the Smalltalk language.
The language was coined by Brad Cox (Brad Cox) in the early 80s of the last century. Cox's goal was to create a language that supports the concept of software IC. This concept refers to the ability to assemble programs from ready-made components (objects), just as complex electronic devices can be easily assembled from a set of ready-made integrated circuits (IC, integrated curcuits). At the same time, such a language should be simple enough and based on the C language to facilitate the transition of developers to it.
One of the goals was also the creation of a model in which the classes themselves are also full-fledged objects, introspection and dynamic processing of messages would be supported.
The resulting Objective-C language was extremely simple - it would take only a few days for C-programmer to master it. It is precisely an extension of the C language - the C language simply adds new features for object-oriented programming. At the same time, any program in C is a program in Objective-C (for C ++, this is not true).
Another feature of the language is that it is message-oriented while C ++ is function-oriented. This means that in it the method calls are interpreted not as a function call (although it usually comes down to it), namely, sending a message (with a name and arguments) to an object, just like it happens in Smalltalk. This approach gives a number of advantages - so any message can be sent to any object. An object can, instead of processing a message, simply send it to another object for processing (the so-called delegation), in particular, this is how you can easily implement distributed objects (that is, objects located in different address spaces and even on different computers). Binding of the message to the corresponding function occurs directly at the execution stage.
Objective-C language supports work with meta-information - thus, an object can be asked to directly ask its class, the list of methods (with the types of arguments passed) and instance-variables, to check whether the class is a descendant of the given and whether it supports the specified protocol, etc. P.
The language has normal protocol support (i.e. the concept of the object interface and the protocol are clearly separated). For objects, inheritance (non-multiple) is supported, for protocols, multiple inheritance is supported. An object can be inherited from another object and several protocols at once (although this is rather not protocol inheritance, but its support).
At the moment, Objective-C is supported by the gcc compiler. Quite a lot in the language is transferred to the runtime library and is highly dependent on it. A minimal version of such a library is supplied with the gcc compiler. You can also download the free runtime library from Apple: Apple's Objective-C runtime. These two runtime libraries are quite similar (mostly the difference is in the names of the methods), although I will continue to focus on the runtime library from Apple.
Language syntax
In Objective-C, a special type
id is used to designate objects. A variable of type
id is actually a pointer to an arbitrary object. To denote a null pointer to an object, the constant
nil is used . Speaking of id: the Doom game engine was developed on Next workstations, so there may be a connection between the id type and the idSoftware name.
In this case, instead of
id, you can use a more familiar designation with an explicit indication of the class. In particular, the latter allows the compiler to perform some verification of the message support by objects — if the compiler cannot make a conclusion about the object’s support for this message, it will give a warning (not an error!). Thus, the language supports type checking, but in a non-strict form (i.e., found inconsistencies are returned as warnings, not errors).
The following syntax is used to send messages:
[receiver message];
The message may also contain parameters:
[myRect setOrigin: 30.0: 50.0];
In this example, the name of the method (message) is setOrigin ::. Note that each argument passed corresponds to exactly one colon. In this example, the first argument has a label (the text before the colon), and the second is not.
Objective-C language allows you to label each argument, which significantly increases the readability of the code and reduces the likelihood of passing the wrong parameter.
[myRect setWidth: 10.0 height: 20.0];
In this example, setWidth: height: appears as the message name.
It also supports the ability to pass an arbitrary number of arguments in the message:
[myObject makeGroup: obj1, obj2, obj3, obj4, nil];
Like functions, messages can return values, while, unlike C, the type returned by default is id.
float area = [myRect area];
The result of one message can be immediately used in another message:
[myRect setColor: [otherRect color]];
As already mentioned, in Objective-C, classes themselves are objects. The main task of such objects (called class objects) is to create instances of this class. In this case, the class name itself plays a dual role - on the one hand, it acts as a data type (that is, it can be used to describe pointers to objects of this class). On the other hand, the class name can act as an object to which a message is sent (the class name can only participate as a receiver in messages). In Objective-C, there is no built-in type for Boolean values; therefore, this type is usually introduced artificially. Next, I will use the Boolean type for boolean values ​​with possible YES and NO values ​​(IMHO is clearer, but not as “politically correct” as true / false).
Creating new classes
All new directives to the compiler in the Objective-C language begin with the symbol @. As in C ++, the class description and its implementation are separated (usually the description is placed in header files with the h extension, and implementations are placed in the files with the m extension).
The following is the general structure for describing a new class:
@interface ClassName: SuperClass
{
instance variable declarations
}
method declarations
end
In Apple’s runtime version, all classes have a common ancestor — the NSObject class, which contains a number of important methods. The description of variables is no different from the description of variables in structures in the C language:
@interface Rect: NSObject
{
float width;
float height;
BOOL isFilled;
NSColor * color;
}
end
Each description begins with a plus or minus sign. The plus sign means that this method is a class method (that is, it can only be sent to a class object, not to instances of this class). In fact, class methods are analogous to static methods in classes in the C ++ language. The minus sign is used to denote the methods of objects - instances of this class. Note that in Objective-C all methods are virtual, i.e. can be overridden.
The following are descriptions of possible methods for the class Rect.
@interface Rect: NSObject
{
float x, y;
float width;
float height;
BOOL isFilled;
NSColor * color;
}
+ newRect;
- (void) display;
- (float) width;
- (float) height;
- (float) area;
- (void) setWidth: (float) theWidth;
- (void) setHeight: (float) theHeight;
- (void) setX: (float) theX y: (float) theY;
end
Please note that the name of the method may coincide with the name of the instance variable of this class (for example, width and heigh).
The type of the value returned by the method is indicated in parentheses immediately after the plus or minus sign (but before the method name). If the type is not specified, it is considered that the value of type id is returned. Next comes the name of the method, where after each colon the type of the argument (in parentheses) and the argument itself are specified. The Objective-C language also allows one of the following descriptors to be specified for method arguments — oneway, in, out, inout, bycopy, and byref. These descriptors are used to specify the direction of data transmission and the method of transmission.
A method that accepts an arbitrary number of parameters can be described as follows:
- makeGroup: (id) object, ...;
To connect the header file in Objective-C, instead of the
#include directive, the
#import directive is used, which is completely analogous to
#include , but it guarantees that the data file will be connected only once.
The implementation of the class methods is as follows:
#import "ClassName.h"
@implmentation ClassName
method implementations
end
The following is an example of the implementation of the methods of the class Rect, described earlier.
#import "Rect.h"
@implmentation Rect
+ newRect {
Rect * rect = [[Rect alloc] init];
[rect setWidth: 1.0f];
[rect setHeight: 1.0f];
[rect setX: 0.0fy: 0.0f];
}
- (float) width {return width; }
- (float) height {return height; }
- (float) area {return [self width] * [self height]; }
- (void) setWidth: (float) theWidth {width = theWidth; }
- (void) setHeight: (float) theHeight {height = theHeight; }
- (void) setX: (float) theX y: (float) theY {
x = theX;
y = theY;
}
end
As can be seen from the example above, all instance variables are available in the methods. However, as in C ++, it is possible to control the visibility of variables (the visibility of methods cannot be controlled) using the directives
private ,
protected and
public (acting in exactly the same way as C ++).
How the messaging engine works
The compiler translates every message package, i.e. construction of the [object msg] type in the function call objc_msgSend.
This function takes as its first parameter a pointer to the recipient object of the message, the second parameter is the so-called. selector that serves to identify the message being sent. If the message contains arguments, they are also passed to objc_msgSend as the third, fourth, etc. options. Next, the search for a suitable function among the functions of this class, if such is not found, then among the functions of the parent class, if not found there, among the functions of the parent class of the parent class (:-)), etc. If a method (i.e., the corresponding function) is found, then it is called with the transfer of all necessary arguments.
Otherwise, the object is given the last chance to process the message before calling an exception — the message selector along with the parameters is wrapped in a special object of the NSInvocation type and the object is sent a message forwardInvocation: where the object of the NSInvocation class is used as a parameter.
If the object supports forwardInvocation:, then it can either process the message sent by itself, or send it to another object for processing:
- (void) forwardInvocation: (NSInvocation *) anInvocation
{
if ([someOtherObject respondsToSelector: [anInvocation selector]])
[anInvocation invokeWithTarget: someOtherObject];
else
...
}
Creating and destroying objects
In Objective-C itself, there are no special commands for creating and destroying objects (like new and delete). This task falls on the runtime library and is implemented using the mechanism for sending messages.
Creating a new object is divided into two steps - memory allocation and object initialization. The first step is implemented by the class alloc method (implemented in the NSObject class), which allocates the necessary amount of memory (this method is used to allocate memory not only for objects of the NSObject class, but also for any class inherited from it). In this case, the allocated memory is reset and a pointer is written to the isa attribute on the class object of the corresponding class.
Note that the alloc message is sent to the class object of the required class and this message returns a pointer to the memory allocated for the object.
Actually, the object itself is initialized (i.e. setting the values ​​of its instance variables, allocating additional resources, etc.) by other methods, according to tradition, the names of these methods begin with init. Usually such a message is sent immediately after the alloc message, to the address returned by this message.
id anObject = [[Rectangle alloc] init];
When creating a new class, there is usually no need to override the alloc method, but the need to override the init method occurs quite often (although in many cases you can rely on memory reset by the allocator).
Please note that the init method (s) is an ordinary method that does not stand out among the rest (unlike C ++, where the constructor is a special method from which, for example, the address cannot be taken). Therefore, when creating a new class and init method, calling the overridden init method (using [super init]) must be made explicitly at the very beginning of the method.
Mac OS X (like NextStep) uses reference counting to control the lifetime of objects - each object contains a counter within itself, which, when created, is set to one.
Sending a retain message to an object increases the value of this counter by one (so all the container classes of the Foundation library, when the object is placed in them, send him a retain message). It is a common practice to send a message to a retain object by all parties (objects) interested in it, i.e. if you memorize an object reference, you should send him a retain message.
When an object ceases to be needed, it simply sends a release message. This message reduces the counter value by one and, if this value is less than one, destroys this object.
Before an object is destroyed, a dealloc message is sent to it, allowing the object to de-initialize itself. At the same time, this is also a regular message, and in it you obviously have to call the inherited implementation at the end via [super dealloc].
- (void) dealloc
{
...
[super dealloc];
}
Objective-C 2.0
On WDC2006, Apple introduced a new version of the language - 2.0. Among the innovations were noted garbage collection, fast enumeration, properties in classes, 64-bit support and much more. It should be noted that these innovations are only available for Leopard.
Garbage collection
Objective-C 2.0 allows automatic garbage collection, though this is optional.
Properties
Previously, to change and read instance variables, it was necessary to write return methods and set values ​​(so-called getters and setters), now you can write like this:
@interface Person: NSObject {
}
@property (readonly) NSString * name;
@property (readonly) int age;
- (id) initWithName: (NSString) name age: (int) age;
end
You can get the name like this:
NSString * name = aPerson.name;
Fast enumeration
Now an analogue of the foreach operator has been added:
for (Person * p in thePeople) NSLog (@ "% @ is% i years old.", [p getName], [p getAge]);
For the first part, that's enough. In compiling the article, materials from developer.apple.com and steps3d.narod.ru were used (by the way, the only site that has information about programming in Mac OS X in Russian).
In the next part, I’ll tell you about the Xcode development environment and Interface Builder interface editor, and also show you how to create a very simple application.