I am migrating some legacy code (pre iOS 5) where I lazy load some readonly
properties. I want to update this code to iOS 5+ with ARC. But I just learning about ARC.
.h
@property (nonatomic, retain, readonly) NSDateFormatter *timeFormatter;
.m
- (NSDateFormatter *)timeFormatter {
if (timeFormatter == nil) {
timeFormatter = [[NSDateFormatter alloc] init];
[timeFormatter setDateFormat:@"h:mm a"];
}
return timeFormatter;
}
I have tried to simply update my code, but receive an: Assignment to readonly property.
.h
@property (nonatomic, strong, readonly) NSDateFormatter *timeFormatter;
.m
- (NSDateFormatter *)timeFormatter {
if (self.timeFormatter == nil) {
self.timeFormatter = [[NSDateFormatter alloc] init];
[self.timeFormatter setDateFormat:@"h:mm a"];
}
return self.timeFormatter;
}
I also reviewed:
What is the correct way to lazy-load a readonly
property in iOS 5+ with ARC? Would appreciate code samples for both .h and .m.
For a custom (lazy) getter method you have to access the instance variable directly (whether you use ARC or not). So you should synthesize the property as
@synthesize timeFormatter = _timeFormatter;
Then your getter method is
- (NSDateFormatter *)timeFormatter {
if (_timeFormatter == nil) {
_timeFormatter = [[NSDateFormatter alloc] init];
[_timeFormatter setDateFormat:@"h:mm a"];
}
return _timeFormatter;
}
You only have to add some synchronization mechanism if the property is accessed from multiple threads concurrently, that is also independent of ARC or not.
(Remark: Newer Xcode versions can create a @synthesize
statement automatically and use the underscore prefix for instance variables. In this case however, since the property is read-only and you provide a getter method, Xcode does not synthesize the property automatically.)
ADDED: Here is a complete code example for your convenience:
MyClass.h:
#import <Foundation/Foundation.h>
@interface MyClass : NSObject
@property (nonatomic, strong, readonly) NSDateFormatter *timeFormatter;
@end
MyClass.m:
#import "MyClass.h"
@implementation MyClass
@synthesize timeFormatter = _timeFormatter;
- (NSDateFormatter *)timeFormatter {
if (_timeFormatter == nil) {
_timeFormatter = [[NSDateFormatter alloc] init];
[_timeFormatter setDateFormat:@"h:mm a"];
}
return _timeFormatter;
}
@end
MORE INFORMATION: In fact, your pre-ARC timeFormatter
getter method works without changes also with ARC, if the property is synthesized as
@synthesize timeFormatter; // or: @synthesize timeFormatter = timeFormatter;
The only "mistake" you made was to replace timeFormatter
by self.timeFormatter
inside the getter method. This creates two problems:
self.timeFormatter
inside the getter method leads to infinite recursion.self.timeFormatter
is not allowed because of the read-only attribute.So if you just leave the timeFormatter
getter method as it was (using the timeFormatter
instance variable inside the method) then it works also with ARC.
I would still recommend to prefix instance variables for properties with an underscore as in my code example, because Xcode does it the same way for automatically synthesized properties.
(I hope that this helps and does not increase the confusion!)
Readonly properties are just that: read only. There should be no setters involved. The nice part is, if you redeclare the variable in a class extension (usually with a pair of empty parenthesis), as readwrite (or even just remove the readonly entirely), then you can assign to it within the .m, but classes that import it will see it as readonly.
@interface MyClass ()
@property (nonatomic, strong) NSDateFormatter *timeFormatter;
@end
This redeclaration allows a cleaner way to access and mutate the property internally without resorting to fragile iVar synthesis (which is becoming an antiquity now that the compiler does it for you). You can, or course, still use the iVar as shown in the other answer, but iVar access outside of -init or synthesized getters is unnecessary.*
*As Martin correctly pointed out, even if your assignment had succeeded, you still would have caused an infinite recursion, so iVar access is necessary, unless you explicitly declare a getter, then you may use property access.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With