Is there a way to define a setter method that will run on setting a property like:
i want to call
object.something = 0;
meanwhile in object class i want to achieve something like
- (void)setSomething:(NSInteger)something{
self.something = something;
// some other work too
}
What you want is called property.
you define property in class #interface like:
#interface MyClass()
#property (strong, nonatomic) SomeClass *object;
#end
It will automatically create ivar _object, setter and getter for it.
You can override accessor methods. But if you override both setter and getter, you need to synthesize property like:
#implementation MyClass
#synthesize object = _object;
//setter
- (void)setObject:(SomeClass *)object
{
_object = object;
}
//getter
- (SomeClass *)object
{
return _object;
}
//class implementation
#end
You can do it like this:
#property (nonatomic, weak, setter = setSomething:) UIImageView *photoImageView;
Anyway, setSomething: is the default method for a property named something. You just need to replace self.something with _something, as pointed in the comments.
Related
I am trying to re-struct my project code with an inheritance style, what is the best practice for how to inherit property from parent class and override setter and getter?
I give the demo code, in the demo, ChartModel is a base class in a ChartViewController, and LineChartModel is a sub class of ChartModel in LineChartViewController.
I want to override the setter and getter of LineChartModel *dataModel in sub view controller. Please include any #synthesize and protected instance variable, or if it is automatically generated by compiler, please mark. Thank in advance.
// ChartModel.h
#interface ChartModel : NSObject
-(BOOL)hasData;
#end
// LineChartModel.h
#interface LineChartModel : chartModel
-(void)getLineColor;
#property (nonatomic, strong) NSArray* dataArray;
#end
// ChartViewController.h
#interface ChartViewController: UIViewController
#property (nonatomic, strong) ChartModel *dataModel;
-(void)updateUI;
#end
// ChartViewController.m
#implementation ChartViewController
-(void)updateUI {
if ([self.dataModel hasData]) {
[self.view setHidden:NO];
} else {
self.view.hidden = YES;
}
// setter and getter here
#end
// LineChartViewController.h
#interface LineChartViewController : ChartViewController
// pay attension here, same name but a sub class of chartModel
#property (nonatomic, strong) LineChartModel *dataModel;
#end
// LineChartViewController.m
#implementation LineChartViewController
//override dataModel setter here
//override dataModel getter here
#end
Technically, the only thing you need in the implementation of LineChartViewController is:
#dynamic dataModel;
That tells the compiler that the getter and setter will be supplied in some way it can't immediately see. In actuality, they will be supplied by the superclass.
However, that allows for a problem. A LineChartViewController is-a ChartViewController. That means that an instance of LineChartViewController can be passed to a method or function which is declared to take a ChartViewController and that method/function is entitled to do anything to it that is allowed by the interface of ChartViewController. That includes assigning an instance of ChartModel (not LineChartModel) to its dataModel property. Presumably, LineChartViewController will break if its dataModel property is not a LineChartModel.
In technical terms, your design violates the Liskov substitution principle.
It's not a fix for the design issue, but you can catch the problem at run time if it happens, by implementing an override of the setter like this:
- (void) setDataModel:(LineChartModel*)dataModel
{
if (dataModel && ![dataModel isKindOfClass:[LineChartModel class]])
{
NSString* reason = [NSString stringWithFormat:#"%# is not a valid dataModel for LineChartViewController; it must be a kind of LineChartModel", dataModel];
[[NSException exceptionWithName:NSInvalidArgumentException reason:reason userInfo:nil] raise];
}
[super setDataModel:dataModel];
}
I have a class below:
#interface Person : NSObject {
NSString *_firstname;
}
#property NSString *firstName;
#end
#implementation Person
#synthesize firstname;
#end
This will declare three variables: firstname, self.firstname and _firstname
What is the difference between the three variables and how do you using with each case?
In modern Objective-C you don't need to create instance variable if you already synthesizing properties.
From what you write it appears that you are confusing properties and ivar.
Properties create getters and setters to your ivars, but they are not ivars, they are methods that access you ivars to set or get their values.
Your class can be sum up like that:
#interface Person : NSObject
#property NSString *firstName;
#end
#implementation Person
#end
At compile time this will ensure that you can access your ivar using methods and name your ivar as _firstName.
Dot notation create access to properties so self.firstName (using ARC and default property option -nonatomic,strong-)calls that method
- (NSString*)firstName
{
return _firstName;
}
While calling self.firstName = #"foo"; calls:
- (void) setFirstName:(NSString*)name
{
if (_firstName == name) {
return;
}
_firstName = name;
}
Underlining implementation could be a little different.
The first one NSString *_firstname; is an instance variable. The #property is a property which is syntesized (you don't have to manually synthesize properties in modern Objective-C). When you declare a property you can access its instance variable with _propertyName or with self.propertyName.
It is up to you whether you declare your variables as instance variables or as properties but it is more common and suggested to declare them as properties (using properties you can have access to getters and setters, which means that you can run code before the value of the property will be set or will be read).
You can chain instance with property using
#interface Person : NSObject {
NSString *_firstname;
}
#property NSString *firstName;
#end
#implementation Person
#synthesize firstName = _firstname;
#end
Both pointers are pointing same instance now.
I am practicing inheritance in Objective-C and this is my Person parent class
// This is Person.h
#interface Person : NSObject
#property(nonatomic, strong) NSNumber *age;
#property(nonatomic, strong) NSString *race;
-(instancetype)init;
-(instancetype)initWithAge:(NSNumber*)age andRace:(NSString*)race;
#end
This is what I'm trying to do in my Student class
// This is Student.h
#import "Person.h"
#interface Student : Person
#property(nonatomic, strong) NSString *classification;
#property(nonatomic, strong) NSString *major;
#end
And
// This is Student.m
#import "Student.h"
#import "Person.h"
#implementation Student
-(instancetype)init
{
return [self initWithClassification:#"Freshman" andMajor:#"Computer Science"
andAge:[[NSNumber alloc] initWithInt:20] andRace:#"Caucasian"];
}
-(instancetype)initWithClassification:(NSString*)classification andMajor:(NSString*)major
andAge:(NSNumber*)age andRace:(NSString*)race
{
self = [super init];
if (self)
{
_classification = classification;
_major = major;
_age = age;
_race = race;
}
return self;
}
#end
The compiler is not liking my doing
_age = age;
_race = race;
Use of undeclared identifier _age did you mean age? Can someone tell me where I went wrong? Thank you.
When you declare a property like that, clang will automatically #synthesize it for you (i.e it will create a getter and setter), but synthesized properties are not visible to subclasses, you have different alternatives to make it working.
You can synthesize the ivar in the interface of the subclass
#synthesize age = _age;
Or you can declare the ivar protected on the interface of the superclass, so that will be visible on the subclasses.
#interface Person : NSObject {
#protected NSNumber *_age;
}
Or you can use self.age = ... on your subclass, without using the ivar at all.
Since clang compiler now auto-synthesise properties you don't have, in most cases, to synthesise your properties.
Objective-C Autosynthesis of Properties
Clang provides support for autosynthesis of declared properties. Using
this feature, clang provides default synthesis of those properties not
declared #dynamic and not having user provided backing getter and
setter methods. __has_feature(objc_default_synthesize_properties)
checks for availability of this feature in version of clang being
used.
But in some cases (some examples are in this question) you should explicitly synthesise them.
In this case, to solve your problems you should just add:
#synthesize age = _age;
#synthesize race = _race;
to your code, and you'll be fine.
The subclass has access to the property, but not the backing variable. So you should set it with
self.age = age;
I tried to assign a value to recordingStatus -
ie recordingStatus = 1
But it doesn't go into the setter which i want some custom code.. what's wrong with my code?
Thanks.
Pier.
In file.h
#property (strong, nonatomic) IBOutlet UILabel *recordingStatusText;
#property (nonatomic)int recordingStatus;
....
In file.m
/* -------------------- Property Setter and Getters ----------------------*/
#synthesize recordingStatus;
- (int) getRecordingStatus {
return recordingStatus;
}
- (void) setRecordingStatus:(int)status
{
[_recordingStatusText setText: #"Just testing!"];
recordingStatus = status;
}
To set and get your property, you should use self.property = newValue;.
OVERRIDING SETTERS AND GETTERS
For getters you don't need to write 'get' in the method signature. So, your getter method uses the wrong name. If you want to override it, the method should be
-(int) recordingStatus {
// Custom Getter Method
return _recordingStatus;
}
In the case of ints, Objective-c wants to see your setter and getter methods in the format of
-(void)setValue:(int)newValue;
-(int)value;
Can you show the code where you call the setter? I'm assuming you're accessing the ivar directly by doing something like this (assuming your ivar is named recordingStatus):
recordingStatus = 1
Instead try this:
self.recordingStatus = 1
I am having following condition:
#interface MyClass:NSObject
#public NSString *str;
#end
#implementation
-(id)init{
}
#end
Now I want to access str variable outside MyClass in Other Class, (1) Using MyClass Object (2) without using MyClass Object, How can I achieve that?
You can call using this:
MyClass *a;
a.str;
Without the object, you cannot call an instance variable. However, you can call static method with this declaration:
#interface MyClass:NSObject
+ (void)doX;
#end
#implementation
+ (void)doX {
// do whatever
}
then in another class you just need to call:
[MyClass doX];
However, let a public instance variable is not a good practice. The reason is that it will let any class, methods change that instance variable without your control. For example, they can set the NSString *str to nil and then nobody can call anything, or they may forget to do memory management when they call.
A better practice for public variable is using #property
For example, your string should be declared like:
#property (nonatomic, retain) NSString * str;
and then in the implementation:
#implementation MyClass
#synthesize str;
The good thing about property is that compiler will generate gettter and setter methods for you and those setters will handle memory correctly for you.
More about properties here
Sigh, i realise this post is LONG dead but I believe The above answer is incorrect.
well the first bit.
Please see the link below.
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocObjectsClasses.html#//apple_ref/doc/uid/TP30001163-CH11-SW1
for the above interface to work, you NEED to declare a property for use outside of its class
Because the instance variable it is not visible outside its class.
well; You don't NEED to. Doing something like MyClass->str is valid.
Please see this example
#interface Foo : NSObject {
#public NSInteger publicMember;
#private NSInteger aproperty;
}
#property (assign) NSInteger aproperty;`
then the calling class
Foo *f = [Foo new];
f.aproperty = 90;
//f.publicMember = 100; property 'publicMember' not found of type Foo *
f->publicMember = 100;
But as the above post said, you should always use #properties because if var public was a string, you are not retaining the string in any way.