背景:我在 iPhone 和 iPad 代码中使用了大量的 NSDictionary 对象。我厌倦了获取/设置这些状态字典的键的冗长方式。
所以做了一点实验:我刚刚创建了一个名为 Remap 的类。
Remap 将采用任意 set[VariableName]:(NSObject *) obj 选择器并将该消息转发给一个函数,该函数将 obj 插入到键 [vairableName] 下的内部 NSMutableDictionary 中。
Remap 还将采用任何(零参数)任意 [variableName] 选择器并返回在键 [variableName] 下的 NSMutableDictionary 中映射的 NSObject。
例如
Remap * remap = [[Remap alloc] init];
NSNumber * testNumber = [NSNumber numberWithInt:46];
[remap setTestNumber:testNumber];
testNumber = [remap testNumber];
[remap setTestString:@"test string"];
NSString * testString = [remap testString];
NSMutableDictionary * testDict = [NSMutableDictionary dictionaryWithObject:testNumber forKey:@"testNumber"];
[remap setTestDict:testDict];
testDict = [remap testDict];
在 Remap 中实际上没有定义任何属性 testNumber、testString 或 testDict。
疯狂的事?它有效......我唯一的问题是如何禁用“可能不响应”警告只访问 Remap?
PS:我可能最终会放弃这个并使用宏,因为消息转发效率很低......但除此之外,还有人看到 Remap 的其他问题吗?
这里是 Remap 的 .m 给那些好奇的人:
#import "Remap.h"
@interface Remap ()
@property (nonatomic, retain) NSMutableDictionary * _data;
@end
@implementation Remap
@synthesize _data;
- (void) dealloc
{
relnil(_data);
[super dealloc];
}
- (id) init
{
self = [super init];
if (self != nil) {
NSMutableDictionary * dict = [[NSMutableDictionary alloc] init];
[self set_data:dict];
relnil(dict);
}
return self;
}
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
NSString * selectorName = [NSString stringWithUTF8String: sel_getName([anInvocation selector])];
NSRange range = [selectorName rangeOfString:@"set"];
NSInteger numArguments = [[anInvocation methodSignature] numberOfArguments];
if (range.location == 0 && numArguments == 4)
{
//setter
[anInvocation setSelector:@selector(setData:withKey:)];
[anInvocation setArgument:&selectorName atIndex:3];
[anInvocation invokeWithTarget:self];
}
else if (numArguments == 3)
{
[anInvocation setSelector:@selector(getDataWithKey:)];
[anInvocation setArgument:&selectorName atIndex:2];
[anInvocation invokeWithTarget:self];
}
}
- (NSMethodSignature *) methodSignatureForSelector:(SEL) aSelector
{
NSString * selectorName = [NSString stringWithUTF8String: sel_getName(aSelector)];
NSMethodSignature * sig = [super methodSignatureForSelector:aSelector];
if (sig == nil)
{
NSRange range = [selectorName rangeOfString:@"set"];
if (range.location == 0)
{
sig = [self methodSignatureForSelector:@selector(setData:withKey:)];
}
else
{
sig = [self methodSignatureForSelector:@selector(getDataWithKey:)];
}
}
return sig;
}
- (NSObject *) getDataWithKey: (NSString *) key
{
NSObject * returnValue = [[self _data] objectForKey:key];
return returnValue;
}
- (void) setData: (NSObject *) data withKey:(NSString *)key
{
if (key && [key length] >= 5 && data)
{
NSRange range;
range.length = 1;
range.location = 3;
NSString * firstChar = [key substringWithRange:range];
firstChar = [firstChar lowercaseString];
range.length = [key length] - 5; // the 4 we have processed plus the training :
range.location = 4;
NSString * adjustedKey = [NSString stringWithFormat:@"%@%@", firstChar, [key substringWithRange:range]];
[[self _data] setObject:data forKey:adjustedKey];
}
else
{
//assert?
}
}
@end