为什么在类方法BAD中“self = [[Rectangle alloc] init]”?

时间:2022-11-15 18:55:37

In the document "Objective-C Programming Language" by Apple, page 48 says:

在Apple的文档“Objective-C Programming Language”中,第48页说:

+ (Rectangle *)rectangleOfColor:(NSColor *) color
{
    self = [[Rectangle alloc] init]; // BAD
    [self setColor:color];
    return self;
}

+ (id)rectangleOfColor:(NSColor *)color
{
     id newInstance = [[Rectangle alloc] init]; // GOOD
     [newInstance setColor:color];
     return newInstance;
}


+ (id)rectangleOfColor:(NSColor *)color
{
     id newInstance = [[self alloc] init]; // EXCELLENT
     [newInstance setColor:color];
     return newInstance;
}

One is bad, one is good, and the other is excellent. Why is that?

一个是坏的,一个是好的,另一个是优秀的。这是为什么?

3 个解决方案

#1


18  

There is a fourth pattern....

还有第四种模式......

(1) type mismatch is BAD.

(1)类型不匹配是BAD。

(2) static reference to class yields method that won't behave correctly in subclasses

(2)静态引用类产生的方法在子类中不能正常运行

(3) dynamic reference to class means subclasses will be instantiated as subclass instances

(3)对类的动态引用意味着子类将被实例化为子类实例


(4)

+ (instancetype)rectangleOfColor:(NSColor *)color // Über-bestest evar!
{
     Rectangle *newInstance = [[self alloc] init];
     [newInstance setColor:color];
     return newInstance;
}

llvm added the instancetype keyword that says "yo! this method returns an instance of whatever class it was called on". Thus, if you were to subclass the above, you could:

llvm添加了instancetype关键字,表示“哟!这个方法返回它被调用的任何类的实例”。因此,如果你是上面的子类,你可以:

RectangleSub *rs = [RectangleSub rectangleOfColor:[NSColor paisleyColor]];

But this would warn (beyond the awful color choice):

但这会警告(超出可怕的颜色选择):

RectangleSub *rs = [Rectangle rectangleOfColor:[NSColor puceColor]];

Whereas the (id) return type would not warn in the second case.

而(id)返回类型在第二种情况下不会发出警告。

Note that I also switched declared newInstance to be explicitly of type Rectangle*. This is more better, too, in that within the context of that method, newInstance can only be safely treated as a Rectangle*.

请注意,我还将声明的newInstance切换为显式类型为Rectangle *。这也更好,因为在该方法的上下文中,newInstance只能被安全地视为Rectangle *。

#2


9  

+ (Rectangle *)rectangleOfColor:(NSColor *) color
{
    self = [[Rectangle alloc] init]; // BAD
    [self setColor:color];
    return self;
}

In class method self refers to the class, not a instance object of it.

在类方法中,self指的是类,而不是它的实例对象。


+ (id)rectangleOfColor:(NSColor *)color
{
    id newInstance = [[Rectangle alloc] init]; // GOOD
    [newInstance setColor:color];
    return newInstance;
}

If Rectangle would be subclassed (MyFancyRectangle), this still would return a plain Rectangle object, while

如果Rectangle将被子类化(MyFancyRectangle),那么仍然会返回一个普通的Rectangle对象

+ (id)rectangleOfColor:(NSColor *)color
{
    id newInstance = [[self alloc] init]; // EXCELLENT
    [newInstance setColor:color];
    return newInstance;
}

would return a MyFancyReactangle if called like MyFancyReactangle *r = [MyFancyReactangle rectangleOfColor:[UIColor redColor]], as [self alloc]is called on the sublass. Note, that here self is again called on the class, as +alloc is a class method.

如果调用MyFancyReactangle * r = [MyFancyReactangle rectangleOfColor:[UIColor redColor]],则返回MyFancyReactangle,因为在子类上调用[self alloc]。注意,这里再次调用self,因为+ alloc是一个类方法。

For the same reason all init… and convenient creator methods should return id. It allows subclasses to return subclass'ed objects without the compiler going mad.

出于同样的原因,所有init ...和方便的创建者方法都应该返回id。它允许子类返回子类的对象,而不会让编译器发疯。

#3


6  

In the first case, you assign the self pointer (which should point to the Rectangle class object) to an instance of Rectangle. This is absolutely incorrect.

在第一种情况下,将自指针(应指向Rectangle类对象)分配给Rectangle实例。这绝对不正确。

In the second, you hard code a class to instantiate - Rectangle in this case.

在第二种情况下,您需要对要实例化的类进行硬编码 - 在这种情况下为Rectangle。

In the third, you allow the class's identity to determine the class of the instance, rather than specifying it explicitly in code. Then, if your Dodecahedron class needs to use this same code, it won't require changing the class name.

在第三种情况下,您允许类的标识来确定实例的类,而不是在代码中明确指定它。然后,如果您的Dodecahedron类需要使用相同的代码,则不需要更改类名。

#1


18  

There is a fourth pattern....

还有第四种模式......

(1) type mismatch is BAD.

(1)类型不匹配是BAD。

(2) static reference to class yields method that won't behave correctly in subclasses

(2)静态引用类产生的方法在子类中不能正常运行

(3) dynamic reference to class means subclasses will be instantiated as subclass instances

(3)对类的动态引用意味着子类将被实例化为子类实例


(4)

+ (instancetype)rectangleOfColor:(NSColor *)color // Über-bestest evar!
{
     Rectangle *newInstance = [[self alloc] init];
     [newInstance setColor:color];
     return newInstance;
}

llvm added the instancetype keyword that says "yo! this method returns an instance of whatever class it was called on". Thus, if you were to subclass the above, you could:

llvm添加了instancetype关键字,表示“哟!这个方法返回它被调用的任何类的实例”。因此,如果你是上面的子类,你可以:

RectangleSub *rs = [RectangleSub rectangleOfColor:[NSColor paisleyColor]];

But this would warn (beyond the awful color choice):

但这会警告(超出可怕的颜色选择):

RectangleSub *rs = [Rectangle rectangleOfColor:[NSColor puceColor]];

Whereas the (id) return type would not warn in the second case.

而(id)返回类型在第二种情况下不会发出警告。

Note that I also switched declared newInstance to be explicitly of type Rectangle*. This is more better, too, in that within the context of that method, newInstance can only be safely treated as a Rectangle*.

请注意,我还将声明的newInstance切换为显式类型为Rectangle *。这也更好,因为在该方法的上下文中,newInstance只能被安全地视为Rectangle *。

#2


9  

+ (Rectangle *)rectangleOfColor:(NSColor *) color
{
    self = [[Rectangle alloc] init]; // BAD
    [self setColor:color];
    return self;
}

In class method self refers to the class, not a instance object of it.

在类方法中,self指的是类,而不是它的实例对象。


+ (id)rectangleOfColor:(NSColor *)color
{
    id newInstance = [[Rectangle alloc] init]; // GOOD
    [newInstance setColor:color];
    return newInstance;
}

If Rectangle would be subclassed (MyFancyRectangle), this still would return a plain Rectangle object, while

如果Rectangle将被子类化(MyFancyRectangle),那么仍然会返回一个普通的Rectangle对象

+ (id)rectangleOfColor:(NSColor *)color
{
    id newInstance = [[self alloc] init]; // EXCELLENT
    [newInstance setColor:color];
    return newInstance;
}

would return a MyFancyReactangle if called like MyFancyReactangle *r = [MyFancyReactangle rectangleOfColor:[UIColor redColor]], as [self alloc]is called on the sublass. Note, that here self is again called on the class, as +alloc is a class method.

如果调用MyFancyReactangle * r = [MyFancyReactangle rectangleOfColor:[UIColor redColor]],则返回MyFancyReactangle,因为在子类上调用[self alloc]。注意,这里再次调用self,因为+ alloc是一个类方法。

For the same reason all init… and convenient creator methods should return id. It allows subclasses to return subclass'ed objects without the compiler going mad.

出于同样的原因,所有init ...和方便的创建者方法都应该返回id。它允许子类返回子类的对象,而不会让编译器发疯。

#3


6  

In the first case, you assign the self pointer (which should point to the Rectangle class object) to an instance of Rectangle. This is absolutely incorrect.

在第一种情况下,将自指针(应指向Rectangle类对象)分配给Rectangle实例。这绝对不正确。

In the second, you hard code a class to instantiate - Rectangle in this case.

在第二种情况下,您需要对要实例化的类进行硬编码 - 在这种情况下为Rectangle。

In the third, you allow the class's identity to determine the class of the instance, rather than specifying it explicitly in code. Then, if your Dodecahedron class needs to use this same code, it won't require changing the class name.

在第三种情况下,您允许类的标识来确定实例的类,而不是在代码中明确指定它。然后,如果您的Dodecahedron类需要使用相同的代码,则不需要更改类名。