2009-12-08 24 views
37

Tôi muốn kiểm tra xem một đối tượng có @property có thể ghi trong SDK iPhone không.Làm cách nào để bạn biết liệu khóa có tồn tại cho một đối tượng sử dụng Mã hóa khóa-giá trị không?

Một cách có thể thực hiện việc này là kiểm tra phương pháp -valueForKey: nhưng điều đó có vẻ khá không phù hợp!

Ví dụ:

@try { 
    id *value = [instance valueForKey:@"myProperty"]; 
    } 
    @catch (NSException * e) { 
    // Key did not exist 
    } 

Có cách nào tốt hơn để làm điều này?

+0

http: // stackoverflow.com/a/3059053/294884 – Fattie

Trả lời

49

Nếu bạn đang tạo đối tượng đang được kiểm tra, bạn có thể ghi đè valueForUndefinedKey:setValue:forUndefinedKey để làm điều gì đó hữu ích hơn việc tăng ngoại lệ.

Nếu, mặt khác, bạn đang cố gắng quan sát các đối tượng mà bạn không biết về thời gian chạy, bạn sẽ phải sử dụng các phương pháp thời gian chạy để thực hiện điều đó. Bạn có thể sử dụng chính thời gian chạy-mục tiêu-c và gọi số class_copyPropertyList hoặc protocol_copyPropertyList và xử lý chúng hoặc sử dụng Foundation và gọi respondsToSelector trên đối tượng cho bộ thu/giải mã KVC cho một thuộc tính cụ thể, ví dụ như đối với thuộc tính foo bạn sẽ gọi một cái gì đó như [someObject respondsToSelector:NSSelectorFromString(@"foo")];.

5

Không có cách nào, nói chung, để xác định xem một đối tượng có một giá trị cho một khóa nhất định hay không. Một cá thể có thể quyết định trả lại giá trị cho một khóa không xác định khác từ phương thức -valueForUndefinedKey: của nó. Hoặc nó có thể cho phép thực hiện mặc định ném một ngoại lệ. Đối tượng hiện đại-C (2.0) thường khai báo các thuộc tính liên quan trong API công khai của chúng. Đối với các đối tượng này, bạn có thể sử dụng thời gian chạy của đối tượng là class_copyPropertyList hoặc protocol_copyPropertyList để có danh sách các thuộc tính có sẵn. Bạn cũng có thể mô phỏng danh sách tìm kiếm KVC, kiểm tra qua repondsToSelector: nếu cá thể phản hồi phương thức getter thích hợp. Cuối cùng, bạn có thể sử dụng thời gian chạy của class_copyIvarList để kiểm tra các ivars cho một ivar thích hợp. Đây là số công việc mà bạn không được thực hiện. Nó sẽ không đảm bảo rằng bạn biết liệu một trường hợp đối tượng sẽ tăng một ngoại lệ khi gửi một tin nhắn valueForKey: và nó cho thấy một vấn đề lớn hơn ...

Nếu bạn có một bộ sưu tập các đối tượng phải cung cấp một giá trị cho một quan trọng, nhưng một số thì không, bạn có vấn đề về thiết kế. Bạn nên xác định một giao diện thích hợp (một @protocol trong Mục tiêu-C) khai báo các thuộc tính cần thiết. Sau đó bạn có thể kiểm tra sự phù hợp với giao thức này, hoặc sử dụng trình biên dịch để thực hiện một số kiểm tra kiểu biên dịch cho bạn để đảm bảo cá thể phù hợp với giao thức (nếu bạn đang đi qua các cá thể đơn).

+0

Có lẽ nó sẽ giúp làm rõ! Ứng dụng chạy trên nhiều thời gian chạy, với các phiên bản thư viện khác nhau có sẵn. Trong một phiên bản, một thuộc tính tôi cần thiết lập (của kiểu nguyên thủy) không tồn tại - do đó, yêu cầu kiểm tra xem nó có tồn tại hay không. – Ted

+3

Nếu bạn kiểm soát các lớp được đề cập và biết rằng lớp * sẽ * trả lời cho bộ chọn thích hợp trong phiên bản thư viện bao gồm thuộc tính của bạn, hãy sử dụng 'respondsToSelector:'. Trong trường hợp này, bạn nên sử dụng phương thức getter trực tiếp thay vì 'valueForKey:' vì nó sẽ nhanh hơn. –

+0

Thật không may, tôi không kiểm soát các lớp được đề cập. Để làm cho mọi thứ trở nên dễ dàng hơn, tôi đã chuyển sang sử dụng 'NSInvocation' để thiết lập thuộc tính được gõ nguyên gốc thay vì sử dụng Mã hóa khóa-giá trị (và thực sự đang sử dụng' respondsToSelector: '). – Ted

0

Làm theo từ @ Jason Coco trả lời.

Đây là đề nghị của tôi về cách kiểm tra cụ thể cho bộ chọn "bộ chọn" tồn tại của thuộc tính bạn đang cố đặt.

Đây là sự bẩn thỉu thuần túy nhưng nó hoạt động và tôi thấy mình đang sử dụng nó. Bạn đã được cảnh báo ..

NS_INLINE SEL _Nonnull IBPropertySetSelectorForPropertyName(NSString*_Nonnull propertyName) 
{ 

    NSString* firstLetter = [propertyName substringToIndex:1]; 
    propertyName = [propertyName substringFromIndex:1]; 

    propertyName = [[NSString alloc] initWithFormat:@"set%@%@:",firstLetter.capitalizedString,propertyName]; 

    return NSSelectorFromString(propertyName); 
} 

Cách sử dụng:

Trong nhanh chóng: Thêm đoạn mã vào tiêu đề cầu nối của bạn sau đó:

let key = "someKey"  
let sel = IBPropertySetSelectorForPropertyName(key) 

if SOMETHING.responds(to: sel) {SOMETHING.setValue(value, forKeyPath: key)} 
+0

Tại sao không chỉ sử dụng 'NSSelectorFromString (" setPropertyName: ")' trong 'Swift'? –

+0

một điểm hợp lệ nhưng có vẻ như nó tạo ra cảnh báo nguy hiểm. –

+0

Chỉ 'Selector (" selectorName: ")' tạo ra một cảnh báo. 'NSSelectorFromString (...)' không. Rõ ràng nó không an toàn hơn nữa. –

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