Iphone:使用反射替换功能

时间:2012-01-08 15:52:35

标签: iphone reflection swizzling

我有一个小函数,我想重写,所以该函数对每个类都有效。 目前我有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 个答案:

答案 0 :(得分:2)

你可以使用这样的反射:

- (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

答案 1 :(得分:0)

编辑:清除了我对此的所有答案:

我认为无法检查某个类是否具有指定类型的成员变量。您只能检查类是否具有指定的方法。

因此,在这种情况下,最好是使所有NSMutableArray列表具有相同的名称,然后为此列表创建声明的属性,然后在... GetReferencedNested方法中执行respondsToSelector。

因此,例如,在您的所有类中创建此属性:

@property (nonatomic, retain) NSMutableArray * list;

然后在..MissionGetReferencedNested方法中:

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

如果我错了,请纠正我......

答案 2 :(得分:0)

在风格方面,我也会遵循Abhi的建议。

但是如果你真的想要检查一个你坚持使用的类,例如用你能找到的第一个NSMutableArray变量构建一个NSCountedSet,你可以这样做:

#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

当然这对现实世界的使用非常有限,因为它取决于你知道第一个阵列将是你感兴趣的阵列。

答案 3 :(得分:0)

我想我必须使用运行时类型编辑。(http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html)

协议的想法很好,但我必须在课堂上改变很多东西。(这是不可能的/允许的)对我来说。我的意图只是改变函数,以便我只有一个函数用于所有类。

我认为通过运行时类型编辑,我可以检查我有哪些类和属性(?)我是对的吗? 有人已经使用了运行时类型编辑吗?