2009-12-01 14 views
11

Bất cứ khi nào tôi triển khai một phương thức trong mã của riêng tôi có thể chấp nhận hoặc trả về các đối tượng của nhiều lớp, tôi luôn cố gắng sử dụng lớp cha cụ thể nhất có sẵn. Ví dụ, nếu tôi định triển khai một phương thức có thể trả về một NSArray * hoặc một NSDictionary * tùy thuộc vào đầu vào của nó, tôi sẽ cung cấp cho phương thức đó kiểu trả về của NSObject *, vì đó là lớp siêu phổ biến trực tiếp nhất. Dưới đây là ví dụ:Tại sao sử dụng (id) trong một chữ ký phương thức khi (NSObject *) sẽ chính xác hơn?

@interface MyParser() 
- (BOOL)stringExpressesKeyValuePairs:(NSString *)string; 
- (BOOL)stringExpressesAListOfEntities:(NSString *)string; 
- (NSArray *)parseArrayFromString:(NSString *)string; 
- (NSDictionary *)parseDictionaryFromString:(NSString *)string; 
@end 

@implementation MyParser 
- (NSObject *)parseString:(NSString *)string { 
    if ([self stringExpressesKeyValuePairs:string]) { 
     return [self parseDictionaryFromString:string]; 
    } 
    else if ([self stringExpressesAListOfEntities:string]) { 
     return [self parseArrayFromString:string]; 
    } 
} 
// etc... 
@end 

Tôi đã nhận thấy nhiều trường hợp trong Foundation và các API khác mà Apple sử dụng (id) trong các chữ ký phương thức nhất định khi (NSObject *) sẽ chính xác hơn. Ví dụ, đây là một phương pháp NSPropertyListSerialization:

+ (id)propertyListFromData:(NSData *)data 
      mutabilityOption:(NSPropertyListMutabilityOptions)opt 
        format:(NSPropertyListFormat *)format 
      errorDescription:(NSString **)errorString 

Các loại lại có thể từ phương pháp này là NSData, NSString, NSArray, NSDictionary, NSDate, và NSNumber. Dường như với tôi rằng một kiểu trả về (NSObject *) sẽ là một lựa chọn tốt hơn (id), vì người gọi sau đó sẽ có thể gọi các phương thức NSObject như giữ lại mà không có kiểu đúc.

Tôi thường cố gắng mô phỏng thành ngữ được thiết lập bởi các khung chính thức, nhưng tôi cũng muốn hiểu điều gì thúc đẩy họ. Tôi chắc chắn rằng Apple có một số lý do hợp lệ để sử dụng (id) trong các trường hợp như thế này, nhưng tôi chỉ không nhìn thấy nó. Tôi đang thiếu gì?

+0

NSProxy không phải là NSObject. NSObject không phải là lớp gốc duy nhất, do đó, nó không phải là một giả định an toàn rằng mọi thứ sẽ là một lớp con của nó. –

Trả lời

8

Sử dụng id cho trình biên dịch nó sẽ là đối tượng của loại không xác định. Sử dụng NSObject trình biên dịch sau đó sẽ mong bạn chỉ sử dụng các tin nhắn có sẵn cho NSObject. Vì vậy ... Nếu bạn biết một mảng đã được trả về và nó được gán là id, bạn có thể gọi objectAtIndex: không có cảnh báo trình biên dịch. Trong khi quay trở lại với một dàn diễn viên NSObject, bạn sẽ nhận được cảnh báo.

1

Bạn đã có thể gọi -giữ trên con trỏ của id loại mà không cần truyền. Nếu bạn sử dụng một loại siêu lớp cụ thể, bạn sẽ phải bỏ con trỏ mỗi lần bạn gọi phương thức của lớp con để tránh cảnh báo trình biên dịch. Sử dụng id để trình biên dịch sẽ không cảnh báo bạn và để thể hiện rõ hơn ý định của bạn.

19

Lý do tại sao (id) được sử dụng trong khai báo phương pháp là hai lần:

(1) Phương pháp này có thể lấy hoặc trả về bất kỳ loại nào. NSArray chứa bất kỳ đối tượng ngẫu nhiên nào và do đó, objectAtIndex: sẽ trả về một đối tượng của bất kỳ loại ngẫu nhiên nào. Truyền nó tới NSObject* hoặc id <NSObject> sẽ không chính xác vì hai lý do; đầu tiên, một mảng có thể chứa các lớp con không NSObject miễn là chúng triển khai một tập hợp các phương thức nhỏ nhất định và, thứ hai, một kiểu trả về cụ thể sẽ yêu cầu truyền.

(2) Mục tiêu-C không hỗ trợ khai báo biến đổi. Xem xét:

@interface NSArray:NSObject 
+ (id) array; 
@end 

Bây giờ, bạn có thể gọi +array trên cả NSArrayNSMutableArray. Cái cũ trả về một mảng bất biến và mảng sau là một mảng có thể thay đổi được. Bởi vì thiếu sự hỗ trợ khai báo biến thể của Objective-C, nếu ở trên được khai báo là trả về (NSArray*), các máy khách của phương thức lớp con sẽ phải truyền tới `(NSMutableArray *). Xấu xí, mong manh và dễ bị lỗi. Vì vậy, việc sử dụng kiểu generic là, nói chung, giải pháp đơn giản nhất.

Vì vậy, ... nếu bạn khai báo phương thức trả về phiên bản của một lớp cụ thể, hãy nhập rõ ràng. Nếu bạn khai báo một phương thức sẽ bị ghi đè và ghi đè đó có thể trả lại một lớp con thực tế là nó trả về một lớp con sẽ được tiếp xúc với khách hàng, sau đó sử dụng (id).

Không cần phải gửi lỗi - đã có một số lỗi.


Lưu ý rằng ObjC hiện nay đã hạn chế hỗ trợ co-sai qua các từ khóa instancetype.

I.e. + Phương pháp mảng NSArray 's bây giờ có thể được khai báo là:

+ (instancetype) array; 

Và trình biên dịch sẽ đối xử với [NSMutableArray array] như trả lại một NSMutableArray* khi [NSArray array] sẽ được coi là trở NSArray*.

1

(id) cũng thường được trả lại để cho phép các đối tượng được phân lớp dễ dàng hơn. Ví dụ, trong các phương thức khởi tạo và thuận tiện, trả về (id) có nghĩa là bất kỳ lớp con nào không phải ghi đè lên các phương thức của lớp cha trừ khi có một lý do cụ thể để làm như vậy.

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