Is #synthesize required for readwrite property? - ios

From Xcode 4.4 onwards has Default Synthesis Of Properties. It generates this automatically:
#synthesize name = _name;
source
And from source2
readwrite vs readonly determines whether a synthesized property has a synthesized accessor or not (readwrite has a setter and is the default, readonly does not).
Therefore, I've concluded that #synthesize name = _name; is not required for readwrite but it's needed for readonly
However, in Apple's spritekit Adventure code (adventure code download link), APAAdventureScene.m:
"heroes" (readwrite) is synthesize in the example. If it's not synthesize it will give this error: Use of undeclared identifier '_heroes'
Is #synthesize required for readwrite property, I'm confuse?
Thank you
#interface APAAdventureScene () <SKPhysicsContactDelegate>
...
#property (nonatomic, readwrite) NSMutableArray *heroes; // our fearless adventurers
#property (nonatomic) NSMutableArray *goblinCaves; // whence cometh goblins
...
#end
#implementation APAAdventureScene
#synthesize heroes = _heroes;
- (id)initWithSize:(CGSize)size {
...
_heroes = [[NSMutableArray alloc] init];
_goblinCaves = [[NSMutableArray alloc] init];
...
}
- (void)updateWithTimeSinceLastUpdate:(CFTimeInterval)timeSinceLast {
// Update all players' heroes.
for (APAHeroCharacter *hero in self.heroes) {
[hero updateWithTimeSinceLastUpdate:timeSinceLast];
}
// Update the caves (and in turn, their goblins).
for (APACave *cave in self.goblinCaves) {
[cave updateWithTimeSinceLastUpdate:timeSinceLast];
}
}
#end

#synthesize isn't required for anything anymore as long as you are using a modern LLVM compiler (the default for over 1 year now).
readwrite is the default so both properties are read/write. There is NO reason for the #synthesize line in the posted code.
The only exception to this is if you explicitly provide both the "setter" and "getter" for a readwrite property. Then the ivar is not automatically generated. For a readonly property the ivar isn't generated if you supply an explicit "getter".

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

Obj C: Can't inherit public property from parent class in child class

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;

No automatic underscore ivar in Xcode 5.1.1

I just noticed that, for some reason, I don't seem to have automatically created underscore iVars in my iOS 7 project, and I wonder why that is. My setup:
MyClass.h
#property (readonly) NSNumber *aNumber;
MyClass.m
#interface MyClass ()
#property (readwrite, strong) NSNumber *aNumber;
#end
#implementation MyClass
(...)
- (NSNumber *)aNumber {
return _aNumber;
}
- (void)setANumber:(NSNumber *)aNumber {
_aNumber = aNumber;
}
#end
This results in Use of undeclared identifier: '_aNumber'.
Why is that so? I thought that underscore iVars are always automatically synthesized? Is it because of the class extension I use? If I put in #synthesize aNumber = _aNumber; it (obviously) works.
There is one exception to the automatic synthesize rule.
If you override both the getter and the setter of a property then you will have to manually synthesize the property.
This has been the case ever since auto synthesis came in.
Just add the #synthesize line and it will be fine.

How to make an immutable readonly property in the header file (.h), a mutable readwrite property in implementaion (.m)

I have an object that holds a dictionary JSONData. From the header file, and to the other classes that'll access it, I want this property to only be read-only and immutable.
#interface MyObject : NSObject
#property (readonly, strong, nonatomic) NSDictionary *JSONData;
#end
However, I need it to be readwrite and mutable from the implementation file, like this, but this doesn't work:
#interface MyObject ()
#property (readwrite, strong, nonatomic) NSMutableDictionary *JSONData;
#end
#implementation MyObject
// Do read/write stuff here.
#end
Is there anything I can do to enforce the kind of abstraction I'm going for? I looked at the other questions and while I already know how to make a property readonly from .h and readwrite from .m, I can't find anything about the difference in mutability.
You need a separate private mutable variable in your implementation. You can override the getter to return an immutable object.
#interface MyObject () {
NSMutableDictionary *_mutableJSONData;
}
#end
#implementation MyObject
// ...
-(NSDictionary *)JSONData {
return [NSDictionary dictionaryWithDictionary:_mutableJSONData];
}
// ...
#end
No need to implement the setter, as it is readonly.

Why does the compiler think this variable is undeclared?

#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#interface Employee : NSManagedObject
#property (nonatomic, retain) NSString* name;
#property (nonatomic, retain) NSNumber* pin;
-(id) initWithName:(NSString*)name Pin:(NSNumber*)pin;
#end
#implementation Employee
#dynamic name;
#dynamic pin;
-(id) initWithName:(NSString*)iname Pin:(NSNumber*)ipin{
self = [super init];
if(self){
name = iname;
pin = ipin;
}
return self;
}
#end
Compiler says name and pin are undeclared in the .m file. What am I doing wrong?
Putting self.name and self.pin works, but could someone tell if this is proper or why this works? etc.
Thank you in advance for help.
You wrote this in your initializer:
name = iname;
Since you don't have a local variable named name, the compiler looks for an instance variable named name, or a static or global variable named name. You don't have an instance variable named name, or a static or global either. You have a property named name. To set the property, you need to either use “dot notation”:
self.name = iname;
or you need to send a setName: message:
[self setName:iname];
Both of these compile to exactly the same code.
Note that since your superclass is NSManagedObject, I assume Employee is an entity defined in your Core Data model with attributes name and pin. If so, using #dynamic is correct. If those are not attributes defined in your model, you should probably be using #synthesize (or omitting both #dynamic and #synthesize and letting the compiler auto-synthesize the properties).

Resources