2015-06-11 24 views
4

我用我的屬性延遲實例,有我的類創建和快速使用成爲可能。要做到這一點,我寫很多這樣的「空」干將:比寫幾十個空吸氣劑更好的方法?

- (VMPlacesListFilter *)currentFilter 
{ 
    if (!_currentFilter) { 
     _currentFilter = [[VMPlacesListFilter alloc] init]; 
    } 

    return _currentFilter; 
} 

他們都是一樣的:如果實例變量是nil,則調用類的財產-alloc-init,然後返回實例變量。非常普通和直接。

如果我自己沒有創建這個getter,Objective-C的自動合成爲我創建了一個getter,它只執行返回部分(如果實例變量爲nil,則不會初始化對象)。

有什麼辦法來避免寫這個樣板代碼?

+0

使用宏我猜。 –

回答

2

不,恐怕有它周圍沒有什麼好辦法,如果你真的想偷懶的初始化。就個人而言,我通常保存的東西,真的是耗時或佔用大量內存延遲初始化(比如加載圖像或視圖控制器),並在init初始化便宜的東西(如簡單的數據結構或模型對象)。

- (instancetype) init { 
    self = [super init]; 
    if(self) { 
     _cheapThing1 = [NSMutableArray array]; 
     _cheapThing2 = [[MyModelObject alloc] init]; 
    } 
    return self; 
} 

- (ExpensiveThing*) expensiveThing 
{ 
    if(_expensiveThing == nil) { 
     _expensiveThing = [[ExpensiveThing alloc] init]; 
    } 
    return _expensiveThing; 
} 

除非你從磁盤或網絡加載東西,我不會擔心太多的初始化時間。當然,簡介它。

我知道這是一個Objective-C的問題,但值得注意的是,雨燕有懶初始化內置。

lazy var currentFilter = VMPlacesListFilter() 
+0

我也很享受,我總是知道我的屬性在哪裏初始化,而不必搜索它們。 – gklka

2

首先,我完全@zpasternack說,「延遲加載」不應被濫用同意。但是,使用Objective-C運行時的功能完全可以自動生成setter和getter。實際上,CoreData正在這樣做。

無論如何,我都拿出了一些愚蠢的代碼實現了一個名爲LazyClass類,您可以在其中像lazyArray聲明動態特性(見下文)。使用動態方法分辨率,當屬性首次被訪問時,調用相應類的默認+alloc-init方法的getter將被自動添加到類中。所有基礎實例變量都存儲在名爲myVarsNSMutableDictionary中。當然你也可以通過運行時API來操作ivars,但是使用字典應該可以節省一些工作。

請注意,這只是實現顯示它是如何工作的基本思路。它缺乏錯誤檢查,不應該被運送。

LazyClass.h

@interface LazyClass : NSObject 

@property NSMutableDictionary *myVars; 

// lazily initialized property 
@property NSArray *lazyArray; 

@end 

LazyClass。米

#import "LazyClass.h" 
#import <objc/objc-runtime.h> 

@implementation LazyClass 

@dynamic lazyArray; 

- (instancetype)init { 
    self = [super init]; 

    self.myVars = [NSMutableDictionary dictionary]; 

    return self; 
} 

- (NSMutableDictionary *)getMyVars { 
    return self.myVars; 
} 

// the generated getter method 
id dynamicGetterMethodIMP(id self, SEL _cmd) { 
    // selector name, which is also the property name 
    const char *selName = sel_getName(_cmd); 
    NSString *selNSName = [NSString stringWithCString:selName encoding:NSUTF8StringEncoding]; 

    NSString *keyPath = [NSString stringWithFormat:@"myVars.%@", selNSName]; 
    if (![self valueForKeyPath:keyPath]) { 
     // get the actual type of the property 
     objc_property_t property = class_getProperty([self class], selName); 
     const char *attr = property_getAttributes(property); 
     NSString *attrString = [[NSString alloc] initWithCString:attr encoding:NSUTF8StringEncoding]; 
     NSString *typeAttr = [[attrString componentsSeparatedByString:@","] firstObject]; 
     NSString *typeName = [typeAttr substringWithRange:NSMakeRange(3, typeAttr.length - 4)]; 

     // the default initialization 
     Class typeClass = NSClassFromString(typeName); 
     [self setValue:[[typeClass alloc] init] forKeyPath:keyPath]; 
    } 

    return [self valueForKeyPath:keyPath]; 
} 

// the generated setter method 
void dynamicSetterMethodIMP(id self, SEL _cmd, id value) { 
    // get the property name out of selector name 
    // e.g. setLazyArray: -> lazyArray 
    NSString *propertyName = NSStringFromSelector(_cmd); 
    propertyName = [propertyName stringByReplacingOccurrencesOfString:@"set" withString:@""]; 
    propertyName = [propertyName stringByReplacingOccurrencesOfString:@":" withString:@""]; 
    propertyName = [NSString stringWithFormat:@"%@%@", [propertyName substringToIndex:1].lowercaseString, [propertyName substringFromIndex:1]]; 

    NSString *keyPath = [NSString stringWithFormat:@"myVars.%@", propertyName]; 
    [self setValue:value forKeyPath:keyPath]; 
} 

// dynamic method resolution 
+ (BOOL)resolveInstanceMethod:(SEL)aSEL { 
    if ([NSStringFromSelector(aSEL) containsString:@"set"]) { 
     class_addMethod([self class], aSEL, (IMP)dynamicSetterMethodIMP, "^?"); 
    } else { 
     class_addMethod([self class], aSEL, (IMP)dynamicGetterMethodIMP, "[email protected]:"); 
    } 

    return YES; 
} 

@end 

Documentation

1

如果它是你煩惱的冗長外,我想你可以壓縮懶initialisers只需要使用三元運算符一行初始化:

- (VMPlacesListFilter *)currentFilter 
{ 
    return _currentFilter ? : (_currentFilter = [[VMPlacesListFilter alloc] init]); 
} 

免責聲明:我不這樣做,但有趣的是它可以完成