Objective-C:id和void之间的区别*


Answers:


240

void * 意思是“对带有未类型化/未知内容的随机块内存的引用”

id 表示“对未知类的某些随机Objective-C对象的引用”

还有其他语义差异:

  • 在“仅GC”或“ GC支持”模式下,编译器将为type的引用id而不是type的引用发出写屏障void *。在声明结构时,这可能是关键的区别。void *_superPrivateDoNotTouch;如果_superPrivateDoNotTouch实际上将iVars声明为对象,则将导致对象的过早收割。不要那样做

  • 尝试在void *类型的引用上调用方法将阻止编译器警告。

  • id@interface在编译器看到的任何声明中都没有声明被调用的方法时,才尝试警告该类型的方法。

因此,永远不要将一个对象称为void *。同样,应该避免使用id类型化变量来引用对象。可以使用最具体的类键入引用。甚至NSObject *比这样做更好,id因为编译器至少可以针对该引用提供更好的方法调用验证。

的一种常见且有效的用法void *是作为通过其他API传递的不透明数据引用。

考虑以下sortedArrayUsingFunction: context:方法NSArray

- (NSArray *)sortedArrayUsingFunction:(NSInteger (*)(id, id, void *))comparator context:(void *)context;

排序函数将声明为:

NSInteger mySortFunc(id left, id right, void *context) { ...; }

在这种情况下,NSArray只会将您作为context参数传入的任何内容作为参数传递给方法context。就NSArray而言,它是指针大小的数据的不透明块,您可以随意将其用于任何目的。

如果没有语言的闭包类型功能,这是通过功能携带大量数据的唯一方法。例; 如果您希望mySortFunc()有条件区分大小写或不区分大小写,同时仍然是线程安全的,则可以在上下文中传递is-区分大小写的指示符,可能会在进出时强制转换。

易碎且容易出错,但这是唯一的方法。

块解决了这一问题-块是C的闭包。它们在Clang中可用-http: //llvm.org/,在Snow Leopard中无处不在(http://developer.apple.com/library/ios/documentation/Performance /Reference/GCD_libdispatch_Ref/GCD_libdispatch_Ref.pdf)。


此外,我非常确定id假定an 可以响应-retainand -release,而a void*对于被调用者是完全不透明的。您不能将任意指针传递给-performSelector:withObject:afterDelay:它(保留对象),也不能假定+[UIView beginAnimations:context:]将保留上下文(动画委托应保留该上下文的所有权; UIKit保留动画委托)。
tc。

3
无法做出关于id响应的假设。一个id可以很容易地引用一个类是不从固有的一个实例NSObject。但是,实际上,您的陈述最符合现实世界的行为。您不能将非<NSObject>实现类与Foundation API 混合使用,并且走得很远,这是肯定的!
bbum 2010年

2
现在idClass类型被视为ARC下的可保留对象指针。因此,该假设至少在ARC下是正确的。
尼尔2012年

什么是“过早收割”?
布拉德·托马斯

1
@BradThomas当垃圾收集程序在程序完成之前收集内存时。
bbum

21

id是指向目标C对象的指针,其中void *是指向任何对象的指针。

id还关闭与调用未知方法有关的警告,例如:

[(id)obj doSomethingWeirdYouveNeverHeardOf];

不会对未知方法发出通常的警告。当然,除非obj为nil或确实实现了该方法,否则它将在运行时引发异常。

通常,您应该使用NSObject*id<NSObject>优先于id,它至少确认返回的对象是Cocoa对象,因此您可以安全地在其上使用诸如keep / release / autorelease之类的方法。


2
对于方法调用,如果尚未在任何地方声明目标方法,则类型(id)的目标将生成警告。因此,在您的示例中,必须将doSomethingWeirdYouveNeverHeardOf声明为不发出警告的某个位置。
bbum

您说得对,更好的例子是诸如storagePolicy之类的东西。
Peter N Lewis,

@PeterNLewis我不同意Often you should use NSObject*而不是id。通过指定,NSObject*您实际上是在明确地说该对象是NSObject。对对象的任何方法调用都将导致警告,但只要该对象确实响应了该方法调用,就不会出现运行时异常。该警告显然很烦人,因此id更好。粗略地讲id<MKAnnotation>,例如,可以说得更具体些,在这种情况下,这意味着无论对象是什么,它都必须符合MKAnnotation协议。
pnizzle

1
如果要使用id <MKAnnotation>,那么也可以使用NSObject <MKAnnotation> *。然后,您可以使用MKAnnotation中的任何方法和NSObject中的任何方法(即,正常NSObject根类层次结构中的所有对象),并针对其他任何情况获得警告,这比没有警告和运行时崩溃。
彼得·N·刘易斯

8

如果方法的返回类型为id,则可以返回任何Objective-C对象。

void 意味着,该方法将不会返回任何内容。

void *只是一个指针。您将无法在指针指向的地址上编辑内容。


2
由于它适用于方法的返回值,通常是正确的。由于它不适用于声明变量或参数。而且,如果您想读取/写入内容,则始终可以将(void *)转换为更具体的类型-这样做不是一个好主意。
bbum

8

id是指向Objective-C对象的指针。void *任何东西的指针。您可以使用void *代替id,但不建议使用它,因为您永远都不会收到任何编译器警告。

您可能需要查看stackoverflow.com/questions/466777/whats-the-difference-between-declaring-a-variable-id-and-nsobjectunixjunkie.blogspot.com/2008/03/id-vs-nsobject-vs -id.html


1
不完全的。(void *)类型的变量根本不能成为方法调用的目标。结果是编译器发出“警告:无效的接收器类型'void *'”。
bbum

@bbum:void *键入的变量绝对可以成为方法调用的目标-这是一个警告,而不是错误。不仅如此,您还可以执行以下操作:int i = (int)@"Hello, string!";并继续执行:printf("Sending to an int: '%s'\n", [i UTF8String]);。这是一个警告,而不是错误(并非完全建议,也不是可移植的)。但是您可以执行这些操作的原因都是基本的
C。– johne

1
抱歉。你是对的; 这是警告,不是错误。我只是将警告视为无处不在的错误。
bbum

4
/// Represents an instance of a class.
struct objc_object {
    Class isa  OBJC_ISA_AVAILABILITY;
};

/// A pointer to an instance of a class.
typedef struct objc_object *id;

上面的代码来自objc.h,因此id似乎是objc_object struct的一个实例,isa指针可以与任何Objective C Class对象绑定,而void *只是一个无类型的指针。


2

我的理解是id表示一个对象的指针,而void *可以指向任何对象,只要将其转换为要使用的类型即可


如果将(void *)转换为某些对象类型(包括id),则很可能做错了。这样做是有原因的,但是它们之间很少,相差甚远,并且几乎总是表明存在设计缺陷。
bbum

1
引用“这样做的理由是充分的,但之间却很少。” 这取决于实际情况。但是,我不会在没有上下文的情况下做出笼统的声明,例如“您很可能做错了”。
hhafez

我要作一揽子声明;由于类型转换为错误类型而中间存在void *,因此不得不寻找并修复太多该死的错误。一个例外是基于回调的API,这些API带有void *上下文参数,其合同规定,在设置回调和接收回调之间,上下文将保持不变。
bbum

0

除了已经说过的以外,与集合相关的对象和指针之间也有区别。例如,如果要在NSArray中放入某些内容,则需要一个对象(“ id”类型),并且不能在其中使用原始数据指针(“ void *”类型)。您可以使用[NSValue valueWithPointer:rawData]转换void *rawDdata为“ id”类型,以便在集合中使用它。通常,“ id”更灵活,并且具有与附加到它的对象有关的更多语义。这里还有更多示例说明了Objective C的id类型

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.