objective-c rename/override parent property? - ios

Is there an easy way to rename a parent's property or have a new property reuse an existing one?
For example, I have a parent class defined as such:
#interface Parent : NSObject
#property NSString* Name;
#end
If I want a child class to use that Name property but to call it something else, I know I can do something like this:
#interface Child : Parent
#property NSString* ChildName;
#end
#implementation Child
- (void) setChildName:(NSString *)ChildName
{
[super setName:ChildName];
}
- (NSString *) ChildName
{
return [super Name];
}
#end
But is there an easier way of doing this with getter/setter? This didn't work for me:
#interface Child : Parent
#property (getter=Name, setter=setName) NSString* ChildName;
#end

Oren is correct, you've got some work to do with understanding properties, but you could try setting the getter/setter like this:
#interface Child : Parent
#property (getter=childName, setter=setChildName) NSString* Name;
#end
You will likely want to add (strong, nonatomic) in your property description as well (or whatever would be appropriate for your context).

Related

#dynamic property in Objective C

I'm trying to implement a Dynamic property in my project
This is my code.
MyClass.h
#interface MyClass : UIView
#property (strong, nonatomic) NSString *name;
#end
MyClass.m
#implementation MyClass
#dynamic name;
-(void)setName:(NSString *)name{
self.name = name;
}
#end
But when I run my app has crashed.
When I use an ivar had this error.
A property is just a bundle of two methods: a getter and a setter. So, when you write
#property (strong, nonatomic) NSString *name;
what you are really saying is
- (NSString *)name;
- (void)setName:(NSString *)name;
After that, each time the compiler encounters an expression of the form obj.name, it translates it to [obj name]. And each time you see a statement like obj.name = #"hello";, the compiler translates it to [obj setName:#"hello"].
The next thing is you have to make sure the property behaves properly. You have many options:
Write getters and setters manually, referring to an iVar
Synthesize getter and setter
Autosynthesize getter and setter
Write custom getters and setters
Use #dynamic to avoid compile time warnings, because you intend to do runtime magic. (Really, that's not what you want to do, because you need to understand the basics first.)
Write getters and setters manually, referring to an iVar
#interface MyClass : UIView {
NSString *_name;
}
#property (strong, nonatomic) NSString *name;
#end
and in the implementation
#implementation MyClass
- (NSString *)name {
return _name;
}
- (void)setName:(NSString *)name {
_name = name;
}
#end
Synthesize getter and setter
The last section is basically equivalent to this
#interface MyClass : UIView {
NSString *_name;
}
#property (strong, nonatomic) NSString *name;
#end
#implementation MyClass
#synthesize name = _name;
#end
Autosynthesize getter and setter
In practice, you would just use "autosynthetisation".
#interface MyClass : UIView
#property (strong, nonatomic) NSString *name;
#end
#implementation MyClass
#end
This means,
if you just declare a property
don't call #synthesize or #dynamic
don't implement any custom getter and setter
the code above will just create an iVar named _name and a getter and setter that looks exactly like the one in the first example.
This means that the the first two and this sections are equivalent, because they produce the same code.
Write custom getters and setters
This is what the term "dynamic property" really means. For example, you may want the name to be always uppercase. So you may write a property like this.
#interface MyClass : UIView {
NSString *_name;
}
#property (copy, nonatomic) NSString *name;
#end
#implementation MyClass
- (NSString *)name {
return _name;
}
- (void)setName:(NSString *)name {
_name = [name uppercaseString];
}
#end
(in the code above, I changed strong to copy - don't worry, this is just a comment anyways. And it's a true one, because the uppercaseString will never be the same, it will always be a copy of the original.)
This is maybe the only really interesting case! For example, this kind of property is what UIKit uses all the time, e.g. the text property of UILabel is a dynamic property like that. It doesn't just set some iVar, but it also makes sure that the visible text on the screen changes too.
#dynamic properties
they are really tricky to get right, and most of the time they are not worth the hassle IMHO.
Note: I simplified some things and left out details which are only detectable when using objc runtime inspection APIs
This StackOverflow answer: https://stackoverflow.com/a/1160545/7833793 does a good job of explaining what the differences between #synthesize and #dynamic are. Typically you use #dynamic if you're delegating the task of implementing the accessors (get, set). It seems to me like you would want to use #synthesize here. But with modern objective c, you shouldn't even need to specify and the iVar will be created for you automatically.
i.e.:
MyClass.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
#interface MyClass : NSObject
#property (strong, nonatomic) NSString *name;
#end
NS_ASSUME_NONNULL_END
MyClass.m
#import "MyClass.h"
#implementation MyClass
- (void)setName:(NSString *)name {
_name = name;
}
#end
Your solution leads to recursion, you are getting crash since you are not using ivar in setter, try this instead:
-(void)setName:(NSString *)name{
_name = name;
}

Objective-C Implementing method in Base class with the child class type property

I have some code that I am trying to accomplish. I have a base class and some properties that are same for my child classes. Getter and setters needs to be implemented. In my child class I have a property and reference to it, ExampleType &type. ExampleType parent is BaseType witch cannot be instanced. Getter and setters in base class from my properties depend upon BaseType. So if I have in my base class something like this BaseType *type. Example of my getter:
-(NSString *) property {
return self.type->returnString;
}
This really depends on my child property type ExampleType.I do not want to have copy/paste code in my child classes for properties. Does anyone know how to accomplish this?
I think I know what you wanted to accomplish. Here are the type classes:
#interface BaseType : NSObject
#property (nonatomic, strong) NSString* returnString;
#end
#implementation BaseType
#end
#interface ExampleType : BaseType
#end
#implementation ExampleType
- (NSString *)returnString
{
return #"returnString from ExampleType";
}
#end
And here are the main objects, incapsulating the type classes:
#interface Parent : NSObject
#property (nonatomic, strong) NSString* property;
#end
#implementation Parent
- (BaseType*)type
{
NSAssert(NO, #"should be implemented in child class");
return nil;
}
- (NSString *)property
{
return self.type.returnString;
}
#end
#interface Child : Parent
#property (nonatomic, strong) ExampleType* type;
#end
#implementation Child
#end
So now you have a property ExampleType in your child class, and the property implementation is present only in the parent class.

best practice for how to inherit property from parent class and override setter and getter

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];
}

objective-C how to declare private property for category?

I'm new to objective-C, so apologies if this is repeated somewhere. I have a category(?) that is something like:
inside SomeClass.h:
#interface SomeClass (SomeCategory) <SomeDelegate>
#property (nonatomic, retain) id somePublicProperty;
#property (nonatomic, retain) id someProperty; // <-- i want to move this to "private"
#end
and now in my SomeClass.m, all i have is:
#implementation SomeClass (SomeCategory)
// dynamic setters/getters here for someProperty.
#end
I think the someProperty is public. how do i make this "private"? (in other words, how do i syntactically put this in the .m file? i tried to use
#interface SomeClass (SomeCategory) {
#property (nonatomic, retain) somePrivateProperty;
}
#end
but it just complains that i have duplicate definition of the category. how do i do this correctly?
In your .h file, you should not give the category. Just use:
#interface SomeClass : SomeBaseClass < SomeDelegate>
#property (nonatomic, retain) id somePublicProperty;
#end
In your .m file, define your private property inside a class extension:
#interface SomeClass ()
#property (nonatomic, retain) id somePrivateProperty;
#end
A class extension is not a like category in that it allows you to extend an interface as well as add new storage to your class.
In a class category, you can define new properties, but no storage will be allocated for it, so you have to do it by hand:
#interface SomeClass (SomeBaseCategory)
#property (nonatomic, retain) id somePrivateProperty;
#end
#implementation SomeClass {
id _somePrivateProperty;
}
- (void)setSomePrivateProperty:(id)property {
_somePrivateProperty = property;
}
- (id)somePrivateProperty {
return _somePrivateProperty;
}
#end
Otherwise your app will crash.
In any case, keep in mind that given the dynamic nature of Objective-C, your property will never be fully private, since you can always send a message to an Objective-C object through objc_msgsend and thus set or read the property value.
EDIT:
If you do not have the source code for a class implementation, you cannot define a class extension (as per source linked above).
In this case, you could use object association to define properties.
Just add the category definition in the .m file OUTSIDE the implementation block
Like so:
#interface MyClass (MyCategory)
#property (assign) BOOL myPrivateProperty;
#end
#implementation MyClass
...
#end
Categories are best used for adding capability to code you do not own and cannot change. Adding properties via categories is not impossible, but is much more difficult.
Class Extensions are best used for keeping properties your object needs, but are not intended to be public.
If you do truly need to add properties to this object, the way to do it is with the Objective-C runtime's associated objects
There's an excellent writeup of when/how to use them here

How to add a relationship to the parent with Mantle?

I have Parent/child class like this:
#interface Parent : MTLModel <MTLJSONSerializing>
- (void)someMethod;
#property a,b,c...; // from the JSON
#property NSArray *childs; // from the JSON
#end
#interface Child : MTLModel <MTLJSONSerializing>
#property d,e,f,...; // from the JSON
#property Parent *parent; // *not* in the JSON
#end
All the fields a to f are in the JSON, with the same name (hence my JSONKeyPathsByPropertyKey method return nil), and the proper JSONTransformer is correctly setup so that the childs array in parent is containing Child class and not NSDictionary.
Everything work forwardly.
But I want, as a convenience, a property in my Child model that reference back to the parent that own it. So that in the code I can do that:
[childInstance.parent someMethod]
How do I do that with Mantle ??
I want, when the parent is parsing the child's JSON and creating the Child class, to add a ref to itself. (With an init method ??)
Thanks.
I do this by overriding MTLModel -initWithDictionary:error: method. Something like this.
Child interface:
#interface BRPerson : MTLModel <MTLJSONSerializing>
#property (nonatomic, copy, readonly) NSString *name;
#property (strong, nonatomic) BRGroup *group; // parent
#end
In parent implementation:
- (instancetype)initWithDictionary:(NSDictionary *)dictionaryValue error:(NSError **)error {
self = [super initWithDictionary:dictionaryValue error:error];
if (self == nil) return nil;
// iterates through each child and set its parent
for (BRPerson *person in self.people) {
person.group = self;
}
return self;
}
Techinical note:
If you are curious like me, I already tried to tweak MTLJSONAdapter by changing its forwardBlock and reversibleBlock. But I can't, because those are inside MTLReversibleValueTransformer superclass, and that class is declared privately in "MTLValueTransformer.m". So the initWithDictionary approach above should be much easier.

Resources