1 ////////////////////////////////////////////////////////////////////////////
3 // Copyright 2014 Realm Inc.
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
9 // http://www.apache.org/licenses/LICENSE-2.0
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
17 ////////////////////////////////////////////////////////////////////////////
19 #import "RLMObject_Private.hpp"
21 #import "RLMAccessor.h"
23 #import "RLMCollection_Private.hpp"
24 #import "RLMObjectBase_Private.h"
25 #import "RLMObjectSchema_Private.hpp"
26 #import "RLMObjectStore.h"
27 #import "RLMProperty.h"
28 #import "RLMQueryUtil.hpp"
29 #import "RLMRealm_Private.hpp"
30 #import "RLMSchema_Private.h"
32 #import "collection_notifications.hpp"
35 @interface RLMPropertyChange ()
36 @property (nonatomic, readwrite, strong) NSString *name;
37 @property (nonatomic, readwrite, strong, nullable) id previousValue;
38 @property (nonatomic, readwrite, strong, nullable) id value;
41 // We declare things in RLMObject which are actually implemented in RLMObjectBase
42 // for documentation's sake, which leads to -Wunimplemented-method warnings.
43 // Other alternatives to this would be to disable -Wunimplemented-method for this
44 // file (but then we could miss legitimately missing things), or declaring the
45 // inherited things in a category (but they currently aren't nicely grouped for
47 @implementation RLMObject
49 // synthesized in RLMObjectBase
50 @dynamic invalidated, realm, objectSchema;
52 #pragma mark - Designated Initializers
54 - (instancetype)init {
58 - (instancetype)initWithValue:(id)value schema:(RLMSchema *)schema {
59 return [super initWithValue:value schema:schema];
62 - (instancetype)initWithRealm:(__unsafe_unretained RLMRealm *const)realm schema:(RLMObjectSchema *)schema {
63 return [super initWithRealm:realm schema:schema];
66 #pragma mark - Convenience Initializers
68 - (instancetype)initWithValue:(id)value {
69 return [super initWithValue:value schema:RLMSchema.partialPrivateSharedSchema];
72 #pragma mark - Class-based Object Creation
74 + (instancetype)createInDefaultRealmWithValue:(id)value {
75 return (RLMObject *)RLMCreateObjectInRealmWithValue([RLMRealm defaultRealm], [self className], value, false);
78 + (instancetype)createInRealm:(RLMRealm *)realm withValue:(id)value {
79 return (RLMObject *)RLMCreateObjectInRealmWithValue(realm, [self className], value, false);
82 + (instancetype)createOrUpdateInDefaultRealmWithValue:(id)value {
83 return [self createOrUpdateInRealm:[RLMRealm defaultRealm] withValue:value];
86 + (instancetype)createOrUpdateInRealm:(RLMRealm *)realm withValue:(id)value {
88 RLMObjectSchema *schema = [self sharedSchema];
89 if (!schema.primaryKeyProperty) {
90 NSString *reason = [NSString stringWithFormat:@"'%@' does not have a primary key and can not be updated", schema.className];
91 @throw [NSException exceptionWithName:@"RLMExecption" reason:reason userInfo:nil];
93 return (RLMObject *)RLMCreateObjectInRealmWithValue(realm, [self className], value, true);
96 #pragma mark - Subscripting
98 - (id)objectForKeyedSubscript:(NSString *)key {
99 return RLMObjectBaseObjectForKeyedSubscript(self, key);
102 - (void)setObject:(id)obj forKeyedSubscript:(NSString *)key {
103 RLMObjectBaseSetObjectForKeyedSubscript(self, key, obj);
106 #pragma mark - Getting & Querying
108 + (RLMResults *)allObjects {
109 return RLMGetObjects(RLMRealm.defaultRealm, self.className, nil);
112 + (RLMResults *)allObjectsInRealm:(__unsafe_unretained RLMRealm *const)realm {
113 return RLMGetObjects(realm, self.className, nil);
116 + (RLMResults *)objectsWhere:(NSString *)predicateFormat, ... {
118 va_start(args, predicateFormat);
119 RLMResults *results = [self objectsWhere:predicateFormat args:args];
124 + (RLMResults *)objectsWhere:(NSString *)predicateFormat args:(va_list)args {
125 return [self objectsWithPredicate:[NSPredicate predicateWithFormat:predicateFormat arguments:args]];
128 + (RLMResults *)objectsInRealm:(RLMRealm *)realm where:(NSString *)predicateFormat, ... {
130 va_start(args, predicateFormat);
131 RLMResults *results = [self objectsInRealm:realm where:predicateFormat args:args];
136 + (RLMResults *)objectsInRealm:(RLMRealm *)realm where:(NSString *)predicateFormat args:(va_list)args {
137 return [self objectsInRealm:realm withPredicate:[NSPredicate predicateWithFormat:predicateFormat arguments:args]];
140 + (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate {
141 return RLMGetObjects(RLMRealm.defaultRealm, self.className, predicate);
144 + (RLMResults *)objectsInRealm:(RLMRealm *)realm withPredicate:(NSPredicate *)predicate {
145 return RLMGetObjects(realm, self.className, predicate);
148 + (instancetype)objectForPrimaryKey:(id)primaryKey {
149 return RLMGetObject(RLMRealm.defaultRealm, self.className, primaryKey);
152 + (instancetype)objectInRealm:(RLMRealm *)realm forPrimaryKey:(id)primaryKey {
153 return RLMGetObject(realm, self.className, primaryKey);
156 #pragma mark - Other Instance Methods
158 - (BOOL)isEqualToObject:(RLMObject *)object {
159 return [object isKindOfClass:RLMObject.class] && RLMObjectBaseAreEqual(self, object);
162 - (RLMNotificationToken *)addNotificationBlock:(RLMObjectChangeBlock)block {
163 return RLMObjectAddNotificationBlock(self, ^(NSArray<NSString *> *propertyNames,
164 NSArray *oldValues, NSArray *newValues, NSError *error) {
166 block(false, nil, error);
168 else if (!propertyNames) {
169 block(true, nil, nil);
172 auto properties = [NSMutableArray arrayWithCapacity:propertyNames.count];
173 for (NSUInteger i = 0, count = propertyNames.count; i < count; ++i) {
174 auto prop = [RLMPropertyChange new];
175 prop.name = propertyNames[i];
176 prop.previousValue = RLMCoerceToNil(oldValues[i]);
177 prop.value = RLMCoerceToNil(newValues[i]);
178 [properties addObject:prop];
180 block(false, properties, nil);
185 + (NSString *)className {
186 return [super className];
189 #pragma mark - Default values for schema definition
191 + (NSArray *)indexedProperties {
195 + (NSDictionary *)linkingObjectsProperties {
199 + (NSDictionary *)defaultPropertyValues {
203 + (NSString *)primaryKey {
207 + (NSArray *)ignoredProperties {
211 + (NSArray *)requiredProperties {
217 @implementation RLMDynamicObject
219 + (BOOL)shouldIncludeInDefaultSchema {
223 - (id)valueForUndefinedKey:(NSString *)key {
224 return RLMDynamicGetByName(self, key, false);
227 - (void)setValue:(id)value forUndefinedKey:(NSString *)key {
228 RLMDynamicValidatedSet(self, key, value);
233 @implementation RLMWeakObjectHandle {
239 - (instancetype)initWithObject:(RLMObjectBase *)object {
240 if (!(self = [super init])) {
245 _info = object->_info;
246 _objectClass = object.class;
251 - (RLMObjectBase *)object {
252 RLMObjectBase *object = RLMCreateManagedAccessor(_objectClass, _info->realm, _info);
253 object->_row = std::move(_row);
257 - (id)copyWithZone:(__unused NSZone *)zone {
258 RLMWeakObjectHandle *copy = [[RLMWeakObjectHandle alloc] init];
261 copy->_objectClass = _objectClass;
267 static bool treatFakeObjectAsRLMObject = false;
268 void RLMSetTreatFakeObjectAsRLMObject(BOOL flag) {
269 treatFakeObjectAsRLMObject = flag;
272 BOOL RLMIsObjectOrSubclass(Class klass) {
273 if (RLMIsKindOfClass(klass, RLMObjectBase.class)) {
277 if (treatFakeObjectAsRLMObject) {
278 static Class FakeObjectClass = NSClassFromString(@"FakeObject");
279 return RLMIsKindOfClass(klass, FakeObjectClass);
284 BOOL RLMIsObjectSubclass(Class klass) {
285 auto isSubclass = [](Class class1, Class class2) {
286 class1 = class_getSuperclass(class1);
287 return RLMIsKindOfClass(class1, class2);
289 if (isSubclass(class_getSuperclass(klass), RLMObjectBase.class)) {
293 if (treatFakeObjectAsRLMObject) {
294 static Class FakeObjectClass = NSClassFromString(@"FakeObject");
295 return isSubclass(klass, FakeObjectClass);
300 @interface RLMObjectNotificationToken : RLMCancellationToken
302 @implementation RLMObjectNotificationToken {
304 realm::Object _object;
308 RLMNotificationToken *RLMObjectAddNotificationBlock(RLMObjectBase *obj, RLMObjectNotificationCallback block) {
310 @throw RLMException(@"Only objects which are managed by a Realm support change notifications");
312 [obj->_realm verifyNotificationsAreSupported:true];
315 void (^block)(NSArray<NSString *> *, NSArray *, NSArray *, NSError *);
316 RLMObjectBase *object;
318 NSArray<NSString *> *propertyNames = nil;
319 NSArray *oldValues = nil;
320 bool deleted = false;
322 void populateProperties(realm::CollectionChangeSet const& c) {
326 if (!c.deletions.empty()) {
330 if (c.columns.empty()) {
334 auto properties = [NSMutableArray new];
335 for (size_t i = 0; i < c.columns.size(); ++i) {
336 if (c.columns[i].empty()) {
339 if (auto prop = object->_info->propertyForTableColumn(i)) {
340 [properties addObject:prop.name];
343 if (properties.count) {
344 propertyNames = properties;
348 NSArray *readValues(realm::CollectionChangeSet const& c) {
352 populateProperties(c);
353 if (!propertyNames) {
357 auto values = [NSMutableArray arrayWithCapacity:propertyNames.count];
358 for (NSString *name in propertyNames) {
359 id value = [object valueForKey:name];
360 if (!value || [value isKindOfClass:[RLMArray class]]) {
361 [values addObject:NSNull.null];
364 [values addObject:value];
370 void before(realm::CollectionChangeSet const& c) {
372 oldValues = readValues(c);
376 void after(realm::CollectionChangeSet const& c) {
378 auto newValues = readValues(c);
380 block(nil, nil, nil, nil);
382 else if (newValues) {
383 block(propertyNames, oldValues, newValues, nil);
390 void error(std::exception_ptr err) {
393 rethrow_exception(err);
396 NSError *error = nil;
397 RLMRealmTranslateException(&error);
398 block(nil, nil, nil, error);
402 } callback{block, obj};
404 realm::Object object(obj->_realm->_realm, *obj->_info->objectSchema, obj->_row);
405 auto token = [[RLMObjectNotificationToken alloc] initWithToken:object.add_notification_callback(callback) realm:obj->_realm];
406 token->_object = std::move(object);
410 @implementation RLMPropertyChange