Объекты стиля Javascript в Objective-C - PullRequest
4 голосов
/ 26 мая 2010

Фон: Я использую тонну объектов NSDictionary в своем коде iPhone и iPad. Мне надоел многословный способ получения / установки ключей для этих государственных словарей.

Итак, небольшой эксперимент : я только что создал класс, который я называю Remap.

Remap будет принимать любой произвольный набор селектора obj [VariableName] :( NSObject *) и перенаправлять это сообщение функции, которая вставит obj во внутренний NSMutableDictionary под ключом [vairableName].

Remap также принимает любой (нулевой аргумент) произвольный селектор [variableName] и возвращает объект NSObject, сопоставленный в NSMutableDictionary под ключом [variableName].

, например

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];

где ни одно из свойств testNumber, testString или testDict фактически не определено в Remap.

Сумасшедшая вещь? Это работает ... Мой единственный вопрос как я могу отключить предупреждения "может не отвечать" для JUST доступа к Remap ?

P.S. : Я, вероятно, в конечном итоге откажусь от этого и перейду к макросам, поскольку пересылка сообщений довольно неэффективна ... но кроме этого кто-нибудь видит другие проблемы с Remap?

Вот Remap's .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

Ответы [ 3 ]

3 голосов
/ 27 мая 2010

Классный класс. Мне это нравится.

Я не могу придумать способ подавления всех ваших предупреждений, но мы можем свести их к одной строке для каждого свойства. Добавьте это в свой 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.

1 голос
/ 26 мая 2010

Если я правильно читаю ваш код, я думаю, что потенциальная проблема с этим подходом может заключаться в том, что вы не можете иметь имена ключей, такие как hash (или другие методы из NSObject, предполагая, что ваш Remap наследует от NSObject). В результате вы получите хеш-значение экземпляра Remap, вместо того, чтобы Remap нашел ключ с именем hash в пределах _data, потому что [remap hash], насколько я могу судить, не вызовет forwardIncovation:. 1011 *

0 голосов
/ 26 мая 2010

В качестве альтернативы для создания словарей, более похожих на объекты общего назначения, я создал категории NSDictionary, которые обертывают методы получения и установки для определенных ключей. Просто используйте очень описательные имена, поскольку вы заполняете глобальное пространство имен.

@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

Единственная проблема со свойствами сеттера состоит в том, что вы должны определить геттер в обеих категориях, чтобы избежать предупреждения.

Вам все еще нужно объявить все, но вы все равно должны будете это сделать, чтобы избавиться от предупреждений для Remap.

...