其实我是来自java背景,我正在学习目标c。我对目标C的奇怪行为感到非常困惑。“请阅读第三个问题,重要的一个。”
问题是按顺序提供的,所以请按顺序给出答案,这对我和其他人来说是可以理解的。
问题1
我有两个派生自NSObject
的类:A
和B
:
@interface A : NSObject
@end
@interface B : NSobject
-(void)display; // It displays "I am class B"
@end
现在,如果我这样做:
A *a = [[B alloc]init]; // Show warning not error (it must be illegal)
[a display]; // prints "I am class B"
它调用了B类的显示方法。我不认为它应该发生,因为:
A
没有方法display
。通过多态性。
这可能是一个安全威胁,因为我正在创建任何类的引用并传递任何其他类的对象并通过它访问数据。
可能存在设计问题,Dog
类实例获取Printer
类的对象,现在我在print
实例上调用Dog
方法。
我引用了NSArray
并传递了NSMutableArray
的对象,现在我在这个实例上调用了NSMutableArray
方法。
[nsarr addObject:@:abc]; //看起来很奇怪
问题2
如果我有Foo
协议,并且有任何课程没有确认。不应该允许在协议引用中获取该类的对象。
@protocol Foo
@required
-(void)abc;
@end
如果我打电话:
id<Foo> obj= [[B alloc]init]; // Shows warning ignore it for now as it must be illegal also
[obj display]; // It will call display method which should be illegal
B
不符合协议Foo
而obj
正在使用B
对象并调用B
实例方法。我认为它因为多态性和安全性而非常糟糕 问题3
如果我的类有一个类方法,它返回该类的一个非自动释放的对象,编译器会显示警告。如果我将该类返回的对象(不符合协议)方法传递给协议引用。 (它应该是一个错误)。
id<Foo> obj = [Abc aClassMethodReturnsObjectWhichNotAutoreleased]; //show warning
它显示了一个很好的警告。 Abc
不符合协议Foo
BUT
id<Foo> obj = [NSArray arrayWithObjects:@"abc",@"def",nil]; // It does **not** show a warning as it will return autorelease object. NSArray doesn't conform protocol Foo
为什么上面NSArray
类的赋值没有显示上一个示例中显示的警告。
提前致谢。
修改
* 回答第3个问题: *由于NSArray返回id对象,它将允许传入“id obj”,但在“aClassMethodReturnsObjectWhichNotAutoreleased”情况下,该方法返回“ABC *”指针,这就是为什么编译器在这种情况下发出警告。
答案 0 :(得分:2)
Objective-C和Java具有非常不同的类型规则,正如您所发现的那样。
Java是严格静态类型,这意味着类型必须匹配,并且您永远不能进行类型转换规则不允许的分配。
Objective-C是动态类型,带有可选的静态类型。您可以随时突破类型系统。在某些情况下,编译器会发出警告,但仍然允许它。
这就是你看到这种行为的原因。 Objective-C没有被破坏,它只有与你从Java中学到的规则不同的规则。
Apple有很多具体规则的文档,也许你想阅读Enabling Static Behavior。
以下是有关动态与静态类型的更多资源:
Dynamic type languages versus static type languages和What do people find so appealing about dynamic languages?
答案 1 :(得分:2)
问题1:
A *a = [[B alloc]init]; //Show warning not error (it must be illegal)
[a display]; //prints "I am class B"
在这里,您对名为A
的变量使用静态类型a
。然后,您将为变量分配不同类型的对象(B
)。
与java不同,Objective-C没有强制执行静态类型要求,但是在编译时它会警告,因为编译器检测到声明之间存在差异类型和对象的实际类型。尽管如此,它很乐意将B对象填充到您的变量中,因此a
现在指向您创建的B
对象。程序编译并运行后(在运行时),A *a
被视为与id a
相同。
Objective-C的另一个特性是您可以随时向任何对象发送任何消息。这是Objective-C的动态特性的一部分。显然,有些情况下,向对象发送错误消息会导致坏事(tm)发生,因此您需要确保只发送适当的消息。有各种函数可以在运行时测试对象的类,甚至可以测试它是否能够在发送之前处理特定的消息以防止坏事。如果您使用的是静态类型(如本示例所示),编译器将发出警告,告诉您可能犯了错误并应查看代码。
问题2:
这实际上与问题1非常相似。编译器警告您正在为变量分配看似不正确的值,但是在运行时,您可以向任何对象发送任何消息,因此它将起作用在实际对象上而不是类型声明中的“expected”对象。
问题3:
好问题。我原以为你会在那里得到警告。也许其他人可以提供帮助。我的第一个想法是,这是一个错误,应该如此报告,但可能有一个原因,我不知道....
答案 2 :(得分:1)
A *a = [[B alloc]init]; //Show warning not error (it must be illegal) [a display]; //prints "I am class B"
因为您从B类初始化了具有显示属性的变量。 这是正确的