Lazy Load Property on iOS 5+ with ARC

Problem

I am porting some old code (pre iOS 5) where I am lazy loading some readonly properties. I want to upgrade this code to iOS 5+ using ARC. But I just found out about ARC.

.h

 @property (nonatomic, retain, readonly) NSDateFormatter *timeFormatter; 

wow

 - (NSDateFormatter *)timeFormatter { if (timeFormatter == nil) { timeFormatter = [[NSDateFormatter alloc] init]; [timeFormatter setDateFormat:@"h:mm a"]; } return timeFormatter; } 

What i tried

I tried just updating my code, but I get: Assignment for the readonly property.

.h

 @property (nonatomic, strong, readonly) NSDateFormatter *timeFormatter; 

wow

 - (NSDateFormatter *)timeFormatter { if (self.timeFormatter == nil) { self.timeFormatter = [[NSDateFormatter alloc] init]; [self.timeFormatter setDateFormat:@"h:mm a"]; } return self.timeFormatter; } 

I also reviewed:

Question

What is the correct way to lazy-load the readonly property in iOS 5+ with ARC? Would read code samples for .h and .m.

+6
source share
2 answers

For a custom (lazy) getter method, you need to access the instance variable directly (regardless of whether you use ARC or not). Therefore, you must synthesize the property as

 @synthesize timeFormatter = _timeFormatter; 

Then your getter method

 - (NSDateFormatter *)timeFormatter { if (_timeFormatter == nil) { _timeFormatter = [[NSDateFormatter alloc] init]; [_timeFormatter setDateFormat:@"h:mm a"]; } return _timeFormatter; } 

You only need to add a synchronization mechanism if the resource is accessed simultaneously from several threads, which also does not depend on ARC or not.

(Note: newer versions of Xcode can automatically create the @synthesize operator and use the underscore prefix for instance variables. In this case, however, since the property is read-only and you provide the getter method, Xcode does not automatically synthesize the property.)

ADDED: The following 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 

ADDITIONAL INFORMATION . In fact, your pre-ARC timeFormatter getter method works unchanged also with ARC if the property is synthesized as

 @synthesize timeFormatter; // or: @synthesize timeFormatter = timeFormatter; 

The only โ€œmistakeโ€ you made was to replace timeFormatter with self.timeFormatter inside the getter method. This creates two problems:

  • Reading self.timeFormatter inside the getter method results in infinite recursion.
  • Setting self.timeFormatter not allowed due to a read-only attribute.

So, if you just leave the getter timeFormatter method as it is (using the timeFormatter instance timeFormatter inside the method), then it also works with ARC.

I would recommend prefix instance variables for underlined properties, as in my code example, because Xcode does the same for automatically synthesized properties.

(I hope this helps and does not increase confusion!)

+14
source

Readonly Properties: This is read only. There should be no setters. The good part is that if you override a variable in the class extension (usually with a pair of empty brackets) as readwrite (or even just delete completely readonly), then you can assign it within .m, but the classes that import will see it as soon as read.

 @interface MyClass () @property (nonatomic, strong) NSDateFormatter *timeFormatter; @end 

This update allows a cleaner way of accessing and changing properties inside without resorting to the fragile synthesis of iVar (which now becomes antique when the compiler does this for you). You can either continue using iVar as shown in another answer, but iVar access outside of -init or synthesized getters is not needed. *

* As Martin correctly pointed out, even if your task succeeded, you would still call for infinite recursion, so iVar access is required, if you do not explicitly declare getter, then you can use access to properties. Sub>

+7
source

All Articles