| I l@ve RuBoard |
|
1.3 Elements of the LanguageObjective-C extends the C language with the following concepts and constructs:
The following sections will describe these aspects of Objective-C. 1.3.1 ObjectsObjects are the compound values—data and operations—at the heart of object-oriented programming. They are generalizations of simple structured types found in C and many other languages. Like C structs, objects have components—data fields—that maintain state. For example, a Book object might have fields to specify the author and publisher. In addition, objects interact through message passing, which provides an alternative to the function calls of a procedural language. Objective-C objects have these attributes:
Objective-C objects are implemented so that:
When someone says of an Objective-C variable: "c is an instance of C", you should understand that to mean that c is a pointer to an object of type C. 1.3.2 ClassesClasses are types in Objective-C. The interface of a class specifies the structure of its instances; the implementation provides its code. The interface and implementation of a class are separate, usually in different files. Categories add to an existing class without subclassing it; they also have separate interface and implementation. Protocols are pure interface declarations. Classes declare the following attributes:
Because Objective-C makes calling class methods syntactically identical to calling instance methods, classes themselves behave much like objects. Section 1.9 discusses class objects. 1.3.2.1 Declaring an interfaceAn interface names a class and declares its parent (if any), fields, and methods, without specifying any implementation. (You can't use C++-style inline methods, implemented in the header.) A header file can contain any number of interface declarations, but it is conventional to put each interface in a separate header file. By default, gcc expects the filename to end in .h. Following is an example interface declaration. (Note that the line numbers are not part of the source code.) 1 #import "Graphic.h "
2
3 @class Point ;
4
5 @interface Circle : Graphic {
6 @protected // or @public or @private
7 float radius ;
8 Point * center ;
9 }
10 -(void )scaleBy :(float )factor ;
11 +(void )numCircles ;
12 @end
Line 1. The #import directive is like C's #include directive, except that the compiler ensures that no file is included more than once. You always need to import the declaration of your class's parent class (if any). You don't need to import any other Objective-C class declarations, but it may be convenient to import some umbrella header files as a matter of routine. Line 3. You use the ec@class declaration when your class's fields, or the return values or parameters of your class's methods, are instances of another class. You can use separate @class declarations for distinct classes, or use a single declaration with the class names separated by commas. A class declaration doesn't need any more information about any other class, so you don't need to import a header unless the header has other declarations (e.g., macros or globals) that your class declaration needs. Line 5. Specify the name of your class and that of the parent class (if any). The name of your class will be visible to any code that includes the header file. All class names exist in the global namespace, along with global variable names and type names. Line 6. Access keywords control compile-time checking of access to fields. You can repeat these as often as you want; a field has the access permission specified by the most recent preceding keyword. If there is no preceding keyword, access permission defaults to protected. In brief, public fields are visible to all subclasses and all external code, protected fields are visible only to subclasses, and private fields are visible only in the class being declared. Section 1.3.4 gives exact rules. Line 7. Declare fields in the same manner as you declare structure members in C. Fields can be of any C type, as well as of any class or other type (described in Section 1.3.9) added by Objective-C. Fields can have the same name as methods in the same class. Fields are not shared between instances—that is, Objective-C does not support class variables. But you can get the same effect by declaring ordinary variables as static in the implementation file. (See the following Section 1.3.2.2 for an example.) Line 8. You incorporate other objects only by pointer, not by value. Objective-C's predefined id, Class, and Protocol types are pointer types already. Line 9. No semicolon after the closing brace. Line 10. An instance method is marked with a - character. Instance methods operate on instances of the class. Method signatures use Objective-C's infix syntax, discussed later in Section 1.3.5. You don't need to redeclare a method if you are inheriting it. It is conventional to redeclare a method if your class inherits and overrides it. Section 1.3.5.4 explains why you should declare a method in all other cases. (But not necessarily in the header file; see Section 1.3.5.5.) Methods can have the same name as fields of the same class, and instance methods can share names with class methods. Line 11. A class method is marked with a + character. Class methods perform operations or return information about the class as a whole, and don't pertain to any instance of the class. Line 12. No semicolon after the @end keyword. 1.3.2.2 Implementing a classYou implement a class by writing the code (i.e., the bodies) for each of the class's methods. A file can contain any number of class implementations, but it is conventional to put each implementation in a separate file. By default, gcc expects the filename to end in .m (for Objective-C) or .mm or .M (for Objective-C++). Even if a class has no methods, it must have an empty implementation.
Here is a simple implementation for the class declared in the previous section: 1 #import "Circle.h "
2
3 static int count ;
4
5 @implementation Circle
6 // No field section.
7 +(void )numCircles { return count ; }
8 -(void )scaleBy :(float )factor { radius *= factor ;}
9 @end
Line 1. Always import the header file that declares your class. If your code uses other classes (e.g., it sends messages to them or their instances) you need to import the headers of those classes too. There is little point to using an @class declaration here—if you use another class in an implementation you will need its interface declaration. Line 3. This is a pure C declaration, reserving space for a per-class variable. (In this example, it would be used to keep count of the number of objects created. That code is not shown.) It will be visible only in this implementation file. Line 5. Specify the name of the class you are implementing. Line 6. You can't add any more fields here, so there is no brace-delimited section corresponding to the one in the interface declaration. Lines 7, 8. Define methods with the same signature syntax as in the header; follow each method definition with the code, in braces. For more information on writing the code, see Section 1.3.5. Line 9. No semicolon after the @end keyword. 1.3.3 Inheritance and SubtypingInheritance and subtyping are the fundamental object-oriented features: inheritance lets you declare how one type differs from another, and subtyping lets you substitute one kind of object for another when their types are compatible. A class's interface may declare that it is based on another class: @interface MyClass : Parent This is described by saying MyClass is a subclass of Parent, and has the following runtime effects:
These effects are summarized by saying MyClass inherits Parent's fields and methods. These properties imply that inheritance is transitive: any subclass of MyClass also inherits from Parent. Declaring a class and specifying a parent also has these effects at compile time:
This is described by saying MyClass is a subtype of Parent. Subtyping is also transitive. For example, any subclass of MyClass is a subtype of Parent. You can't redeclare fields in a subclass, either identically or with a different type. (Unlike C++, this holds even if the inherited field was declared private.) A subclass can replace, or override , an inherited method with a different version. You do this by providing a new implementation for the method when you write the subclass. At runtime, instances of the subclass will execute the new code in the overriding method instead of the code of the inherited method. By definition, the overriding method has the same name as the inherited one; it must also have the same return and parameter types. (You can't overload methods as in C++.) You don't need to redeclare the overriding method, but it is conventional to do so. 1.3.4 FieldsFields are data members of objects. Each object gets its own copy of the fields it inherits or declares. Inside the methods of a class you can refer directly to fields of self, either declared in the class or (if they are not private) inherited: @interface Circle : Graphic {
float radius ; // Protected field.
// etc.
@end
@implementation Circle
-(float )diameter {
return 2 * radius ; // No need to say self->radius.
}
@end
If an object other than self is statically typed, you can refer to its public fields with the dereference operator: AClass* obj; // Static typing required. obj->aField = 7; // Presuming aField is public. 1.3.4.1 Access modifiersAccess to an object's field depends on four things:
There is no notion of read-only members in Objective-C: if you can read a field, you can also write to it. You declare a field's access permission in its class's interface. Any of three access keywords—@public , @protected, and @private—may appear any number of times in an interface declaration and affects all fields declared after it until the next access keyword. (See the example in Section 1.3.2.) If there is no preceding keyword, a field has protected access. Following are the keywords and their effects on access to an object's fields:
1.3.5 MethodsMethods are the functions associated with an object, and are used as interfaces for querying or changing the object's state, or for requesting it to perform some action. Objective-C uses the terms "calling a method" and "sending a message" interchangeably. This is because method dispatch is less like dereferencing a function pointer and more like searching for a recipient and delivering a message. Most of the work of method lookup is done at runtime. You send messages to objects using Objective-C's bracket syntax. Method names are associated with parameters using an infix syntax borrowed from Smalltalk. Objective-C provides directives for accessing the runtime dispatch mechanism directly. 1.3.5.1 Declaring a methodYou declare a method in an interface or protocol by specifying who handles it (either the class or an instance of the class), its return type, its name, and its parameter types (if any). The name is broken up so that part of it precedes each parameter. The form of a declaration depends on the number of parameters, as described in the following sections. 1.3.5.1.1 No parameters-(id)init; This declaration can be broken down as follows:
1.3.5.1.2 One parameter+(void )setVersion:(int )v ; This declaration can be broken down as follows:
1.3.5.1.3 More than one parameter-(id )perform:(SEL )sel with:(id )obj ; This declaration can be broken down as follows:
1.3.5.1.4 A variable number of parameters-(id )error:(char *)format ,...; This declaration can be broken down as follows:
1.3.5.2 Implementing a methodThe body of a method appears in the implementation section of a class. The method starts with a declaration, identical to that of the interface section, and is followed by code surrounded by braces. For example: -(void )scaleBy :(float )factor {
radius *= factor ;
}
Inside a method body, refer to fields the class declares or inherits with no qualification. Refer to fields of other objects using the dereference (->) operator. Objective-C defines three special variables inside each method: Section 1.3.9 describes these special variables in more detail. 1.3.5.3 Calling a methodEvery method call has a receiver—the object whose method you are calling—and a method name. You enclose a method call in brackets [ ] with the receiver first and the method name following. If the method takes parameters, they follow the corresponding colons in the components of the method name. Separate receiver and name components by spaces. For example: [aCircle initWithCenter:aPoint andRadius:42]; If a method takes a variable number of parameters, separate them with commas: [self error:"Expected %d but got %d ", i , j ]; A method call is an expression, so if the method returns a value you can assign it to a variable: int theArea = [aCircle area ]; Method calls can be nested: Circle* c1 = ... // whatever
Circle* c2 =
[[Circle alloc] initWithCenter:[c1 center]
andRadius:[c1 radius]];
1.3.5.4 Naming collisionsWhen the compiler encodes a method call, it needs to set up, then and there, room for the parameters and return value (if any). This is a nondynamic aspect of method calls. To encode the call properly, the compiler must know the parameter and return types. If you leave the type of a receiver unknown (that is, declared as id) until runtime, the name of a method may be all the information the compiler has when it compiles the method call. The compiler will encode the method call based on previous uses of the same method:
The compiler will not let you declare, in related classes, methods with the same name but different parameter types (no C++-style overloading), but you can do this in unrelated classes. To avoid forcing the compiler to guess your intentions, methods with the same name, even in different classes, should usually have the same signature. You can relax this restriction if you know the receiver of the call will always be statically typed. But you may not have control over all future use of your method. 1.3.5.5 Private methodsBecause the compiler needs to know parameter types to generate method calls, you should also not omit method declarations in an attempt to make them private. A better way to make a method private is to move the method declaration to its class's implementation file, where it will be visible to the code that uses it but not to any other part of your program. The following example shows how to use a category (described at greater length in Section 1.3.6) to declare a method in an implementation file: 1 @interface Circle (PrivateMethods )
2 -(float )top ;
3 @end
4
5 @implementation Circle
6 // Public definitions as before.
7 ...
8 // Now implement the private methods.
9 -(float )top { return [center y ] - radius ; }
8 @end
Line 1. At the beginning of your class's implementation file, declare a category that extends the class. Line 2. Methods declared here are just as much part of the class as the ones declared in the regular interface, but will be visible only in this file. Line 5. Your class's implementation, in the usual fashion. Line 6. Implement the methods declared in the header file. They can call the private methods (as well as other public methods, of course). Line 8. Implement the private methods. They can call public and private methods. 1.3.5.6 AccessorsAccessors are methods for setting and returning an object's fields. Objective-C has several conventions for writing accessors:
Here is an example of how to write accessors for a simple field: @implementation Circle
-(float )radius { return radius ; }
-(void )radius :(float )r { radius = r ; }
@end
If you are using reference counting, there are more design patterns you should follow when writing accessors. These are discussed in Section 1.12.2. 1.3.5.7 Message search pathsWhen you send an object a message (call one of its methods), the code that actually runs is determined by a search performed at runtime. The dispatch of the method call proceeds as follows:
Section 1.9 provides more details about method dispatch. 1.3.5.8 Special receiversThe receiver is the object to which you are sending a message. Its instance methods will be given the first chance to handle the call, followed by the instance methods of its ancestor classes. Besides the typical case of sending a message to a named receiver, there are several special targets:
1.3.5.9 SelectorsObjective-C methods are distinguished by their names, which are the concatenation of the component parts of the method name, including the colon characters. At compile time, each name is matched to a unique integer called the selector. The Objective-C type SEL represents a selector's type. When a method call is compiled, it is distilled to its receiver, selector, and parameters. At runtime, the message is dispatched by matching the selector with a list maintained by the receiver's class object. You can use selectors to make a runtime decision about what method to call—the Objective-C version of function or method pointers. 1 SEL mySel = @selector (center ); 2 Point * p = [aCircle perform:mySel ]; Line 1. The compiler knows the mapping from selector names to SELs and will emit code assigning to mySel the value corresponding to the method name center. Line 2. Using the selector, you can instruct the receiver to respond to the message as if it had been sent in the usual way. The effect is the same as executing the direct method call: Point* p = [aCircle center]; Section 1.10 gives more information about the -perform: methods. 1.3.6 CategoriesObjective-C provides the category construct for modifying an existing class "in place." This differs from writing a new subclass: with inheritance you can only create a new leaf in the inheritance tree; categories let you modify interior nodes in the tree. With a category, the full declaration of a class can be spread out over multiple files and compiled at different times. This has several advantages:
1.3.6.1 Declaring a categoryHere is an example of declaring a category to add methods to the Circle class: 1 #import "Circle.h " 2 3 @interface Circle (Motion ) 4 // No field section. 5 -(void )moveRight :(float )dx ; 6 -(void )moveUp :(float )dy ; 7 @end Line 1. The declaration can be in the same header file as the declaration of the class it modifies, or in a separate file. If it is in a separate header file, you may need to include the header file of the modified class. Line 3. Declare the name of the class you are modifying and the name of your category. In this example, Circle is the class name, and Motion is the category name. Category names have their own namespace, so a category can have the same name as a class or a protocol. Line 4. There is no fields section in the category declaration, so you can't use a category to add fields to a class. Lines 5, 6. Declare methods here as in a class interface. Line 7. No semicolon after the @end keyword. You can declare a category in a header or an implementation file. If the declaration is in a header file, its methods are full members of the modified class. Any code can use the new methods with the modified class (and its subclasses). If the declaration is in an implementation file, only code in that file can use the new methods, and their implementation must appear in that file. This is a way of making methods private. If you declare in your category a method with the same name as one in the modified class, the new method overrides the old one. When such an overriding method sends messages to super, they go to the same method in the parent class (just as they would in the overridden version) and not to the overridden method itself. You can't send messages to the overridden version. You should simply avoid using categories to override methods. If several categories that modify the same class all declare a method with the same name, the results are implementation dependent. In other words, don't do this. The gcc compiler does not warn you about this. You don't have to implement all or even any of the methods you declare in a category. The compiler will warn you if you have an implementation section for your category and omit any methods. If you have no implementation at all for a category, this is called declaring an informal protocol. Section 1.3.7 discusses these further. 1.3.6.2 Implementing a categoryYou implement the methods of the category in an implementation file, like this: 1 #import "Motion.h "
2
3 @implementation Circle (Motion )
4 -(void )moveRight :(float )dx { ... }
5 -(void )moveUp :(float )dy { ... }
5 @end
Line 1. You need to include the declaration of the category you are implementing. If the declaration is in the same file as the implementation, you don't need this line. Line 3. Specify the name of the modified class and the name of your category. Line 4. Methods take the same form and have the same access to fields as in a regular class implementation. Line 5. No semicolon after the @end keyword. 1.3.7 ProtocolsProtocols are lists of method declarations that are not associated with a specific class declaration. Protocols let you express that unrelated classes share a common set of method declarations. (This facility inspired Java's interface construct.) Protocols let you do the following:
Objective-C declarations can specify that an instance must support a protocol, instead of (or in addition to) conforming to a class. (See Section 1.3.8.) 1.3.7.1 Declaring a protocolYou declare a protocol in a header file like this: 1 @protocol AnotherProtocol ; 2 3 @protocol Printable <Drawable > 4 -(void )print ; 5 @end Line 1. You need the forward protocol declaration if return or parameter types of methods in your protocol use the other protocol. Protocol names have their own namespace, so a protocol can have the same name as a class or a category. Line 3. If your protocol extends other protocols, name those in the protocol list. The list is a sequence of protocol names, separated by commas. If the list is empty, you can omit the angle brackets. You don't need to redeclare the methods of the listed protocols. When a class adopts the protocol you are declaring, it must implement all the methods declared in your protocol and all the other protocols in the list. (You can't write partially abstract classes as in C++, with some methods declared but not implemented.) Line 4. You declare your methods here in the same form as in a class interface. Line 5. No semicolon after the @end keyword. 1.3.7.2 Adopting a protocolWhen you want your class to adopt (implement) one or more protocols, you declare it like this: #import "Printable.h " @interface Circle : Graphic <Printable > When you want your category to adopt a protocol you declare it like this: #import "Printable.h " @interface Circle (Motion ) <Printable > The list of protocols, inside the angle brackets, consists of names separated by commas. You have to import the header files where the protocols are declared. Don't redeclare the protocol's methods in your interface; just define them in your implementation. You have to define all the methods of the protocol. When you declare a field or variable, you can specify that it represents an instance whose class conforms to a specific protocol like this: id <Printable > obj ; ClassName <Printable >* obj ; For more about this, see Section 1.3.8. 1.3.7.3 Checking for conformity to a protocolAt runtime, you can check if an object's class conforms to a protocol by using the -conformsTo: (from the root class Object) or +conformsToProtocol: (from NSObject) methods: [obj conformsTo:@protocol (Printable )]; Like classes, protocols have special runtime structures associated with them called protocol objects. Section 1.9 describes these. 1.3.7.4 Informal protocolsYou can get some of the functionality of a protocol by declaring but not implementing a category. Such a category is called an informal protocol . You can't declare that a class does or does not implement an informal protocol, so you can't use it for static type checking. You can use an informal protocol to specify a group of methods that all subclasses of the modified class may implement, but are not obliged to implement. This serves as something less than a full protocol but more than just textual documentation. If you need a protocol, you're better off using a formal protocol. When a subclass implements an informal protocol, it doesn't refer to the original declaration, but declares in its interface which of the methods it will implement and defines the methods in its implementation in the usual way. 1.3.8 DeclarationsYou can declare Objective-C objects in many different ways. However, the way in which you declare an object has no effect on that object's runtime behavior. Rather, the way that you declare an object controls how the compiler checks your program for type safety. 1.3.8.1 Dynamic typingUse the id type to declare a pointer to an unspecified kind of object. This is called dynamic typing. For example: id obj ; With this declaration, obj can be a pointer to an object of any type. An id has the following compile-time properties:
1.3.8.2 Static typingIn Objective-C, any deviation from fully dynamic typing is called static typing. With static typing you instruct the compiler about the types of values you intend variables to have. All static typing is done within the inheritance relation: where a variable's class or protocol is declared, a value from a descendant class or protocol is always acceptable. You can use static typing in three ways, shown in the following examples:
1.3.8.3 Type qualifiersType qualifiers go before a C or Objective-C type in a method declaration, and modify that type. Supported qualifiers are: These qualifiers can only be used in formal protocols and implementations, not in class or category declarations. They specify how parameters are to be passed when you are using remote messaging. Type qualifiers can be combined, although not all combinations make sense. They are discussed at greater length in Section 1.6 . 1.3.9 Predefined Types, Constants, and VariablesObjective-C adds the special type id, which is generic like a void* in C++, but which does not preclude you from sending messages. In addition, the Objective-C environment provides some C types, constants, and variables to support object-oriented programming. 1.3.9.1 TypesAlong with class types that you define, Objective-C introduces these built-in types you can use when declaring variables:
1.3.9.2 ConstantsThe following constants are all defined as preprocessor symbols:
1.3.9.3 VariablesThese are special variables, set up for you by the Objective-C runtime:
|
| I l@ve RuBoard |
|