Iphone: Replace functions using reflection
-
15-04-2021 - |
سؤال
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?
المحلول
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
نصائح أخرى
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...
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.
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?