Câu trả lời:
void *
có nghĩa là "một tham chiếu đến một số bộ nhớ o 'chunk ngẫu nhiên với nội dung chưa được đánh dấu / không xác định"
id
có nghĩa là "một tham chiếu đến một số đối tượng Objective-C ngẫu nhiên của lớp chưa biết"
Có sự khác biệt về ngữ nghĩa:
Trong chế độ Chỉ hỗ trợ hoặc Chỉ được hỗ trợ của GC, trình biên dịch sẽ phát ra các rào cản ghi cho các tham chiếu loại id
, nhưng không phải cho loại void *
. Khi khai báo cấu trúc, đây có thể là một sự khác biệt quan trọng. Khai báo iVars như thế void *_superPrivateDoNotTouch;
sẽ gây ra sự gặt hái sớm của các đối tượng nếu _superPrivateDoNotTouch
thực sự là một đối tượng. Đừng làm vậy.
cố gắng gọi một phương thức trên tham chiếu void *
kiểu sẽ loại bỏ cảnh báo trình biên dịch.
cố gắng gọi một phương thức trên một id
kiểu 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ỳ @interface
khai báo nào mà trình biên dịch nhìn thấy.
Vì vậy, người ta không bao giờ nên coi một đối tượng là a void *
. Tương tự, người ta nên tránh sử dụng một id
biến được gõ để chỉ một đối tượng. Sử dụng các tài liệu tham khảo lớp cụ thể nhất mà bạn có thể. Thậm chí NSObject *
còn tốt hơn id
bởi vì ít nhất, trình biên dịch có thể cung cấp xác thực tốt hơn các yêu cầu phương thức đối với tham chiếu đó.
Việc sử dụng phổ biến và hợp lệ void *
là dưới dạng tham chiếu dữ liệu mờ được truyền qua một số API khác.
Hãy xem xét sortedArrayUsingFunction: context:
phương pháp NSArray
:
- (NSArray *)sortedArrayUsingFunction:(NSInteger (*)(id, id, void *))comparator context:(void *)context;
Hàm 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ỉ chuyển bất cứ thứ gì bạn truyền vào làm context
đối số cho phương thức thông qua như làcontext
đối số. Nó là một khối dữ liệu có kích thước con trỏ mờ, theo như NSArray có liên quan, và bạn có thể 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 trong ngôn ngữ, đây là cách duy nhất để mang theo một khối dữ liệu có chức năng. Thí dụ; nếu bạn muốn mySortFunc () sắp xếp theo điều kiện là phân biệt chữ hoa chữ thường hoặc không phân biệt chữ hoa chữ thường, trong khi vẫn an toàn cho luồng, bạn sẽ vượt qua chỉ báo phân biệt chữ hoa chữ thường trong ngữ cảnh, có khả năng truyền vào và thoát ra.
Mong manh và dễ bị lỗi, nhưng cách duy nhất.
Các khối giải quyết điều này - Các khối đóng cửa cho C. Chúng có sẵn trong Clang - http://llvm.org/ và có sức lan tỏa trong Snow Leopard ( http://developer.apple.com/l Library / ios / documentation / Formformance /Reference/GCD_libdispatch_Ref/GCD_libdispatch_Ref.pdf ).
id
phản ứng với. An id
có thể dễ dàng tham khảo một thể hiện của một lớp không có từ đó NSObject
. Thực tế, nói, tuyên bố của bạn phù hợp nhất với hành vi trong thế giới thực; bạn không thể trộn các <NSObject>
lớp không triển khai với API nền tảng và đi rất xa, điều đó là chắc chắn!
id
và Class
các loại đang được coi là con trỏ đối tượng có thể giữ lại trong ARC. Vì vậy, giả định là đúng ít nhất là theo ARC.
id là một con trỏ tới một đối tượng C, trong đó void * là một con trỏ tới bất cứ thứ gì.
id cũng tắt các cảnh báo liên quan đến việc gọi mthods không xác định, ví dụ:
[(id)obj doSomethingWeirdYouveNeverHeardOf];
sẽ không đưa ra cảnh báo thông thường về các phương pháp chưa biết. Tất nhiên, nó sẽ đưa ra một ngoại lệ trong thời gian chạy trừ khi obj không hoặc thực sự thực hiện phương pháp đó.
Thường thì bạn nên sử dụng NSObject*
hoặc id<NSObject>
ưu tiên id
, điều này ít nhất xác nhận rằng đối tượng được trả về là đối tượng Cacao, vì vậy bạn có thể sử dụng các phương thức như giữ lại / phát hành / tự động trên nó một cách an toàn.
Often you should use NSObject*
thay vì id
. Bằng cách chỉ định NSObject*
bạn thực sự nói rõ ràng rằng đối tượng là NSObject. Bất kỳ cuộc gọi phương thức nào đến đối tượng sẽ dẫn đến một cảnh báo, nhưng không có ngoại lệ thời gian chạy miễn là đối tượng đó thực sự đáp ứng cuộc gọi phương thức. Cảnh báo rõ ràng là khó chịu vì vậy id
là tốt hơn. Nói chung id<MKAnnotation>
, bạn có thể nói cụ thể hơn bằng cách nói , 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.
Nếu một phương thức có kiểu trả về, id
bạn có thể trả về bất kỳ đối tượng Objective-C nào.
void
có nghĩa là, phương thức sẽ không trả lại bất cứ đ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ỏ tới.
id
là một con trỏ tới một đối tượng Objective-C. void *
là một con trỏ đến bất cứ điều gì . Bạn có thể sử dụng void *
thay vì id
, nhưng không nên vì bạn không bao giờ nhận được cảnh báo về 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-b between-declaring-a-variable-id- and- nsobject và unixjunkie.blogspot.com/2008/03/id-vs-nsobject-vs -id.html .
void *
các biến được gõ chắc chắn có thể là mục tiêu của các yêu cầu phương thức - đó là một cảnh báo, không phải là lỗi. Không chỉ vậy, bạn có thể làm điều này: int i = (int)@"Hello, string!";
và theo dõi với : printf("Sending to an 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 được khuyến nghị, cũng không phải là di động). Nhưng lý do tại sao bạn có thể làm những việc này là tất cả cơ bản C.
/// 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 có vẻ như id là một thể hiện của objc_object struct và con trỏ isa có thể liên kết với bất kỳ đối tượng Objective C Class nào, trong khi void * chỉ là một con trỏ chưa được gõ.
Tôi hiểu rằng 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ứ thứ gì thực sự, miễn là sau đó bạn chuyển nó sang loại bạn muốn sử dụng nó như
Ngoài những gì đã nói, có một sự khác biệt giữa các đối tượng và con trỏ liên quan đến các 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 (loại "id") và bạn không thể sử dụng một con trỏ dữ liệu thô ở đó (loại "void *"). Bạn có thể sử dụng [NSValue valueWithPointer:rawData]
để chuyển đổi void *rawDdata
sang loại "id" để sử dụng nó trong bộ sưu tập. Nói chung, "id" linh hoạt hơn và có nhiều ngữ nghĩa liên quan đến các đối tượng gắn liền với nó. Có nhiều ví dụ giải thích loại id của Objective C ở đây .
id
giả định được phản hồi-retain
và-release
trong khi đóvoid*
hoàn toàn mờ đục đối với callee. Bạn không thể chuyển một con trỏ tùy ý đến-performSelector:withObject:afterDelay:
(nó giữ lại đối tượng) và bạn không thể cho rằng nó+[UIView beginAnimations:context:]
sẽ giữ lại bối cảnh (đại biểu hoạt hình nên duy trì quyền sở hữu bối cảnh; UIKit giữ lại ủy nhiệm hoạt hình).