Подавить "Определение метода для% @ не найдено" - PullRequest
0 голосов
/ 15 мая 2018

В интерфейсе подклассов я объявляю метод, реализованный суперклассом, как возвращающий другой тип объекта, поскольку подкласс является специализированным. Однако мне не нужно реализовывать метод в подклассе - реализация суперкласса работает нормально. К сожалению, этот шаблон теперь, кажется, дает мне Method definition for … not found error в последних версиях XCode.

Я знаю, что могу отключить все таких предупреждений, отключив -Wincomplete-implementation с diagnostic push и pop вокруг @implementation, но я бы Скорее не принимайте такой крайний подход, так как предупреждение полезно в других контекстах. (Если это может пометить отдельные методы, чтобы замолчать, я все уши)

Вот одна из нескольких примеров реализации, которые демонстрируют эту проблему:

test.h

@interface GeneralItem : NSObject

@end

@interface GeneralGroup : NSObject

- (GeneralItem *)item;

@end


@interface ItemA : GeneralItem

@end

@interface GroupA : GeneralGroup

- (ItemA *)item;

@end

Test.m

#import "Test.h"

@implementation GeneralGroup

- (GeneralItem *)item
{
    return nil;
}

@end

@implementation GroupA
// Warning: Method definition for 'item' not found

@end

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

Я бы хотел избежать реализации метода и простого вызова super, так как я считаю его полностью избыточным кодом, но если кто-то может подтвердить мне, что компилятор оптимизирует прямой вызов super, так что среда выполнения может делать свое обычное дело без необходимости делать пит-стоп для подкласса, и это единственный способ заставить замолчать это предупреждение, пусть будет так.

<ч />

Редактировать 1

Вот несколько других сценариев, в которых это происходит.

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

BlockTest.h

@interface GeneralItem : NSObject

@end

@interface GeneralGroup : NSObject

@end


@interface ItemA : GeneralItem

@end

@interface GroupA : GeneralGroup

- (void)loadItemWithCompletionHandler:(void (^)(ItemA *item))completionHandler;

@end


@interface ItemB : GeneralItem

@end

@interface GroupB : GeneralGroup

- (void)loadItemWithCompletionHandler:(void (^)(ItemB *item))completionHandler;

@end

BlockTest.m

#import "BlockTest.h"

@interface GeneralGroup ()

- (id)item;
- (void)loadItemWithCompletionHandler:(void (^)(Item *item))completionHandler;

@end

@implementation GeneralGroup

- (void)loadItemWithCompletionHandler:(void (^)(Item *item))completionHandler;
{
    if (completionHandler) completionHandler(self.item);
}

@end

@implementation GroupA
// Warning: Method definition for 'loadItemWithCompletionHandler:' not found
// A custom implementation of -item can be here, for instance

@end

Следует отметить, что вы не получите предупреждения, если вы объявите метод как - (void)loadItemWithCompletionHandler:(void (^)(Item *item))completionHandler; в общедоступном заголовке в GroupA, но вы do получите предупреждение "определение метода не найдено" при изменении тип, который будет подклассом первоначально указанного типа.

<ч />

Во-вторых, вы просто хотите дать больше контекста методу, предоставленному суперклассом (используя NSObject copy здесь, но это относится к любому методу, типизированному как id, который мы хотели бы повторно объявляйте с более точными типами, чтобы такие вещи, как связывание свойств, работали менее безболезненно

Subclass.h

@interface Subclass : NSObject

- (instancetype)copy;

@end

Subclass.m

#import "Subclass.h"

@implementation Subclass
// Warning: Method definition for 'copy' not found

@end

Ответы [ 2 ]

0 голосов
/ 15 мая 2018

Это похоже на возможность каким-то образом указать, что -[GeneralGroup item] может вернуть экземпляр чего-то, что является подклассом GeneralItem.

Самый быстрый способ сделать это - использовать аннотацию __kindof в -[GeneralGroup item], указывая, что возвращаемое значение является GeneralItem или любым подклассом:

@interface GeneralGroup : NSObject
- (__kindof GeneralItem *)item;
@end

@interface GroupA : GeneralGroup
@end

Поскольку вы больше не переименовываете -item в GroupA, вы не получите предупреждение в его реализации - но вывсе еще можете переопределить -item, если хотите, возвращая экземпляр ItemA.

Если вы хотите получить более конкретную информацию, вы можете рассмотреть возможность использования облегченных обобщений :

@interface GeneralGroup<__covariant ItemType: GeneralItem*> : NSObject
- (ItemType)item;
@end

Это равносильно тому же, что и компилятору, но выражает немного больше информации другим разработчикам, читающим заголовок: они знают, что -item возвращает GeneralItem или подкласс, и - из-за присутствияатрибут __covariant - что подклассам GeneralGroup может потребоваться более конкретный связанный тип элемента, чем GeneralItem.

Вызывающие абоненты cзатем свободно введите возвращенное значение -[GroupA item] как ItemA:

GroupA *ga = [[GroupA alloc] init];
ItemA *ia = ga.item; // no warning

При дальнейшем рефакторинге вы можете обнаружить, что вам не нужны определенные типы групп для каждого элемента;вместо этого часто может быть достаточно реализации общей группировки для нескольких типов элементов с определенным типом элемента, объявленным в локальных экземплярах группы.(Вот как работают такие коллекции, как NSArray: они объявляют общий тип содержимого объекта, а конкретные экземпляры массива аннотируются как содержащие конкретный объект в точке, в которой они созданы или переданы.)

0 голосов
/ 15 мая 2018

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

Перед строкой, которая вызывает предупреждение:

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wthe-error-to-ignore"

А после строки:

// this resets clang's diagnostic system to the state it was in before we called "push"
#pragma clang diagnostic pop
...