7

问题


我遇到了一个有趣的问题,但找不到任何关于它的文档...有时properties在 a 中声明的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);

propertiesprotocol。这仅发生在某些课程中,并且似乎是零星的。

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

例子


如果您运行此代码:

@protocol MyProtocol <NSObject>

@property (nonatomic, strong) id someData;

@end

@interface MyObject : NSObject <MyProtocol>

@end

@implementation MyObject

@dynamic someData;

@end

然后你跑

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

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

临时修复


我的临时解决方法是复制.h 文件property中的所有定义protocol并将它们粘贴到 .h 文件中:

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

@implementation MyClass
@dynamic someProperty;
@end

这运行良好,尽管它远非理想。但是,它表明我的代码工作正常,问题出在其他地方。

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

4

3 回答 3

2

协议定义方法、可选方法和必需方法。

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

(假设非脆弱 ABI / 现代运行时)为简单起见使用只读

@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 存储的类似问题)

请参阅:使用 NSMutableDictionary 作为属性的后备存储

于 2013-07-10T18:48:18.737 回答
1

似乎有一个困惑:

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

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

我的建议:

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

  2. 要调试,也尝试使用class_conformsToProtocol. 有一个符合协议但没有协议声明的属性的类会很奇怪。

于 2013-07-11T14:43:17.383 回答
0

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

有时,当 aproperty在 a 中定义,protocol然后类符合所述时,如果将标记为protocol,则运行时不知道property's属性(例如 class_getProperty 失败)。propertydynamic

记住,没有dynamic提供任何实现,它只是抑制警告,但是,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;
}
于 2013-07-11T18:36:18.957 回答