Каков наилучший способ вызвать установщик свойства по имени? - PullRequest
4 голосов
/ 29 декабря 2010

Я создаю универсальный пользовательский интерфейс, который может подключаться к ряду базовых свойств объекта, поэтому я бы хотел вызывать метод получения и установки по имени.Я играл с использованием NSInvocation, а также видел, как другие используют setValue:forKey.Но я бы хотел использовать самый быстрый метод.

Если я держу ссылку на экземпляр NSInvocation, я могу довольно быстро вызвать invoke, но у меня возникает проблема - как я могу вызвать установщик свойств, используя его?Он отлично работает на геттере так:

SEL propSelector = NSSelectorFromString(propertyName);
NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[[target class]instanceMethodSignatureForSelector:propSelector]];
[inv setSelector:propSelector];
[inv setTarget:target];
[inv invoke];
float value;
[inv getReturnValue:&value];

Но я не смог найти способ присвоить значение селектору.Использование setArgument:atIndex:, похоже, не работает.

Итак, я прибегаю к методу setValue:forKey:, но я обеспокоен тем, что производительность со временем резко возрастет.

Есть идеи?

Ответы [ 4 ]

4 голосов
/ 29 декабря 2010

Я не совсем понимаю, чего вы пытаетесь достичь, но я быстро проверил производительность valueForKey: по сравнению с вашим методом вызова.В моих тестах код ниже, valueForKey: почти в 5 раз быстрее, чем метод NSInvocation.

  1. 1.620685 миллисекунд
  2. 30.357355 миллисекунд
  3. 7.593611 миллисекунд

Код:

#import <Foundation/Foundation.h>
#include <mach/mach_time.h>


@interface Foo : NSObject
{
    NSNumber *value;
}

@property (nonatomic, retain) NSNumber *value;

@end

@implementation Foo

@synthesize value;

- (void) dealloc
{
    [value release];
    [super dealloc];
}

@end

#pragma mark MAIN
int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    // prepare loops
    NSUInteger i;
    NSUInteger max = 10000;
    Foo *target = [[[Foo alloc] init] autorelease];
    target.value = [NSNumber numberWithFloat:12.345f];
    NSString *valueKey = @"value";

    // prepare mach timebase
    mach_timebase_info_data_t timebase;
    mach_timebase_info(&timebase);
    double ticksToNanoseconds = (double)timebase.numer / timebase.denom;

    // old-fashioned
    uint64_t startTime = mach_absolute_time();
    for (i = 0; i < max; i++) {
        NSNumber *numValue = [target valueForKey:valueKey];
    }
    uint64_t elapsedTime = mach_absolute_time() - startTime;
    double elapsedTimeInNanoseconds = elapsedTime * ticksToNanoseconds;
    NSLog(@"1: %f millisec", elapsedTimeInNanoseconds / 1000000);

    // invocation
    startTime = mach_absolute_time();
    for (i = 0; i < max; i++) {
        SEL propSelector = NSSelectorFromString(valueKey);
        NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[[target class] instanceMethodSignatureForSelector:propSelector]];
        [inv setSelector:propSelector];
        [inv setTarget:target];
        [inv invoke];
        NSNumber *numValue;
        [inv getReturnValue:&numValue];
    }
    elapsedTime = mach_absolute_time() - startTime;
    elapsedTimeInNanoseconds = elapsedTime * ticksToNanoseconds;
    NSLog(@"2: %f millisec", elapsedTimeInNanoseconds / 1000000);

    // reused invocation
    SEL propSelector = NSSelectorFromString(valueKey);
    NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[[target class] instanceMethodSignatureForSelector:propSelector]];

    startTime = mach_absolute_time();
    for (i = 0; i < max; i++) {
        [inv setSelector:propSelector];
        [inv setTarget:target];
        [inv invoke];
        NSNumber *numValue;
        [inv getReturnValue:&numValue];
    }
    elapsedTime = mach_absolute_time() - startTime;
    elapsedTimeInNanoseconds = elapsedTime * ticksToNanoseconds;
    NSLog(@"3: %f millisec", elapsedTimeInNanoseconds / 1000000);

    // clean and return
    [pool drain];
    return 0;
}
0 голосов
/ 29 декабря 2010

setValue: forKey: определенно правильный путь для этого.Многие кусочки какао используют этот механизм.Если я правильно помню, точечный синтаксис Objective-C для свойств фактически просто переводится в setValue: forKey: / valueForKey: (или, по крайней мере, что-то функционально похожее) во время компиляции.На вашем месте я бы использовал это до тех пор, пока вы не сможете определить, с помощью реальных измерений, что это отрицательно влияет на скорость.В противном случае вы действуете странным и не идиоматическим образом без реальной выгоды.

0 голосов
/ 29 декабря 2010

Вы правы, что беспокоитесь о спектакле setValue:forKey:.Это обязательно имеет значительные накладные расходы по сравнению с прямым сообщением объекту. Однако , как и во всех вопросах оптимизации производительности, вы должны , а не преждевременно писать дополнительный (или трудно поддерживаемый) код, пока не сможете доказать , что производительность снизиласьнедопустимо в вашем случае использования .setValue:forKey:, вероятно, является правильным решением в вашей ситуации.

Под капотом setValue:forKey: должен сделать что-то похожее на танец NSInvocation, который вы пробуете.Я подозреваю, однако, что он сделает это по крайней мере так же (и, вероятно, намного лучше), чем ваши первые несколько попыток.setValue:forKey:, в конце концов, является ключевым элементом инфраструктуры Cocoa, и вы можете поспорить, что инженеры Apple оптимизировали его до чертиков.

0 голосов
/ 29 декабря 2010

setArgument:atIndex: работает, но вы должны изменить имя селектора на правильную форму.То есть set + propertyNameWithCapitalizedFirstLetter + :.

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