2

I have a small function which I want to rewrite, so that function is valid for every class. At the moment I have 10 of the same functions which all work same but every function is for another class. I know, that I have to do it with reflections, but I am not so sure how to do it. I already read this link: http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html

The functions I am talking about are:

-(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 and DamageAccountList are both NSMutableArrays from two different classes. Is it possible to see if a class consists a NSMutableArray and if yes then it should call the .... modelObject.MyMutableArray?

Charles
  • 50,943
  • 13
  • 104
  • 142
DeFlo
  • 141
  • 1
  • 1
  • 9

4 Answers4

2

You can use reflection like this:

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

But a more common technique among cocoa programmers would be:

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

Where you would accept any object which confirms to the protocol MyCustomProtocol. The protocol is defined in a header files somewhere, using:

@protocol MyCustomProtocol

@property (readonly) NSArray *missionArray;

@end

And then in each of your classes, declare it as implementing the protocol:

@interface MissionSectionListClass <MyCustomProtocol>

And add a method implementation:

@implementation MissionSectionListClass <MyCustomProtocol>

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

@end

Using protocols is a bit more code, but it's the "right" way to go. It allows you to add support for new classes, without any change to your MissiongGet... method.

More info about protocols: http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocProtocols.html

Abhi Beckert
  • 32,787
  • 12
  • 83
  • 110
  • Hi! Thank you. I think this could be the way to do it. I'll try thiand see what else I can do with protocols. – DeFlo Jan 08 '12 at 18:26
0

EDIT : Cleared all my answer to this :

I think it's not possible to check if a class has a member variable of specified type. You can only check if a class has a specified method.

So, in this case it will be best if you make all your NSMutableArray list the same name, and then create a declared property for this list, and then do a respondsToSelector in your ...GetReferencedNested method.

So, for example, in all of your class create this property :

@property (nonatomic, retain) NSMutableArray * list;

and then in the ..MissionGetReferencedNested method :

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

Correct me if i'm wrong...

Coolant
  • 448
  • 6
  • 13
  • I think, this is not that what I am looking for. My target is not to write this modelObject.MissionSectionList. Instead I want, if there is a NSMutableArray, take this array and do modelObject.Array... I want to do this because i have a lot of this classes and I dont want to do a distinction of cases every time. – DeFlo Jan 08 '12 at 16:40
  • I think I'll try the idea with the protocols first. I think I am not allowed to change the names. But if protocols are not working I'll try your idea. Thanks :) – DeFlo Jan 08 '12 at 18:28
0

In terms of style I'd also follow Abhi's suggestion.

But if you really want to inspect a class that you are stuck with and, for example build a NSCountedSet with the first NSMutableArray variable you can find, you could do it like this:

#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

Of course this has very limited real world use because it depends on you knowing that the first array will be the one you're interested in.

k1th
  • 1,332
  • 10
  • 25
0

I think I have to go with the runtime type editing.(http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html)

The idea with the protocols was good but there I have to change a lot of things in the classes.(which is not possible/allowed) for me. My intension was only to change the functions so that I have only one function for all classes.

I think with the runtime type editing I can check what classes and attributes I have (?) Am I right? Did somebody already work with runtime type editing?

DeFlo
  • 141
  • 1
  • 1
  • 9