首先,我完全赞同@zpasternack的观点,“懒加载”不应该被滥用。然而,利用Objective-C运行时的强大功能,自动生成setter和getter是完全可行的。事实上,
CoreData
正在这样做。
无论如何,我想出了一些愚蠢的代码来实现一个名为
LazyClass
的类,其中可以声明动态属性,如
lazyArray
(见下文)。使用
动态方法解析,当首次访问属性时,将自动向类中添加调用相应类的默认
+alloc
和
-init
方法的getter。所有底层实例变量都存储在名为
myVars
的
NSMutableDictionary
中。当然,您也可以通过运行时API操作ivars,但使用字典应该可以节省一些工作。
请注意,此实现仅展示了其工作原理的基本思想。它缺乏错误检查,并且不应该被发布。
LazyClass.h
@interface LazyClass : NSObject
@property NSMutableDictionary *myVars;
@property NSArray *lazyArray;
@end
LazyClass.m
#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;
}
id dynamicGetterMethodIMP(id self, SEL _cmd) {
const char *selName = sel_getName(_cmd);
NSString *selNSName = [NSString stringWithCString:selName encoding:NSUTF8StringEncoding];
NSString *keyPath = [NSString stringWithFormat:@"myVars.%@", selNSName];
if (![self valueForKeyPath:keyPath]) {
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)];
Class typeClass = NSClassFromString(typeName);
[self setValue:[[typeClass alloc] init] forKeyPath:keyPath];
}
return [self valueForKeyPath:keyPath];
}
void dynamicSetterMethodIMP(id self, SEL _cmd, id value) {
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];
}
+ (BOOL)resolveInstanceMethod:(SEL)aSEL {
if ([NSStringFromSelector(aSEL) containsString:@"set"]) {
class_addMethod([self class], aSEL, (IMP)dynamicSetterMethodIMP, "^?");
} else {
class_addMethod([self class], aSEL, (IMP)dynamicGetterMethodIMP, "v@:");
}
return YES;
}
@end
文档
这是关于动态方法解析的官方文档链接。