Синтезировать свойство в базовый класс 'ivar - PullRequest
3 голосов
/ 16 декабря 2011

У меня есть иерархия объектов модели, которые я буду отображать в подклассах UITableViewCell другого типа. Все решения принимаются на лету относительно того, какой объект модели следует использовать, и соответствующий объект подкласса UITableViewCell создается и затем устанавливает объект модели в объект подкласса UITableViewCell, чтобы он мог извлекать значения из него.

Моя иерархия UITableViewCell выглядит примерно так:

Базовый класс Ячейка иерархии:

@interface BaseCell : UITableViewCell
{
  Base *baseObj_;
}
@end

Подкласс иерархии ячеек:

@interface DerivedCell : BaseCell
{
}
@property (nonatomic, retain) Derived *derivedObject;
@end

@implementation DerivedCell
@synthesize derivedObject = baseObj_;
@end

Базовый класс объекта Model:

@interface Base : NSObject
{
  NSString *title_;
}
@property (nonatomic, retain) NSString *title;
@end

Подкласс модельной иерархии

@interface Derived : Base
{
  NSString *detailedText_;
}
@property (nonatomic, retain) NSString *detailedText;
@end

При этом у меня появляются ошибки в этой строке:

@synthesize derivedObject = baseObj_;

Что гласит:

  1. Свойство производного объекта, пытающегося использовать ivar baseObj_, объявленное в суперклассе BaseCell.

  2. Тип свойства 'outputObject' (Derived *) не соответствует типу ивара 'baseObj_' ('Base * __strong')

Я хочу использовать свойства и синтезировать их, чтобы я мог использовать использование свойств (например, использование точечных обозначений и т. Д.). На данный момент я использовал аксессоры и сеттеры, которые решают проблему:

@interface DerivedCell : BaseCell
{
}
-(Derived*)derivedObject;
-(void)setDerivedObject:(Derived*)newDerivedObject;
@end

Но мне было просто интересно, смогу ли я как-то исправить эти ошибки, используя только свойства.

Спасибо, Радж

Ответы [ 4 ]

2 голосов
/ 24 декабря 2011

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

@interface BaseModel : NSObject 
@end

@interface DerivedModel : BaseModel
@end

@interface BaseCell : UITableViewCell
{
    BaseModel *baseObj_;
}
@property (nonatomic, retain) BaseModel *modelObject;
@end

@interface DerivedCell : BaseCell
@end

@interface DerivedCell (DowntypedPropertyCategory)
@property (nonatomic, retain) DerivedModel *modelObject;
@end

@implementation BaseModel
@end
@implementation DerivedModel
@end
@implementation BaseCell
@synthesize modelObject = baseObj_;
@end
@implementation DerivedCell
@end

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

@interface UnrelatedObject : NSObject
@end

@implementation UnrelatedObject
- (void)unrelatedMethod: (DerivedCell*)dc
{
    DerivedModel* dm = dc.modelObject;
    NSLog(@"dm: %@", dm);
}
@end

Опять же, разница в улове / незначительном факте заключается в том, что для того, чтобы это работало, базовый класс должен определить свойство с тем же именем (или эквивалентные методы получения / установки). Тем не менее, свойство / методы в базовом классе могут быть объявлены (или в случае методов, даже НЕ задержаны) и определены только в файле реализации базового класса, и, таким образом, не будут видны другим файлам, включая заголовок.

Еще одно примечание: при использовании этого подхода вы пропускаете проверки времени компиляции на наличие таких вещей, как несоответствие между спецификаторами свойств ([nonatomic | atomic], [readonly | readwrite], [assign | retain | copy]). Я нашел эту модель невероятно полезной, но есть некоторые потенциальные ловушки, на которые стоит обратить внимание.

2 голосов
/ 22 декабря 2011

Попробуйте приведенный ниже код. Я немного изменил ваш код, как показано ниже

Поскольку вы можете назначить объект класса Base классу Derived в @synthesize, это может быть достигнуто этим способом, я знаю, что вы уже пробовали это, я пробовал это с помощью приведенного ниже кода и смог получить доступ к переменным с точкой, попробуйте код ниже и дайте мне знать, если он не работает

@interface DerivedCell : BaseCell
{
    Derived *derivedObject;
}
@property (nonatomic, retain) Derived *derivedObject;

@end

@implementation DerivedCell
@dynamic derivedObject;
- (void)setDerivedObject:(Base *)baseObj {
    if (self.derivedObject == nil) {
        derivedObject = [[Derived alloc] init];
    }
    derivedObject.detailedText = baseObj.title;
}
- (Derived *)derivedObject {

    return derivedObject;
}

@interface Derived : Base
{
    NSString *detailedText_;
}
@property (nonatomic, retain) NSString *detailedText;
@end

@implementation Derived
@synthesize detailedText = detailedText_;
@end

@interface BaseCell : UITableViewCell
{
    Base *baseObj_;
}
@property (nonatomic, retain) Base *baseObj;
@end

@implementation BaseCell
@synthesize baseObj = baseObj_;
@end

@interface Base : NSObject
{
    NSString *title_;
}
@property (nonatomic, retain) NSString *title;
@end

@implementation Base
@synthesize title = title_;
@end


    Base *b = [[Base alloc] init];
    b.title = @"Hello Raj";
    BaseCell *bc = [[BaseCell alloc] init];
    bc.baseObj = b;

    DerivedCell *dc = [[DerivedCell alloc] init];
    dc.derivedObject = b;

    NSLog(@"Derive dc %@", dc.derivedObject.detailedText);

Другое решение, которое я предоставил, имеет проблему, когда я проверил его

@interface BaseCell : UITableViewCell
    {
        NSString *baseTitle_;
    }
    @property (nonatomic, retain) NSString *baseTitle;
    @end
    @implementation BaseCell
    @synthesize baseTitle = baseTitle_;
    @end


    @interface DerivedCell : BaseCell
    {
        NSString *derivedTitle_;
    }
    @property (nonatomic, retain) NSString *derivedTitle;

    @implementation DerivedCell
    @synthesize derivedTitle = baseTitle;
    @end

Когда я создал экземпляр для класса и как показано ниже

DerivedCell *dCell = [[DerivedCell alloc] init];

dCell.baseTitle = @"Hello";

NSLog(@"%@",dCell.baseTitle);//Output was Hello
NSLog(@"%@",dCell.derivedTitle);//Output was (null)

Он не присвоил значение производному заголовку, если он работает для вас, пожалуйста, дайте мне знать


Другое решение с обращением к памяти

@interface BaseCell : UITableViewCell
{
    NSMutableString *baseTitle_;
}
@property (nonatomic, retain) NSMutableString *baseTitle;
@end

@implementation BaseCell
@synthesize baseTitle = baseTitle_;
@end

@interface DerivedCell : BaseCell
{

}

@property (nonatomic, retain) NSMutableString *derivedTitle;
@end

@implementation DerivedCell
@synthesize derivedTitle;
- (id) init
{
    if ( self = [super init] )
    {
        baseTitle_ = [[NSMutableString alloc] init];
        derivedTitle = baseTitle_;
    }
    return self;
}

@end

    DerivedCell *dCell = [[DerivedCell alloc] init];

    [dCell.baseTitle appendString:@"Hello"];

    NSLog(@"baseTitle : %@",dCell.baseTitle);
    NSLog(@"derivedTitle :%@",dCell.derivedTitle);

Вывод на консоль baseTitle: Hello производныйTitle: Hello

1 голос
/ 22 декабря 2011

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

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

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

- (Base *) baseObj {
    return _baseObj;
}

- (void) setBaseObj:(Base *)val {
    if( val != _baseObj ) {
        [_baseObj release];
        _baseObj = [val retain];
    }
}

Надеюсь, это поможет!

1 голос
/ 16 декабря 2011

Надеюсь, я правильно понял вопрос, как насчет того, чтобы напечатать модель как id?

@interface BaseCell : UITableViewCell
@property(retain, nonatomic) id model;
@end

@implementation BaseCell
@synthesize model;
@end

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...