2009-08-20 46 views

Trả lời

231

void * có nghĩa là "một tham chiếu đến một số đoạn ngẫu nhiên o' bộ nhớ với untyped/không rõ nội dung"

id có nghĩa là 'một tham chiếu đến một số đối tượng Objective-C ngẫu nhiên không rõ lớp'

Có thêm ngữ nghĩa khác biệt:

  • Theo GC Chỉ hoặc chế độ GC được hỗ trợ, trình biên dịch sẽ phát ra những rào cản ghi tài liệu tham khảo của loại id, nhưng không phải cho loại void *. Khi khai báo cấu trúc, điều này có thể là một sự khác biệt quan trọng. Khai báo các iVars như void *_superPrivateDoNotTouch; sẽ gây ra sự gặt hái các đối tượng sớm nếu _superPrivateDoNotTouch thực sự là một đối tượng. Đừng làm thế.

  • cố gắng gọi phương thức trên tham chiếu của loại void * sẽ chặn cảnh báo trình biên dịch.

  • cố gắng gọi phương thức trên loại id sẽ chỉ cảnh báo nếu phương thức được gọi chưa được khai báo trong bất kỳ tờ khai @interface nào được trình biên dịch nhìn thấy.

Vì vậy, bạn không nên tham khảo đối tượng dưới dạng void *. Tương tự, bạn nên tránh sử dụng biến được nhập id để chỉ một đối tượng. Sử dụng tham chiếu đã nhập lớp cụ thể nhất mà bạn có thể. Thậm chí NSObject * là tốt hơn id vì trình biên dịch có thể, ít nhất, cung cấp xác nhận tốt hơn các lời gọi phương thức đối với tham chiếu đó.

Sử dụng phổ biến và hợp lệ void * là một tham chiếu dữ liệu mờ được chuyển qua một số API khác.

Xem xét các phương pháp sortedArrayUsingFunction: context: của NSArray:

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

Chức năng sắp xếp sẽ được khai báo là:

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

Trong trường hợp này, NSArray chỉ đơn thuần là đi bất cứ điều gì bạn vượt qua trong khi context lập luận để phương thức thông qua đối số context. Nó là một mảng dữ liệu có kích thước con trỏ mờ đục, theo như NSArray có liên quan, và bạn được tự do sử dụng nó cho bất kỳ mục đích nào bạn muốn.

Không có tính năng loại đóng cửa bằng ngôn ngữ, đây là cách duy nhất để mang theo một đoạn dữ liệu có chức năng. Thí dụ; nếu bạn muốn mySortFunc() có điều kiện phân biệt chữ hoa chữ thường hoặc phân biệt chữ hoa chữ thường, trong khi vẫn an toàn theo luồng, bạn sẽ chuyển chỉ báo phân biệt chữ hoa chữ thường trong ngữ cảnh, có khả năng truyền trên đường vào và ra.

Dễ vỡ và dễ bị lỗi, nhưng cách duy nhất.

Khối giải quyết vấn đề này - Các khối được đóng cho C. Chúng có sẵn trong Clang - http://llvm.org/ và phổ biến trong Snow Leopard (http://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/GCD_libdispatch_Ref.pdf).

+0

Ngoài ra, tôi khá chắc chắn rằng một 'id' được giả định để đáp ứng với' -retain' và '-release', trong khi' void * 'hoàn toàn mờ đục với callee. Bạn không thể chuyển một con trỏ tùy ý tới '-performSelector: withObject: afterDelay:' (nó giữ lại đối tượng), và bạn không thể giả định rằng '+ [UIView beginAnimations: context:]' sẽ giữ lại ngữ cảnh (ủy nhiệm hoạt hình) nên duy trì quyền sở hữu của bối cảnh; UIKit giữ lại ủy quyền hoạt hình). –

+3

Không có giả định nào có thể được thực hiện về những gì 'id' trả lời. Một 'id' có thể dễ dàng tham chiếu đến một cá thể của một lớp không vốn có từ' NSObject'. Thực tế nói, mặc dù, tuyên bố của bạn phù hợp nhất với hành vi thế giới thực; bạn không thể kết hợp các lớp thực thi không có '' với Foundation API và nhận được rất xa, điều đó chắc chắn là chắc chắn! – bbum

+2

Bây giờ các kiểu 'id' và' Lớp' đang được coi là [* con trỏ đối tượng có thể lưu lại *] (http://clang.llvm.org/docs/AutomaticReferenceCounting.html#objects) trong ARC. Vì vậy, giả định là đúng ít nhất theo ARC. – Eonil

8

Nếu phương thức có kiểu trả về là id, bạn có thể trả về bất kỳ đối tượng Mục tiêu-C nào.

void có nghĩa là, phương pháp sẽ không trả lại bất kỳ điều gì.

void * chỉ là một con trỏ. Bạn sẽ không thể chỉnh sửa nội dung trên địa chỉ mà con trỏ trỏ đến.

+2

Vì nó áp dụng cho giá trị trả lại của một phương pháp, chủ yếu là đúng. Vì nó áp dụng để khai báo các biến hoặc đối số, không hoàn toàn. Và bạn luôn có thể bỏ dấu (void *) thành một loại cụ thể hơn nếu bạn muốn đọc/ghi nội dung - không phải là ý tưởng hay là làm như vậy. – bbum

2

sự hiểu biết của tôi là id đại diện cho một con trỏ đến một đối tượng trong khi void * có thể trỏ đến bất cứ điều gì thực sự, miễn là bạn sau đó quăng nó vào loại bạn muốn sử dụng nó như

+0

Nếu bạn đang truyền từ (void *) đến một số loại đối tượng, bao gồm cả id, bạn rất có thể làm sai. Có nhiều lý do để làm như vậy, nhưng chúng rất ít, rất xa, và hầu như luôn là dấu hiệu của một lỗi thiết kế. – bbum

+1

trích dẫn "có những lý do để làm như vậy, nhưng họ rất ít, xa giữa" đúng đó. Nó phụ thuộc vào tình hình. Tuy nhiên tôi sẽ không đưa ra tuyên bố về chăn như "bạn rất có thể làm sai" với một số bối cảnh. – hhafez

+0

Tôi sẽ làm một tuyên bố chăn; đã phải săn lùng và sửa chữa quá nhiều lỗi chết tiệt bởi vì bị đưa vào loại sai với khoảng trống * ở giữa.Một ngoại lệ là API dựa trên cuộc gọi lại có tham số ngữ cảnh * trống mà hợp đồng của nó cho biết ngữ cảnh sẽ không bị ảnh hưởng giữa việc thiết lập cuộc gọi lại và nhận cuộc gọi lại. – bbum

19

id là một con trỏ đến một đối tượng C mục tiêu, trong đó như void * là một con trỏ tới bất cứ thứ gì.

id cũng sẽ tắt các cảnh báo liên quan đến gọi mthods chưa biết, vì vậy ví dụ:

[(id)obj doSomethingWeirdYouveNeverHeardOf];

sẽ không cung cấp các cảnh báo thông thường về các phương pháp rõ. Nó sẽ, tất nhiên, tăng một ngoại lệ tại thời gian chạy trừ khi obj là nil hoặc thực sự thực hiện phương pháp đó.

Thông thường bạn nên sử dụng NSObject* hoặc id<NSObject> trong ưu tiên cho id, đó có ít nhất khẳng định rằng đối tượng quay trở lại là một đối tượng Cocoa, vì vậy bạn có thể sử dụng các phương pháp như giữ lại/phát hành/autorelease vào nó một cách an toàn.

+2

Đối với các lời gọi phương thức, một mục tiêu kiểu (id) sẽ tạo ra một cảnh báo nếu phương thức được nhắm mục tiêu chưa được khai báo ở bất kỳ đâu. Do đó, trong ví dụ của bạn, doSomethingWeirdYouveNeverHeardOf sẽ phải được khai báo ở đâu đó để không có cảnh báo. – bbum

+0

Bạn đang wuite đúng, một ví dụ tốt hơn sẽ là một cái gì đó như storagePolicy. –

+0

@PeterNLewis Tôi không đồng ý về 'Thường thì bạn nên sử dụng NSObject *' thay cho 'id'. Bằng cách chỉ định 'NSObject *', bạn thực sự nói rõ ràng rằng đối tượng là một NSObject. Bất kỳ lời gọi phương thức nào đến đối tượng sẽ dẫn đến cảnh báo, nhưng không có ngoại lệ thời gian chạy cho đến khi đối tượng đó thực sự phản hồi cuộc gọi phương thức. Cảnh báo rõ ràng là gây phiền nhiễu nên 'id' là tốt hơn. Của thô bạn có thể cụ thể hơn bằng cách ví dụ như 'id ', trong trường hợp này có nghĩa là bất kỳ đối tượng nào, nó phải tuân theo giao thức MKAnnotation. – pnizzle

7

id là con trỏ đến đối tượng Mục tiêu-C. void * là con trỏ đến mọi thứ. Bạn có thể sử dụng void * thay vì id, nhưng nó không được khuyến khích vì bạn không bao giờ nhận được cảnh báo trình biên dịch cho bất cứ điều gì.

Bạn có thể muốn xem 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

Không hoàn toàn. (void *) các biến đã nhập không thể là đích của các lời gọi phương thức. Nó kết quả trong "cảnh báo: không hợp lệ nhận loại 'void *'" từ trình biên dịch. – bbum

+0

@bbum: 'void *' các biến đã gõ chắc chắn nhất có thể là đích của lời gọi phương thức - đó là cảnh báo, không phải lỗi. Không chỉ vậy, bạn có thể làm điều này: 'int i = (int) @" Xin chào, chuỗi! ";' Và theo dõi với: 'printf (" Gửi đến một int: '% s' \ n ", [i UTF8String ]); '. Đó là một cảnh báo, không phải là một lỗi (và không chính xác đề nghị, cũng không phải di động). Nhưng lý do tại sao bạn có thể làm những điều này là tất cả các cơ bản C. – johne

+0

Xin lỗi. Bạn nói đúng; nó là một cảnh báo, không phải là một lỗi. Tôi chỉ coi cảnh báo là lỗi luôn ở mọi nơi. – 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; 

Đoạn mã trên là từ objc.h, vì vậy trông giống như id là một thể hiện của objc_object struct và isa con trỏ có thể ràng buộc với bất kỳ đối tượng C Lớp Mục tiêu, trong khi void * chỉ là một con trỏ không định kiểu.

0

Ngoài những gì đã được nói, có sự khác biệt giữa các đối tượng và con trỏ liên quan đến bộ sưu tập. Ví dụ, nếu bạn muốn đặt một cái gì đó vào NSArray, bạn cần một đối tượng (thuộc loại "id"), và bạn không thể sử dụng một con trỏ dữ liệu thô ở đó (thuộc loại "void *"). Bạn có thể sử dụng [NSValue valueWithPointer:rawData] để chuyển đổi void *rawDdata thành loại "id" để sử dụng nó bên trong bộ sưu tập. Nói chung "id" linh hoạt hơn và có nhiều ngữ nghĩa hơn liên quan đến các đối tượng gắn liền với nó. Có nhiều ví dụ hơn giải thích id type of Objective C here.

Các vấn đề liên quan