Использование self в методе класса - PullRequest
9 голосов
/ 27 января 2012

Я наткнулся на этот код в ShareKit, и я не понимаю, что имел в виду автор, используя self в методе класса.Предупреждение: Несовместимые типы указателей отправляют «Класс» в тип параметра id<FBSessionDelegate>. Я хочу убрать эти предупреждения, чтобы видеть те, которые могут пострадать позже.Что я могу / должен сделать, чтобы это не сломало?

Это файл SHKFacebook.m и имя класса SHKFacebook

+ (void)logout
{
    FBSession *fbSession; 

    if(!SHKFacebookUseSessionProxy){
        fbSession = [FBSession sessionForApplication:SHKFacebookKey
                                                 secret:SHKFacebookSecret
                                               delegate:self];

    }else {
        fbSession = [FBSession sessionForApplication:SHKFacebookKey
                                        getSessionProxy:SHKFacebookSessionProxyURL
                                               delegate:self];
    }

    [fbSession logout];
}

1 Ответ

14 голосов
/ 27 января 2012

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

поэтому метод класса new может быть реализован так:

+ (id)new
{
  return [[self alloc] init];
}

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

ex a:

NSArray * a = [NSArray new]; // << a is an NSArray

ex b:

NSMutableArray * a = [NSMutableArray new]; // << a is an NSMutableArray

см. примечание ниже.

Итак, с чем вы действительно столкнулись, так это с гарантией того, что в протоколе присутствуют только методы экземпляра, и что методы (Class) self планируют принять методы экземпляра в протоколе.

Что касается дизайна... ну, скажем так, я бы так не написал.Синглтон был бы яснее и правильнее, но я даже не люблю синглтоны, поэтому я бы не пошел по этому пути.

Предупреждение выдается, потому что экземпляр Class (чтопередано) принимает @protocol, заданное параметром delegate.Экземпляр Class не является экземпляром класса.Объявление протокола действительно применяется к экземплярам класса.Например, если вы примете NSLocking, ожидает ли компилятор реализации методов класса для каждого метода экземпляра, объявленного в протоколе?Ответ: никогда.Реализация, с которой вы работаете, - это IMO, один из тех случаев, когда это неправильное использование языка, но оно работает.

Чтобы прояснить терминологию:

Экземпляр "Class"is self в методе класса:

+ (void)foo { self; }

" Экземпляр класса "равен self в методе экземпляра:

- (void)foo { self; }

На практике -[NSObject conformsToProtocol:]is +[NSObject conformsToProtocol:] и +[NSObject class] просто возвращает self, поэтому ошибок при выполнении нет.

Тогда мне все еще не ясно, почему я получаю предупреждение, если код соответствуеткритерии, которые вы описали.

Критерии, которые я описал, применимы к выполнение , но они отличаются от семантики языка - таким образом, компилятор абсолютно прав в этом.

Для решения проблемы: Невозможно сказать компилятору «Экземпляр моего класса соответствует протоколу », поскольку декларация принятия применяется к экземплярам класса .

У вас есть два основных варианта:

  1. Чистый, правильный подход: используйте экземпляр класса и реализуйте протокол, как определено.

  2. или Typecast экземпляр класса к протоколу:

    идентификатор делегата = (идентификатор) себя;fbSession = [FBSession sessionForApplication: SHKFacebookKey getSessionProxy: SHKFacebookSessionProxyURL делегат: делегат];

Если вы выберете # 2, это может помочь определить методы экземпляра протокола для использования методов класса, напримерИтак:

+ (void)protocolMethod { /* do stuff */ }
- (void)protocolMethod { [self.class protocolMethod]; }

, что также подразумевает, что вам никогда не нужны экземпляры.Это помогло бы, потому что это добавит предупреждения, если протокол изменится.Эти предупреждения будут распространяться на методы класса, если вы будете следовать соглашению.

Чтобы уменьшить шум, вы можете также рассмотреть возможность создания некоторого метода, чтобы уменьшить типовое преобразование до одного местоположения:

+ (id<SomeProtocol>)sharedSomeProtocolDelegate
{
  return (id<SomeProtocol>)self;
}

- (id<SomeProtocol>)sharedSomeProtocolDelegate
{
  return [[self class] sharedSomeProtocolDelegate];
}

, затем выможно просто написать:

fbSession = [FBSession sessionForApplication:SHKFacebookKey
                             getSessionProxy:SHKFacebookSessionProxyURL
                                    delegate:[self sharedSomeProtocolDelegate]];

(обратите внимание, что реализации этих типов на самом деле являются кластерами классов, и вы увидите что-то другое в отладчике или при печати)

...