Почему "self = [[Rectangle alloc] init]" в методе класса BAD?
В документе Apple "Язык программирования Objective-C" на странице 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 ответа
Существует четвертый шаблон....
(1) несоответствие типов ПЛОХО.
(2) статическая ссылка на класс дает метод, который не будет вести себя правильно в подклассах
(3) динамическая ссылка на класс означает, что подклассы будут создаваться как экземпляры подкласса
(4)
+ (instancetype)rectangleOfColor:(NSColor *)color // Über-bestest evar!
{
Rectangle *newInstance = [[self alloc] init];
[newInstance setColor:color];
return newInstance;
}
llvm добавил instancetype
ключевое слово "yo! этот метод возвращает экземпляр любого класса, к которому он был обращен". Таким образом, если бы вы подкласс выше, вы могли бы:
RectangleSub *rs = [RectangleSub rectangleOfColor:[NSColor paisleyColor]];
Но это предупредит (за пределами ужасного выбора цвета):
RectangleSub *rs = [Rectangle rectangleOfColor:[NSColor puceColor]];
Принимая во внимание, что возвращаемый тип (id) не будет предупреждать во втором случае.
Обратите внимание, что я также переключился объявил newInstance
быть явно типа Rectangle*
, Это также лучше в том, что в контексте этого метода, newInstance
можно только безопасно рассматривать как Rectangle*
,
+ (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
, Это позволяет подклассам возвращать объекты подкласса без сумасшедшего компилятора.
В первом случае вы назначаете self
указатель (который должен указывать на Rectangle
объект класса) к экземпляру Rectangle
, Это абсолютно неверно.
Во втором случае вы жестко программируете класс для создания экземпляра - Rectangle
в этом случае.
В-третьих, вы позволяете идентификатору класса определять класс экземпляра, а не указывать его явно в коде. Тогда, если ваш Dodecahedron
Класс должен использовать этот же код, он не потребует изменения имени класса.