#objective-c #categories #superclass #super
#objective-c #Категории #суперкласс #супер
Вопрос:
У меня есть вызываемый класс Animal
, который является подклассом BaseEntity
. У меня есть протокол CoreDataConversions
, который называется как Animal
и BaseEntity
категории, которые соответствуют. В протоколе у меня определен метод:
- (instancetype)initWithManagedObject:(NSManagedObject *)managedObject dataManager:(id<DataManager>)dataManager
BaseEntity
категория прекрасно реализует это. Затем, потому Animal
что это подкласс BaseEntity
, к которому я обращаюсь [super initWithManagedObject:managedObject dataManager:dataManager];
в категории animal.
Я получаю сбой с сообщением:
[Animal setDataManager:]
нераспознанный селектор отправлен экземпляру…
Я установил точку останова при вызове super и вышел из системы следующим образом:
po [self class] // Prints Animal
po [self superclass] // Prints BaseEntity
po [super class] // Prints Animal
po [super superclass] // Prints BaseEntity
Итак: почему Animal
категория super
выполняет вызовы Animal
, хотя вызовы superclass
явно ссылаются на BaseEntity
?
Редактировать:
Вот код, который вызывает сбой:
- (id)insertEntityForClass:(Class)class
{
if (![class conformsToProtocol:@protocol(CoreDataConversions)]) {
return nil;
}
// A class method defined in CoreDataConversions
NSString *entityName = [class coreDataEntityName];
// Insert core data entity
NSManagedObject *managedObject = [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:self.currentMainContext];
// Init with the managed object
id entity = [[class alloc] initWithManagedObject:managedObject dataManager:self];
return entity;
}
У меня есть тест, который просто вызывает Animal *animal = [dataManager insertEntityForClass:[Animal class]];
, а затем подтверждает существование animal
.
Animal
реализует метод следующим образом:
- (instancetype)initWithManagedObject:(NSManagedObject *)managedObject dataManager:(id<RHDataManager>)dataManager
{
self = [super initWithManagedObject:managedObject dataManager:dataManager];
if (self) {
// TODO
}
return self;
}
И BaseEntity
реализует метод следующим образом:
- (instancetype)initWithManagedObject:(id)managedObject dataManager:(id<RHDataManager>)dataManager
{
self = [super init];
if (self) {
self.dataManager = dataManager;
}
return self;
}
Комментарии:
1. Можете ли вы опубликовать код вокруг сбоя, указав класс, в котором он находится. Просто чтобы прояснить вопрос.
2. @danh только что добавил код вокруг сбоя
3. Возможно, я слишком плотный, но я не вижу вызова
setDataManager
в опубликованном коде4. Да, вы правы, оказывается, это связано с объявлениями свойств.
5. Обратите внимание, что при таком подходе очень легко перейти к неопределенному поведению. Категория не может переопределять методы суперкласса, и если две категории реализуют один и тот же метод, не определено, какой из них вызывается. Вы не совсем это делаете, так что это на самом краю определенного поведения. Цель категорий — добавить независимую функциональность, а не участвовать в наследовании. Есть много способов сделать это незаметно (и на удивление) неправильно, когда категории задействованы в переопределении того, как вы это делаете здесь.
Ответ №1:
Оказывается, это на самом деле связано с объявлениями свойств. У меня были readonly
свойства, т.Е. @property (nonatomic, strong, readonly) id<DataManager> dataManager;
Определенные в исходном классе.
В категории я переопределил свойство следующим образом:
@property (nonatomic, strong) id<DataManager> dataManager;
Сбой был вызван тем, что для параметра не был установлен Animal
параметр dataManager
, поскольку он все еще считывал его как readonly
.