12

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

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

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

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

8 回答 8

9

你说:

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

在 Objective-C 中有几种方法可以做这种事情。Apple 的框架通过添加返回所需信息的类方法来执行此类操作。示例:KVO 中的依赖键+[CALayer needsDisplayForKey:]以及相关方法

因此,让我们创建一个类方法,该方法返回一个类数组,可以在给定属性名称的情况下进入您的容器属性。首先,我们将添加一个类别来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
}
于 2012-12-06T22:00:09.110 回答
5

此功能没有本机支持,但您可以查看以下解决方案 - <a href="https://github.com/epam/lib-obj-c-attr/" rel="noreferrer"> https://github.com/epam/lib-obj-c-attr/ 它是属性的编译时实现。基于定义而不是注释的属性定义,如 ObjectiveCAnnotate 等其他解决方案。

于 2014-03-25T22:38:04.663 回答
3

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


实现这种事情的一种方法是破解 Clang 以读取评论并将元数据对象与原始对象相关联。但是,您将被绑定到您被黑的编译器。

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 
    }
}
于 2012-12-06T21:18:52.203 回答
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 

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

于 2012-12-06T21:36:47.077 回答
2

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

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

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。)

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

于 2012-12-06T20:40:44.370 回答
1

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

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

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

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

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

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

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

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

于 2014-05-21T06:37:47.750 回答
0

The answer to your question is that Objective-C does not have a direct equivalent of annotations as found in Java/C#, and though as some have suggested you might be able to engineer something along the same lines it probably is either far too much work or won't pass muster.

To address your particular need see this answer which shows how to construct an array which holds objects of only one type; enforcement is dynamic and not static as with parametric types/generics, but that is what you'd be getting with your annotation so it probably matches your particular need in this case. HTH.

于 2012-12-06T21:47:24.540 回答
0

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

于 2014-02-26T16:33:13.097 回答