带有ARC的iOS 5+中的延迟加载属性

Jas*_*ary 6 lazy-loading ios ios5

问题

我正在迁移一些遗留代码(iOS 5之前的版本),我懒得加载一些readonly属性.我想用ARC将此代码更新到iOS 5+.但我刚刚学习ARC.

.H

@property (nonatomic, retain, readonly) NSDateFormatter *timeFormatter;
Run Code Online (Sandbox Code Playgroud)

.M

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

    return timeFormatter;
}
Run Code Online (Sandbox Code Playgroud)

我尝试了什么

我试图简单地更新我的代码,但收到:分配给readonly属性.

.H

@property (nonatomic, strong, readonly) NSDateFormatter *timeFormatter;
Run Code Online (Sandbox Code Playgroud)

.M

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

    return self.timeFormatter;
}
Run Code Online (Sandbox Code Playgroud)

我还审查了:

readonly使用ARC在iOS 5+中延迟加载属性的正确方法是什么?非常感谢.h.m的代码示例.

Mar*_*n R 14

对于自定义(惰性)getter方法,您必须直接访问实例变量(无论您是否使用ARC).所以你应该将属性合成为

@synthesize timeFormatter = _timeFormatter;
Run Code Online (Sandbox Code Playgroud)

然后你的getter方法是

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

    return _timeFormatter;
}
Run Code Online (Sandbox Code Playgroud)

如果同时从多个线程访问属性,则只需添加一些同步机制,也可以独立于ARC.

(注:较新版本的Xcode可以创建一个@synthesize自动的语句,并使用下划线前缀实例变量在这种情况下,但是由于属性是只读的,你提供一个getter方法时,Xcode不会.不会自动合成属性.)

ADDED:以下是一个完整的代码示例,以方便您:

MyClass.h:

#import <Foundation/Foundation.h>

@interface MyClass : NSObject
@property (nonatomic, strong, readonly) NSDateFormatter *timeFormatter;
@end
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

更多信息:事实上timeFormatter,如果属性合成为,则ARC 之前的getter方法也可以在不更改ARC的情况下工作

@synthesize timeFormatter; // or: @synthesize timeFormatter = timeFormatter;
Run Code Online (Sandbox Code Playgroud)

唯一的"错误"你做了更换timeFormatterself.timeFormattergetter方法里面.这会产生两个问题:

  • 读取self.timeFormattergetter方法会导致无限递归.
  • self.timeFormatter由于只读属性,因此不允许进行设置.

因此,如果您只是保留timeFormattergetter方法(使用方法中的timeFormatter实例变量),那么它也适用于ARC.

我仍然建议在我的代码示例中为带有下划线的属性添加实例变量,因为Xcode对自动合成属性的处理方式相同.

(我希望这有助于而且不会增加混乱!)


Cod*_*aFi 7

Readonly属性就是:只读.不应该涉及制定者.好的部分是,如果您在类扩展中重新声明变量(通常使用一对空括号),作为readwrite(或者甚至只是完全删除readonly),那么您可以在.m中分配它,但是类可以导入它会将其视为只读.

@interface MyClass ()

@property (nonatomic, strong) NSDateFormatter *timeFormatter;

@end
Run Code Online (Sandbox Code Playgroud)

这种重新声明允许更清晰的方式在内部访问和改变属性,而不需要使用脆弱的iVar综合(现在编译器为您完成了这个现象).你可以,或者当然,仍然使用iVar,如另一个答案所示,但不需要在-init或合成的getter之外进行iVar访问.*

*正如Martin正确指出的那样,即使你的任务成功,你仍然会导致无限递归,所以iVar访问是必要的,除非你明确声明一个getter,然后你可以使用属性访问.

  • +1,因为这是重写readonly属性的方法.过去使用这种模式偶尔攻击UIKit的只读属性.然而,这并没有回答这个问题,我仍然发现解释使用类别来使属性读写是一个很好的提示. (2认同)