The Form of Initializers(初始化方法)
NSObject
declares the init
prototype for initializers; it is an instance method typed to return an object of type
id
. Overriding
init
is fine for subclasses that require no additional data to initialize their objects. But often initialization depends on external data to set an object to a reasonable initial state. For example, say you have an
Account
class; to initialize an
Account
object appropriately requires a unique account number, and this must be supplied to the initializer. Thus initializers can take one or more parameters; the only requirement is that the initializing method begins with the letters “init”. (The stylistic convention
init...
is sometimes used to refer to initializers.)
init
method and then use “set” accessor methods immediately after initialization to set the object to a useful initial state. (Accessor methods enforce encapsulation of object data by setting and getting the values of instance variables.) Or, if the subclass uses properties and the related access syntax, it may assign values to the properties immediately after initialization.Cocoa has plenty of examples of initializers with parameters. Here are a few (with the defining class in parentheses):
-
-
- (id)initWithArray:(NSArray *)array;
(fromNSSet
) -
- (id)initWithTimeInterval:(NSTimeInterval)secsToBeAdded sinceDate:(NSDate *)anotherDate;
(fromNSDate
) -
- (id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)aStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag;
(fromNSWindow
) -
- (id)initWithFrame:(NSRect)frameRect;
(fromNSControl
andNSView
)
id
. Other than that, they follow the Cocoa conventions for multiparameter methods, often using With
Type:
or From
Source:
before the first and most important parameter.
Issues with Initializers
Although init...
methods are required by their method signature to return an object, that object is not necessarily the one that was most recently allocated—the receiver of the init...
message. In other words, the object you get back from an initializer might not be the one you thought was being initialized.
尽管init……方法被他们的方法签名要求返回一个对象,该对象并不是必须是init……方法的接收者。换句话说,从初始化方法中返回的对象或许不是如你所想的进行初始化
Two conditions prompt the return of something other than the just-allocated object. The first involves two related situations: when there must be a singleton instance or when the defining attribute of an object must be unique. Some Cocoa classes—NSWorkspace
, for instance—allow only one instance in a program; a class in such a case must ensure (in an initializer or, more likely, in a class factory method) that only one instance is created, returning this instance if there is any further request for a new one.
A similar situation arises when an object is required to have an attribute that makes it unique. Recall the hypothetical Account
class mentioned earlier. An account of any sort must have a unique identifier. If the initializer for this class—say, initWithAccountID:
—is passed an identifier that has already been associated with an object, it must do two things:
类似的情况,当一个对象要求其某一个属性是唯一的。回想之前假设的Account类。Account类的实例必须有一个唯一的id。假如Account类有这样一个初始化方法
initWithAccountID:并且接收了一个已经使用过的id,它必须做下面两件事:-
Release the newly allocated object (in memory-managed code) 释放新分配的对象内存
-
Return the
Account
object previously initialized with this unique identifier 返回之前使用该唯一id创建的对象
By doing this, the initializer ensures the uniqueness of the identifier while providing what was asked for: an Account
instance with the requested identifier.
这样,这个初始化过程就为Account对象保证了唯一的id
init...
method cannot perform the
initialization requested. For example, an
initFromFile:
method expects to initialize an object from the contents of a file, the path to which is passed as a parameter. But if no file exists at that location, the object cannot be initialized. A similar problem happens if an
initWithArray:
initializer is passed an NSDictionary
object instead of an NSArray
object. When an
init...
method cannot initialize an object, it should:
-
Release the newly allocated object (in memory-managed code) 释放为新对象分配的内存
-
Return
nil 返回nil
nil
from an initializer indicates that the requested object cannot be created. When you create an object, you should generally check whether the returned value is
nil
before proceeding:
id anObject = [[MyClass alloc] init];
if (anObject) {
[anObject doSomething];
// more messages...
} else {
// handle error
}
init...
method might return
nil
or an object other than the one explicitly allocated, it is dangerous to use the instance returned by
alloc
or
allocWithZone:
instead of the one returned by the initializer. Consider the following code
id myObject = [MyClass alloc];
[myObject init];
[myObject doSomething];
init
method in this example could have returned
nil
or could have substituted a different object. Because you can send a message to
nil
without raising an exception, nothing would happen in the former case except (perhaps) a debugging headache. But you should always rely on the initialized instance instead of the “raw” just-allocated one. Therefore, you should nest the allocation message inside the initialization message and test the object returned from the initializer before proceeding.
id myObject = [[MyClass alloc] init];
if ( myObject ) {
[myObject doSomething];
} else {
// error recovery...
}
NSString *aStr = [[NSString alloc] initWithString:@"Foo"];
aStr = [aStr initWithString:@"Bar"];
Implementing an Initializer
init...
method that serves as a class’s sole initializer or, if there are multiple initializers, its
designated initializer
(described in
Multiple Initializers and Designated Initializer
):
-
Always invoke the superclass (
super
) initializer first. -
Check the object returned by the superclass. If it is
nil
, then initialization cannot proceed; returnnil
to the receiver. -
When initializing instance variables that are references to objects, retain or copy the object as necessary (in memory-managed code).初始化对象的实例变量,在需要的时候调用retain和release
-
After setting instance variables to valid initial values, return
self
unless: 在为实例变量设置好有效的值后,返回self除非:-
It was necessary to return a substituted object, in which case release the freshly allocated object first (in memory-managed code).要求返回一个替代的对象,这种情况下要先释放新分配的对象
-
A problem prevented initialization from succeeding, in which case return
nil
.在初始化的过程中发生了问题导致不能继续,返回nil
-
- (id)initWithAccountID:(NSString *)identifier {
if ( self = [super init] ) {
Account *ac = [accountDictionary objectForKey:identifier];
if (ac) { // object with that ID already exists
[self release];
return [ac retain];
}
if (identifier) {
accountID = [identifier copy]; // accountID is instance variable
[accountDictionary setObject:self forKey:identifier];
return self;
} else {
[self release];
return nil;
}
} else
return nil;
}Note: Although, for the sake of simplicity, this example returns
nil
if the parameter is
nil
, the better Cocoa practice is to raise an exception.
super
first, you help to ensure that the instance variables defined by classes up the inheritance chain are initialized first. The immediate superclass, in its initializer, invokes the initializer of its superclass, which invokes the main init...
method of its superclass, and so on (see Figure 6-1). The proper order of initialization is critical because the later initializations of subclasses may depend on superclass-defined instance variables being initialized to reasonable values.init...
method sufficiently initializes instances of your class. But because it is more likely it won’t, you should override the superclass’s initializer. If you don’t, the superclass’s implementation is invoked, and because the superclass knows nothing about your class, your instances may not be correctly initialized.