Iphone: заменить функции с помощью отражения - PullRequest
2 голосов
/ 08 января 2012

У меня есть небольшая функция, которую я хочу переписать, чтобы эта функция действовала для каждого класса. На данный момент у меня есть 10 одинаковых функций, которые работают одинаково, но каждая функция предназначена для другого класса. Я знаю, что я должен сделать это с отражениями, но я не очень уверен, как это сделать. Я уже прочитал эту ссылку: http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html

Функции, о которых я говорю:

-(NSCountedSet *)MissionGetReferecedNested:(id)modelObject
{
    setOfObjects = [[NSCountedSet alloc]initWithArray:modelObject.MissionSectionList];
    return setOfObjects;
}
-(NSCountedSet *)MissionGetSectionReferecedNested:(id)modelObject
{
    setOfObjects = [[NSCountedSet alloc]initWithArray:modelObject.DamageAccountList];
    return setOfObjects;
}

MissionSectionList и DamageAccountList являются NSMutableArrays из двух разных классов. Можно ли увидеть, состоит ли класс из NSMutableArray и если да, тогда он должен вызвать .... modelObject.MyMutableArray?

Ответы [ 4 ]

2 голосов
/ 08 января 2012

Вы можете использовать отражение следующим образом:

- (NSCountedSet *)MissionGet:(id)modelObject
{
    SEL propertySelector = NULL;

    if ([modelObject respondsToSelector:@selector(MissionSectionList)]) {
        propertySelector = @selector(MissionSectionList);
    } else if ([modelObject respondsToSelector:@selector(DamageAccountList)]) {
        propertySelector = @selector(DamageAccountList);
    }

    if (!propertySelector) {
      [NSException raise:@"Invalid modelObject value" format:@"Model object %@ does not contain any recognised selectors", modelObject];
    }

    return [[NSCountedSet alloc] initWithArray:[modelObject performSelector:propertySelector]];
}

Но более распространенным методом среди программистов какао будет:

- (NSCountedSet *)MissionGet:(id <MyCustomProtocol>)modelObject
{
    return [[NSCountedSet alloc] initWithArray:[modelObject missionArray]];
}

Где бы вы приняли любой объект, который подтверждает протокол MyCustomProtocol. Протокол определяется где-то в заголовочных файлах, используя:

@protocol MyCustomProtocol

@property (readonly) NSArray *missionArray;

@end

И затем в каждом из ваших классов объявите его как реализующий протокол:

@interface MissionSectionListClass <MyCustomProtocol>

И добавить метод реализации:

@implementation MissionSectionListClass <MyCustomProtocol>

- (NSArray *)missionArray
{
    return self.MissionSectionList;
}

@end

Использование протоколов - это немного больше кода, но это "правильный" путь. Он позволяет добавлять поддержку новых классов без каких-либо изменений в методе MissiongGet....

Подробнее о протоколах: http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocProtocols.html

0 голосов
/ 09 января 2012

Я думаю, что мне нужно идти с редактированием типов во время выполнения. (http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html)

Идея с протоколами была хорошей, но там я должен многое изменить в классах. (Что невозможно / разрешено) для меня. Мое намерение состояло только в том, чтобы изменить функции так, чтобы у меня была только одна функция для всех классов.

Я думаю, что при редактировании типов времени выполнения я могу проверить, какие классы и атрибуты у меня есть (?) Я прав? Кто-то уже работал с редактированием типов во время выполнения?

0 голосов
/ 08 января 2012

С точки зрения стиля я бы также следовал предложению Абхи.

Но если вы действительно хотите проверить класс, с которым вы застряли, и, например, создать NSCountingSet с первой найденной переменной NSMutableArray, вы можете сделать это следующим образом:

#import "Utilities.h"
#import <Foundation/Foundation.h>
#import <objc/objc-runtime.h>

@implementation Utilities

+ (NSCountedSet*)initCountedSetWithFirstArrayinObject:(id)someObject {

    unsigned int c;

    Ivar *ivar_arr = class_copyIvarList([someObject class], &c);

    for (unsigned int i = 0; i < c; i++) {
        if ([@"@\"NSMutableArray\"" isEqualToString:
             [NSString stringWithCString:ivar_getTypeEncoding(ivar_arr[i]) encoding:NSUTF8StringEncoding]
             ]) {
             return [[NSCountedSet alloc] initWithArray:object_getIvar(someObject, ivar_arr[i])];
        }
    }

    return nil;
}

@end

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

0 голосов
/ 08 января 2012

РЕДАКТИРОВАТЬ: Очистить все мои ответы на это:

Я думаю, что невозможно проверить, есть ли в классе переменная-член указанного типа.Вы можете только проверить, есть ли у класса указанный метод.

Итак, в этом случае будет лучше, если вы сделаете все свои списки NSMutableArray одинаковыми, а затем создадите объявленное свойство для этого списка, а затемсделайте responsedsToSelector в вашем методе ... GetReferencedNested.

Так, например, во всем вашем классе создайте это свойство:

@property (nonatomic, retain) NSMutableArray * list;

, а затем в методе ..MissionGetReferencedNested:

if ([modelObject respondsToSelector:@selector(list)])
    ...

Поправь меня, если я ошибаюсь ...

...