Objective C中id类型详解

好文章,有空再来翻译。
id vs NSObject* vs id<NSObject>

There's often confusion about the difference between the following three declarations in Objective-C:


  1. id foo1;
  2. NSObject *foo2;
  3. id<NSObject> foo3;

The first one is the most common. It simply declares a pointer to some Objective-C object (see  /usr/include/objc/objc.h).  id gives the compiler no information about the actual type of the object, so the compiler cannot do compile-time type checking for you. Thus, the compiler will let you send any (*) message to objects declared  id. Actually, this is why the common idiom of  [[Foo alloc] init] doesn't cause the compiler to complain.  +alloc is declared to return type  id, so the compiler won't yell when you then send the returned object the message  init (or even  initWithMyFoo:blah).

So, objects declared using  id are just dynamically typed at runtime. The compiler has no useful information about the object's real type, so it can't warn you if you send it a message that it may not respond to.

Just because we know that an  id is an Objective-C object does  not mean that it points to an object that derives from NSObject, or that it even has common methods like retain and release. One solution is to statically type our variable using  NSObject* as shown in number 2 above. This gives the compiler information about the class of the object pointed to by  foo2 so the compiler can warn if you send a message to  foo2 that an  NSObject doesn't respond to. This means you can safely call retain, release, description, etc., but the compiler will warn if you call  length or  count or anything that an NSObject doesn't respond to.

So, declaring a generic pointer of type NSObject* is very similar to what you would do in other languages, like Java, but it's really a bit too restrictive for a language as flexible as Objective-C. Despite what you may have learned at one point, not all Foundation/Cocoa objects derive from NSObject. As an example, NSProxy is not derived from NSObject, so the  foo2 pointer above would not be able to hold an NSProxy subclass, even though NSProxy does implement common methods like retain and release. What you really want is a pointer to  any object that behaves like an NSObject. And that's exactly what the third case does.

Declaring an object as  id<NSObject> tells the compiler that you don't care what type the object is, but you do care that it conforms to the specified NSObject protocol**. The compiler will ensure that all objects you assign to that pointer conform to the required protocol. A pointer typed like this can safely hold any NSObject (because NSObject conforms to the NSObject protocol), but it could also hold any NSProxy, because NSProxy also conforms to the NSObject protocol. In english, the declaration  id<NSObject> foo3; says "foo3 is a pointer to an object of any type that behaves like an NSObject". This is very powerful, convenient, and expressive. In reality, we often don't care what type an object is, we just care that it responds to the messages that we want to send it (e.g., retain, release). 

So how do you decide which form you want to use? It's pretty easy. If you don't want (or can't have) any type checking, then use a plain  id. This is very common for return types on methods that don't know the type of object they're returning (e.g.,  +alloc). It is also common to declare delegates to be type  id, because delegates are generally checked at runtime with  respondsToSelector:, and they usually aren't retained. 

However, if you do want compile-time type checking, you must decide between the second and third cases. Well, let me just help you out—you want the third case! :-) I've very, very, VERY rarely seen a situation where  NSObject * worked but  id<NSObject> would not. And using the protocol form has the advantage that it will work with NSProxys. You may think that you never use NSProxys, but Cocoa's distributed objects system makes heavy use of NSProxy subclasses. Additionally, the common case is that you simply want to ensure that an object can be retained or released, and in that case the protocol form conveys that intent better; you really don't care what class the object is, you only care that it  behaves like an NSObject.
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值