|
This section summarizes the syntax and semantics associated with classes. Class DefinitionA class definition consists of declaring the instance variables and methods in an interface section and defining the code for each method in an implementation section. Interface SectionGeneral Format: @interface className : parentClass <protocol, ...> { instanceVariableDeclarations } methodDeclaration methodDeclaration ... @end The class className is declared with the parent class parentClass. If className also adopts one or more formal protocols, the protocol names are listed inside a pair of angular brackets after parentClass. In that case, the corresponding implementation section must contain definitions for all such methods in the listed protocols. If the colon and parentClass are omitted, a new root class is declared. Instance Variable DeclarationsThe optional instanceVariableDeclarations section lists the type and name of each instance variable for the class. Each instance of className gets its own set of these variables, plus any variables inherited from parentClass.All such variables can be referenced directly by name either by instance methods defined in className or by any subclasses of className. If access has been restricted with an @private directive, subclasses cannot access the variables declared as such (refer to the section “Instance Variables”). Class methods do not have access to instance variables. Property DeclarationsGeneral Format: @property (attributes) nameList; This declares properties with the specified comma-separated list of attributes. nameList is a comma-separated list of property names of a declared type: (type) propertyName1, propertyName2, propertyName3,... An @property directive can appear anywhere inside the method declaration section for a class, protocol, or category. Table B.7 Property Attributes
| | Attribute | Meaning
| | assign | Use simple assignment to set the value of the instance variable in the setter method. (This is a default attribute.) | | copy | Use the copy method to set the value of the instance variable. | getter=name | Use name for the name of the getter method instead of propertyName, which is the default for the synthesized getter method. | nonatomic | The value from a synthesized getter method can be returned directly. If this attribute is not declared, then the accessor methods are atomic—meaning access to the instance variables is mutex-locked. This provides protection in a multithreaded environment by ensuring the get or set operation runs in a single thread. Further, by default, in a nongarbage-collected environment, the synthesized getter method retains and autoreleases the property before its value is returned. | | readonly | The property’s value cannot be set. No setter method is expected from the compiler, nor will one be synthesized. (This is a default attribute.) | | readwrite | The property’s value can be retrieved and set. The compiler expects you to provide both getter and setter methods or will synthesize both methods if @synthesize is used.
| | retain | The property should be retained on assignment. This can only be specified for Objective-C types. | | setter=name | Use name for the name of the setter method instead of setPropertyName, which is the default for the synthesized accessor method. |
|
You can only specify one of the attributes assign, copy, or retain. If you don’t use garbage collection, then one of these attributes should be explicitly used; otherwise you will get a warning from the compiler. If you use garbage collection and you don’t specify one of these three attributes, the default attribute, assign, will be used. In that case, the compiler gives a warning only if the class conforms to the NSCopying protocol (in which case you might want to copy and not assign the property). If you use the copy attribute, the object’s copy method will be used by the synthesized setter method.This results in an immutable copy.You must supply your own setter method if you need a mutable copy instead. Method DeclarationGeneral Format: mType (returnType) name1 : (type1) param1 name2 : (type2) param2, ...; The method name1:name2:.. is declared, which returns a value of type returnType and has formal parameters param1,param2, ....param1 is declared to be of type type1, param2 is declared to be of type type2, and so on. Any of the names after name1 (meaning name2 , ...) can be omitted, in which case a colon is still used as a placeholder and becomes part of the method name (see the following example). If mType is +, a class method is declared, but if mType is –, an instance method is declared. If the declared method is inherited from a parent class, the parent’s definition is overridden by the new definition. In such a case, the method from the parent class can still be accessed by sending a message to super. Class methods are invoked when a corresponding message is sent to a class object, whereas instance methods are invoked when a corresponding message is sent to an instance of the class. Class methods and instance methods can have the same name. The same method name can also be used by different classes.The capability of objects from different classes to respond to the same named method is known as polymorphism. If the method does not return a value, returnType is void. If the function returns an id value, returnType can be omitted, although specifying id as the return type is better programming practice. If , ... is used as the last (or only) parameter in the list, the method takes a variable number of arguments, as in -(void) print: (NSSTRING *) format, ... { ... } As an example of a class declaration, the following interface declaration section declares a class called Fraction whose parent is NSObject: @interface Fraction: NSObject { int numerator, denominator; } +(Fraction *) newFract; -(void) setTo: (int) n : (int) d; -(void) setNumerator: (int) n andDenominator: (int) d; -(int) numerator; -(int) denominator; @end The Fraction class has two integer instance variables called numerator and denominator. It also has one class method called newFract, which returns a Fraction Classes 549 object. It has two instance methods called setTo:: and setNumerator:andDenominator:, each of which takes two arguments and does not return a value. It also has two instance methods called numerator and denominator that take no arguments and return an int. Implementation SectionGeneral Format: @implementation className; methodDefinition methodDefinition ... @end The class called className is defined.The parent class and instance variables are not typically redeclared in the implementation section (although they can be) because they have been previously declared in the interface section. Unless the methods for a category are being implemented (see the section “Category Definition”), all the methods declared in the interface section must be defined in the implementation section. If one or more protocols were listed in the interface section, all the protocols’ methods must be defined—either implicitly through inheritance or explicitly by definition in the implementation section. Each methodDefinition contains the code that will be executed when the method is invoked. Method DefinitionGeneral Format: mType (returnType) name1 : (type1) param1 : name2 (type2) param2, ... { variableDeclarations programStatement programStatement ... return expression; } The method name1:name2:... is defined, which returns a value of type returnType and has formal parameters param1,param2, ....param1 is declared to be of type type1, param2 is declared to be of type type2, and so on. If mType is +, a class method is defined; if mType is –, an instance method is defined.This method declaration must be consistent with the corresponding method declaration from the interface section or from a previously defined protocol definition. An instance method can reference the class’s instance variables and any variables it has inherited directly by name. If a class method is being defined, it cannot reference any instance variables. The identifier self can be used inside a method to reference the object on which the method was invoked—that is, the receiver of the message. The identifier super can be used inside a method to reference the parent class of the object on which the method was invoked. If returnType is not void, one or more return statements with expressions of type returnType must appear in the method definition. If returnType is void, use of a return statement is optional, and if used, it cannot contain a value to return. As an example of a method definition, the following defines a setNumerator:andDenominator: method in accordance with its declaration (refer to the section “Method Declaration”): -(void) setNumerator: (int) n andDenominator: (int) d { numerator = n; denominator = d; } The method sets its two instance variables to the supplied arguments and does not execute a return (although it could) because the method is declared to return no value. Declarations for single-dimensional array arguments do not have to specify the number of elements in the array. For multidimensional arrays, the size of each dimension except the first must be specified. Local variables can be declared inside a method and are typically declared at the start of the method definition.Automatic local variables are allocated when the method is invoked and deallocated when the method is exited. See the section “The return Statement” for a discussion of the return statement. Synthesized Accessor MethodsGeneral Format: @synthesize property_1, property_2, ... This specifies that methods should be synthesized for the listed properties property_1, property_2, .... The notation property=instance_var can be used in the list to specify that property will be associated with the instance variable instance_var.The synthesized methods will have characteristics based on attributes declared for the property through a prior @property directive. Category DefinitionGeneral Format: @interface className (categoryName) <protocol,...> methodDeclaration methodDeclaration ... @end This defines the category categoryName for the class specified by className with the associated listed methods. If one or more protocols are listed, the category adopts the listed protocols. The compiler must know about className through a previous @interface section declaration for the class. You can define as many categories as you want in as many different source files as you want.The listed methods become part of the class and are inherited by subclasses. Categories are uniquely defined by className/categoryName pairs. For example, in a given program there can be only one NSArray (Private) category. However, individual category names can be reused. So, a given program can include an NSArray (Private) category and an NSString (Private) category, and both categories will be distinct from each other. You do not need to implement the methods defined in a category that you do not intend to use. A category can only extend the definition of a class with additional methods, or it can override existing methods in the class. It cannot define any new instance variables for the class. If more than one category declares a method with the same name for the same class, it does not define which method will be executed when invoked. As an example, the following defines a category for the Complex class called ComplexOps, with four instance methods: #import “Complex.h” @interface Complex (ComplexOps) -(Complex *) abs; -(Complex *) exp; -(Complex *) log; -(Complex *) sqrt; @end Presumably, a corresponding implementation section appears somewhere that implements one or more of these methods: #import “ComplexOps.h” @implementation Complex (ComplexOps) -(Complex *) abs { ... } -(Complex *) exp { ... } -(Complex *) log { ... } -(Complex *) sqrt { ... } @end A category that defines methods meant for other subclasses to implement is known as an informal protocol or abstract category. Unlike formal protocols, the compiler does not perform any checks for conformance to an informal protocol.At runtime, an object might or might not test for conformance to an informal protocol on an individual method basis. For example, one method might be required at runtime, whereas another method in the same protocol might not. Protocol DefinitionGeneral Format: @protocol protocolName <protocol, ...> methodDeclarations @optional methodDeclarations @required methodDeclarations ... @end The protocol called protocolName is defined with associated methods. If other protocols are listed, protocolName also adopts the listed protocols. This definition is known as a formal protocol definition. A class conforms to the protocolName protocol if it defines or inherits all the required methods declared in the protocol plus all the methods of any other listed protocols.The compiler checks for conformance and generates a warning if a class does not conform to a declared formal protocol. Objects might or might not be tested for conformance to a formal protocol at runtime. The @optional directive can precede a list of methods whose implementation is optional. An @required directive can subsequently be used to resume the list of required methods that must be implemented for conformance to the protocol. Protocols are often not associated with any particular class but provide a way to define a common interface that is shared among classes. Special Type ModifiersThe method parameters and return type declared in protocols can use the type qualifiers listed in Table B.8.These qualifiers are used for distributed object applications. Table B.8 Special Protocol Type Modifiers
| | Qualifier | Meaning | | in | The argument references an object whose value will be changed by the sender and sent (that is, copied) back to the receiver. | | out | The argument references an object whose value will be changed by the receiver and sent back to the sender. | | inout | The argument references an object whose value will be set by both the sender and the receiver and will be sent back and forth; this is the default. | | oneway | It’s used for return type declarations; typically (one way void) is used to specify that the invoker of this method does not have to wait for a return value—that is, the method can execute asynchronously. | | bycopy | The argument or return value is to be copied. | | byref | The argument or return value is passed by reference and not copied. |
|
Object DeclarationGeneral Format: className *var1, *var2, ...; This defines var1,var2, ... to be objects from the class className. Note that this declares pointer variables and does not reserve space for the actual data contained in each object. The declaration Fraction *myFract; defines myFract as a Fraction object or, technically, as a pointer to one.To allocate the actual space for the data structure of a Fraction, the alloc or new method is typically invoked on the class, like so: myFract = [Fraction alloc]; This causes enough space to be reserved for a Fraction object and a pointer to it to be returned and assigned to myFract.The variable myFract is often referred to as an object or as an instance of the Fraction class.As the alloc method in the root object is defined, a newly allocated object has all its instance variables set to 0.However, that does not mean the object has been properly initialized and an initialization method (like init) should be invoked on the object before it is used. Because the myFract variable has been explicitly declared as an object from the Fraction class, the variable is said to be statically typed.The compiler can check the use of statically typed variables for consistency by consulting the class definition for proper use of methods and their arguments and return types. id Object DeclarationGeneral Format: id <protocol,...> var1, var2, ...; This declares var1,var2, ... to be objects from an indeterminate class that conform to the protocols listed in the angular brackets.The protocol list is optional. Objects from any class can be assigned to id variables, and vice versa. If one or more protocols is listed, the compiler checks that methods used from the listed protocols on any of the declared variables are used in a consistent manner—that is, consistent with respect to argument and return types for the methods declared in the formal protocol. For example, in the statements id <MathOps> number; ... result = [number add: number2]; the compiler checks whether the MathOps protocol defines an add: method. If it does, it then checks for consistency with respect to the argument and return types for that method. So, if the add: method takes an integer argument and you are passing it a Fraction object above, the compiler complains. The system keeps track of the class to which each object belongs; therefore, at runtime it can determine the class of an object and then select the correct method to invoke. These two processes are known as dynamic typing and dynamic binding, respectively. Message ExpressionsFormat 1: [receiver name1: arg1 name2: arg2, name3: arg3 .. ] The method name1:name2:name3 ... from the class specified by receiver is invoked and the values arg1,arg2, ... are passed as arguments.This is called a message expression. The value of the expression is the value returned by the method, or void if the method is declared as such and returns no value.The type of the expression is that of the type declared for the method invoked. Format 2: [receiver name]; If a method takes no arguments, this format is used to invoked the method name from the class specified by receiver. If receiver is an id type, the compiler looks among the declared classes for a definition or inherited definition of the specified method. If no such definition is found, the compiler issues a warning that the receiver might not respond to the specified message. It further assumes the method returns a value of type id and converts any float arguments to type double and performs integral promotion on any integer arguments as outlined earlier in the section “Conversion of Basic Data Types.” Other method arguments are passed without conversion. If receiver is a class object (which can be created by simply specifying the class name), the specified class method is invoked. Otherwise,receiver is an instance of a class, and the corresponding instance method is invoked. If receiver is a statically typed variable or expression, the compiler looks in the class definition for the method (or for any inherited methods) and converts any arguments (where possible) to match the expected arguments for the method. So, a method expecting a floating value that is passed an integer has that argument automatically converted when the method is invoked. If receiver is a null object pointer—that is, nil—it can be sent messages. If the method associated with the message returns an object, the value of the message expression is nil. If the method does not return an object, the value of the expression is not defined. If the same method is defined in more than one class (either by explicit definition or from inheritance), the compiler checks for consistency for argument and return types among the classes. All arguments to a method are passed by value; therefore, their values cannot be changed by the method. If a pointer is passed to a method, the method can change values referenced by the pointer, but it still cannot change the value of the pointer itself. Format 3: receiver.property This calls the getter method (by default property) for receiver, unless this expression is used as an lvalue (see Format 4). The getter method name can be changed with an @property directive, in which case that will be the method that gets called. If the default getter method name is used, then the previous expression is equivalent to the following: [receiver property] Format 4: receiver.property = expression This calls the setter method associated with the property property, passing as its argument the value of expression. By default, the setter method setProperty: gets called, unless another setter method name was assigned to the property using a prior @property directive. If the default setter property name is used, the previous expression is equivalent to writing the following: [receiver setProperty: expression] |