什么更好,为什么?
在类init方法和代码中的常用变量等情况下,哪种情况更好?
答案 0 :(得分:11)
什么更好,为什么?
明确的输入信息总是更好,除非你因某些原因无法使用它(见下文)。
它允许编译器更严格地验证代码,并在编译时捕获许多错误,否则会导致应用程序在运行时崩溃。
很久很久以前,API中的所有内容都使用id
。事实证明这是一个完全痛苦的对接;脆弱,并导致许多崩溃,这些崩溃本来会被特定类型捕获。
所以,它改变了。 (这是〜1994年)。
在这种情况下有什么好处 类init方法和通常的 代码中的变量?
对于init
,您别无选择,只能使用通用(id)
返回类型。 Objective-C不支持共变量或反变量声明,也没有一种机制来推广init
的声明,同时还提供对特定类型检查的支持。
同样适用于retain
,objectAtIndex:
,addObject:
以及许多其他方法,这些方法接受或返回多种对象中的一种(或将其作为参数)。
而且,不,id
与NSView*
之间绝对没有任何性能差异。
你可以在明确时给出一个例子 打字会引起问题吗?
如果你写了:
- (MyClass *) init;
在子类中:
- (MySubclass *) init;
你最有可能得到编译器警告,或者你必须对wazoo进行类推。
答案 1 :(得分:5)
在clang的最新版本(在Lion中),您实际上不应该返回id
,而是返回instancetype
。这是一个在返回类型中使用的关键字,用于指定它返回的类型是接收消息的类的实例。它现在是OS X Lion上init方法的首选返回类型。
答案 2 :(得分:4)
显式输入提供构建时保护,如果您对可能无法正常工作的内容进行转换或执行操作等操作,则会通知您可能出现的问题。
显式打字还有助于防止输入错误的对象的非明显转移,这是通过您未考虑的代码中的路径传播的结果。在程序经过大量测试之后,这种错误通常不会变得清晰,更常见的是在发布之后。
对于未来的程序员(包括你未来的自己)也很有帮助,试图使用你的代码,使他们更有可能一眼就知道一个对象应该是什么。因此,它使代码更加“自我记录”。
有些东西不能有意义的类型,因为实际上没有类型适用。其他时候您需要使用id
因为您需要能够接受任何类型的对象。例如,Cocoa Touch在引用消息的sender
时会使用它,因为任何东西都可以发送它;指定显式类型根本不起作用。
绝大多数时候,显性类型对您有利。
答案 3 :(得分:2)
尽可能使用特定类型,但不是更多。考虑如何使用任何特定的变量,参数或返回类型并适当地设置其类型。
例如,UITableView的dataSource
属性声明为id<UITableViewDataSource>
,因为表视图只关心其数据源是符合UITableViewDataSource
协议的对象。这允许数据源足够灵活,可以与任何实现协议的特定类一起使用,但如果您尝试将未实现该协议的对象分配为数据源,则仍允许编译器发出警告。
如果你太具体了,那么你的代码变得不灵活,只接受非必要的特定实现(即当你可以真正使用任何NSString时要求NSMutableString)。
如果你太模糊(例如,输入id
所有内容),那么你就无法识别何时将未识别的选择器发送到特定实例,并且编译器无法识别任何数量的无效语句。
对于init
方法,请遵循The Objective-C Programming Language
初始化方法的返回类型应为id。
这样做的原因是id表示该类是故意不被考虑的 - 该类是未指定的并且可能会发生变化,具体取决于调用的上下文。例如,NSString提供了一个方法initWithFormat:。但是,当发送到NSMutableString(NSString的子类)的实例时,该消息返回NSMutableString的实例,而不是NSString。 (另请参阅“组合分配和初始化”中给出的单例示例。)
答案 4 :(得分:0)
我认为两者之间没有性能差异。 您可以让id返回init类型,因为您可以转换init的结果。
例如:
Toto *foo = [[Toto alloc] init];
id foo2 = [[Toto alloc] init];
两者都有效,但您必须像foo2
一样强制转换(Toto *)foo
变量,以便在不创建编译器警告的情况下访问实例的属性或方法。即使它工作正常......
我认为一些开发人员会使用id,因为他们只是通过变量槽实例传递而不使用它。这种使用允许不导入.h
的问候,
KL94