Realm.io - objective-c 中的复合主键

Realm.io - Compound primary key in objective-c

我正在努力寻找一种将多个属性组合成一个主键的方法,使用 Realm.io 和 Objective-c。

以这个对象为例:

@interface Beacon : RLMObject

@property NSString *uuid;
@property int major;
@property int minor;

@end

RLM_ARRAY_TYPE(Beacon)

然后我将如何组合例如uuid、major 和 minor 合并到一个主键中?

我在当前项目中的做法是为复合键添加一个新的 属性,使其成为主键。然后,覆盖复合键所有部分的设置器并调用更新复合键的函数。

我的 ObjC 很弱,所以我无法为您提供用该语言编写的可靠示例,但是 Swift 中的示例可能会有所帮助。

class Thing: Object {
    dynamic var part1: String = "" {
        didSet {
            self.updateCompoundKey()
        }
    }
    dynamic var part2: String = "" {
        didSet {
            self.updateCompoundKey()
        }
    }
    dynamic var compoundKey: String = ""

    override static func primaryKey() -> String? {
        return "compoundKey"
    }

    private func updateCompoundKey() {
        self.compoundKey= "\(self.part1)\(self.part2)"
    }
}

这是一个 Objective-C 解决方案。它利用了只能覆盖 ignoredProperties 的事实。它比 Swift 版本更冗长,但是它可以工作,所以如果您需要 ObjC 中的复合键,这可能是您最好的选择。一个可能的限制是 public 属性的 KVO 将不起作用,但是,由于 Realm 期望主键在对象添加到 Realm 后保持不变,所以这个限制可能很小。如果您确实需要 KVO,您可能可以通过 in this document from Apple.

中描述的注册依赖键来绕过它

在CompositeKeyObject.h中:

@interface CompositeKeyObject : RLMObject

@property (nonatomic, strong) NSString* partOne;
@property (nonatomic, strong) NSString* partTwo;

@end

在CompositeKeyObject.m中:

@interface CompositeKeyObject ()

@property (nonatomic, strong) NSString* partOneValue;
@property (nonatomic, strong) NSString* partTwoValue;
@property (nonatomic, strong) NSString* compositeKey;

@end

@implementation CompositeKeyObject

- (instancetype)initWithValue:(id)value
{
    // Need to make a copy and set the correct "value" properties.
    // Otherwise, the object won't be created properly. 
    NSMutableDictionary* valueCopy = [value mutableCopy];
    if(valueCopy[@"partOne"] != nil) {
        valueCopy[@"partOneValue"] = valueCopy[@"partOne"];
    }
    if(valueCopy[@"partTwo"] != nil) {
        valueCopy[@"partTwoValue"] = valueCopy[@"partTwo"];
    }

    self = [super initWithValue:[valueCopy copy]];
    if(self != nil) {
        // Make sure primary key is in sync.
        [self updatePrimaryKey];
    }
    return self;
}

- (void)setPartOne:(NSString *)partOne
{
    self.partOneValue = partOne;
    [self updatePrimaryKey];
}

- (void)setPartTwo:(NSString *)partTwo
{
    self.partTwoValue = partTwo;
    [self updatePrimaryKey];
}

- (NSString*)partOne
{
    return self.partOneValue;
}

- (NSString*)partTwo
{
    return self.partTwoValue;
}

- (void)updatePrimaryKey
{
    self.compositeKey = [NSString stringWithFormat:@"%@ <><><> %@", self.partOne, self.partTwo];
}

+ (NSString *)primaryKey
{
    return @"compositeKey";
}

+ (RLMResults *)objectsWhere:(NSString *)predicateFormat args:(va_list)args {
    predicateFormat = [predicateFormat stringByReplacingOccurrencesOfString:@"partOne" withString:@"partOneValue"];
    predicateFormat = [predicateFormat stringByReplacingOccurrencesOfString:@"partTwo" withString:@"partTwoValue"];
    return [self objectsWithPredicate:[NSPredicate predicateWithFormat:predicateFormat arguments:args]];
}

+ (RLMResults *)objectsInRealm:(RLMRealm *)realm where:(NSString *)predicateFormat args:(va_list)args {
    predicateFormat = [predicateFormat stringByReplacingOccurrencesOfString:@"partOne" withString:@"partOneValue"];
    predicateFormat = [predicateFormat stringByReplacingOccurrencesOfString:@"partTwo" withString:@"partTwoValue"];
    return [self objectsInRealm:realm withPredicate:[NSPredicate predicateWithFormat:predicateFormat arguments:args]];
}

@end

单元测试:

- (void)testCompositeObject
{
    CompositeKeyObject* object = [[CompositeKeyObject alloc] init];
    object.partOne = @"ONE";
    object.partTwo = @"TWO";

    RLMRealm* realm = [RLMRealm defaultRealm];
    [realm transactionWithBlock:^{
        [realm addObject:object];
    }];

    XCTAssertNotNil([CompositeKeyObject objectForPrimaryKey:@"ONE <><><> TWO"]);

    object = [[CompositeKeyObject alloc] init];
    object.partOne = @"ONE";
    object.partTwo = @"TWO";

    [realm transactionWithBlock:^{
        XCTAssertThrows([realm addObject:object]);
    }];
}

- (void)testCompositeObject2
{
    CompositeKeyObject* object = [[CompositeKeyObject alloc] initWithValue:@{@"partOne": @"ONE", @"partTwo": @"TWO"}];

    RLMRealm* realm = [RLMRealm defaultRealm];
    [realm transactionWithBlock:^{
        [realm addObject:object];
    }];

    XCTAssertNotNil([CompositeKeyObject objectForPrimaryKey:@"ONE <><><> TWO"]);
    object = [[CompositeKeyObject alloc] initWithValue:@{@"partOne": @"ONE", @"partTwo": @"TWO"}];

    [realm transactionWithBlock:^{
        XCTAssertThrows([realm addObject:object]);
    }];
}