2015-05-13 11 views
7

Một NSNumber chứa một Bool có thể dễ dàng bị nhầm lẫn với các loại khác có thể được bọc trong lớp NSNumber:Có cách nào đúng để xác định rằng một NSNumber có nguồn gốc từ một Bool sử dụng Swift không?

NSNumber(bool:true).boolValue // true 
NSNumber(integer: 1).boolValue // true 
NSNumber(integer: 1) as? Bool // true 
NSNumber(bool:true) as? Int // 1 

NSNumber(bool:true).isEqualToNumber(1) // true 
NSNumber(integer: 1).isEqualToNumber(true) // true 

Tuy nhiên, thông tin về loại ban đầu của nó được giữ lại, như chúng ta có thể thấy ở đây:

NSNumber(bool:true).objCType.memory == 99 // true 
NSNumber(bool:true).dynamicType.className() == "__NSCFBoolean" // true 
NSNumber(bool:true).isEqualToValue(true) || NSNumber(bool:true).isEqualToValue(false) //true 

Câu hỏi đặt ra là: phương pháp nào trong số những cách tiếp cận này là cách tiếp cận tốt nhất (và/hoặc an toàn nhất) để xác định khi nào một Bool được bọc trong một NSNumber chứ không phải cái gì khác? Tất cả đều hợp lệ không? Hoặc, là có một giải pháp tốt hơn?

Trả lời

10

Bạn có thể đặt cùng một câu hỏi cho Mục tiêu-C và đây là câu trả lời trong Mục tiêu-C - mà bạn có thể gọi từ hoặc dịch sang Swift.

NSNumber là cầu nối miễn phí đến CFNumberRef, một cách khác để nói một đối tượng NSNumber thực ra là một CFNumber một (và ngược lại).Bây giờ CFNumberRef có một loại cụ thể cho dữ liệu boolean, CFBooleanRef, và điều này được sử dụng khi tạo một boolean CFNumberRef aka NSNumber * ... Vì vậy, tất cả các bạn cần làm là kiểm tra xem bạn NSNumber * là một thể hiện của CFBooleanRef:

- (BOOL) isBoolNumber:(NSNumber *)num 
{ 
    CFTypeID boolID = CFBooleanGetTypeID(); // the type ID of CFBoolean 
    CFTypeID numID = CFGetTypeID((__bridge CFTypeRef)(num)); // the type ID of num 
    return numID == boolID; 
} 

Lưu ý: Bạn có thể nhận thấy rằng NSNumber/CFNumber đối tượng được tạo từ boolean thực sự là các đối tượng cố định được xác định trước; một cho số YES, một cho số NO. Bạn có thể bị cám dỗ dựa vào điều này để nhận dạng. Tuy nhiên, mặc dù hiện tại có vẻ là đúng và được hiển thị trong Apple's source code, với kiến ​​thức của chúng tôi là không được ghi lại vì vậy không nên dựa vào đó.

HTH

Phụ Lục

Swift dịch mã (bằng GoodbyeStackOverflow):

func isBoolNumber(num:NSNumber) -> Bool 
{ 
    let boolID = CFBooleanGetTypeID() // the type ID of CFBoolean 
    let numID = CFGetTypeID(num) // the type ID of num 
    return numID == boolID 
} 
+0

Tôi đã đánh dấu câu trả lời này là câu trả lời đúng vì bạn đã cung cấp giải pháp làm việc. Vì nó không có trong Swift, tôi đã chỉnh sửa câu trả lời của bạn để bao gồm mã Swift (chỉ cần đợi xem xét ngang hàng của bản chỉnh sửa đó). Cảm ơn. – sketchyTech

+0

@GoodbyeStackOverflow - đã phê duyệt bản dịch của bạn cho bạn. – CRD

+0

Tuyệt vời, cảm ơn một lần nữa. – sketchyTech

0

Cái đầu tiên là chính xác.

NSNumber là lớp Objective-C. Nó được xây dựng cho Objective-C. Nó lưu trữ các loại bằng cách sử dụng các loại mã hóa của Objective-C. Vì vậy, trong Objctive-C, giải pháp tốt nhất sẽ là:

number.objCType[0] == @encoding(BOOL)[0] // or string compare, what is not necessary here 

Điều này đảm bảo rằng việc thay đổi loại mã hóa sẽ hoạt động sau khi biên dịch lại.

AFAIK bạn không có @encoding() trong Swift. Vì vậy, bạn phải sử dụng một chữ. Tuy nhiên, điều này sẽ không phá vỡ, bởi vì @encoding() được thay thế tại thời gian biên dịch và thay đổi các mã hóa sẽ phá vỡ với mã biên dịch. Không chắc.

Cách tiếp cận thứ hai sử dụng số nhận dạng nội bộ. Đây có thể là chủ đề thay đổi.

Tôi nghĩ rằng cách tiếp cận thứ ba sẽ có dương tính giả.

+0

Vấn đề là '@encode (BOOL) 'và' @encode (đã ký char) ' và '@encode (char)' tất cả trả về 'c' /' 99', (cung cấp '-funsigned-char' không được cung cấp cho trình biên dịch). – dreamlax

+0

Đây là thuộc tính của 'NSNumber' không thể thay đổi. Và tôi không thấy một vấn đề với điều đó. –

+0

Vì OP được hỏi cụ thể về việc xác định liệu một 'NSNumber' có đang bao bọc một đối tượng' BOOL' * thay vì cái gì khác *. – dreamlax

0

Đừng dựa vào tên lớp vì nó có thể thuộc về một cụm lớp và đó là chi tiết triển khai (và do đó có thể thay đổi).

Thật không may, loại Objective-C BOOL ban đầu là một chỉ typedef cho một signed char trong C, mà luôn luôn mã hóa như c (đây là 99 giá trị mà bạn đang nhìn thấy, kể từ c trong ASCII là 99).

Trong hiện đại Objective-C, tôi tin rằng các loại BOOL là một kiểu Boolean thực tế (ví dụ: không còn chỉ là một typedef cho signed char) nhưng đối với khả năng tương thích, nó vẫn mã hóa như c khi trao cho @encode().

Vì vậy, không có cách nào để nói cho dù 99 ban đầu được gọi một signed char hoặc một BOOL, như xa như NSNumber là có liên quan họ đều giống nhau.

Có thể nếu bạn giải thích lý do tại sao bạn cần biết liệu NSNumber ban đầu là BOOL, có thể có giải pháp tốt hơn.

+0

Lý do là vì khi sử dụng Bool NSJSONSerialization được nhập dưới dạng trường hợp NSNumber và tôi cần phải phân biệt giữa các trường hợp này để xử lý JSON theo kiểu an toàn, ví dụ: chỉ cho phép một Bool thay đổi thành true hoặc false không phải 10 hoặc 100, hoặc 999.99, và để đảm bảo chúng được chuyển thành true và false không 1 và 0 trong JSON được xuất. – sketchyTech

+0

Khi chuyển trở lại JSON, hãy gọi '-boolValue' trên' NSNumber' để nhận 1 hoặc 0. –

+1

Khi tôi hiểu nó từ tài liệu, boolValue chỉ cung cấp một cách giải thích bool của bất kỳ cá thể NSNumber nào: "Giá trị 0 luôn luôn có nghĩa là sai và bất kỳ giá trị nào khác được hiểu là đúng. " Nhưng tôi muốn duy trì biểu diễn đúng/sai và không nhầm lẫn giữa các số và số. – sketchyTech

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