How to change a property class in sub class in Objective-C - 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

Related

Wrong class type in objective C

I have a parent class and a child class. GameObjectBase (parent) GameObjectPlayer(child). When I override a method in Child class and call it using
[myPlayerClass showNextFrame]
It is calling the parent class one. It turns out in the debugger, I see the myPlayerClass was indeed class type GameObjectBase (which is the parent class) How come?
GameObjectBase.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#class GameLayer;
#interface GameObjectBase : NSObject
{
/* CCSprite *gameObjectSprite; // Sprite representing this game object
GameLayer *parentGameLayer; */ // Reference of the game layer this object
// belongs to
}
#property (nonatomic, assign) CCSprite *gameObjectSprite;
#property (nonatomic, assign) GameLayer *parentGameLayer;
// Class method. Autorelease
+ (id) initWithGameLayer:(GameLayer *) gamelayer
imageFileName:(NSString *) fileName;
// "Virtual methods" that the derived class should implement.
// If not implemented, this method will be called and Assert game
- (void) update: (ccTime) dt;
- (void) showNextFrame;
#end
GameObjectPlayer.h
#import <Foundation/Foundation.h>
#import "GameObjectBase.h"
#interface GameObjectPlayer : GameObjectBase
{
int direction;
}
#property (nonatomic) int direction;
#end
GameLayer.h
#import "cocos2d.h"
#import "GameObjectPlayer.h"
#interface GameLayer : CCLayer
{
}
// returns a CCScene that contains the GameLayer as the only child
+(CCScene *) scene;
#property (nonatomic, strong) GameObjectPlayer *player;
#end
When I call examine in debugger what type "temp" is in this function inside GameLayer class, it's giving parent class GameObjectBase instead of subclass GameObjectPlayer
- (void) update:(ccTime) dt
{
GameObjectPlayer *temp = _player;
[temp showNextFrame];
}
You are just assigning the value. Please allocate it & then assign it.
Hope it resolves your issue.

property public and private but not protected?

I'm learning the objective C language and i ask a simple question,
when i do that :
// ParentClass.h
#interface ParentClass : NSObject
#property (read, strong) NSString *parentPublicStr;
#end
// ParentClass.m
#interface ParentClass ()
#property (readwrite, strong) NSString *parentPrivateStr;
#end
#implementation ParentClass
#synthesize parentPublicStr;
#synthesize parentPrivateStr;
#end
// Subclass SubClass.h
#interface SubClass : ParentClass
- (void) test;
#end
#implementation SubClass
- (void) test
{
// Its not possible to do that : [self setParentPrivateStr:#"myStrin"]
// And for parentPublicStr, it is public property so not protected, because i can change the value
// in main.c, and it's so bad..
}
#end
I would like create a property that is protected :x
Thx you. (Sorry for my english)
Objective-C does not provide for protected methods/properties. See this question.
Edit: Also see this answer. You can still practice encapsulation by declaring the property in a class extension and including the extension in subclasses.
You can manually create an ivar for the property as long as you use the same name prefixed with an underscore:
#interface ParentClass : NSObject
{
#protected
NSString* _parentPublicStr;
}
#property (read, strong) NSString *parentPublicStr;
#end
That makes the synthesized ivar for the property #protected (default is #private) and subclasses can then use the super class' ivar directly.

Objective-C calling a delegate method from a class where the delegate method resides within another class

If I have a delegate which resides within its own header file myDelegate.h. Then I have a class (ClassOne) that implements the delegate protocol (therefore implementing the delegate function(s)). Then I create another class (ClassTwo) that has a instance variable of myDelegate. Can I then use this variable to call the function that resides in ClassOne?
Here is the code:
//myDelegate.h
#protocol myDelegate <NSObject>
- (BOOL)myFunction:(NSString*)sString;
#end
//ClassOne.h
#interface ClassOne : NSObject <myDelegate> {
}
- (BOOL)myFunction:(NSString*)sString;
#end
//ClassOne.m
#import "ClassOne.h"
#implementation ClassOne
- (BOOL)myFunction:(NSString*)sString
{
//do stuff
}
#end
//ClassTwo.h
#import "myDelegate.h"
#interface ClassTwo : NSObject {
id<myDelegate> del;
}
#property (nonatomic, retain) id<myDelegate> del;
#end
//ClassTwo.m
#import "ClassTwo.h"
#implementation ClassTwo
- (void)aFunction:(NSString*)string
{
[del myFunction:string];
}
#end
Yes, that is exactly right.
Except myDelegate should be MyDelegate. It's not a syntax error and will execute perfectly, but standard objective-c conventions say that you should never define a delegate with a lowercase first character.

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:
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!

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