【问题标题】:Objective-C: difference between id and void *Objective-C: id 和 void * 的区别
【发布时间】:2010-11-21 05:01:45
【问题描述】:

idvoid * 有什么区别?

【问题讨论】:

    标签: objective-c void-pointers


    【解决方案1】:

    void * 的意思是“对一些随机内存块的引用,其中包含无类型/未知内容”

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

    还有额外的语义差异:

    • 在 GC Only 或 GC Supported 模式下,编译器将为 id 类型的引用发出写屏障,但不会为 void * 类型的引用发出写屏障。在声明结构时,这可能是一个关键的区别。如果_superPrivateDoNotTouch 实际上是一个对象,则声明像void *_superPrivateDoNotTouch; 这样的iVar 将导致对象的过早收获。不要那样做。

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

    • 尝试调用 id 类型的方法只会在被调用的方法未在编译器看到的任何 @interface 声明中声明时发出警告。

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

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

    考虑NSArraysortedArrayUsingFunction: context:方法:

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

    排序函数将被声明为:

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

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

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

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

    块解决了这个问题——块是 C 的闭包。它们在 Clang 中可用——http://llvm.org/ 并且在 Snow Leopard (http://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/GCD_libdispatch_Ref.pdf) 中普遍存在。

    【讨论】:

    • 此外,我很确定id 被假定为响应-retain-release,而void* 对被调用者完全不透明。您不能将任意指针传递给-performSelector:withObject:afterDelay:(它保留对象),也不能假设+[UIView beginAnimations:context:] 将保留上下文(动画委托应保持上下文的所有权;UIKit 保留动画委托) .
    • 无法假设 id 的响应内容。一个id 可以很容易地引用一个不是NSObject 固有的类的实例。但实际上,您的陈述最符合现实世界的行为。您不能将非<NSObject> 实现类与 Foundation API 混合使用并走得很远,这是肯定的!
    • 现在 idClass 类型在 ARC 下被视为 retainable object pointer。所以这个假设至少在 ARC 下是正确的。
    • 什么是“早熟”?
    • @BradThomas 当垃圾收集器在程序完成之前收集内存。
    【解决方案2】:

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

    id 也会关闭与调用未知方法相关的警告,例如:

    [(id)obj doSomethingWeirdYouveNeverHeardOf];

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

    您通常应该使用NSObject*id<NSObject> 而不是id,这至少可以确认返回的对象是一个Cocoa 对象,因此您可以安全地使用诸如retain/release/autorelease 之类的方法。

    【讨论】:

    • 对于方法调用,如果目标方法未在任何地方声明,则类型为 (id) 的目标将生成警告。因此,在您的示例中,必须在某处声明 doSomethingWeirdYouveNeverHeardOf 才不会出现警告。
    • 你说得对,更好的例子是 storagePolicy。
    • @PeterNLewis 我不同意Often you should use NSObject* 而不是id。通过指定NSObject*,您实际上是在明确地说该对象是一个NSObject。对该对象的任何方法调用都会导致警告,但只要该对象确实响应了方法调用,就不会出现运行时异常。警告显然很烦人,所以id 更好。粗略地说,您可以通过例如说 id<MKAnnotation> 来更具体,在这种情况下,这意味着无论对象是什么,它都必须符合 MKAnnotation 协议。
    • 如果你打算使用 id,那么你也可以使用 NSObject*。然后允许您使用 MKAnnotation 中的任何方法和 NSObject 中的任何方法(即,正常 NSObject 根类层次结构中的所有对象),并获得其他任何警告,这比没有警告和运行时崩溃。
    【解决方案3】:

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

    void 表示,该方法不会返回任何内容。

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

    【讨论】:

    • 因为它适用于方法的返回值,大部分是正确的。因为它适用于声明变量或参数,但并不完全如此。如果你想读/写内容,你总是可以将 (void *) 转换为更具体的类型——这并不是一个好主意。
    【解决方案4】:

    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

    【讨论】:

    • 不完全。 (void *) 类型变量根本不能成为方法调用的目标。它会导致编译器发出“警告:无效的接收器类型'void *'”。
    • @bbum: void * 类型变量绝对可以成为方法调用的目标——这是一个警告,而不是错误。不仅如此,您还可以这样做:int i = (int)@"Hello, string!"; 并跟进:printf("Sending to an int: '%s'\n", [i UTF8String]);。这是一个警告,而不是错误(不完全推荐,也不可移植)。但之所以能做这些事,都是基本的C语言。
    • 对不起。你是对的;这是一个警告,而不是错误。我只是随时随地将警告视为错误。
    【解决方案5】:
    /// 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* 只是一个无类型指针。

    【讨论】:

      【解决方案6】:

      我的理解是 id 代表一个指向对象的指针,而 void * 可以指向任何东西,只要你然后将它转换为你想要使用它的类型

      【讨论】:

      • 如果您从 (void *) 转换为某些对象类型,包括 id,您很可能做错了。这样做是有原因的,但它们很少,相差甚远,而且几乎总是表明存在设计缺陷。
      • 引用“有理由这样做,但他们很少,相差甚远”确实如此。这取决于实际情况。但是,在没有上下文的情况下,我不会做出像“你很可能做错了”这样的笼统陈述。
      • 我会做一个笼统的声明;由于转换为错误的类型,中间有 void*,因此不得不寻找并修复太多该死的错误。一个例外是基于回调的 API,它采用 void* 上下文参数,其合同规定在设置回调和接收回调之间上下文将保持不变。
      【解决方案7】:

      除了已经说过的之外,与集合相关的对象和指针之间存在差异。例如,如果你想在 NSArray 中放入一些东西,你需要一个对象(“id”类型),并且不能在其中使用原始数据指针(“void *”类型)。您可以使用[NSValue valueWithPointer:rawData]void *rawDdata 转换为“id”类型,以便在集合中使用它。一般来说,“id”更灵活,并且具有更多与附加对象相关的语义。还有更多解释id type of Objective C here的例子。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-06-26
        • 1970-01-01
        • 2019-12-05
        • 1970-01-01
        • 2011-04-25
        • 2015-09-26
        • 1970-01-01
        相关资源
        最近更新 更多