You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
415 lines
12 KiB
415 lines
12 KiB
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright 2014 Realm Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
#import "RLMObject_Private.hpp"
|
|
|
|
#import "RLMAccessor.h"
|
|
#import "RLMArray.h"
|
|
#import "RLMCollection_Private.hpp"
|
|
#import "RLMObjectBase_Private.h"
|
|
#import "RLMObjectSchema_Private.hpp"
|
|
#import "RLMObjectStore.h"
|
|
#import "RLMProperty.h"
|
|
#import "RLMQueryUtil.hpp"
|
|
#import "RLMRealm_Private.hpp"
|
|
#import "RLMSchema_Private.h"
|
|
|
|
#import "collection_notifications.hpp"
|
|
#import "object.hpp"
|
|
|
|
@interface RLMPropertyChange ()
|
|
@property (nonatomic, readwrite, strong) NSString *name;
|
|
@property (nonatomic, readwrite, strong, nullable) id previousValue;
|
|
@property (nonatomic, readwrite, strong, nullable) id value;
|
|
@end
|
|
|
|
// We declare things in RLMObject which are actually implemented in RLMObjectBase
|
|
// for documentation's sake, which leads to -Wunimplemented-method warnings.
|
|
// Other alternatives to this would be to disable -Wunimplemented-method for this
|
|
// file (but then we could miss legitimately missing things), or declaring the
|
|
// inherited things in a category (but they currently aren't nicely grouped for
|
|
// that).
|
|
@implementation RLMObject
|
|
|
|
// synthesized in RLMObjectBase
|
|
@dynamic invalidated, realm, objectSchema;
|
|
|
|
#pragma mark - Designated Initializers
|
|
|
|
- (instancetype)init {
|
|
return [super init];
|
|
}
|
|
|
|
- (instancetype)initWithValue:(id)value schema:(RLMSchema *)schema {
|
|
return [super initWithValue:value schema:schema];
|
|
}
|
|
|
|
- (instancetype)initWithRealm:(__unsafe_unretained RLMRealm *const)realm schema:(RLMObjectSchema *)schema {
|
|
return [super initWithRealm:realm schema:schema];
|
|
}
|
|
|
|
#pragma mark - Convenience Initializers
|
|
|
|
- (instancetype)initWithValue:(id)value {
|
|
return [super initWithValue:value schema:RLMSchema.partialPrivateSharedSchema];
|
|
}
|
|
|
|
#pragma mark - Class-based Object Creation
|
|
|
|
+ (instancetype)createInDefaultRealmWithValue:(id)value {
|
|
return (RLMObject *)RLMCreateObjectInRealmWithValue([RLMRealm defaultRealm], [self className], value, RLMUpdatePolicyError);
|
|
}
|
|
|
|
+ (instancetype)createInRealm:(RLMRealm *)realm withValue:(id)value {
|
|
return (RLMObject *)RLMCreateObjectInRealmWithValue(realm, [self className], value, RLMUpdatePolicyError);
|
|
}
|
|
|
|
+ (instancetype)createOrUpdateInDefaultRealmWithValue:(id)value {
|
|
return [self createOrUpdateInRealm:[RLMRealm defaultRealm] withValue:value];
|
|
}
|
|
|
|
+ (instancetype)createOrUpdateModifiedInDefaultRealmWithValue:(id)value {
|
|
return [self createOrUpdateModifiedInRealm:[RLMRealm defaultRealm] withValue:value];
|
|
}
|
|
|
|
+ (instancetype)createOrUpdateInRealm:(RLMRealm *)realm withValue:(id)value {
|
|
RLMVerifyHasPrimaryKey(self);
|
|
return (RLMObject *)RLMCreateObjectInRealmWithValue(realm, [self className], value, RLMUpdatePolicyUpdateAll);
|
|
}
|
|
|
|
+ (instancetype)createOrUpdateModifiedInRealm:(RLMRealm *)realm withValue:(id)value {
|
|
RLMVerifyHasPrimaryKey(self);
|
|
return (RLMObject *)RLMCreateObjectInRealmWithValue(realm, [self className], value, RLMUpdatePolicyUpdateChanged);
|
|
}
|
|
|
|
#pragma mark - Subscripting
|
|
|
|
- (id)objectForKeyedSubscript:(NSString *)key {
|
|
return RLMObjectBaseObjectForKeyedSubscript(self, key);
|
|
}
|
|
|
|
- (void)setObject:(id)obj forKeyedSubscript:(NSString *)key {
|
|
RLMObjectBaseSetObjectForKeyedSubscript(self, key, obj);
|
|
}
|
|
|
|
#pragma mark - Getting & Querying
|
|
|
|
+ (RLMResults *)allObjects {
|
|
return RLMGetObjects(RLMRealm.defaultRealm, self.className, nil);
|
|
}
|
|
|
|
+ (RLMResults *)allObjectsInRealm:(__unsafe_unretained RLMRealm *const)realm {
|
|
return RLMGetObjects(realm, self.className, nil);
|
|
}
|
|
|
|
+ (RLMResults *)objectsWhere:(NSString *)predicateFormat, ... {
|
|
va_list args;
|
|
va_start(args, predicateFormat);
|
|
RLMResults *results = [self objectsWhere:predicateFormat args:args];
|
|
va_end(args);
|
|
return results;
|
|
}
|
|
|
|
+ (RLMResults *)objectsWhere:(NSString *)predicateFormat args:(va_list)args {
|
|
return [self objectsWithPredicate:[NSPredicate predicateWithFormat:predicateFormat arguments:args]];
|
|
}
|
|
|
|
+ (RLMResults *)objectsInRealm:(RLMRealm *)realm where:(NSString *)predicateFormat, ... {
|
|
va_list args;
|
|
va_start(args, predicateFormat);
|
|
RLMResults *results = [self objectsInRealm:realm where:predicateFormat args:args];
|
|
va_end(args);
|
|
return results;
|
|
}
|
|
|
|
+ (RLMResults *)objectsInRealm:(RLMRealm *)realm where:(NSString *)predicateFormat args:(va_list)args {
|
|
return [self objectsInRealm:realm withPredicate:[NSPredicate predicateWithFormat:predicateFormat arguments:args]];
|
|
}
|
|
|
|
+ (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate {
|
|
return RLMGetObjects(RLMRealm.defaultRealm, self.className, predicate);
|
|
}
|
|
|
|
+ (RLMResults *)objectsInRealm:(RLMRealm *)realm withPredicate:(NSPredicate *)predicate {
|
|
return RLMGetObjects(realm, self.className, predicate);
|
|
}
|
|
|
|
+ (instancetype)objectForPrimaryKey:(id)primaryKey {
|
|
return RLMGetObject(RLMRealm.defaultRealm, self.className, primaryKey);
|
|
}
|
|
|
|
+ (instancetype)objectInRealm:(RLMRealm *)realm forPrimaryKey:(id)primaryKey {
|
|
return RLMGetObject(realm, self.className, primaryKey);
|
|
}
|
|
|
|
#pragma mark - Other Instance Methods
|
|
|
|
- (BOOL)isEqualToObject:(RLMObject *)object {
|
|
return [object isKindOfClass:RLMObject.class] && RLMObjectBaseAreEqual(self, object);
|
|
}
|
|
|
|
- (RLMNotificationToken *)addNotificationBlock:(RLMObjectChangeBlock)block {
|
|
return RLMObjectAddNotificationBlock(self, ^(NSArray<NSString *> *propertyNames,
|
|
NSArray *oldValues, NSArray *newValues, NSError *error) {
|
|
if (error) {
|
|
block(false, nil, error);
|
|
}
|
|
else if (!propertyNames) {
|
|
block(true, nil, nil);
|
|
}
|
|
else {
|
|
auto properties = [NSMutableArray arrayWithCapacity:propertyNames.count];
|
|
for (NSUInteger i = 0, count = propertyNames.count; i < count; ++i) {
|
|
auto prop = [RLMPropertyChange new];
|
|
prop.name = propertyNames[i];
|
|
prop.previousValue = RLMCoerceToNil(oldValues[i]);
|
|
prop.value = RLMCoerceToNil(newValues[i]);
|
|
[properties addObject:prop];
|
|
}
|
|
block(false, properties, nil);
|
|
}
|
|
});
|
|
}
|
|
|
|
+ (NSString *)className {
|
|
return [super className];
|
|
}
|
|
|
|
#pragma mark - Default values for schema definition
|
|
|
|
+ (NSArray *)indexedProperties {
|
|
return @[];
|
|
}
|
|
|
|
+ (NSDictionary *)linkingObjectsProperties {
|
|
return @{};
|
|
}
|
|
|
|
+ (NSDictionary *)defaultPropertyValues {
|
|
return nil;
|
|
}
|
|
|
|
+ (NSString *)primaryKey {
|
|
return nil;
|
|
}
|
|
|
|
+ (NSArray *)ignoredProperties {
|
|
return nil;
|
|
}
|
|
|
|
+ (NSArray *)requiredProperties {
|
|
return @[];
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation RLMDynamicObject
|
|
|
|
+ (BOOL)shouldIncludeInDefaultSchema {
|
|
return NO;
|
|
}
|
|
|
|
- (id)valueForUndefinedKey:(NSString *)key {
|
|
return RLMDynamicGetByName(self, key, false);
|
|
}
|
|
|
|
- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
|
|
RLMDynamicValidatedSet(self, key, value);
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation RLMWeakObjectHandle {
|
|
realm::Row _row;
|
|
RLMClassInfo *_info;
|
|
Class _objectClass;
|
|
}
|
|
|
|
- (instancetype)initWithObject:(RLMObjectBase *)object {
|
|
if (!(self = [super init])) {
|
|
return nil;
|
|
}
|
|
|
|
_row = object->_row;
|
|
_info = object->_info;
|
|
_objectClass = object.class;
|
|
|
|
return self;
|
|
}
|
|
|
|
- (RLMObjectBase *)object {
|
|
RLMObjectBase *object = RLMCreateManagedAccessor(_objectClass, _info->realm, _info);
|
|
object->_row = std::move(_row);
|
|
return object;
|
|
}
|
|
|
|
- (id)copyWithZone:(__unused NSZone *)zone {
|
|
RLMWeakObjectHandle *copy = [[RLMWeakObjectHandle alloc] init];
|
|
copy->_row = _row;
|
|
copy->_info = _info;
|
|
copy->_objectClass = _objectClass;
|
|
return copy;
|
|
}
|
|
|
|
@end
|
|
|
|
static bool treatFakeObjectAsRLMObject = false;
|
|
void RLMSetTreatFakeObjectAsRLMObject(BOOL flag) {
|
|
treatFakeObjectAsRLMObject = flag;
|
|
}
|
|
|
|
BOOL RLMIsObjectOrSubclass(Class klass) {
|
|
if (RLMIsKindOfClass(klass, RLMObjectBase.class)) {
|
|
return YES;
|
|
}
|
|
|
|
if (treatFakeObjectAsRLMObject) {
|
|
static Class FakeObjectClass = NSClassFromString(@"FakeObject");
|
|
return RLMIsKindOfClass(klass, FakeObjectClass);
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
BOOL RLMIsObjectSubclass(Class klass) {
|
|
auto isSubclass = [](Class class1, Class class2) {
|
|
class1 = class_getSuperclass(class1);
|
|
return RLMIsKindOfClass(class1, class2);
|
|
};
|
|
if (isSubclass(class_getSuperclass(klass), RLMObjectBase.class)) {
|
|
return YES;
|
|
}
|
|
|
|
if (treatFakeObjectAsRLMObject) {
|
|
static Class FakeObjectClass = NSClassFromString(@"FakeObject");
|
|
return isSubclass(klass, FakeObjectClass);
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
@interface RLMObjectNotificationToken : RLMCancellationToken
|
|
@end
|
|
@implementation RLMObjectNotificationToken {
|
|
@public
|
|
realm::Object _object;
|
|
}
|
|
@end
|
|
|
|
RLMNotificationToken *RLMObjectAddNotificationBlock(RLMObjectBase *obj, RLMObjectNotificationCallback block) {
|
|
if (!obj->_realm) {
|
|
@throw RLMException(@"Only objects which are managed by a Realm support change notifications");
|
|
}
|
|
[obj->_realm verifyNotificationsAreSupported:true];
|
|
|
|
struct {
|
|
void (^block)(NSArray<NSString *> *, NSArray *, NSArray *, NSError *);
|
|
RLMObjectBase *object;
|
|
|
|
NSArray<NSString *> *propertyNames = nil;
|
|
NSArray *oldValues = nil;
|
|
bool deleted = false;
|
|
|
|
void populateProperties(realm::CollectionChangeSet const& c) {
|
|
if (propertyNames) {
|
|
return;
|
|
}
|
|
if (!c.deletions.empty()) {
|
|
deleted = true;
|
|
return;
|
|
}
|
|
if (c.columns.empty()) {
|
|
return;
|
|
}
|
|
|
|
auto properties = [NSMutableArray new];
|
|
for (size_t i = 0; i < c.columns.size(); ++i) {
|
|
if (c.columns[i].empty()) {
|
|
continue;
|
|
}
|
|
if (auto prop = object->_info->propertyForTableColumn(i)) {
|
|
[properties addObject:prop.name];
|
|
}
|
|
}
|
|
if (properties.count) {
|
|
propertyNames = properties;
|
|
}
|
|
}
|
|
|
|
NSArray *readValues(realm::CollectionChangeSet const& c) {
|
|
if (c.empty()) {
|
|
return nil;
|
|
}
|
|
populateProperties(c);
|
|
if (!propertyNames) {
|
|
return nil;
|
|
}
|
|
|
|
auto values = [NSMutableArray arrayWithCapacity:propertyNames.count];
|
|
for (NSString *name in propertyNames) {
|
|
id value = [object valueForKey:name];
|
|
if (!value || [value isKindOfClass:[RLMArray class]]) {
|
|
[values addObject:NSNull.null];
|
|
}
|
|
else {
|
|
[values addObject:value];
|
|
}
|
|
}
|
|
return values;
|
|
}
|
|
|
|
void before(realm::CollectionChangeSet const& c) {
|
|
@autoreleasepool {
|
|
oldValues = readValues(c);
|
|
}
|
|
}
|
|
|
|
void after(realm::CollectionChangeSet const& c) {
|
|
@autoreleasepool {
|
|
auto newValues = readValues(c);
|
|
if (deleted) {
|
|
block(nil, nil, nil, nil);
|
|
}
|
|
else if (newValues) {
|
|
block(propertyNames, oldValues, newValues, nil);
|
|
}
|
|
propertyNames = nil;
|
|
oldValues = nil;
|
|
}
|
|
}
|
|
|
|
void error(std::exception_ptr err) {
|
|
@autoreleasepool {
|
|
try {
|
|
rethrow_exception(err);
|
|
}
|
|
catch (...) {
|
|
NSError *error = nil;
|
|
RLMRealmTranslateException(&error);
|
|
block(nil, nil, nil, error);
|
|
}
|
|
}
|
|
}
|
|
} callback{block, obj};
|
|
|
|
realm::Object object(obj->_realm->_realm, *obj->_info->objectSchema, obj->_row);
|
|
auto token = [[RLMObjectNotificationToken alloc] initWithToken:object.add_notification_callback(callback) realm:obj->_realm];
|
|
token->_object = std::move(object);
|
|
return token;
|
|
}
|
|
|
|
@implementation RLMPropertyChange
|
|
@end
|
|
|