2010-08-24 28 views
16

Giả sử (vì lợi ích của đối số) mà tôi có một lớp xem có chứa một NSDictionary. Tôi muốn toàn bộ các thuộc tính, tất cả đều truy cập vào các thành viên của từ điển đó. Ví dụ: Tôi muốn @property NSString* title@property NSString* author.Viết thuộc tính @dynamic của riêng tôi trong Cocoa

Đối với mỗi một trong các thuộc tính này, việc triển khai cũng giống nhau: đối với người khởi động, hãy gọi [dictionary objectForKey:propertyName]; và đối với người đặt làm tương tự với setObject: forKey :.

Sẽ mất nhiều thời gian và sử dụng vô số mã sao chép và dán để viết tất cả các phương pháp đó. Có cách nào để tạo tất cả tự động, chẳng hạn như Core Data có thuộc tính @dynamic cho lớp con NSManagedObject không? Để rõ ràng, tôi chỉ muốn phương tiện truy cập này cho các thuộc tính mà tôi xác định trong tiêu đề, không chỉ bất kỳ khóa tùy ý nào.

Tôi đã đi qua valueForUndefinedKey: như một phần của mã hóa giá trị khóa, có thể xử lý các getters, nhưng tôi không hoàn toàn chắc chắn cho dù đây là cách tốt nhất để đi.

Tôi cần những thuộc tính rõ ràng này để tôi có thể liên kết với chúng trong Trình tạo giao diện: Cuối cùng tôi lên kế hoạch viết bảng IB cho chế độ xem này.

(BTW, tôi biết ví dụ của tôi về việc sử dụng NSDictionary để lưu trữ những thứ này có chút khó hiểu. Tôi thực sự viết một lớp con của WebView và các thuộc tính sẽ tham chiếu đến ID của các phần tử HTML, nhưng điều đó không quan trọng đối với logic của câu hỏi của tôi!)

Trả lời

17

Tôi đã tự giải quyết vấn đề này sau khi rót tài liệu thời gian chạy mục tiêu-c.

tôi thực hiện phương pháp lớp này:

+ (BOOL) resolveInstanceMethod:(SEL)aSEL 
{ 
    NSString *method = NSStringFromSelector(aSEL); 

    if ([method hasPrefix:@"set"]) 
    { 
     class_addMethod([self class], aSEL, (IMP) accessorSetter, "[email protected]:@"); 
     return YES; 
    } 
    else 
    { 
     class_addMethod([self class], aSEL, (IMP) accessorGetter, "@@:"); 
     return YES; 
    } 
    return [super resolveInstanceMethod:aSEL]; 
} 

Tiếp theo là một cặp chức năng C:

NSString* accessorGetter(id self, SEL _cmd) 
{ 
    NSString *method = NSStringFromSelector(_cmd); 
    // Return the value of whatever key based on the method name 
} 

void accessorSetter(id self, SEL _cmd, NSString* newValue) 
{ 
    NSString *method = NSStringFromSelector(_cmd); 

    // remove set 
    NSString *anID = [[[method stringByReplacingCharactersInRange:NSMakeRange(0, 3) withString:@""] lowercaseString] stringByReplacingOccurrencesOfString:@":" withString:@""]; 

    // Set value of the key anID to newValue 
} 

Kể từ khi mã này cố gắng để thực hiện bất kỳ phương pháp đó được gọi là trên lớp và chưa thực hiện , nó sẽ gây ra vấn đề nếu ai đó cố gắng gọi một cái gì đó bạn đang lưu ý mong đợi. Tôi dự định thêm một số kiểm tra sanity, để đảm bảo tên phù hợp với những gì tôi mong đợi.

+2

Chỉ có tinh chỉnh tôi sẽ thực hiện là chỉ viết thường chữ cái đầu tiên (và đảm bảo chuỗi _cmd có ít nhất 5 ký tự): NSString * property = NSStringFromSelector (_cmd); NSString * firstLetter = [[thuộc tính substringWithRange: NSMakeRange (3, 1)] lowercaseString]; property = [firstLetter stringByAppendingString: [property substringWithRange: NSMakeRange (4, [độ dài thuộc tính] - 5)]]; – charles

+0

Ví dụ tuyệt vời. Tôi đã nhận được như là một loại IMP đông lạnh (tức là cho 'setName:' ​​IMP của tôi là 'setValueForName') nhưng việc bạn sử dụng' NSStringFromSelector (_cmd) 'đã cho tôi chính xác cú đá trong chiếc quần tinh thần mà tôi đang tìm kiếm tổng quát điều này thành 'setWhatever:'. Cảm ơn bạn đã viết nó lên. – matt

0

có vẻ như bạn nên sử dụng một lớp học thay vì từ điển. bạn đang tiến gần đến việc thực hiện bằng tay những gì ngôn ngữ đang cố gắng cung cấp cho bạn.

+1

Vâng, điều từ điển là một ví dụ giả tạo. Tôi đang thực sự sử dụng điều này để tạo một khung nhìn cho một tài liệu HTML mà bạn có thể kết nối với các ràng buộc. Bạn nói đúng rằng nếu đó là tất cả những gì tôi đang làm, nó sẽ là một kế hoạch xấu :) –

2

Bạn có thể sử dụng kết hợp các lựa chọn đề nghị của bạn:

  • sử dụng từ khóa @dynamic
  • ghi đè valueForKey: và setValue: forKey: để truy cập từ điển
  • sử dụng API của Objective-C phản ánh method class_getProperty và kiểm tra nó cho nil. Nếu không có thì lớp của bạn có tài sản như vậy. Nó không phải là nó.
  • sau đó gọi phương thức siêu trong trường hợp không có thuộc tính như vậy.

Tôi hy vọng điều này sẽ hữu ích. Có thể có vẻ hơi khó hiểu (sử dụng sự phản chiếu) nhưng thực sự đây là giải pháp rất linh hoạt và hoàn toàn "hợp pháp" đối với vấn đề ...

PS: cách coredata là có thể nhưng sẽ là quá mức cần thiết trong trường hợp của bạn .. .

+0

Tôi sẽ thêm bit class_getProperty vào giải pháp của mình, tôi nghĩ vậy. Cảm ơn. –

2

Befriend Macro? Điều này có thể không chính xác 100%.

#define propertyForKey(key, type) \ 
    - (void) set##key: (type) key; \ 
    - (type) key; 

#define synthesizeForKey(key, type) \ 
    - (void) set##key: (type) key \ 
    { \ 
     [dictionary setObject];// or whatever \ 
    } \ 
    - (type) key { return [dictionary objectForKey: key]; } 
0

Có một blog đẹp với mã ví dụ có kiểm tra mạnh mẽ hơn về thuộc tính động tại https://tobias-kraentzer.de/2013/05/15/dynamic-properties-in-objective-c/ cũng là câu trả lời SO rất hay tại số Objective-C dynamic properties at runtime?.

Vài điểm trên câu trả lời. Có lẽ muốn khai báo một @property trong giao diện để cho phép typeahead cũng khai báo các thuộc tính là động trong việc triển khai thực hiện.

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