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

时间:2013-03-15 15:50:41

标签: objective-c cocoa class-method

在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;
}

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

3 个答案:

答案 0 :(得分:17)

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

(1)类型不匹配是坏的。

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

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


(4)

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

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

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

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

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

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

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

答案 1 :(得分:9)

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

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


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

如果Rectangle是子类(MyFancyRectangle),这仍然会返回一个普通的Rectangle对象,而

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

会返回MyFancyReactangle *r = [MyFancyReactangle rectangleOfColor:[UIColor redColor]],因为在子类上调用了[self alloc]。请注意,此处self再次在类上调用,因为+alloc是一个类方法。

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

答案 2 :(得分:6)

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

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

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