2012-04-06 16 views
7

Tôi muốn viết một hàm hoặc một chỉ thị giống như NSLog() có bất kỳ loại biến, nguyên thủy và đối tượng nào. Trong hàm đó tôi muốn phân biệt chúng.Mục tiêu-C: Cách kiểm tra xem biến có phải là một đối tượng hay không, cấu trúc hoặc nguyên mẫu khác

tôi biết làm thế nào nó hoạt động cho các đối tượng:

- (void)test:(id)object { 
    if ([object isKindOfClass:[NSString class]]) 
     ... 

nhưng làm thế nào để phân biệt các đối tượng từ cấu trúc hoặc thậm chí số nguyên hoặc nổi. Giống như:

"isKindOfStruct:CGRect" or "isInt" 

chẳng hạn?

Điều này có khả thi không? Tôi nghĩ vì bạn có thể gửi tất cả mọi thứ cho NSLog (@ "...", các đối tượng, ints, cấu trúc) nó phải có thể?

Cảm ơn bạn đã trợ giúp!

EDIT

Mục tiêu cuối cùng của tôi là triển khai một số loại đa hình.

Tôi muốn để có thể gọi hàm của tôi:

MY_FUNCTION(int) 
MY_FUNCTION(CGRect) 
MY_FUNCTION(NSString *) 
... 

or [self MYFUNCTION:int]... 

và trong MY_FUNCTION

-(void)MYFUNCTION:(???)value { 
    if ([value isKindOf:int]) 
     ... 
    else if ([value isKindOf:CGRect]) 
     ... 
    else if ([value isKindOfClass:[NSString class]]) 
     ... 
} 

Tôi biết rằng isKindOf không tồn tại và thậm chí bạn không thể thực hiện phương pháp này trên nguyên thủy . Tôi cũng không chắc về "???" loại chung của "giá trị" trong tiêu đề hàm.

Điều đó có khả thi không?

+0

isKindOf không tồn tại. isKindOfClass kiểm tra nếu một lớp là một thành viên hoặc phân lớp, và isMemberOfClass sẽ kiểm tra xem một lớp có * chính xác * một loại lớp hay không. https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Protocols/NSObject_Protocol/Reference/NSObject.html –

Trả lời

3

Một chức năng như NSLog() có thể cho biết loại mong đợi nào trong danh sách tham số của nó từ chuỗi định dạng mà bạn chuyển làm tham số đầu tiên. Vì vậy, bạn không truy vấn tham số để tìm ra loại của nó - bạn tìm ra loại bạn mong đợi dựa trên chuỗi định dạng, và sau đó bạn giải thích tham số cho phù hợp.

+0

OK, điều đó có ý nghĩa. Có cách nào để gửi chỉ một biến chung chung và phân biệt loại sau này không? – Django

+0

Tôi khuyên bạn nên sử dụng 'NSValue' để chuyển đổi bất kỳ giá trị không đối tượng nào thành đối tượng. Sẽ dễ dàng hơn nếu đưa ra lời khuyên chắc chắn nếu bạn giải thích trong câu hỏi của mình những gì bạn đang thực sự cố gắng thực hiện. – Caleb

2

Bạn không thể chuyển cấu trúc C hoặc nguyên thủy như thông số loại id. Để làm như vậy, bạn sẽ phải bọc nguyên thủy trong một đối tượng NSNumber hoặc NSValue.

ví dụ:

[self test: [NSNumber numberWithInt: 3.0]]; 

id được định nghĩa là con trỏ đến đối tượng Objective-C.

+0

Tôi biết rằng bạn không thể làm điều đó. ID chỉ dành cho các đối tượng không dành cho nguyên thủy. Vì vậy, không có loại chung cho tất cả các biến mà tôi có thể phân biệt sau này? – Django

+0

Không. Bạn có thể vượt qua một 'void *', đó là một con trỏ đến bất cứ điều gì, nhưng sẽ không có cách nào để nói những gì nó đã được một khi bạn đã có nó. – joerick

+1

NSValue có thể lấy bất kỳ dữ liệu nào với phương thức 'valueWithBytes: objCType:', nhưng bạn phải chuyển kiểu '@ encode' cũng như con trỏ' void * '. – joerick

0

Điều quan trọng cần lưu ý là id đại diện cho bất kỳ đối tượng Mục tiêu-C nào. Và bởi đối tượng Objective-C, tôi có nghĩa là một đối tượng được định nghĩa bằng cách sử dụng @interface. Nó không đại diện cho một cấu trúc hoặc kiểu nguyên thủy (int, char, vv).

Ngoài ra, bạn chỉ có thể gửi tin nhắn (cú pháp [...]) cho đối tượng Objective-C, vì vậy bạn không thể gửi thông báo isKindOf: tới cấu trúc thông thường hoặc nguyên thủy.

Nhưng bạn có thể chuyển đổi số nguyên v.v ... thành NSNumber, char * thành NSString và bọc cấu trúc bên trong lớp được phủ lớp NSObject. Sau đó, chúng sẽ là đối tượng Objective-C.

8
#define IS_OBJECT(T) _Generic((T), id: YES, default: NO) 

NSRect a = (NSRect){1,2,3,4}; 
NSString* b = @"whatAmI?"; 
NSInteger c = 9; 

NSLog(@"%@", IS_OBJECT(a)[email protected]"YES":@"NO"); // -> NO 
NSLog(@"%@", IS_OBJECT(b)[email protected]"YES":@"NO"); // -> YES 
NSLog(@"%@", IS_OBJECT(c)[email protected]"YES":@"NO"); // -> NO 

Ngoài ra, hãy kiểm tra Vincent Gable của The Most Useful Objective-C Code I’ve Ever Written cho một số công cụ rất tiện dụng mà sử dụng các chỉ thị @encode() trình biên dịch (đó) trả về một chuỗi mô tả bất kỳ loại nó cho ..."

LOG_EXPR (x) là một macro in ra x, bất kể loại x là gì, mà không phải lo lắng về các chuỗi định dạng (và các sự cố có liên quan từ ví dụ như in một chuỗi C giống như một NSString). Nó hoạt động trên Mac OS X và iOS.

1

#define IS_OBJECT (x) (strchr ("@ #", @encode (typeof (x)) [0])! = NULL) Vi này hoạt động mà tôi gặp ở đâu đó trong ngăn xếp tràn.

3

@alex câu trả lời xám không hoạt động (hoặc ít nhất không hoạt động trên iOS SDK 8.0). Bạn có thể sử dụng câu trả lời @ deepax11, tuy nhiên tôi muốn chỉ ra cách 'macro ma thuật' này hoạt động. Nó dựa vào kiểu mã hóa được cung cấp từ hệ thống. Theo tài liệu của Apple:

Để hỗ trợ hệ thống thời gian chạy, trình biên dịch mã hóa trả về và loại đối số cho mỗi phương pháp trong chuỗi ký tự và liên kết chuỗi với bộ chọn phương pháp. Lược đồ mã hóa nó sử dụng cũng hữu ích trong các ngữ cảnh khác và do đó được công bố công khai với chỉ thị trình biên dịch @encode(). Khi được đưa ra một đặc tả kiểu, @encode() trả về một chuỗi mã hóa kiểu đó. Kiểu này có thể là một kiểu cơ bản như int, một con trỏ, một cấu trúc được gắn thẻ hoặc một liên kết hoặc một tên lớp — bất kỳ kiểu nào, trên thực tế, có thể được sử dụng như một đối số cho toán tử C sizeof().

Để tách macro ra, trước tiên chúng tôi nhận "typeOf" biến của chúng tôi, sau đó gọi @encode() trên loại đó và cuối cùng so sánh giá trị trả về cho loại 'đối tượng' và 'lớp' từ bảng mã.

Full dụ nên hình như:

const char* myType = @encode(typeof(myVar));//myVar declared somewhere 
    if([@"@" isEqualToString:@(myType)] || [@"#" isEqualToString:@(myType)]) 
    { 
     //myVar is object(id) or a Class 
    } 
    else if (NSNotFound != [[NSString stringWithFormat:@"%s", myType] rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@"{}"]].location) 
    { 
     //myVar is struct 
    } 
    else if ([@"i" isEqualToString:@(myType)]) 
    { 
     //my var is int 
    } 

Xin lưu ý rằng NSInteger sẽ trở lại int trên các thiết bị 32-bit, và dài trên các thiết bị 64-bit. Danh mục đầy đủ của mã hóa:

‘c’ - char 
‘i’ - int 
’s’ - short 
‘l’ - long 
‘q’ - long long 
‘C’ - unsigned char 
‘I’ - unsigned int 
’S’ - unsigned short 
‘L’ - unsigned long 
‘Q’ - unsigned long long 
‘f’ - float 
‘d’ - double 
‘B’ - C++ bool or a C99 _Bool 
‘v’ - void 
‘*’ - character string(char *) 
‘@’ - object(whether statically typed or typed id) 
‘#’ - class object(Class) 
‘:’ - method selector(SEL) 
‘[<some-type>]’ - array 
‘{<some-name>=<type1><type2>}’ - struct 
‘bnum’ - bit field of <num> bits 
‘^type’ - pointer to <type> 
‘?’ - unknown type(may be used for function pointers) 

Đọc thêm về Type Encodings at Apple