Objective-C是否支持泛型?

问题描述 投票:12回答:6

我想知道Objective-C是否提供对泛型的任何支持吗?

例如,考虑一种方法:

-(void) sort: (NSMutableArray *) deck {
}

我有什么办法让它只处理纸牌组?这样的事情可以强制执行吗?

-(void) sort: (NSMutableArray <Card *>) deck {
}
objective-c cocoa-touch generics
6个回答
6
投票

您可以使用由objective-c运行时提供的introspection tools

[基本上,这意味着您可以检查数组中的所有对象是kind of class(A类或它的一个子类)还是member of class(A类),还是对象conforms to a protocol或[C0 ](存在某种方法)。

responds to a selector

您可以在-(void) sort: (NSMutableArray *) deck { for(id obj in deck){ if(obj isKindOfClass:[A class]]){ //this is of right class } } } 上编写一个Category方法,以便在每个对象上进行检出。

NSArray

通常,您实际上不需要很多,因为您知道要放入集合中的内容。

如果您需要检查数组中对象的类型或功能,则可能表明您的体系结构已损坏


[另一个选项可以是BOOL allAreKindOfA = [array allObjectsAreKindOfClass:[A class]]; 的子类,它仅接受某些类。但是请注意NSMutableArrayNSMutableArray的子类化注释,因为它们是类集群,因此不容易子类化。

注意:在我的NSArray中,我创建了一个other answer子类,该子类使用块进行测试(如果满足特定要求)。如果您针对班级成员身份进行测试,那么它将完全满足您的要求。使用第二个块进行错误处理。


11
投票

Objective C现在通过Xcode 7支持泛型。

如果类型不匹配,Xcode 7编译器会向您发出警告。

例如,以下行将引发编译器警告,因为数组中的第二个对象导致类型不匹配。数组仅允许NSMutableArray个对象。

NSString

5
投票

自Xcode 7发行以来,Apple已添加了对Objective-C泛型的支持。

NSArray <NSString *> *myArray = [@"str2", @1, @"str2"];

4
投票

NSArray <NSString *> *arrayOfStrings = @[@"a", @"b"]; NSDictionary <NSString *, NSDate *> *dictionaryOfDates = @{ @"a" : @1 }; 的启发,我想到了另一个想法:

在NSMutableArray上创建一个子类,它需要两个块:

  • AddBlock-一个测试块,如果一个或多个需求已满,并且仅在对象通过测试时才添加对象
  • FailBlock-一个块,定义如果测试不成功,将发生的情况。

AddBlock可以测试某些类的成员资格,例如

MonomorphicArray

并且FailBlock可以引发异常,静默失败或将未能通过测试的元素添加到另一个Array。如果未提供failBlock,则默认块将引发错误。

这些块将定义数组是作为通用数组还是作为过滤器。我将给出第二种情况的完整示例。

VSBlockTestedObjectArray.h

^BOOL(id element) {
    return [element isKindOfClass:[NSString class]];
}

VSBlockTestedObjectArray.m

#import <Foundation/Foundation.h>
typedef BOOL(^AddBlock)(id element); 
typedef void(^FailBlock)(id element); 

@interface VSBlockTestedObjectArray : NSMutableArray

@property (nonatomic, copy, readonly) AddBlock testBlock;
@property (nonatomic, copy, readonly) FailBlock failBlock;

-(id)initWithTestBlock:(AddBlock)testBlock FailBlock:(FailBlock)failBlock Capacity:(NSUInteger)capacity;
-(id)initWithTestBlock:(AddBlock)testBlock FailBlock:(FailBlock)failBlock;
-(id)initWithTestBlock:(AddBlock)testBlock;    
@end

使用方式:

#import "VSBlockTestedObjectArray.h"

@interface VSBlockTestedObjectArray ()
@property (nonatomic, retain) NSMutableArray *realArray;
-(void)errorWhileInitializing:(SEL)selector;
@end

@implementation VSBlockTestedObjectArray
@synthesize testBlock = _testBlock;
@synthesize failBlock = _failBlock;
@synthesize realArray = _realArray;


-(id)initWithCapacity:(NSUInteger)capacity
{
    if (self = [super init]) {
        _realArray = [[NSMutableArray alloc] initWithCapacity:capacity];
    }

    return self;
}

-(id)initWithTestBlock:(AddBlock)testBlock 
             FailBlock:(FailBlock)failBlock 
              Capacity:(NSUInteger)capacity
{
    self = [self initWithCapacity:capacity];
    if (self) {
        _testBlock = [testBlock copy];
        _failBlock = [failBlock copy];
    }

    return self;
}

-(id)initWithTestBlock:(AddBlock)testBlock FailBlock:(FailBlock)failBlock
{
    return [self initWithTestBlock:testBlock FailBlock:failBlock Capacity:0];
}

-(id)initWithTestBlock:(AddBlock)testBlock
{
    return [self initWithTestBlock:testBlock FailBlock:^(id element) {
        [NSException raise:@"NotSupportedElement" format:@"%@ faild the test and can't be add to this VSBlockTestedObjectArray", element];
    } Capacity:0];
}


- (void)dealloc {
    [_failBlock release];
    [_testBlock release];
    self.realArray = nil;
    [super dealloc];
}


- (void) insertObject:(id)anObject atIndex:(NSUInteger)index
{
    if(self.testBlock(anObject))
        [self.realArray insertObject:anObject atIndex:index];
    else
        self.failBlock(anObject);
}

- (void) removeObjectAtIndex:(NSUInteger)index
{
    [self.realArray removeObjectAtIndex:index];
}

-(NSUInteger)count
{
    return [self.realArray count];
}

- (id) objectAtIndex:(NSUInteger)index
{
    return [self.realArray objectAtIndex:index];
}



-(void)errorWhileInitializing:(SEL)selector
{
    [NSException raise:@"NotSupportedInstantiation" format:@"not supported %@", NSStringFromSelector(selector)];
}
- (id)initWithArray:(NSArray *)anArray { [self errorWhileInitializing:_cmd]; return nil;}
- (id)initWithArray:(NSArray *)array copyItems:(BOOL)flag { [self errorWhileInitializing:_cmd]; return nil;}
- (id)initWithContentsOfFile:(NSString *)aPath{ [self errorWhileInitializing:_cmd]; return nil;}
- (id)initWithContentsOfURL:(NSURL *)aURL{ [self errorWhileInitializing:_cmd]; return nil;}
- (id)initWithObjects:(id)firstObj, ... { [self errorWhileInitializing:_cmd]; return nil;}
- (id)initWithObjects:(const id *)objects count:(NSUInteger)count { [self errorWhileInitializing:_cmd]; return nil;}

@end

注意:此代码未完全测试。可能应实现某些未实现的方法,以供在实际程序中使用。


4
投票

不是直接,不。有几种方法可以对其进行仿真,但是它需要大量包装代码,样板代码和运行时开销。我只切换到Objective-C ++,并在需要或需要适当的泛型时使用C ++模板。

因此,如果您想向NSArray引入类型安全性/检查,则可以使用类似的方法来实现它:

VSBlockTestedObjectArray *stringArray = [[VSBlockTestedObjectArray alloc] initWithTestBlock:^BOOL(id element) {
    return [element isKindOfClass:[NSString class]];
} FailBlock:^(id element) {
    NSLog(@"%@ can't be added, didn't pass the test. It is not an object of class NSString", element);
}];


VSBlockTestedObjectArray *numberArray = [[VSBlockTestedObjectArray alloc] initWithTestBlock:^BOOL(id element) {
    return [element isKindOfClass:[NSNumber class]];
} FailBlock:^(id element) {
    NSLog(@"%@ can't be added, didn't pass the test. It is not an object of class NSNumber", element);
}];


[stringArray addObject:@"test"];
[stringArray addObject:@"test1"];
[stringArray addObject:[NSNumber numberWithInt:9]];
[stringArray addObject:@"test2"];
[stringArray addObject:@"test3"];


[numberArray addObject:@"test"];
[numberArray addObject:@"test1"];
[numberArray addObject:[NSNumber numberWithInt:9]];
[numberArray addObject:@"test2"];
[numberArray addObject:@"test3"];


NSLog(@"%@", stringArray);
NSLog(@"%@", numberArray);

使用中:

template <typename T>
class t_typed_NSMutableArray {
public:
    t_typed_NSMutableArray() : d_array([NSMutableArray new]) {}
    ~t_typed_NSMutableArray() { [d_array release]; }

    /* ... */

    T* operator[](const size_t& idx) {
        T* const obj([this->d_array objectAtIndex:idx]);
        assert([obj isKindOfClass:[T class]]);
        return obj;
    }

    void addObject(T* const obj) {
        assert([obj isKindOfClass:[T class]]);
        [this->d_array addObject:obj];
    }

private:
    NSMutableArray * const d_array;
};

然后,在传递集合时,还将获得类型安全性和重载,因此无法将 t_typed_NSMutableArray<Card> array([self cards]); // < note this exact constructor is not defined Card * firstCard = array[0]; // << ok NSString * string = array[0]; // << warning 作为t_typed_NSArray<Card>传递。


4
投票

有一种简单有效的方法(我已经在项目中使用了几年了)。可悲的是,有人删除了答案,而我恢复它的尝试被拒绝了。再来一遍:

您可以在Obj-C中重新实现C ++模板的简化版本,因为Obj-C封装了所有C(并且C ++模板是具有某些改进的编译器/调试器支持的C-宏):

这只需使用一个头文件执行一次。有人为您完成了此操作:

t_typed_NSArray<NSURL>

您最终获得了如下所示的100%合法的Obj-C代码:

https://github.com/tomersh/Objective-C-Generics

[这一切在XCode中都可以正常工作,并且具有自动完成功能,等等。

© www.soinside.com 2019 - 2024. All rights reserved.