Obj-c协议属性未在符合类中实现

时间:2013-07-10 17:34:38

标签: ios objective-c objective-c-runtime

问题


我遇到了一个有趣的问题,但却无法找到任何相关文档... properties中声明的protocol有时未在特定类中实现到那个protocol并发生运行时异常。在某些奇怪的情况下,dynamic property定义是否被优化了?可以protocolsproperties一起使用dynamic吗?protocol任何有关这方面的见解将不胜感激。

以下是一些更多细节。

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

protocol

和实现@interface MyClass <MyProtocol> @end @implementation MyClass @dynamic someProperty; @end 的类如此:

class_getProperty(myClass, propertyName);

我注意到有时我无法通过调用

获取任何信息
properties

代表protocol中的@protocol MyProtocol <NSObject> @property (nonatomic, strong) id someData; @end @interface MyObject : NSObject <MyProtocol> @end @implementation MyObject @dynamic someData; @end 。这只发生在一些类中,似乎是零星的。

我正在运行最新的Xcode 4并链接到iOS 6 SDK。我确实在同一台机器上安装了预发布的Xcode 5,虽然它不是默认的(通过xcode-select)。

实施例


如果您运行此代码:

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

然后你运行

property

即使property不存在,您也会获得property的元数据。换句话说,您不需要合成属性来获取它的属性。运行时仍然知道它。亲自试试吧。问题是有时这种情况不会发生。我想知道导致运行时不知道property属性的条件。

临时修复


我的临时解决方法是复制protocol中的所有@interface MyClass <MyProtocol> @property (nonatomic, strong) id someProperty; @end @implementation MyClass @dynamic someProperty; @end 定义并将其粘贴到.h文件中:

{{1}}

这很好,虽然远非理想。但是,它表明我的代码工作正常,问题出在其他地方。

如果需要,我很乐意提供更多详细信息或背景信息。

3 个答案:

答案 0 :(得分:2)

协议定义方法,可选方法和所需方法。

属性是抽象方法,如果协议将属性定义为必需,则必须实现所需的方法:通常使用@synthesize ...但可以通过其他方式完成

(假设非脆弱的ABI / Modern Runtime)为了简单起见使用readonly

@property(readonly)int dog;

可能会受到启发:

@synthesize dog;

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

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

编辑:重新动态属性

@dynamic是一个关键字,它不会生成满足属性要求的方法,它所做的是通知编译器它以某种其他方式“处理”...

这种动态调度可以在运行时通过几种不同的方法来完成,一种是在运行时添加方法实现,另一种是通过将运行时用于未解析的选择器。 (我有一个关于使用动态属性在字典中使用通用KV存储的类似问题)

请参阅:Using NSMutableDictionary as backing store for properties

答案 1 :(得分:1)

似乎有一种混乱:

  1. 声明属性足以使属性在运行时存在。没有必要实施。这就是Objective-c的工作原理。方法不必在编译时存在,您可以动态添加它们(例如Core Data所做的)。

  2. @dynamic在运行时没有任何作用。在编译时,它是一个占位符,表示“不要给我编译器警告,这里没有定义getter / setter”。在最新的LLVM上,它还说“不要自动合成”。

  3. 我的建议:

    1. 如果要通过类别添加协议,请确保已加载类别。这似乎是运行时反射最常见的问题。

    2. 要进行调试,也请尝试使用class_conformsToProtocol。如果有一个符合协议的类而没有它具有协议声明的属性,那将是很奇怪的。

答案 2 :(得分:0)

经过大量调试和测试后,我得出结论,这是一个错误。如果有人有任何相反的证据或建议随时发布。错误是这样的:

有时 property中定义protocol,然后某个类符合所述protocol时,如果property's被标记为property,则运行时不会意识到dynamic属性(例如,class_getProperty失败)。

Rememeber,dynamic提供 no implementation ,它只是抑制警告,但是,property属性仍应通过运行时检索。

我想添加一些有用的代码片段来解决/调试这些类型的问题:

- (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;
}