Animal
是一个名为BOOL
的{{1}}属性的类。 alive
,Monkey
和Zebra
是Walrus
的子类。如果我有一个名为Animal
的{{1}}实例,其中包含NSArray
,zoo
和Monkey
的混合实例,我想找到第一个活着的Zebra
实例,我可能会这样做:
Walrus
问题是当我设置Zebra
时,编译器会抱怨指针类型不兼容。如果我像Zebra *zebra;
for (Animal *animal in zoo) {
if ([animal isMemberOfClass:[Zebra class]] && animal.alive) {
zebra = animal;
break;
}
}
那样进行一些投射,那么它似乎有效,但我不确定在Objective-C中这种投射是否安全。
处理这种情况的更好方法是什么?
答案 0 :(得分:5)
您遇到了“是一个”问题。
如果您已将zebra
分配给animal
,那么就不存在任何问题,因为zebra
的班级是Zebra
,“是”Animal
1}}。
但是,根据您正在做的事情,您将animal
分配给zebra
,但animal
属于超级类Animal
。 “是一个”测试失败,但是一个简单的演员:
zebra = (Zebra *)animal;
负责编译器警告。是的,这是安全的。
这是一种更现代的做同样事情的方式。顺便说一下,由于在最后一行-objectAtIndex:
返回id
,因此不会出现上述问题:
NSUInteger index = [zoo indexOfObjectPassingTest:^BOOL(Animal *animal, NSUInteger idx, BOOL *stop) {
return ([animal isMemberOfClass:[Zebra class]] && animal.alive);
}];
Zebra *zebra = (index != NSNotFound) ? [zoo objectAtIndex:index] : nil;
请注意,我确实从id obj
到Animal *animal
调整了该块的一个参数;如果您知道您的集合仅包含对Animal
实例的引用,那么您当然可以这样做。
答案 1 :(得分:3)
绝对是在Objective-C中做到这一点的方法。如果您首先检查它是您的铸造类型,那么它非常安全。即使它“键入”其他内容,isMemberOfClass
也会使用运行时来报告真正的内容。
答案 2 :(得分:1)
执行相同任务的另一种方法。
[zoo enumerateObjectsusingBlock:^(id obj, NSUInteger index, BOOL *stop){
if ([obj isMemberOfClass:[Zebra class] && animal.alive){
zebra = (Zebra *)animal;
*stop = YES;
}
}];
顺便说一下,这种铸造方式是安全的。