我可以创建一个所有元素都是类型的NSMutableArray
实例吗?SomeClass
没有人把它放在这里,所以我会这样做!
现在,Objective-C正式支持这一点.从Xcode 7开始,您可以使用以下语法:
NSArray*myArray = @[[MyClass new], [MyClass new]];
注意
重要的是要注意这些只是编译器警告,并且您在技术上仍然可以将任何对象插入到数组中.有些脚本可以将所有警告转换为错误,从而阻止构建.
对于人们从强类型语言(如C++或Java)过渡到更弱或动态类型的语言(如Python,Ruby或Objective-C),这是一个相对常见的问题.在Objective-C中,大多数对象都继承自NSObject
(type id
)(其余的继承自其他根类,例如NSProxy
也可以是类型id
),任何消息都可以发送到任何对象.当然,将消息发送到它无法识别的实例可能会导致运行时错误(并且还会导致编译器警告用适当的-W标志).只要实例响应您发送的消息,您可能不关心它属于哪个类.这通常被称为"鸭子打字",因为"如果它像鸭子一样嘎嘎叫[即响应选择器],它就是一只鸭子[即它可以处理信息;谁在乎它是什么类]".
您可以使用该-(BOOL)respondsToSelector:(SEL)selector
方法测试实例是否在运行时响应选择器.假设你要调用数组中的每个实例的方法,但不保证所有实例可以处理消息(所以你不能只是使用NSArray
的-[NSArray makeObjectsPerformSelector:]
,像这样的工作:
for(id o in myArray) { if([o respondsToSelector:@selector(myMethod)]) { [o myMethod]; } }
如果您控制实现您希望调用的方法的实例的源代码,则更常见的方法是定义@protocol
包含这些方法的实例,并声明所讨论的类在其声明中实现该协议.在此用法中,a @protocol
类似于Java接口或C++抽象基类.然后,您可以测试整个协议的一致性,而不是对每种方法的响应.在前面的示例中,它不会产生太大的影响,但如果您调用多个方法,则可能会简化操作.那么这个例子就是:
for(id o in myArray) { if([o conformsToProtocol:@protocol(MyProtocol)]) { [o myMethod]; } }
假设MyProtocol
声明myMethod
.第二种方法很受欢迎,因为它比第一种方法更能说明代码的意图.
通常,这些方法之一使您无需关心数组中的所有对象是否都是给定类型.如果你仍然关心,标准的动态语言方法是单元测试,单元测试,单元测试.因为此要求中的回归将产生(可能是不可恢复的)运行时(非编译时)错误,所以您需要具有测试覆盖率来验证行为,以便您不会将崩溃释放到野外.在这种情况下,执行修改数组的操作,然后验证数组中的所有实例是否属于给定的类.通过适当的测试覆盖,您甚至不需要额外的运行时开销来验证实例标识.你确实有良好的单元测试覆盖率,不是吗?
您可以使用一种-addSomeClass:
方法创建一个类别,以允许编译时静态类型检查(因此编译器可以让您知道,如果您尝试通过该方法添加它知道的不同类的对象),但是没有真正的方法可以强制执行数组只包含给定类的对象.
通常,在Objective-C中似乎不需要这样的约束.我不认为我曾经听过有经验的Cocoa程序员对该功能的期望.唯一似乎是来自其他语言的程序员仍在使用这些语言的人.如果只需要数组中给定类的对象,则只在该类中粘贴该类的对象.如果要测试代码的行为是否正常,请对其进行测试.
您可以子类化NSMutableArray
以强制类型安全.
NSMutableArray
是一个类集群,因此子类化并不简单.我最终继承NSArray
并转发调用到该类内的数组.其结果是一类叫做ConcreteMutableArray
这是很容易的子类.这是我想出的:
CustomArray.h
CustomArray.m
更新:结帐这从迈克灰博客帖子上继承一个类集群.
在项目中包含这些文件,然后使用宏生成您希望的任何类型:
MyArrayTypes.h
CUSTOM_ARRAY_INTERFACE(NSString) CUSTOM_ARRAY_INTERFACE(User)
MyArrayTypes.m
CUSTOM_ARRAY_IMPLEMENTATION(NSString) CUSTOM_ARRAY_IMPLEMENTATION(User)
用法:
NSStringArray* strings = [NSStringArray array]; [strings add:@"Hello"]; NSString* str = [strings get:0]; [strings add:[User new]]; //compiler error User* user = [strings get:0]; //compiler error
其他想法
它继承自NSArray
支持序列化/反序列化
根据您的喜好,您可能希望覆盖/隐藏通用方法,例如
- (void) addObject:(id)anObject
查看https://github.com/tomersh/Objective-C-Generics,这是Objective-C的编译时(预处理器实现的)泛型实现.这篇博文有一个很好的概述.基本上你得到编译时检查(警告或错误),但是对于泛型没有运行时惩罚.