CRUD - 更新 - Swift SDK
在此页面上
更新 Realm 对象
对 Realm 对象的更新必须在写事务中进行。有关写事务的更多信息,请参阅:事务。
关于本页中的示例
本页中的示例使用以下模型:
// DogToy.h @interface DogToy : RLMObject @property NSString *name; @end // Dog.h @interface Dog : RLMObject @property NSString *name; @property int age; @property NSString *color; // To-one relationship @property DogToy *favoriteToy; @end // Enable Dog for use in RLMArray RLM_COLLECTION_TYPE(Dog) // Person.h // A person has a primary key ID, a collection of dogs, and can be a member of multiple clubs. @interface Person : RLMObject @property int _id; @property NSString *name; // To-many relationship - a person can have many dogs @property RLMArray<Dog *><Dog> *dogs; // Inverse relationship - a person can be a member of many clubs @property (readonly) RLMLinkingObjects *clubs; @end RLM_COLLECTION_TYPE(Person) // DogClub.h @interface DogClub : RLMObject @property NSString *name; @property RLMArray<Person *><Person> *members; @end // Dog.m @implementation Dog @end // DogToy.m @implementation DogToy @end // Person.m @implementation Person // Define the primary key for the class + (NSString *)primaryKey { return @"_id"; } // Define the inverse relationship to dog clubs + (NSDictionary *)linkingObjectsProperties { return @{ @"clubs": [RLMPropertyDescriptor descriptorWithClass:DogClub.class propertyName:@"members"], }; } @end // DogClub.m @implementation DogClub @end
class Dog: Object { var name = "" var age = 0 var color = "" var currentCity = "" var citiesVisited: MutableSet<String> var companion: AnyRealmValue // Map of city name -> favorite park in that city var favoriteParksByCity: Map<String, String> } class Person: Object { true) var id = 0 (primaryKey: var name = "" // To-many relationship - a person can have many dogs var dogs: List<Dog> // Embed a single object. // Embedded object properties must be marked optional. var address: Address? } class Address: EmbeddedObject { var street: String? var city: String? var country: String? var postalCode: String? }
更新对象
您可以像更新任何其他 Swift 或 Objective-C 对象一样,在写事务中修改 Realm 对象的属性。
RLMRealm *realm = [RLMRealm defaultRealm]; // Open a thread-safe transaction. [realm transactionWithBlock:^{ // Get a dog to update. Dog *dog = [[Dog allObjectsInRealm: realm] firstObject]; // Update some properties on the instance. // These changes are saved to the realm. dog.name = @"Wolfie"; dog.age += 1; }];
let realm = try! Realm() // Get a dog to update let dog = realm.objects(Dog.self).first! // Open a thread-safe transaction try! realm.write { // Update some properties on the instance. // These changes are saved to the realm dog.name = "Wolfie" dog.age += 1 }
使用键值编码更新属性
Object
、Result
和List
均符合 键值编码 。当您需要确定要在运行时更新哪个属性时,这非常有用。
将 KVC 应用于集合是批量更新对象的绝佳方法。避免在为每个项目创建访问器时遍历集合所产生的开销。
let realm = try! Realm() let allDogs = realm.objects(Dog.self) try! realm.write { allDogs.first?.setValue("Sparky", forKey: "name") // Move the dogs to Toronto for vacation allDogs.setValue("Toronto", forKey: "currentCity") }
您还可以通过这种方式来为嵌入式对象或关系添加值。在此示例中,我们将一个集合添加到对象的列表属性中:
RLMRealm *realm = [RLMRealm defaultRealm]; [realm transactionWithBlock:^() { // Create a person to take care of some dogs. Person *ali = [[Person alloc] initWithValue:@{@"_id": @1, @"name": @"Ali"}]; [realm addObject:ali]; // Find dogs younger than 2. RLMResults<Dog *> *puppies = [Dog objectsInRealm:realm where:@"age < 2"]; // Batch update: give all puppies to Ali. [ali setValue:puppies forKey:@"dogs"]; }];
let realm = try! Realm() try! realm.write { // Create a person to take care of some dogs. let person = Person(value: ["id": 1, "name": "Ali"]) realm.add(person) let dog = Dog(value: ["name": "Rex", "age": 1]) realm.add(dog) // Find dogs younger than 2. let puppies = realm.objects(Dog.self).filter("age < 2") // Give all puppies to Ali. person.setValue(puppies, forKey: "dogs") }
更新或插入(upsert)对象
更新或插入 (upsert) 操作会插入或更新对象,具体取决于该对象是否已经存在。更新或插入操作要求数据模型具有主键。
要更新或更新或插入(upsert)对象,请调用-[RLMKRealm addOrUpdateObject:]。
RLMRealm *realm = [RLMRealm defaultRealm]; [realm transactionWithBlock:^{ Person *jones = [[Person alloc] initWithValue:@{@"_id": @1234, @"name": @"Jones"}]; // Add a new person to the realm. Since nobody with ID 1234 // has been added yet, this adds the instance to the realm. [realm addOrUpdateObject:jones]; Person *bowie = [[Person alloc] initWithValue:@{@"_id": @1234, @"name": @"Bowie"}]; // Judging by the ID, it's the same person, just with a different name. // This overwrites the original entry (i.e. Jones -> Bowie). [realm addOrUpdateObject:bowie]; }];
要更新或更新或插入(upsert)对象,请调用Realm.add(_: 更新:)并将第二个参数(更新策略)设立为.modified
。
let realm = try! Realm() try! realm.write { let person1 = Person(value: ["id": 1234, "name": "Jones"]) // Add a new person to the realm. Since nobody with ID 1234 // has been added yet, this adds the instance to the realm. realm.add(person1, update: .modified) let person2 = Person(value: ["id": 1234, "name": "Bowie"]) // Judging by the ID, it's the same person, just with a // different name. When `update` is: // - .modified: update the fields that have changed. // - .all: replace all of the fields regardless of // whether they've changed. // - .error: throw an exception if a key with the same // primary key already exists. realm.add(person2, update: .modified) }
您还可以通过传递主节点和要更新的值的子集来部分更新对象:
RLMRealm *realm = [RLMRealm defaultRealm]; [realm transactionWithBlock:^{ // Only update the provided values. // Note that the "name" property will remain the same // for the person with primary key "_id" 123. [Person createOrUpdateModifiedInRealm:realm withValue:@{@"_id": @123, @"dogs": @[@[@"Buster", @5]]}]; }];
let realm = try! Realm() try! realm.write { // Use .modified to only update the provided values. // Note that the "name" property will remain the same // for the person with primary key "id" 123. realm.create(Person.self, value: ["id": 123, "dogs": [["Buster", 5]]], update: .modified) }
更新映射/字典
您可以像更新标准 字典 一样更新域 映射 :
let realm = try! Realm() // Find the dog we want to update let wolfie = realm.objects(Dog.self).where { $0.name == "Wolfie" }.first! print("Wolfie's favorite park in New York is: \(wolfie.favoriteParksByCity["New York"])") XCTAssertTrue(wolfie.favoriteParksByCity["New York"] == "Domino Park") // Update values for keys, or add values if the keys do not currently exist try! realm.write { wolfie.favoriteParksByCity["New York"] = "Washington Square Park" wolfie.favoriteParksByCity.updateValue("A Street Park", forKey: "Boston") wolfie.favoriteParksByCity.setValue("Little Long Pond", forKey: "Seal Harbor") } XCTAssertTrue(wolfie.favoriteParksByCity["New York"] == "Washington Square Park")
更新 MutableSet 属性
您可以在写事务期间将元素 insert
到 MutableSet 中,以将元素添加到属性中。如果您使用多个集,您还可以在一个集中插入或删除另一个集中包含的集元素。或者,您可以对某个集进行更改以仅包含两者的共同元素。
let realm = try! Realm() // Record a dog's name, current city, and store it to the cities visited. let dog = Dog() dog.name = "Maui" dog.currentCity = "New York" try! realm.write { realm.add(dog) dog.citiesVisited.insert(dog.currentCity) } // Update the dog's current city, and add it to the set of cities visited. try! realm.write { dog.currentCity = "Toronto" dog.citiesVisited.insert(dog.currentCity) } XCTAssertEqual(dog.citiesVisited.count, 2) // If you're operating with two sets, you can insert the elements from one set into another set. // The dog2 set contains one element that isn't present in the dog set. try! realm.write { dog.citiesVisited.formUnion(dog2.citiesVisited) } XCTAssertEqual(dog.citiesVisited.count, 3) // Or you can remove elements that are present in the second set. This removes the one element // that we added above from the dog2 set. try! realm.write { dog.citiesVisited.subtract(dog2.citiesVisited) } XCTAssertEqual(dog.citiesVisited.count, 2) // If the sets contain common elements, you can mutate the set to only contain those common elements. // In this case, the two sets contain no common elements, so this set should now contain 0 items. try! realm.write { dog.citiesVisited.formIntersection(dog2.citiesVisited) } XCTAssertEqual(dog.citiesVisited.count, 0)
更新 AnyRealmValue 属性
您可以通过赋值来更新 AnyRealmValue 属性,但在赋值时必须指定值的类型。Realm Swift SDK 提供了一个 AnyRealmValue 枚举,它可以遍历 AnyRealmValue 可以存储的所有类型。
let realm = try! Realm() // Get a dog to update let rex = realm.objects(Dog.self).where { $0.name == "Rex" }.first! try! realm.write { // As with creating an object with an AnyRealmValue, you must specify the // type of the value when you update the property. rex.companion = .object(Dog(value: ["name": "Regina"])) }
更新嵌入式对象属性
若要更新嵌入式对象中的属性,请在写事务中修改该属性。如果嵌入式对象为 null,则更新嵌入式对象属性不起作用。
RLMRealm *realm = [RLMRealm defaultRealm]; [realm transactionWithBlock: ^{ Contact *contact = [Contact objectInRealm:realm forPrimaryKey:[[RLMObjectId alloc] initWithString:@"5f481c21f634a1f4eeaa7268" error:nil]]; contact.address.street = @"Hollywood Upstairs Medical College"; contact.address.city = @"Los Angeles"; contact.address.postalCode = @"90210"; NSLog(@"Updated contact: %@", contact); }];
// Open the default realm let realm = try! Realm() let idOfPersonToUpdate = 123 // Find the person to update by ID guard let person = realm.object(ofType: Person.self, forPrimaryKey: idOfPersonToUpdate) else { print("Person \(idOfPersonToUpdate) not found") return } try! realm.write { // Update the embedded object directly through the person // If the embedded object is null, updating these properties has no effect person.address?.street = "789 Any Street" person.address?.city = "Anytown" person.address?.postalCode = "12345" print("Updated person: \(person)") }
覆盖嵌入式对象
要覆盖嵌入式对象,请在写事务中将一方的嵌入式对象属性重新分配给新实例。
RLMRealm *realm = [RLMRealm defaultRealm]; [realm transactionWithBlock: ^{ Contact *contact = [Contact objectInRealm:realm forPrimaryKey:[[RLMObjectId alloc] initWithString:@"5f481c21f634a1f4eeaa7268" error:nil]]; Address *newAddress = [[Address alloc] init]; newAddress.street = @"Hollywood Upstairs Medical College"; newAddress.city = @"Los Angeles"; newAddress.country = @"USA"; newAddress.postalCode = @"90210"; contact.address = newAddress; NSLog(@"Updated contact: %@", contact); }];
// Open the default realm let realm = try! Realm() let idOfPersonToUpdate = 123 // Find the person to update by ID guard let person = realm.object(ofType: Person.self, forPrimaryKey: idOfPersonToUpdate) else { print("Person \(idOfPersonToUpdate) not found") return } try! realm.write { let newAddress = Address() newAddress.street = "789 Any Street" newAddress.city = "Anytown" newAddress.country = "USA" newAddress.postalCode = "12345" // Overwrite the embedded object person.address = newAddress print("Updated person: \(person)") }
异步更新对象
您可以使用 Swift 并发功能,通过 actor 隔离的 Realm 来异步更新对象。
这个函数来自“结合使用 Realm 与 Actor”页面中给出的示例 RealmActor
中的函数,显示了如何在 actor 隔离的 Realm 中更新对象:
func updateTodo(_id: ObjectId, name: String, owner: String, status: String) async throws { try await realm.asyncWrite { realm.create(Todo.self, value: [ "_id": _id, "name": name, "owner": owner, "status": status ], update: .modified) } }
您可以使用 Swift 的异步语法来执行此更新:
let actor = try await RealmActor() // Read objects in functions isolated to the actor and pass primitive values to the caller func getObjectId(in actor: isolated RealmActor, forTodoNamed name: String) async -> ObjectId { let todo = actor.realm.objects(Todo.self).where { $0.name == name }.first! return todo._id } let objectId = await getObjectId(in: actor, forTodoNamed: "Keep it safe") try await actor.updateTodo(_id: objectId, name: "Keep it safe", owner: "Frodo", status: "Completed")
此操作不会阻塞调用线程或在调用线程上执行 I/O。有关使用 Swift 并发功能写入 Realm 的更多信息,请参阅结合使用 Realm 与 Actor - Swift SDK。
通过类投影更新属性
更改类投影属性
您可以在写事务中更改类投影的属性。
// Retrieve all class projections of the given type `PersonProjection` // and filter for the first class projection where the `firstName` property // value is "Jason" let person = realm.objects(PersonProjection.self).first(where: { $0.firstName == "Jason" })! // Update class projection property in a write transaction try! realm.write { person.firstName = "David" }