来自集合的嵌套子属性的 NSSet

NSSet of nested subproperty from set

我有一组 someProtocol 对象,我想从这个对象上嵌套 属性 的 NSString 创建一组:

// Protocol SomeProtocol
@protocol SomeProtocol<NSObject>

@property(nonatomic, readonly) id<SomeSubProtocol> someSubProtocolObject;

@end

// Protocol SomeSubProtocol
@protocol SomeSubProtocol<NSObject>

@property(nonatomic, readonly) NSString *Id;

@end

我有一组 SomeProtocols:

NSSet<id<SomeProtocol>> *setOfSomeSubProtocols;

我想要获取 Id 属性的 NSSet:

NSSet<NSString *> *idSet = ?; // Calculate from arrayOfSomethings.

我试过:

idSet = [setOfSomeSubProtocols valueForKeyPath @"someSubProtrocolObject.id"];

但我更喜欢在属性更改时抛出编译器错误的东西...

在某处定义以下内容:

@protocol KeyPath <NSObject> @end
typedef NSString<KeyPath> KeyPath;

NS_INLINE KeyPath *GetKeyPath(__unused unsigned long val, NSString *str) { return (KeyPath *)str; }

#define PRIVATE_KEYPATH(obj, key) GetKeyPath(sizeof(obj.key), @#key)
#define PROTOCOL_KEYPATH(proto, key) PRIVATE_KEYPATH(((id<proto>)nil), key)

然后像这样使用它:

[setOfSomeSubProtocols valueForKeyPath:PROTOCOL_KEYPATH(SomeSubProtocol, Id)];

如果 SomeSubProtocol 的类型名称或 属性 名称 Id 更改,这将给您一个编译错误。

P.S.

id 是一个糟糕的 属性 名称,因为它也是一个内置类型名称。考虑改用 identifier 属性 名称。

在 Swift 中,我建议使用 map(),但在 Objective-C 中不存在。您可以在 Objective-C.

中搜索或实现自己的 map 版本

但是,您可以做的是手动循环,因为这是解决问题的正常方法。

__block NSMutableSet *ids = [[NSMutableSet alloc] init];
[setOfSomeSubProtocols enumerateObjectsUsingBlock:^(id<SomeProtocol>  _Nonnull obj, BOOL * _Nonnull stop) {
    [ids addObject:[[obj someSubProtocolObject] identifier]];
}];

它非常简单并且可以完成工作。

您可以如前所述,在 NSSet 上实施 map,如下所示:

@implementation NSSet(地图)

-(NSSet *)mapWithBlock:(id (^)(id))block {
    __block NSMutableSet *set = [[NSMutableSet alloc] init];
    [self enumerateObjectsUsingBlock:^(id  _Nonnull obj, BOOL * _Nonnull stop) {
        id mappedObject = block(obj);
        [set addObject:mappedObject];
    }];
    return set;
}

通话中:

NSSet *ids = [setOfSomeSubProtocols mapWithBlock:^id (id<SomeProtocol> object) {
    return [[object someSubProtocolObject] identifier];
}];
NSLog(@"ids: %@", ids);