简体   繁体   中英

Synthesize property to a Base class' ivar

I have a hierarchy of model objects which I will be displaying on different type of UITableViewCell subclasses. All decision is made on the fly as to which model object should be used and corresponding UITableViewCell subclass' object is spawned and then set the model object to the UITableViewCell's subclass object so that it can fetch values from it.

My UITableViewCell hierarchy is something like this:

The base class Cell hierarchy:

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

The subclass of cell hierarchy:

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

@implementation DerivedCell
@synthesize derivedObject = baseObj_;
@end

The base class of Model object:

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

The subclass of model hierarchy

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

When I do so, I am having errors in this line:

@synthesize derivedObject = baseObj_;

Which reads:

  1. Property 'derivedObject' attempting to use ivar 'baseObj_' declared in super class BaseCell.

  2. Type of property 'derivedObject' (Derived*) does not match type of ivar 'baseObj_' ('Base * __strong')

I want to use properties and synthesize them so that I can leverage the uses of properties (like using dot notation etc.). I have for now used accessors and setters which solves the problem:

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

But I was just wondering if I could somehow fix these errors to use the properties only.

Thanks, Raj

Try the below code I have modified your code a bit as shown below

Since you can assign class Base object to class Derived in @synthesize, it can be achieved by this way, I know you have tried it already, I have tried it with the below code and able to access the variables with dot, try the below code and let me know if it is not working

@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);

Another Solution which I have provided has an issue when I checked it

@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

When I created instance for the class and as shown below

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

dCell.baseTitle = @"Hello";

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

It didn't assign the value to derivedTitle, If it is working for you please let me know


Another solution with memory referncing

@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);

Console Output baseTitle : Hello derivedTitle :Hello

One pattern I've used for situations like this is to re-declare the property in a category on the derived class. The one structural change this approach requires from the code you posted is that it requires a same-named property (or equivalent getter/setter methods) to be defined in the base class. Consider the following snippet:

@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

In this pattern, the base class declares the iVar and the base-typed property, and synthesizes the implementation. The derived class declares the downcast-typed property in a category. Being in a category, the compiler won't force you to implement methods for that property. This gets you out of trying to synthesize against a superclass's iVar, instead relying on implementations that exist in the superclass, but declaring them to be of a different type. At runtime, the runtime will just end up calling the superclass methods (since Obj-C method dispatch is based on selector only, and does not have multiple dispatch .) As a result, clients of these properties can do stuff like this without any compile time warnings or errors:

@interface UnrelatedObject : NSObject
@end

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

Again, the catch/minor difference is that in order for this to work, the base class must define a property of the same name (or equivalent getter/setter methods). That said, the property/methods in the base class could be declared (or in the case of methods, NOT even delayed) and defined in the base class's implementation file only, and thus would not be visible to other files merely including the header.

One other note: by using this approach you're missing out on compile time checks for things like mismatch between the property specifiers ([nonatomic|atomic], [readonly|readwrite], [assign|retain|copy]). I've found this pattern incredibly useful, but there are some potential pitfalls to keep an eye out for.

I hope I understand the question correctly, how about typing the model as id ?

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

@implementation BaseCell
@synthesize model;
@end

Then the derived cells can use whatever model classes they want.

When you initialize an instance variable through synthesize, that variable is not accesible from any class that may inherit it.

It looks like you may have been trying to point synthesize to a public instance variable and I'm not sure if that is possible. It may be trying to declare a new variable with the same name which I'm sure would generate some compiler warnings at the least since that new declaration would hide an existing one and is less accessible.

You could simply write your own getter and setter to expose the instance variable.

- (Base *) baseObj {
    return _baseObj;
}

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

Hope this helps!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM