Docs 菜单
Docs 主页
/ /
Atlas Device SDKs
/ /

CRUD - 更新 - Swift SDK

在此页面上

  • 更新 Realm 对象
  • 关于本页中的示例
  • 更新对象
  • 使用键值编码更新属性
  • 更新或插入(upsert)对象
  • 更新映射/字典
  • 更新 MutableSet 属性
  • 更新 AnyRealmValue 属性
  • 更新嵌入式对象属性
  • 覆盖嵌入式对象
  • 异步更新对象
  • 通过类投影更新属性
  • 更改类投影属性

对 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 {
@Persisted var name = ""
@Persisted var age = 0
@Persisted var color = ""
@Persisted var currentCity = ""
@Persisted var citiesVisited: MutableSet<String>
@Persisted var companion: AnyRealmValue
// Map of city name -> favorite park in that city
@Persisted var favoriteParksByCity: Map<String, String>
}
class Person: Object {
@Persisted(primaryKey: true) var id = 0
@Persisted var name = ""
// To-many relationship - a person can have many dogs
@Persisted var dogs: List<Dog>
// Embed a single object.
// Embedded object properties must be marked optional.
@Persisted var address: Address?
}
class Address: EmbeddedObject {
@Persisted var street: String?
@Persisted var city: String?
@Persisted var country: String?
@Persisted 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
}

提示

更新相关对象和嵌入式对象

要更新嵌入式对象相关对象的的属性,请使用点符号或括号符号来修改该属性,就像在常规嵌套对象中一样。

ObjectResultList 均符合 键值编码 。当您需要确定要在运行时更新哪个属性时,这非常有用。

将 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)对象,请调用-[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")

您可以在写事务期间将元素 insertMutableSet 中,以将元素添加到属性中。如果您使用多个集,您还可以在一个集中插入或删除另一个集中包含的集元素。或者,您可以对某个集进行更改以仅包含两者的共同元素。

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 属性,但在赋值时必须指定值的类型。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"
}

后退

读取