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
Related
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
Let's assume I have two classes (ClassA, ClassB)
ClassA.h
#interface ClassA : SomeSuperClass
#property (some Attributes) ClassB *classB;
#property (some Attributes) NSString *someString;
#end
Now my problem:
I want to access the NSString someString in the classB instance. Whats the best way to achieve this?
I was thinking of the two solutions:
Creating a protocol in classB and let classA respond to this protocol. The only method in this protocol would be to access/change this string.
The second solution I came up with is give this string as a parameter after creating an instance of classB, then classB can store the reference to it and can change it whenever it wants.
Both of solution seem working to me (didn't test it, just assume it), but also they seem to me a bit of an overkill (especially the first one)
Let me know whats the best way to do this, Thanks.
If you made a weak reference to B's parent (A), then you could safely access it's properties without resorting to overkill or creating retain cycles.
#interface ClassA : SomeSuperClass
#property (some Attributes) ClassB *classB;
#property (some Attributes) NSString *someString;
#end
#implementation
-(id)init {
if (self = [super init]) {
_classB = [[ClassB alloc]initWithParent:self];
}
}
#end
#class ClassA;
#interface ClassB : SomeSuperClass
#property (nonatomic, weak) ClassA *classA;
-(id)initWithParent:(ClassA*)parent;
#end
#import "ClassA.h"
#implementation
-(id)initWithParent:(ClassA*)parent {
if (self = [super init]) {
_classA = parent;
NSLog(#"%#", self.classA.someString); //perfectly legal
}
}
#end
In the superclass MyClass:
#interface MyClass : NSObject
#property (nonatomic, strong, readonly) NSString *pString;
#end
#implementation MyClass
#synthesize pString = _pString;
#end
In the subclass MySubclass
#interface MySubclass : MyClass
#end
#implementation MySubclass
- (id)init {
if (self = [super init]) {
_pString = #"Some string";
}
return self;
}
The problem is that the compiler doesn't think that _pString is a member of MySubclass, but I have no problem accessing it in MyClass.
What am I missing?
The instance variable _pString produced by #synthesize is private to MyClass. You need to make it protected in order for MySubclass to be able to access it.
Add an ivar declaration for _pString in the #protected section of MyClass, like this:
#interface MyClass : NSObject {
#protected
NSString *_pString;
}
#property (nonatomic, strong, readonly) NSString *pString;
#end
Now synthesize the accessors as usual, and your variable will become accessible to your subclass.
I am familiar with this problem. You synthesize the variable in your .m class, so it is not imported along with the header since the _pString variable will be created as part of the implementation, and not the interface. The solution is to declare _pString in your header interface and then synthesize it anyway (it will use the existing variable instead of creating a private one).
#interface MyClass : NSObject
{
NSString *_pString; //Don't worry, it will not be public
}
#property (nonatomic, strong, readonly) NSString *pString;
#end
The given answer works perfectly fine. This is an alternative answer, that apparently Apple likes a bit more.
You can define a private extension of your class, a MyClass+Protected.h file, which needs to be included in MyClass.m and MySubclass.m.
Then, in this new file, you redefine the property as readwrite.
#interface MyClass ()
#property (strong, readwrite) NSString * pString;
#end
This alternative allows you to use the accessor self.pString rather than the ivar _pString.
Note: you still need to keep the definition of pString in your MyClass.h as is.
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!
If you synthesize a custom class, do getters and setters get created for it?
This is the custom class I created.
// MyClass.h
#import <Foundation/Foundation.h>
#interface MyClass : NSObject <NSCoding> {
NSString *string1;
NSString *string2;
}
#property (nonatomic, retain) NSString *string1;
#property (nonatomic, retain) NSString *string2;
#end
Here I declare an object of that class as a property
// DetailViewController.h
#import <UIKit/UIKit.h>
#import "MyClass.h"
#interface DetailViewController : UIViewController {
MyClass *myObject;
}
#property(nonatomic, retain) MyClass *myObject;
#end
Here I synthesize the object.
#import "DetailViewController.h"
#import "MyClass.h"
#implementation DetailViewController
#synthesize myObject;
So does it have getters and setters?
When I try to run this code inside RootViewController.m
DetailViewController.myObject = [theArray objectAtIndex:indexPath.row];
I get an error saying "Accessing unkown 'setMyObject:' class method. Object cannot be set - either readonly property or no setter found.'
Only if you declare the desired instance variables as properties, then synthesize propname;, will getters and setters be created. Now, what kind of code goes into the getters and setters depends on what property attributes you define (nonatomic/atomic, assign, retain, copy)
EDIT to OP's revised question: Yes a getter/setter will be created for the myObject instance variable of the DetailViewController class
DetailViewController.myObject = [theArray objectAtIndex:indexPath.row];
You are attempting to set a class variable that isn't defined. DetailViewController is of type Class, not DetailViewController. Perform the same operation on an instance of DetailViewController and you should be all set.