Метод класса Objective-C

Мой класс заголовка выглядит так:

#import "Card.h"

@interface PlayingCard : Card

@property (strong, nonatomic) NSString *suit;
@property (nonatomic) NSUInteger rank;

+ (NSArray *) validSuits;
+ (NSUInteger) maxRank;

@end

И моя реализация:

@implementation PlayingCard

+ (NSArray *) validSuits
{
    return @[@"♥︎", @"♣︎", @"♦︎", @"♠︎"];
}

+ (NSArray *) rankStrings
{
    return @[@"?", @"1", @"2", @"3", @"4"];
}

- (void)setSuit:(NSString *)suit
{
    if ([[PlayingCard validSuits] containsObject:suit])
    {
        _suit = suit;
    }
}

- (NSString *)suit
{
    return _suit ? _suit : @"?"; // if suit !nil return suit, else return ? string.
}

+ (NSUInteger)maxRank
{
    return [[self rankStrings] count] - 1;
}


@end

Итак, я понимаю, что любой метод со знаком + означает, что это метод класса.

Мой вопрос в том, почему я должен использовать [PlayingCard classMethod], например. [PlayingCard validSuits] в методе setSuit, тогда как я могу использовать [self classMethod], например. [self rankStrings] в методе maxRank?

Я предполагаю, что это как-то связано с тем, что метод maxRank является методом класса, а setSuit — нет. Но может ли это быть потому, что setSuit сеттер?

Я действительно не знаю, я не могу представить, что здесь происходит. Я только начал свой набег на Objective-C и исхожу из фона Java.

Я понял, что могу заменить PlayingCard на self в методе maxRank без каких-либо сообщений об ошибках, однако замена self на PlayingCard в методе setSuit дает мне сообщение об ошибке

No visible @interface for 'PlayingCard' declares the selector for 'validSuits'

Любое объяснение того, почему это так и что происходит, было бы здорово. Спасибо!


person monster    schedule 07.08.2014    source источник


Ответы (4)


Значение self в методах

Каждый метод Objective-C получает неявный аргумент self. Методы экземпляра получают экземпляр, а методы класса получают объект класса (помните: классы — это объекты).

Если вы хотите отправить метод класса, компилятор позволяет вам использовать два типа синтаксиса:

  1. [ClassName classMethod]
  2. [classObjectPtr classMethod]

Первый синтаксис используется в [PlayingCard maxRank]. Здесь целью является (явно) класс PlayingCard.

Метод класса уже имеет объект класса в качестве цели для отправки методов класса: аргумент self. Таким образом, они могут использовать [self classMethod] для отправки других методов класса.

Зачем отправлять сообщение self в методах класса?

Преимущество последнего в том, что класс явно не назван. Это позволяет переопределять методы класса в подклассах и вызывать их из базовых классов.

В основном вы получаете ту же динамическую диспетчеризацию методов, что и с методами экземпляра. На самом деле это приятная особенность Objective-C, которой нет в Java или C++.

Методы экземпляра будут использовать динамическую версию, обращаясь к своему классу и отправляя ему сообщение:

- (void)setSuit:(NSString *)suit
{
    if ([[[self class] validSuits] containsObject:suit])
    {
        _suit = suit;
    }
}

Теперь воображаемый подкласс PlayingCard может переопределить метод класса validSuits и неявно изменить поведение setSuit:.

person Nikolai Ruhe    schedule 07.08.2014

self может быть экземпляром или классом в зависимости от типа объявленного метода.

- (void)setSuit: — это метод экземпляра, поэтому self — это экземпляр внутри этого объявления метода.

+ (NSUInteger)maxRank — это метод класса, поэтому self — это класс внутри этого объявления метода.

+ (void)classMethod;

- (void)instanceMethod;

- (void)setSuit
{   // self is an instance here
    [self classMethod]; // warning, class method sent to instance
    [self instanceMethod]; // works, instance method sent to instance
}

+ (NSUInteger)maxRank
{   // self is a class here
    [self classMethod]; // works, class method sent to class
    [self instanceMethod]; // warning, instance method sent to class
}

Вы пытались вызвать «метод класса» в self внутри метода экземпляра, где self в «экземпляре».

person Michael Ho Chum    schedule 07.08.2014
comment
В своем последнем предложении вы говорите, что self является переменной экземпляра. Вы, вероятно, смешиваете экземпляр и переменную экземпляра (== ivar), что означает что-то еще в Objective-C. - person Nikolai Ruhe; 07.08.2014
comment
Здоровья, Майкл. Теперь это имеет смысл! - person monster; 09.08.2014

В методе класса self относится к классу (это относится к объекту, представляющему класс, который создает для вас среда выполнения obj-c), поэтому вы можете использовать его для вызова метода уровня класса.

В методе экземпляра self относится к экземпляру. Если вы хотите вызвать метод уровня класса в методе экземпляра, вместо этого вам нужно использовать имя класса.

Java-аналогией методов класса obj-c является статический метод. Ключевое слово this в Java похоже на self, за исключением того, что его нельзя использовать для ссылки на класс.

person david a.    schedule 07.08.2014
comment
Спасибо, Дэвид. Четко и лаконично! Оценил. - person monster; 09.08.2014

setSuit — это метод экземпляра, а validSuits — метод класса. Однако и maxRank, и rankStrings являются методами класса. Методы класса в основном такие же, как статические методы в C++.

person notadam    schedule 07.08.2014
comment
С одной большой разницей: статические методы в C++ не имеют неявной ссылки this, в то время как методы класса в Objective-C имеют (и self является ссылкой на класс). - person Rudy Velthuis; 07.08.2014