Object Initialization(对象初始化)

来源:互联网 发布:航路查询软件 编辑:程序博客网 时间:2024/06/06 01:18
Initialization sets the instance variables of an object to reasonable and useful initial values. It can also allocate and prepare other global resources needed by the object, loading them if necessary from an external source such as a file. Every object that declares instance variables should implement an initializing method—unless the default set-everything-to-zero initialization is sufficient. If an object does not implement an initializer, Cocoa invokes the initializer of the nearest ancestor instead.

初始化方法赋予对象的实例变量一个合理有用的初始值。它也为对象所需的全局资源分配和准备空间,在需要的时候从外部资源进行加载。每一个声明了实例变量的对象都应该实现相应的初始化方法,除非默认的将所有的实例初始化为零的初始化就已经满足要求。如果一个对象没有实现初始化方法,Cocoa将调用继承链中最近的祖先的初始化方法。

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.)

NSObject类为初始化声明了init原型;它是一个返回id类型的实例方法。对初始化时不需要外部数据的对象来说,重写init方法是不错的选择。但是通常情况下,初始化过程都依赖于外部数据。举个例子,假如你有一个名为Account的类;初始化一个合适的Account对象需要一个唯一的account number,并且这个account number必须传入初始化方法。好在初始化方法可以接收多个参数;唯一的要求是初始化方法使用“init”字符开头(格式上的约定init……用来标注初始化器)

Note: Instead of implementing an initializer with parameters, a subclass can implement only a simple 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.

注意:除了实现一个带有参数的初始化方法,子类也可以通过实现一个简单的init方法,然后紧跟初始化方法使用“set”方法来使对象达到想要的初始化状态。(存取器方法可以设置和获取实例变量的值,从而实现数据的封装)。或者子类使用了属性和相应的access语法,则可以设置属性的值紧跟初始化方法之后。

Cocoa has plenty of examples of initializers with parameters. Here are a few (with the defining class in parentheses):

    Cocoa有很多带有参数的初始化方法。下面举一些例子(括号里面是所定义的类)

    • - (id)initWithArray:(NSArray *)array; (from NSSet)

    • - (id)initWithTimeInterval:(NSTimeInterval)secsToBeAdded sinceDate:(NSDate *)anotherDate; (from NSDate)

    • - (id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)aStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag; (from NSWindow)

    • - (id)initWithFrame:(NSRect)frameRect; (from NSControl and NSView)

    These initializers are instance methods that begin with “init” and return an object of the dynamic type id. Other than that, they follow the Cocoa conventions for multiparameter methods, often using WithType: or FromSource: before the first and most important parameter.
    这些初始化方法都是以“init”开头的实例方法且返回动态id类型。除此之外,他们遵循Cocoa的多参数方法惯例,使用“WithType:”或者“FromSource:”在第一个和和最主要的参数之前。

    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. 

    两种情况迅速的返回something而不是进行分配内存后的对象。第一种包括两种情形:单例和被定义的对象属性是唯一的。例如NSWorkspace,一个程序中只允许一个实例;在这种情况下类必须保证(在一个初始化方法,或者类方法)只有一个实例被创建,当接受到请求新的实例的时候返回同一个实例。

    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

    Sometimes an 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:

    有时,init……方法不能按初始化方法所要求的执行。举个例子,initFromFile:方法希望使用一个文件的内容来初始化对象。文件的路径作为参数传入方法,但是如果文件不存在于指定的路径,那么对象就不能初始化。类似的问题,将NSDictionary对象代替NSArray对象传入initWithArray方法。当init……方法不能初始化一个对象,它应当:
    • Release the newly allocated object (in memory-managed code) 释放为新对象分配的内存

    • Return nil 返回nil

    Returning 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:

    初始化返回nil表明期望的对象不能被创建。当你使用初始化方法创建一个对象时,你应当总是检查返回的值是否是nil。

    id anObject = [[MyClass alloc] init];
    if (anObject) {
    [anObject doSomething];
    // more messages...
    } else {
    // handle error
    }

    Because an 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
    由于init……方法或许会返回nil,或者是一个你不期望的对象,直接使用alloc或者allocWithZone:生成的实例是很危险的,如下情形:

    id myObject = [MyClass alloc];
    [myObject init];
    [myObject doSomething];

    The 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.

    上例中,init方法或许会返回nil或者一个不是你所期待的对象。向nil发送消息并不会引发错误,所以调试起来很头疼。你应当嵌套初始化消息在初始化方法的内部,并且检测方法的返回对象。

    id myObject = [[MyClass alloc] init];
    if ( myObject ) {
    [myObject doSomething];
    } else {
    // error recovery...
    }

    Once an object is initialized, you should not initialize it again. If you attempt a reinitialization, the framework class of the instantiated object often raises an exception. For example, the second initialization in this example would result in NSInvalidArgumentException being raised.
    对象初始化完成后,你不能再次进行初始化。如果你尝试重新初始化,实例化的framework class将报错。如下例,第二次初始化时将导致NSInvalidArgumentException错误

    NSString *aStr = [[NSString alloc] initWithString:@"Foo"];
    aStr = [aStr initWithString:@"Bar"];

    Implementing an Initializer

    There are several critical rules to follow when implementing an 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):
    在实现类的初始化方法时,有几种公认的规则需要遵守,如基础的初始化方法,Multiple Initializers,指定初始化方法。
    • Always invoke the superclass (super) initializer first.

    • Check the object returned by the superclass. If it is nil, then initialization cannot proceed; return nil 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.
    注意:尽管上例中,参数为nil的时候返回nil,更好的Cocoa体验是在此时产生一个警告

    It isn’t necessary to initialize all instance variables of an object explicitly, just those that are necessary to make the object functional.The default set-to-zero initialization performed on an instance variable during allocation is often sufficient. Make sure that you retain or copy instance variables, as required for memory management.
    清晰的初始化对象的实例变量是没有必要的,只需要注意那些对对象功能有影响的实例变量。默认的在allocation阶段进行的set-to-zero初始化方法已经满足。确保对实例变量进行了适当的retain和copy操作

    The requirement to invoke the superclass’s initializer as the first action is important. Recall that an object encapsulates not only the instance variables defined by its class but the instance variables defined by all of its ancestor classes. By invoking the initializer of 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.
    Inherited initializers are a concern when you create a subclass. Sometimes a superclass 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.
    0 0