Synthesize property to a Base class' ivar - objective-c

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:
Property 'derivedObject' attempting to use ivar 'baseObj_' declared in super class BaseCell.
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!

Related

How to change a property class in sub class in Objective-C

I am trying to re-write some code of old project, and ideally I want to achieve below code style, but I got a lot of compile error saying dataModel don't have getLineColor method.
The abstract question is, can I change an inherited object A's class in sub view controller to a sub class A2, while the object A in parent view controller is class A1 that is the super class of A2, and how can I do it correctly? Thank in advance.
Update: I compile it, but I have met another run time error, I tried to overwrite the setter of dataModel in sub view controller. How to correctly write the setDataModel in sub class?
#implementation SubViewController
#pragma mark - setter of dataModel
- (void)setDataModel:(ChartModel *)dataModel { // it stucks at this func name
#end
error trace is
[SubChartViewController setDataModel:](self=0x00000000, _cmd=0x00000000, dataModel=0x00000031) + 12 at BDPAxisChartViewController.m:295, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=2, address=0xbf774ffc)
EDIT:
#interface ChartModel : NSObject
-(BOOL)chartModelhasData;
#end
#interface LineChartModel : chartModel
-(void)getLineColor;
#property (nonatomic, strong) NSArray* seriesNameArray;
#end
#interface ChartViewController: UIViewController
#property (nonatomic, strong) ChartModel *dataModel;
-(void)layoutChartCanvas;
#end
#implementation ChartViewController
-(void)layoutChartCanvas {
if ([self.dataModel chartModelhasData]) {
self.view.hidden = NO;
}
}
#end
#interface LineChartViewController : ChartViewController
// pay attension here, same name but a sub class of chartModel
#property (nonatomic, strong) LineChartModel *dataModel;
-(void)drawLine;
#end
#implementation LineChartViewController
-(void)drawLine {
UIColor *color = [self.dataModel getLineColor];
[self drawLine];
NSArray *tempArray = [self.dataModel.seriesNameArray copy];
}
#end
Probably you want to say:
Can I change the class C of a property already declared in a base class in a subclass to a subclass of C?
This is the set-up
#interface PropertyClass : NSObject
#end
#interface PropertySubclass : PropertyClass
- (void)method; // Additional method
#end
#interface HolderClass : NSObject
#property PropertyClass *property; // Property is of base class
#end
#implementation HolderClass
#end
#interface HolderSubclass : HolderClass
#property PropertySubclass *property; // Property is of subclass
#end
I have no problem to access the property subclass' method in the holder's subclass:
#implementation HolderSubclass
- (void)useIt
{
[self.property method]; // No error or warning
}
#end
In addition to my comment below, I suspect that something like this happens:
// Create a subclass' object
HolderSubclass *object1 = [HolderSubclass new];
…
// Refer this object from a reference that is typed to HolderClass
// **This applies to all usages of self inside #implementation HolderClass**
HolderClass *object2 = object1; // Of course more complex
id value = [object2 method]; // Error
This error is correct. If this is the error, it is solvable. But first we have to clarify that.
BTW: This has nothing to do with better OOP. First of all this is a problem of class based programming languages, not of object orientated programming languages. Second I cannot see that this set-up will break a rule of class based or object orientated programming, esp. it fulfills Liskov's rule.
http://en.wikipedia.org/wiki/Liskov_substitution_principle
One thing you could do is declare a LineChartModel variable and not property and #synthesize dataModel to that ivar:
#interface LineChartViewController: ChartViewController {
LineChartModel *_lineChartModel;
}
-(void)drawLine;
#end
#implementation LineChartViewController
synthesize dataModel = _lineChartModel;
....
So from the outside it looks like you have a ChartModel, but inside the class you have LineChartModel. You will have to apply changes to _lineCharModel directly inside your class.
HOWEVER this is NOT my definition of better OOP!!! There's clearly a fault with the design if you need to cast your variables in subclass.
Another option I discovered after pasting this code into the editor is just use self.variable (which by the way, you should've already been doing).
#interface ChartModel : NSObject
- (BOOL)chartModelhasData;
#end
#interface LineChartModel : ChartModel
- (UIColor *)getLineColor;
#end
#interface ChartViewController: UIViewController
#property (nonatomic, strong) ChartModel *dataModel;
- (void)layoutChartCanvas;
#end
#implementation ChartViewController
- (void)layoutChartCanvas {
if ([self.dataModel chartModelhasData]) {
self.view.hidden = NO;
}
}
#end
#interface LineChartViewController : ChartViewController
// pay attension here, same name but a sub class of chartModel
#property (nonatomic, strong) LineChartModel *dataModel;
- (void)drawLine;
#end
#implementation LineChartViewController
- (void)drawLine {
UIColor *color = [self.dataModel getLineColor];
[self drawLine];
}
#end

Readonly, non-mutable, public and readwrite, mutable, private #property: more information?

I want to expose an NSArray to my user (and I want them to only read it), but in my class, I want to use an NSMutableArray.
I tried the following code and it does not raise any warning:
// In the .h
#interface MyClass : NSObject <NSApplicationDelegate>
#property (nonatomic, readonly) NSArray * test ;
#end
and
// In the .m
#interface MyClass ()
#property (nonatomic, strong, readwrite) NSMutableArray * test ;
#end
#implementation MyClass
- (id)init
{
self = [super init];
if (self)
{
self.test = [[NSMutableArray alloc] init] ;
}
return self;
}
#end
But, if I try to access the #property test from within my class, I can use the method addObject:. So, I guess what precedes is not possible.
Why is there no warning as it is?
I don't think that mixing property type would be a good practice. Instead I would create an accessor that returns a copy of the private mutable array. This is more conventional. Please note, don't use self for property access in your -init: method:
// In the .h
#interface MyClass : NSObject <NSApplicationDelegate>
- (NSArray *)test;
#end
// In the .m
#interface MyClass ()
#property (nonatomic, strong) NSMutableArray *aTest;
#end
#implementation MyClass
- (id)init
{
self = [super init];
if (self)
{
_aTest = [[NSMutableArray alloc] init] ;
}
return self;
}
- (NSArray *)test
{
return [self.aTest copy];
}
#end
The #property is just syntax sugar which automatically creates getter/setter methods for you. With the readonly in the .h file only the getter method will be created for the public but by overriding it in the .m file you get both methods in your implementation.
readwrite is the default (see here) so even if leave out readwrite put still have the #property in you implementation file you will get a setter method. It is good practice to explicitly write readwrite then in your .m file so you and other will get a hint that this variable might only be declared read only in the .h file.

Property of two classes [duplicate]

This question already has an answer here:
How to make a property of a property
(1 answer)
Closed 9 years ago.
I want to have a result that looks like this player.type.property, An example of this is with UILabel, self.label.text. The .text being the property of the two classes.
A suggestion I have had is to do something like this:
player.type = [[MyCustomObject alloc] init];
player.type.property = #"value";
Although I'm not quite sure exactly how to go about doing this correctly, every method I have tried doesn't work.
Here is what I have tried:
Marketplace.h
#import "Item.h"
#interface Marketplace : NSObject
#property (nonatomic, assign) Item *market;
Item.h
#interface Item : NSObject
#property (nonatomic, assign) int price;
Starter.m
#import "Marketplace.h"
#import "Item.h"
#implementation MainGameDisplay
{
Marketplace *market;
Item *itemName;
}
-(void) executedMethod {
market.itemName = [[market alloc] init];
//2 errors: "Property 'itemName not found on object of type 'MarketPlace'" and "No visible #interface for 'MarketPlace' declares the selector alloc"
market.itemName.price = 5; //"Property 'itemName' not found on object of type 'Marketplace*'"
}
Each pointer to class object must be alloc init, so you need to over-write the -(id)init inside its class.
Item.h
#interface Item : NSObject
#property (nonatomic) NSInteger price;
Marketplace.h
#import "Item.h"
#interface Marketplace : NSObject
#property (nonatomic, strong) Item *item;//Item is a class, must use strong or retain
Marketplace.m
-(id)init{
if (self = [super init]) {
self.item = [[Item alloc] init];//Item must alloc together when MarcketPlace init
}
return self;
}
*Then you just init the Marketplace
#implementation MainGameDisplay
{
Marketplace *market;
Item *itemName;
}
-(void) executedMethod {
market = [Marketplace alloc] init];
//Now you can access
market.item.price = 5;
}
1 . make a Interface named PlayerType Put some property there and synthesize them.
2. now make a Interface named Player and import the PlayerType Interface there.
3. make a property of PlayerType Interface like #property(nonatomic, strong) PlayerType *type.
now made variable of Player it will allow you to access property of a property.

Objective C: use instance class in other class

In my code, in an class I have an ivar
FirstClass *first;
and I can use first within an instance of this class.
But if I want to access first from another object instance (or even another class), how can I do that?
I assume you're talking about using FirstClass in another source file than its own, right?
In this case you'd have to import its header by adding this to the top of your second class' ".m"-file:
#import "FirstClass.h"
If you also need to reference in your second class' header ".h"-file, then you can add a
#class FirstClass;
before the #interface block. This will tell the compiler that it should consider a class of that name to be existant, but to not bother you with warnings unless you forget to import the given first class' ".h" file in the second class' ".m" file.
To allow access from foreign objects to your SecondClass' firstClass iVar you'll need to implement a getter method for firstClass.
This is done with
#property (nonatomic, readwrite, retain) FirstClass *firstClass;
in the #interface block, and
#synthesize firstClass;
in the #implementation block.
With this set up you can then either call [secondClassInstance firstClass]; or access it via the dot syntax secondClassInstance.firstClass;.
My sample will also synthesize a setter method called setFirstClass:. To make the property read-only, change readwrite to readonly in the #property declaration.
Sample:
FirstClass.h:
#import <Cocoa/Cocoa.h>
#interface FirstClass : NSObject {
#private
}
//method declarations
#end
FirstClass.m:
#import "FirstClass.h"
#implementation FirstClass
//method implementations
#end
SecondClass.h:
#import <Cocoa/Cocoa.h>
#class FirstClass;
#interface SecondClass : NSObject {
#private
FirstClass *firstClass;
}
#property (nonatomic, readwrite, retain) FirstClass *firstClass;
//method declarations
#end
SecondClass.m:
#import "SecondClass.h"
#import "FirstClass.h"
#implementation SecondClass
#synthesize firstClass;
- (id)init {
if ((self = [super init]) != nil) {
firstClass = [FirstClass alloc] init];
}
return self;
}
- (void)dealloc {
[firstClass release];
[super dealloc];
}
//method implementations
#end
I would use a property. Probably in your header of your second class something like
#property (nonatomic, retain) FirstClass *first;
and in your implementation
#synthesize first;
Than when you create an object of your SecondClass
SecondClass *second = [[SecondClass alloc] init];
you can use
second.first

Objective-C property that is readonly publicly, but has a private setter

I'd like to use the #property syntax to declare a synthesized property that is publicly readonly but has a setter that can be called privately from within the class.
Since it's Objective-C, this basically means that the setFoo: method would be synthesized, but calling it outside of the class itself would result in a warning (unrecognized selector). To trigger the warning I have to declare the property readonly; is there any way to force a synthesized setter that is only available within the class?
I think what you're looking for are called class extensions. You would declare the property read-only in the header:
#interface MyClass : NSObject {
}
#property (readonly, assign) NSInteger myInteger;
#end
Then redeclare in your class extension in the implementation file:
#interface MyClass ()
#property (readwrite, assign) NSInteger myInteger;
#end
#implementation MyClass
#end
For more check out Apple's documentation
I might be late, but without extension i did using the following technique
#interface MyClass : NSObject {
NSString * name;
}
#property (readonly, strong) NSString * name;
#end
on the other hand in implementation file
#implementation MyClass
#synthesize name;
- (id)initWithItems:(NSDictionary *)items {
self = [super init];
if(self)
{
name = #"abc";
}
return self;
}
#end
doing so it will set your value and will be accessible as readonly.
Thanks.