4

背景:我在 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
4

3 回答 3

3

酷课。我喜欢。

我想不出一种方法来抑制您的所有警告,但我们可以将其减少到每个属性一行。将此添加到您的 Remap.h:

#define RemapProperty(PROP) \
@interface Remap(PROP) \
@property (nonatomic, retain) id PROP; \
@end \
@implementation Remap(PROP) \
@dynamic PROP; \
@end

现在,您可以通过将其放在向您发出警告的文件顶部来禁止给定属性的所有重新映射警告:

RemapProperty(propertyName);

这需要一些额外的工作,但它会给你点语法作为奖励。

remap.propertyName = @"Foo";

再做一些工作,您可以定义一个类似的宏,将属性直接添加到 NSDictionary,从而使 Remap 类变得不必要。

于 2010-05-26T21:31:38.290 回答
1

如果我正确阅读了您的代码,我认为这种方法的一个潜在问题可能是您不能拥有诸如hash(或其他方法 from NSObject,假设您Remap继承自NSObject)的键名。你最终会得到Remap实例的哈希值,而不是让Remap查找一个名为hashwithin的键_data,因为据我所知,[remap hash]不会调用。forwardIncovation:

于 2010-05-25T22:11:32.910 回答
-1

作为使字典更像通用对象的替代方法,我所做的是创建 NSDictionary 类别,为特定键包装 getter 和 setter。只需使用非常具有描述性的名称,因为您正在填充全局名称空间。

@interface NSDictionary (MyGetters)
@property (nonatomic,readonly) id something;
-(id) something;
@end

@interface NSDictionary (MyGetters)
-(id) something { return [self objectForKey:@"something"]; }
@end


@interface NSMutableDictionary (MySetters)
-(void) setSomething:(id)inValue;
@end

@interface NSDictionary (MySetters)
-(void) setSomething:(id)inValue { return [self setObject:inValue forKey:@"something"]; }
@end

setter 属性的唯一问题是您必须在两个类别中定义 getter 以避免出现警告。

您仍然必须声明所有内容,但无论如何您都必须这样做以消除 Remap 的警告。

于 2010-05-25T22:09:56.627 回答