2013-07-10 25 views
6

Vấn đề


Tôi đã đi qua một vấn đề thú vị và đã không thể tìm thấy bất kỳ tài liệu về nó ... Đôi khi properties tuyên bố trong một không được triển khai trong một lớp cụ thể phù hợp với điều đó và ngoại lệ thời gian chạy xảy ra. Có phải dynamic property định nghĩa được tối ưu hóa trong một số trường hợp lạ? Có thể protocols không được sử dụng với properties đã được khai báo là dynamic không? Bất kỳ cái nhìn sâu sắc vào điều này sẽ được đánh giá rất nhiều.obj-c tính giao thức không được thực hiện trong lớp Tuân thủ

Dưới đây là một số chi tiết khác.

Cho một :

@protocol MyProtocol <NSObject> 
    @property (nonatomic, strong) id someProperty; 
@end 

và một lớp học thực hiện như vậy:

@interface MyClass <MyProtocol> 
@end 

@implementation MyClass 
@dynamic someProperty; 
@end 

tôi đã nhận thấy rằng đôi khi tôi không thể nhận được bất kỳ thông tin từ gọi

class_getProperty(myClass, propertyName); 

cho số properties trong số . Điều này chỉ xảy ra với một số lớp học và dường như không thường xuyên.

Tôi đang chạy Xcode 4 mới nhất và liên kết với SDK iOS 6. Tôi có Xcode 5 trước khi phát hành được cài đặt trên cùng một máy mặc dù nó không phải là mặc định (thông qua xcode-select).

Ví dụ


Nếu bạn chạy mã này:

@protocol MyProtocol <NSObject> 

@property (nonatomic, strong) id someData; 

@end 

@interface MyObject : NSObject <MyProtocol> 

@end 

@implementation MyObject 

@dynamic someData; 

@end 

và sau đó bạn chạy

const char *name = [@"someData" UTF8String]; 
objc_property_t property = class_getProperty([MyObject class], name); 
const char *attributes = property_getAttributes(property); 

bạn sẽ nhận được dữ liệu meta trên property mặc dù property doesn' t tồn tại. Nói cách khác, bạn không cần phải tổng hợp thuộc tính để lấy thuộc tính của nó là. Thời gian chạy vẫn biết về nó. Hãy thử nó cho chính mình. Vấn đề là đôi khi điều này không xảy ra. Tôi muốn biết các điều kiện khiến thời gian chạy không biết thuộc tính property.

Temporary Fix


sửa chữa tạm thời của tôi là chỉ cần sao chép tất cả các property định nghĩa trong và dán chúng vào các file .h:

@interface MyClass <MyProtocol> 
    @property (nonatomic, strong) id someProperty; 
@end 

@implementation MyClass 
@dynamic someProperty; 
@end 

này chạy tốt, mặc dù nó là xa lý tưởng. Tuy nhiên, nó cho thấy rằng mã của tôi đang hoạt động chính xác và vấn đề nằm ở nơi khác.

Tôi rất sẵn lòng cung cấp thêm chi tiết hoặc nền nếu cần.

+0

Thời gian chạy không tối ưu hóa bất cứ điều gì. Điều duy nhất có thể tối ưu hóa là trình biên dịch (và đôi khi là trình liên kết), nhưng không phải vậy. Giao thức chỉ là một tập hợp các khai báo, không có định nghĩa nào bên trong. ** Bạn ** phải cung cấp chúng, hoặc bằng cách tổng hợp các tài sản hoặc bằng cách thực hiện accessor và mutator bằng tay. –

+0

Nhân tiện, đây không phải là câu hỏi về Xcode. –

+0

Mã bạn mô tả được cho là hoạt động. Nếu bạn có thể viết một bài kiểm tra không hoạt động, vui lòng gửi báo cáo lỗi và bao gồm bài kiểm tra đó. –

Trả lời

1

Dường như có một sự nhầm lẫn:

  1. Tuyên bố một tài sản là đủ để tài sản tồn tại trong thời gian chạy. Không cần thực hiện. Đây là cách mục tiêu-c hoạt động. Các phương thức không cần phải tồn tại trong thời gian biên dịch, bạn có thể thêm chúng theo cách động (ví dụ: Dữ liệu chính nào).

  2. @dynamic hoàn toàn không làm gì trong khi chạy. Tại thời gian biên dịch đó là một trình giữ chỗ có nội dung "không cho tôi cảnh báo trình biên dịch rằng trình lấy/setter không được định nghĩa ở đây". Trên LLVM mới nhất, nó cũng nói "không tổng hợp tự động".

gợi ý của tôi:

  1. Nếu bạn đang thêm giao thức thông qua một thể loại, đảm bảo mục được tải. Điều này có vẻ là vấn đề thường gặp nhất với phản xạ thời gian chạy.

  2. Để gỡ lỗi, hãy thử sử dụng class_conformsToProtocol. Thật kỳ lạ khi có một lớp phù hợp với một giao thức mà không có nó có các thuộc tính được khai báo bởi giao thức.

+0

Đề xuất hữu ích, cảm ơn bạn. Tôi đang làm một cái gì đó tương tự như những gì Core dữ liệu không với các mô hình của nó. Cảm ơn bạn vì những đề xuất này. Tôi rất có thể sẽ đánh dấu đây là câu trả lời nếu không ai khác có bất kỳ đầu vào nào. – Christopher

+0

@Christopher Vâng, dữ liệu cốt lõi không sử dụng các khai báo tài sản như xa như tôi biết. Vẻ đẹp của dữ liệu cốt lõi là họ có thể sử dụng mô hình dữ liệu để tạo ra các getters/setters thuộc tính. – Sulthan

+0

Tôi chắc chắn rằng tôi không bắt chước Dữ liệu cốt lõi bên dưới mui xe nhưng tôi sẽ tự động tạo các phương thức truy cập cho các lớp (mặc dù dựa trên các loại thuộc tính của chúng không phải là mô hình dữ liệu). Để tham khảo, mã mà tôi đang thực sự sử dụng nó ở đây là: https://github.com/FuturaIO/ParseModel-iOS – Christopher

2

giao thức xác định phương thức, phương pháp tùy chọn và phương thức bắt buộc.

tính là phương pháp trừu tượng, nếu giao thức định nghĩa một tài sản như được yêu cầu thì bạn phải thực hiện các biện pháp cần thiết: thường với @synthesize ... nhưng có thể được thực hiện theo những cách khác

(giả sử không mong manh ABI/Runtime hiện đại) Sử dụng readonly vì đơn giản

@property(readonly)int dog; 

có thể được implimented:

@synthesize dog; 

hoặc

@synthesize dog = _dog; // synthesize standard getter for the iVar _dog 

hoặc

- (int) dog 
{ 
    return _dog; // or dog, or cat/5 or 5 or whatever 
} 

EDIT: tái tính năng động

@dynamic là một từ khóa mà không làm gì để tạo ra phương pháp để đáp ứng các yêu cầu của một tài sản, những gì nó làm là thông báo trình biên dịch rằng nó được "quản lý" theo cách khác ...

công văn động này có thể được thực hiện bởi một vài phương pháp khác nhau trong thời gian chạy, một là bằng cách thêm các triển khai phương thức vào thời gian chạy, một phương thức khác sẽ bằng cách sử dụng thời gian chạy cho các bộ chọn chưa được giải quyết. (Tôi có một câu hỏi tương tự về việc sử dụng tính năng động để sử dụng một cửa hàng KV chung trong một từ điển)

see: Using NSMutableDictionary as backing store for properties

+0

Cảm ơn câu trả lời Grady nhưng đã có một số nhầm lẫn với câu hỏi vì vậy tôi đã sửa đổi nó. – Christopher

+0

@Christopher đã thêm một số chi tiết khác ... –

0

Sau nhiều lần gỡ lỗi và thử nghiệm Tôi đã kết luận rằng đây là lỗi.Nếu bất kỳ ai có bất kỳ bằng chứng trái ngược hoặc đề nghị cảm thấy tự do để gửi bài. Lỗi là thế này:

Đôi khi khi một property được định nghĩa trong một và sau đó một lớp học phù hợp với nói , thời gian chạy là không nhận thức được property's thuộc tính (ví dụ class_getProperty thất bại) nếu property được gắn cờ là dynamic .

rememeber, dynamic cung cấp không thực hiện, nó chỉ đơn giản là ngăn chặn cảnh báo, tuy nhiên, property thuộc tính nên vẫn có thể phục hồi thông qua thời gian chạy.

tôi muốn thêm một số đoạn mã hữu ích để giải quyết/gỡ lỗi các loại vấn đề:

- (NSArray *)propertyNamesForClass:(Class)aClass includeInherited:(BOOL)shouldIncludeInherited; 
{ 
    NSMutableArray *names = [NSMutableArray array]; 
    uint propertyCount = 0; 
    objc_property_t *properties = class_copyPropertyList(aClass, &propertyCount); 
    for (uint i = 0; i < propertyCount; i++) { 
     [names addObject:[NSString stringWithUTF8String:property_getName(properties[i])]]; 
    } 

    if (shouldIncludeInherited) { 
     Class superClass = aClass; 
     while ((superClass = class_getSuperclass(superClass))) { 
      uint superPropertyCount = 0; 
      objc_property_t *superProperties = class_copyPropertyList(superClass, &superPropertyCount); 
      for (uint i = 0; i < superPropertyCount; i++) { 
       [names addObject:[NSString stringWithUTF8String:property_getName(superProperties[i])]]; 
      } 
     } 
    } 

    NSArray *sortedNames = [names sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; 
    return sortedNames; 
} 

- (NSArray *)protocolNamesForClass:(Class)aClass includeInherited:(BOOL)shouldIncludeInherited; 
{ 
    NSMutableArray *names = [NSMutableArray array]; 
    uint protocolCount = 0; 
    __unsafe_unretained Protocol **protocolArray = class_copyProtocolList(aClass, &protocolCount); 
    for (uint i = 0; i < protocolCount; i++) { 
     [names addObject:NSStringFromProtocol(protocolArray[i])]; 
    } 

    if (shouldIncludeInherited) { 
     Class superClass = aClass; 
     while ((superClass = class_getSuperclass(superClass))) { 
      uint superProtocolCount = 0; 
      __unsafe_unretained Protocol **superProtocolArray = class_copyProtocolList(superClass, &superProtocolCount); 
      for (uint j = 0; j < superProtocolCount; j++) { 
       [names addObject:NSStringFromProtocol(superProtocolArray[j])]; 
      } 
     } 
    } 

    NSArray *sortedNames = [names sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; 
    return sortedNames; 
} 

- (NSArray *)propertyNamesForProtocol:(Protocol *)aProtocol 
{ 
    NSMutableArray *names = [NSMutableArray array]; 
    uint protocolPropertyCount = 0; 
    objc_property_t *properties = protocol_copyPropertyList(aProtocol, &protocolPropertyCount); 
    for (uint j = 0; j < protocolPropertyCount; j++) { 
     [names addObject:[NSString stringWithUTF8String:property_getName(properties[j])]]; 
    } 

    NSArray *sortedNames = [names sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; 
    return sortedNames; 
} 
+0

Tôi đã đóng gói các phương pháp này và các phương pháp liên quan khác để thuận tiện tại đây: https://github.com/mstrchrstphr/NSRuntime – Christopher

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