NSMutableArray - 强制数组仅保存特定对象类型 [英] NSMutableArray - force the array to hold specific object type only

查看:173
本文介绍了NSMutableArray - 强制数组仅保存特定对象类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有没有办法强制NSMutableArray只保存一个特定的对象类型?



我有类定义如下:

  @interface Wheel:NSObject 
{
int size;
float diameter;
}
@end


@interface Car:NSObject
{
NSString * model;
NSString * make;
NSMutableArray * wheels;
}
@end

如何强制 strong>数组只保存 Wheel 对象? (绝对不是其他对象)

解决方案

2015年更新

这个答案是在2011年初写的,并开始:


我们真正想要的是参数多态性,声明,说, NSMutableArray< NSString> ;


2015年苹果显然改变了这一点,将轻量级泛型引入到Objective-C中,现在您可以声明:

  NSMutableArray< NSString *> * onlyStrings = [NSMutableArray new]; 

但是所有的都不是什么似的,注意轻量级...然后注意初始化部分上面的声明不包含任何通用符号。虽然Apple引入了参数集合,并直接向上面的数组添加了非字符串 onlyStrings ,如:

  [onlyStrings addObject:@ 666]; // < - 警告:不兼容的指针类型... 

会违反指示的警告,类型安全几乎没有皮肤深。考虑方法:

   - (void)push:(id)obj onto:(NSMutableArray *)array 
{
[array addObject:obj];
}

和同一类别的另一个方法中的代码片段:

  NSMutableArray< NSString *> * oops = [NSMutableArray new]; 
[self push:@asdaonto:oops]; // add a string,fine
[self push:@ 42 onto:oops]; //添加一个数字,没有警告...

Apple已经实现的本质上是一个提示系统协助与Swift的自动互操作,它具有类型安全的泛型的风味。然而在Objective-C方面,虽然编译器提供了一些额外的提示,系统是轻量级的,类型完整性仍然最终归于程序员,就像Objective-C的方式。



那么你应该使用什么?新的轻量级/伪类,或者为你的代码设计自己的模式?



例如:如果你的目标是与Swift的互操作,你应该使用轻量级的泛型!然而,如果集合的类型完整性在您的场景中很重要,那么您可以将轻量级泛型与您自己的在Objective-C端的代码组合,这将强制Swift将在其侧面的类型完整性。



2011答案的剩余部分



另一个选项是NSMutableArray的一个简单的子类,在你的单形数组中你想要的对象的种类。这个选项不给你静态的类型检查(在你以前得到的Obj-C),你得到运行时异常插入错误的类型,正如你得到运行时异常索引超出边界等。 p>

这是不是彻底测试,并假设文档上覆盖NSMutableArray是正确的...

  @interface MonomorphicArray:NSMutableArray 
{
Class elementClass;
NSMutableArray * realArray;
}

- (id)initWithClass:(Class)element andCapacity:(NSUInteger)numItems;
- (id)initWithClass:(Class)element;

@end

而实现:

  @implementation MonomorphicArray 

- (id)initWithClass:(Class)element andCapacity:(NSUInteger)numItems
{
elementClass = element;
realArray = [NSMutableArray arrayWithCapacity:numItems];
return self;
}

- (id)initWithClass:(Class)元素
{
elementClass = element;
realArray = [NSMutableArray new];
return self;
}

//覆盖原始NSMutableArray方法并强制单形

- (void)insertObject:(id)anObject atIndex:(NSUInteger)index
{
if([anObject isKindOfClass:elementClass])//允许子类,使用isMemeberOfClass完全匹配
{
[realArray insertObject:anObject atIndex:index];
}
else
{
NSException * myException = [NSException
exceptionWithName:@InvalidAddObject
reason:@添加对象类型错误
userInfo:nil];
@throw myException;
}
}

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

//覆盖原始NSArray方法

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

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


//阻塞所有其他init(一些可能被支持)

static id NotSupported()
{
NSException * myException = [NSException
exceptionWithName:@InvalidInitializer
reason:@只有initWithClass:和initWithClass:andCapacity:supported
userInfo:nil];
@throw myException;
}

- (id)initWithArray:(NSArray *)anArray {return NotSupported(); }
- (id)initWithArray:(NSArray *)array copyItems:(BOOL)flag {return NotSupported(); }
- (id)initWithContentsOfFile:(NSString *)aPath {return NotSupported(); }
- (id)initWithContentsOfURL:(NSURL *)aURL {return NotSupported(); }
- (id)initWithObjects:(id)firstObj,... {return NotSupported(); }
- (id)initWithObjects:(const id *)objects count:(NSUInteger)count {return NotSupported(); }

@end

使用方式:

  MonomorphicArray * monoString = [[MonomorphicArray alloc] initWithClass:[NSString class] andCapacity:3]; 

[monoString addObject:@A string];
[monoString addObject:[NSNumber numberWithInt:42]]; // will throw
[monoString addObject:@Another string];


Is there a way to force NSMutableArray to hold one specific object type only?

I have classes definitions as follow:

@interface Wheel:NSObject  
{    
  int size;  
  float diameter;  
}  
@end  


@interface Car:NSObject  
{  
   NSString *model;  
   NSString *make;  
   NSMutableArray *wheels;  
}  
@end

How can I force wheels array to hold Wheel objects only with code? (and absolutely not other objects)

解决方案

Update in 2015

This answer was first written in early 2011 and began:

What we really want is parametric polymorphism so you could declare, say, NSMutableArray<NSString>; but alas such is not available.

In 2015 Apple apparently changed this with the introduction of "lightweight generics" into Objective-C and now you can declare:

NSMutableArray<NSString *> *onlyStrings = [NSMutableArray new];

But all is not quite what it seems, notice the "lightweight"... Then notice that the initialisation part of the above declaration does not contain any generic notation. While Apple have introduced parametric collections, and adding a non-string directly to the above array, onlyStrings, as in say:

[onlyStrings addObject:@666]; // <- Warning: Incompatible pointer types...

will illicit the warning as indicated, the type security is barely skin deep. Consider the method:

- (void) push:(id)obj onto:(NSMutableArray *)array
{
   [array addObject:obj];
}

and the code fragment in another method of the same class:

NSMutableArray<NSString *> *oops = [NSMutableArray new];
[self push:@"asda" onto:oops]; // add a string, fine
[self push:@42 onto:oops];     // add a number, no warnings...

What Apple have implemented is essentially a hinting system to assist with automatic inter-operation with Swift, which does have a flavour of type-safe generics. However on the Objective-C side, while the compiler provides some extra hints the system is "lightweight" and type-integrity is still ultimately down to the programmer - as is the Objective-C way.

So which should you use? The new lightweight/pseudo generics, or devise your own patterns for your code? There really is no right answer, figure out what makes sense in your scenario and use it.

For example: If you are targeting interoperation with Swift you should use the lightweight generics! However if the type integrity of a collection is important in your scenario then you could combine the lightweight generics with your own code on the Objective-C side which enforces the type integrity that Swift will on its side.

The Remainder of the 2011 Answer

As another option here is a quick general subclass of NSMutableArray which you init with the kind of object you want in your monomorphic array. This option does not give you static type-checking (in as much as you ever get it in Obj-C), you get runtime exceptions on inserting the wrong type, just as you get runtime exceptions for index out of bounds etc.

This is not thoroughly tested and assumes the documentation on overriding NSMutableArray is correct...

@interface MonomorphicArray : NSMutableArray
{
    Class elementClass;
    NSMutableArray *realArray;
}

- (id) initWithClass:(Class)element andCapacity:(NSUInteger)numItems;
- (id) initWithClass:(Class)element;

@end

And the implementation:

@implementation MonomorphicArray

- (id) initWithClass:(Class)element andCapacity:(NSUInteger)numItems
{
    elementClass = element;
    realArray = [NSMutableArray arrayWithCapacity:numItems];
    return self;
}

- (id) initWithClass:(Class)element
{
    elementClass = element;
    realArray = [NSMutableArray new];
    return self;
}

// override primitive NSMutableArray methods and enforce monomorphism

- (void) insertObject:(id)anObject atIndex:(NSUInteger)index
{
    if ([anObject isKindOfClass:elementClass]) // allows subclasses, use isMemeberOfClass for exact match
    {
        [realArray insertObject:anObject atIndex:index];
    }
    else
    {
        NSException* myException = [NSException
            exceptionWithName:@"InvalidAddObject"
            reason:@"Added object has wrong type"
            userInfo:nil];
        @throw myException;
    }
}

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

// override primitive NSArray methods

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

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


// block all the other init's (some could be supported)

static id NotSupported()
{
    NSException* myException = [NSException
        exceptionWithName:@"InvalidInitializer"
        reason:@"Only initWithClass: and initWithClass:andCapacity: supported"
        userInfo:nil];
    @throw myException;
}

- (id)initWithArray:(NSArray *)anArray { return NotSupported(); }
- (id)initWithArray:(NSArray *)array copyItems:(BOOL)flag { return NotSupported(); }
- (id)initWithContentsOfFile:(NSString *)aPath { return NotSupported(); }
- (id)initWithContentsOfURL:(NSURL *)aURL { return NotSupported(); }
- (id)initWithObjects:(id)firstObj, ... { return NotSupported(); }
- (id)initWithObjects:(const id *)objects count:(NSUInteger)count { return NotSupported(); }

@end

Use as:

MonomorphicArray *monoString = [[MonomorphicArray alloc] initWithClass:[NSString class] andCapacity:3];

[monoString addObject:@"A string"];
[monoString addObject:[NSNumber numberWithInt:42]]; // will throw
[monoString addObject:@"Another string"];

这篇关于NSMutableArray - 强制数组仅保存特定对象类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆