编写延迟加载宏实现器是否可能/聪明?

时间:2012-12-17 22:20:39

标签: objective-c macros

我的代码(特别是当我进入TDD时)有很多延迟加载的属性,例如:

@interface MyClass ()

@property (nonatomic, strong) MyFoo *myFoo;

@end

@implementation MyClass

- (MyFoo *)myFoo {
   if (!_myFoo) {
     _myFoo = [MyFoo alloc] sharedFoo]; // or initWithBar:CONST_DEF_BAR or whatever
   }
   return _myFoo;
}

@end

或者,更好的,线程安全的版本:

- (MyFoo *)myFoo {
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    _storeHelper = [SHStoreHelper sharedStoreHelper];
  });
}

我希望Apple能够使这个属性的一个方面符合自动代码生成的条件,例如:

@property (lazyload) MyFoo *myFoo;

但是,除此之外,我想为实现位设置一个宏,比如

#define LAZY_ALLOC(x, y, _y, a, i) -(x *)y { if (!_y) { _y = [[x a] i]; } return _y }

然后代替常规方法实现

LAZY_ALLOC(MyClass, myClass, _myClass, alloc, init)

对于想要

的类来说足够灵活
LAZY_ALLOC(OtherClass, otherClass, _otherClass, sharedClass, nil)

LAZY_ALLOC(OtherClass, otherClass, _otherClass, alloc, initWithFrame:SOME_FRAME)

1)预处理器需要_y。有没有办法让它构建_autosynthesized ivar而不分别传递它? 2)这有什么大问题吗?对我而言,它增强了可读性,因为它实质上比完全写出的版本更快地说“哦那件事” 3)你觉得它风格狡猾吗?风格迥异?

2 个答案:

答案 0 :(得分:3)

我认为你想要的是##。你可以把它写成:

#define LAZY_ALLOC(type, name, initialValue) \
    -(type *)name { \
        static dispatch_once_t onceToken; \
        dispatch_once(&onceToken, ^{ \
            if (! _ ## name) { \
                _ ## name = (initialValue); \
            } \
        } \
        return _ ## name; \
    }

编辑:

要清楚,我不会在单例的sharedInstance - 类型方法之外使用这个特定的习惯用于延迟加载。由于静态dispatch_once_t,此代码仅对每个类(不是每个实例)执行延迟加载。我只是将代码复制出来并将其转换为宏模板以说明该技术,但我觉得我应该澄清一下。

就个人而言,对于延迟加载普通实例变量,我会使用非线程安全版本,只需切换到急切加载或专门处理案例,如果你需要线程安全。有一些技术可以在访问器中组合延迟加载和线程安全,但是:

  1. 它们相当慢。

  2. 很难同时真正需要两者。

  3. 除了在你的访问器方法中粘贴互斥锁之外,你可能不得不为线程安全做一些特殊的架构。

  4. 所以我不会将这种情况纳入标准模板宏。

答案 1 :(得分:0)

编写方法+(id) lazyLoad:(Class)class sharedPointer:(NSObject**)ptrAddr或其他类似方法。使它成为LazyLoader类的一个方法,它只实现这个方法。

使用它:

- (MyFoo *)myFoo {
    return [LazyLoader lazyLoad:[MyFoo class] sharedPointer:&_myFoo];
}

在sharedPointer parm上可能需要一些ARC甜味剂,但基本上它应该可以使用,并且可以根据您的意愿制作线程安全(如果您愿意)。

(我将把它作为练习让读者弄清楚如何进行备用init方法。)