【问题标题】:Does Objective-C have an equivalent to java annotations?Objective-C 是否具有与 java 注释等效的功能?
【发布时间】:2012-12-06 20:23:35
【问题描述】:

Objective-C 是否有等效于 java 注释的功能?

我正在尝试做的是创建一个属性并能够以某种方式访问​​有关它的一些元数据。

我希望能够确定我的数组中应该包含什么类型的类,所以我想以某种方式对其进行注释。然后以后可以通过运行时库之类的东西访问该注释,我可以在其中访问属性列表及其名称。

//Put some sort of annotation giving a class name.
@property (strong) NSArray *myArray;

【问题讨论】:

  • 评论。并且有许多 3rd 方包(可能包括 JavaDoc)可以提取特殊格式的 cmets 并将它们转换为文档。
  • 那么你要使用反射吗?
  • @Hot Licks 注释根据定义是注释,但他要求将注释转换为功能运行时代码的实际编译支持
  • 是的,它需要运行时支持,而不仅仅是评论。
  • 这听起来更像是 Java 泛型而不是注解。

标签: objective-c ios


【解决方案1】:

你说:

我希望能够确定我的数组中应该包含什么类型的类,所以我想以某种方式对其进行注释。然后以后可以通过运行时库之类的东西访问该注释,我可以在其中访问属性列表及其名称。

在 Objective-C 中有几种方法可以做这种事情。 Apple 的框架通过添加返回所需信息的类方法来执行此类操作。示例:dependent keys in KVO、+[CALayer needsDisplayForKey:] and related methods。

因此,让我们创建一个类方法,该方法返回一个类数组,这些类可以在给定属性名称的情况下进入您的容器属性。首先,我们将向NSObject 添加一个类别来实现该方法的通用版本:

@interface NSObject (allowedClassesForContainerProperty)

+ (NSArray *)allowedClassesForContainerPropertyWithName:(NSString *)name;

@end

@implementation NSObject (allowedClassesForContainerProperty)

+ (NSArray *)allowedClassesForContainerPropertyWithName:(NSString *)name {
    if (class_getProperty(self, name.UTF8String)) {
        return @[ [NSObject class] ];
    } else {
        [NSException raise:NSInvalidArgumentException
            format:@"%s called for non-existent property %@", __func__, name];
        abort();
    }
}

@end

如您所见,该方法的默认版本并没有做任何特别有用的事情。但是将它添加到NSObject 意味着我们可以将消息发送到任何类,而不必担心该类是否实现了该方法。

为了使消息返回有用的信息,我们在自己的类中覆盖它。例如:

@implementation MyViewController

+ (NSArray *)allowedClassesForContainerPropertyWithName:(NSString *)name {
    if ([name isEqualToString:@"myArray"]) {
        return @[ [UIButton class], [UIImageView class] ];
    } else {
        return [super allowedClassesForContainerPropertyWithName:name];
    }
}

...

我们可以这样使用它:

SomeViewController *vc = ...;
SomeObject *object = ...;
if ([[vc.class allowedClassesForContainerPropertyWithName:@"bucket"] containsObject:object.class]) {
    [vc.bucket addObject:object];
} else {
    // oops, not supposed to put object in vc.bucket
}

【讨论】:

    【解决方案2】:

    没有对这个功能的原生支持,但是你可以看看下面的解决方案——https://github.com/epam/lib-obj-c-attr/它是属性的编译时实现。属性的定义基于定义,而不是像 ObjectiveCAnnotate 等其他解决方案中的 cmets。

    【讨论】:

    • 有趣的解决方案,如果 Apple 支持类似的语言就更好了。
    【解决方案3】:

    Objective C 不像 Java 那样支持泛型,但该语言当然非常灵活,您可以通过简单的技巧和知识完成几乎任何事情。要实现类似的泛型功能,您可以在 NSArray 类上创建一个类别并创建自己的方法来初始化数组,然后检查对象是否真的是您想要的对象的类型。

    我会在 NSArray 上写一个简单的类别来拥有这样的功能。假设,我希望我的数组只保存 MyClass 类的对象,然后我的类别看起来像,

    @interface NSArray(MyCategory)
    
    @end
    
    @implementation NSArray(MyCategory)
    
    -(NSArray*)arrayWithMyClasses:(NSArray*)classes{
        if([classes count] > 0){
            NSMutableArray *array = [[NSMutableArray alloc] init];
            for(id anObj in classes){
                NSAssert([anObj isKindOfClass:[MyClass class]], @"My array supports only objetcts of type MyClass");
                [array addObject:anObj];
            }
            return array;
        }
        return nil;
    }
    @end 
    

    当然,它有一些限制。由于您已经创建了自己的类别,因此您应该使用自己的方法来初始化和创建自己的数组。

    【讨论】:

    • isKindOfClass 违反了泛型的一般原则 :)
    • 嗯,你知道目标 c 本身没有泛型。因此,要添加这样的功能,您要么必须通过运行时,要么对语言本身进行一些猴子匹配。
    【解决方案4】:

    不,Objective-C 没有注释或泛型支持。


    实现这种事情的一种方法是hack Clang to read comments 并将元数据对象与原始对象相关联。但是,你会被绑定到你被黑的编译器。

    NSString *v1 = [[NSString alloc] init];
    
    // associate
    static char key;
    NSString *v2 = [[NSString alloc] init];
    objc_setAssociatedObject (
        v1,
        &key,
        v2,
        OBJC_ASSOCIATION_RETAIN
    );
    
    // retrieve
    NSString *associate = (NSString *)objc_getAssociatedObject(v1, &key);
    

    使用协议进行限定不会很麻烦,您可以测试集合是否实现了它,但在此过程中,您需要为同一集合上的每种类型创建一个类别。这将需要在编译时使用宏进行不同的集合。过于复杂。

    @interface Tomato:NSObject @end
    @implementation Tomato @end
    
    @protocol TomatoNSArray <NSObject>
    - (Tomato*)objectAtIndexedSubscript:(NSUInteger)index;
    - (void)setObject:(Tomato*)tomato atIndexedSubscript:(NSUInteger)index;
    @end
    
    // here is the problem, you would need to create one of this for each type
    @interface NSMutableArray (TomatoNSArray) <TomatoNSArray>
    @end
    
    int main(int argc, char *argv[]) {
        @autoreleasepool {
            NSMutableArray<TomatoNSArray> *tomatoes = [[NSMutableArray alloc] initWithCapacity:2];
            tomatoes[0] = [Tomato new];
            tomatoes[1] = [NSObject new]; // warning: incompatible pointer types 
        }
    }
    

    【讨论】:

      【解决方案5】:

      Objective-C 是否有等效于 java 注释的功能?

      不完全等同,但有,而且更好。在 Objective-C 中,编译器必须在编译后的代码中存储一些类型和名称信息(因为语言是高度动态的,很多事情发生在运行时而不是编译时),例如方法名称(“选择器”) 、方法类型签名、关于属性、协议等的数据。Objective-C runtime library 然后可以访问这些数据。例如,您可以通过编写来获取对象具有的属性列表

      id object = // obtain an object somehow
      unsigned count;
      objc_property_t *props = class_copyPropertyList([object class], &count);
      

      或者你可以检查一个对象属于哪个类:

      if ([object isKindOfClass:[NSArray class]]) {
          // do stuff
      }
      

      (是的,为了方便,部分运行时库本身被封装到了 NSObject 的一些方法中,其他的只有 C 函数 API。)

      如果您特别想存储有关对象或类的自定义元数据,您可以使用 associated references. 来做到这一点

      【讨论】:

      • 直到最后一段,这并没有解决这个问题,而是描述了 Java 所做的事情——所以“它是否更好”是值得怀疑的。谈到关联引用,如何将它们作为编译时注释添加到属性中?
      • @CRD 您在代码中编写的所有内容都是编译时...objc_setAssociatedObject(object, @"This is a comment"); 对您来说还不够好吗?
      • 我想你可能会开玩笑,编译和运行时显然有区别。这可能是个好点,但在 Java/C# 中,注释是附加到属性元数据的预编译元数据。 objc_setAssociatedObject(object, @"This is a comment") 是一个将在运行时执行的语句,它将为每个属性实例创建一个关联对象。因此,这些机制是不同的,并且根据场景,它们各有优缺点。
      • 我很抱歉让您不高兴,但在回复您的问题后发表的一条评论几乎不是“獾”。就我个人而言,我认为每个属性实例一个元数据与一个关联对象是一些人可能关心的,但有些人当然不会关心。祝你有美好的一天。
      • @CRD 我同意你的看法。这完全不同,甚至不涉及注释。我没有投反对票,但如果对此进行编辑,我会非常赞成。 -- 它回答了问题
      【解决方案6】:

      我希望现在应该很清楚,答案是否,目前还没有。

      有些人发现了一些似乎适用于他们特定用例的替代方案。

      但总的来说,objective-c 中还没有类似的功能。恕我直言,clang 元数据似乎为此提供了良好的基础,但据我所知,只要没有 Apple 的支持,这将无济于事。

      顺便说一句。我想这应该很清楚,但只是重复一遍:需要两个更改来支持 java 中提供的注释。

      1. 语言需要扩展注释,例如源代码中的方法、属性、类等。
      2. 需要标准接口来访问带注释的信息。这只能由苹果提供。

      大多数替代解决方案将注释信息移动到运行时并定义自己的接口。 Objective-c 运行时提供了一个标准接口,但只有一些技巧可以注释属性,但仍然存在运行时填充问题。

      此类功能的典型用例是 IOC 容器(在 Java 中,例如 Spring),它使用带注释的信息来注入其他对象。

      我建议为 Apple 打开一个功能请求以支持此功能。

      【讨论】:

      • 顺便说一句。 Swift 现在知道 Attributes 但没有办法定义你自己的
      【解决方案7】:

      您的问题的答案是,Objective-C 没有直接等效于 Java/C# 中的注释,尽管正如一些人建议的那样,您可能能够按照相同的思路设计一些东西,但它可能还很远工作量太大或无法通过集合。

      要满足您的特殊需求,请参阅this answer,它展示了如何构造一个仅包含一种类型的对象的数组;执行是动态的,而不是像参数类型/泛型那样是静态的,但这就是您从注释中得到的,因此在这种情况下它可能符合您的特定需求。 HTH。

      【讨论】:

        【解决方案8】:

        您可能需要一个用于 Objective-C 的元数据解析器。我使用了 ObjectiveCAnnotate(编译时可检索)和 ROAnnotation(运行时可检索)。

        【讨论】:

          猜你喜欢
          • 2019-03-23
          • 2011-06-13
          • 2019-11-06
          • 2012-01-15
          • 1970-01-01
          • 2012-03-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多