123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468 |
- ////////////////////////////////////////////////////////////////////////////
- //
- // 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_Private.hpp"
- #import "RLMListBase.h"
- #import "RLMObjectSchema_Private.hpp"
- #import "RLMObjectStore.h"
- #import "RLMObservation.hpp"
- #import "RLMOptionalBase.h"
- #import "RLMProperty_Private.h"
- #import "RLMRealm_Private.hpp"
- #import "RLMSchema_Private.h"
- #import "RLMSwiftSupport.h"
- #import "RLMThreadSafeReference_Private.hpp"
- #import "RLMUtil.hpp"
- #import "object.hpp"
- #import "object_schema.hpp"
- #import "shared_realm.hpp"
- using namespace realm;
- const NSUInteger RLMDescriptionMaxDepth = 5;
- static bool isManagedAccessorClass(Class cls) {
- const char *className = class_getName(cls);
- const char accessorClassPrefix[] = "RLM:Managed";
- return strncmp(className, accessorClassPrefix, sizeof(accessorClassPrefix) - 1) == 0;
- }
- static bool maybeInitObjectSchemaForUnmanaged(RLMObjectBase *obj) {
- Class cls = obj.class;
- if (isManagedAccessorClass(cls)) {
- return false;
- }
- obj->_objectSchema = [cls sharedSchema];
- if (!obj->_objectSchema) {
- return false;
- }
- // set default values
- if (!obj->_objectSchema.isSwiftClass) {
- NSDictionary *dict = RLMDefaultValuesForObjectSchema(obj->_objectSchema);
- for (NSString *key in dict) {
- [obj setValue:dict[key] forKey:key];
- }
- }
- // set unmanaged accessor class
- object_setClass(obj, obj->_objectSchema.unmanagedClass);
- return true;
- }
- @interface RLMObjectBase () <RLMThreadConfined, RLMThreadConfined_Private>
- @end
- @implementation RLMObjectBase
- // unmanaged init
- - (instancetype)init {
- if ((self = [super init])) {
- maybeInitObjectSchemaForUnmanaged(self);
- }
- return self;
- }
- - (void)dealloc {
- // This can't be a unique_ptr because associated objects are removed
- // *after* c++ members are destroyed and dealloc is called, and we need it
- // to be in a validish state when that happens
- delete _observationInfo;
- _observationInfo = nullptr;
- }
- static id coerceToObjectType(id obj, Class cls, RLMSchema *schema) {
- if ([obj isKindOfClass:cls]) {
- return obj;
- }
- id value = [[cls alloc] init];
- RLMInitializeWithValue(value, obj, schema);
- return value;
- }
- static id validatedObjectForProperty(__unsafe_unretained id const obj,
- __unsafe_unretained RLMObjectSchema *const objectSchema,
- __unsafe_unretained RLMProperty *const prop,
- __unsafe_unretained RLMSchema *const schema) {
- RLMValidateValueForProperty(obj, objectSchema, prop);
- if (!obj || obj == NSNull.null) {
- return nil;
- }
- if (prop.type == RLMPropertyTypeObject) {
- Class objectClass = schema[prop.objectClassName].objectClass;
- if (prop.array) {
- NSMutableArray *ret = [[NSMutableArray alloc] init];
- for (id el in obj) {
- [ret addObject:coerceToObjectType(el, objectClass, schema)];
- }
- return ret;
- }
- return coerceToObjectType(obj, objectClass, schema);
- }
- return obj;
- }
- void RLMInitializeWithValue(RLMObjectBase *self, id value, RLMSchema *schema) {
- if (!value || value == NSNull.null) {
- @throw RLMException(@"Must provide a non-nil value.");
- }
- RLMObjectSchema *objectSchema = self->_objectSchema;
- if (!objectSchema) {
- // Will be nil if we're called during schema init, when we don't want
- // to actually populate the object anyway
- return;
- }
- NSArray *properties = objectSchema.properties;
- if (NSArray *array = RLMDynamicCast<NSArray>(value)) {
- if (array.count > properties.count) {
- @throw RLMException(@"Invalid array input: more values (%llu) than properties (%llu).",
- (unsigned long long)array.count, (unsigned long long)properties.count);
- }
- NSUInteger i = 0;
- for (id val in array) {
- RLMProperty *prop = properties[i++];
- [self setValue:validatedObjectForProperty(RLMCoerceToNil(val), objectSchema, prop, schema)
- forKey:prop.name];
- }
- }
- else {
- // assume our object is an NSDictionary or an object with kvc properties
- for (RLMProperty *prop in properties) {
- id obj = RLMValidatedValueForProperty(value, prop.name, objectSchema.className);
- // don't set unspecified properties
- if (!obj) {
- continue;
- }
- [self setValue:validatedObjectForProperty(RLMCoerceToNil(obj), objectSchema, prop, schema)
- forKey:prop.name];
- }
- }
- }
- id RLMCreateManagedAccessor(Class cls, RLMClassInfo *info) {
- RLMObjectBase *obj = [[cls alloc] init];
- obj->_info = info;
- obj->_realm = info->realm;
- obj->_objectSchema = info->rlmObjectSchema;
- return obj;
- }
- - (id)valueForKey:(NSString *)key {
- if (_observationInfo) {
- return _observationInfo->valueForKey(key);
- }
- return [super valueForKey:key];
- }
- // Generic Swift properties can't be dynamic, so KVO doesn't work for them by default
- - (id)valueForUndefinedKey:(NSString *)key {
- RLMProperty *prop = _objectSchema[key];
- if (Class accessor = prop.swiftAccessor) {
- return [accessor get:(char *)(__bridge void *)self + ivar_getOffset(prop.swiftIvar)];
- }
- if (Ivar ivar = prop.swiftIvar) {
- return RLMCoerceToNil(object_getIvar(self, ivar));
- }
- return [super valueForUndefinedKey:key];
- }
- - (void)setValue:(id)value forUndefinedKey:(NSString *)key {
- value = RLMCoerceToNil(value);
- RLMProperty *property = _objectSchema[key];
- if (Ivar ivar = property.swiftIvar) {
- if (property.array) {
- value = RLMAsFastEnumeration(value);
- RLMArray *array = [object_getIvar(self, ivar) _rlmArray];
- [array removeAllObjects];
- if (value) {
- [array addObjects:validatedObjectForProperty(value, _objectSchema, property,
- RLMSchema.partialPrivateSharedSchema)];
- }
- }
- else if (property.optional) {
- RLMSetOptional(object_getIvar(self, ivar), value);
- }
- return;
- }
- [super setValue:value forUndefinedKey:key];
- }
- // overridden at runtime per-class for performance
- + (NSString *)className {
- NSString *className = NSStringFromClass(self);
- if ([RLMSwiftSupport isSwiftClassName:className]) {
- className = [RLMSwiftSupport demangleClassName:className];
- }
- return className;
- }
- // overridden at runtime per-class for performance
- + (RLMObjectSchema *)sharedSchema {
- return [RLMSchema sharedSchemaForClass:self.class];
- }
- + (void)initializeLinkedObjectSchemas {
- for (RLMProperty *prop in self.sharedSchema.properties) {
- if (prop.type == RLMPropertyTypeObject && !RLMSchema.partialPrivateSharedSchema[prop.objectClassName]) {
- [[RLMSchema classForString:prop.objectClassName] initializeLinkedObjectSchemas];
- }
- }
- }
- + (nullable NSArray<RLMProperty *> *)_getPropertiesWithInstance:(__unused id)obj {
- return nil;
- }
- - (NSString *)description {
- if (self.isInvalidated) {
- return @"[invalid object]";
- }
- return [self descriptionWithMaxDepth:RLMDescriptionMaxDepth];
- }
- - (NSString *)descriptionWithMaxDepth:(NSUInteger)depth {
- if (depth == 0) {
- return @"<Maximum depth exceeded>";
- }
- NSString *baseClassName = _objectSchema.className;
- NSMutableString *mString = [NSMutableString stringWithFormat:@"%@ {\n", baseClassName];
- for (RLMProperty *property in _objectSchema.properties) {
- id object = [(id)self objectForKeyedSubscript:property.name];
- NSString *sub;
- if ([object respondsToSelector:@selector(descriptionWithMaxDepth:)]) {
- sub = [object descriptionWithMaxDepth:depth - 1];
- }
- else if (property.type == RLMPropertyTypeData) {
- static NSUInteger maxPrintedDataLength = 24;
- NSData *data = object;
- NSUInteger length = data.length;
- if (length > maxPrintedDataLength) {
- data = [NSData dataWithBytes:data.bytes length:maxPrintedDataLength];
- }
- NSString *dataDescription = [data description];
- sub = [NSString stringWithFormat:@"<%@ — %lu total bytes>", [dataDescription substringWithRange:NSMakeRange(1, dataDescription.length - 2)], (unsigned long)length];
- }
- else {
- sub = [object description];
- }
- [mString appendFormat:@"\t%@ = %@;\n", property.name, [sub stringByReplacingOccurrencesOfString:@"\n" withString:@"\n\t"]];
- }
- [mString appendString:@"}"];
- return [NSString stringWithString:mString];
- }
- - (RLMRealm *)realm {
- return _realm;
- }
- - (RLMObjectSchema *)objectSchema {
- return _objectSchema;
- }
- - (BOOL)isInvalidated {
- // if not unmanaged and our accessor has been detached, we have been deleted
- return self.class == _objectSchema.accessorClass && !_row.is_attached();
- }
- - (BOOL)isEqual:(id)object {
- if (RLMObjectBase *other = RLMDynamicCast<RLMObjectBase>(object)) {
- if (_objectSchema.primaryKeyProperty) {
- return RLMObjectBaseAreEqual(self, other);
- }
- }
- return [super isEqual:object];
- }
- - (NSUInteger)hash {
- if (_objectSchema.primaryKeyProperty) {
- id primaryProperty = [self valueForKey:_objectSchema.primaryKeyProperty.name];
- // modify the hash of our primary key value to avoid potential (although unlikely) collisions
- return [primaryProperty hash] ^ 1;
- }
- else {
- return [super hash];
- }
- }
- + (BOOL)shouldIncludeInDefaultSchema {
- return RLMIsObjectSubclass(self);
- }
- + (NSString *)_realmObjectName {
- return nil;
- }
- + (NSDictionary *)_realmColumnNames {
- return nil;
- }
- + (bool)_realmIgnoreClass {
- return false;
- }
- - (id)mutableArrayValueForKey:(NSString *)key {
- id obj = [self valueForKey:key];
- if ([obj isKindOfClass:[RLMArray class]]) {
- return obj;
- }
- return [super mutableArrayValueForKey:key];
- }
- - (void)addObserver:(id)observer
- forKeyPath:(NSString *)keyPath
- options:(NSKeyValueObservingOptions)options
- context:(void *)context {
- if (!_observationInfo) {
- _observationInfo = new RLMObservationInfo(self);
- }
- _observationInfo->recordObserver(_row, _info, _objectSchema, keyPath);
- [super addObserver:observer forKeyPath:keyPath options:options context:context];
- }
- - (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath {
- [super removeObserver:observer forKeyPath:keyPath];
- if (_observationInfo)
- _observationInfo->removeObserver();
- }
- + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
- if (isManagedAccessorClass(self) && [class_getSuperclass(self.class) sharedSchema][key]) {
- return NO;
- }
- return [super automaticallyNotifiesObserversForKey:key];
- }
- #pragma mark - Thread Confined Protocol Conformance
- - (std::unique_ptr<realm::ThreadSafeReferenceBase>)makeThreadSafeReference {
- Object object(_realm->_realm, *_info->objectSchema, _row);
- realm::ThreadSafeReference<Object> reference = _realm->_realm->obtain_thread_safe_reference(std::move(object));
- return std::make_unique<realm::ThreadSafeReference<Object>>(std::move(reference));
- }
- - (id)objectiveCMetadata {
- return nil;
- }
- + (instancetype)objectWithThreadSafeReference:(std::unique_ptr<realm::ThreadSafeReferenceBase>)reference
- metadata:(__unused id)metadata
- realm:(RLMRealm *)realm {
- REALM_ASSERT_DEBUG(dynamic_cast<realm::ThreadSafeReference<Object> *>(reference.get()));
- auto object_reference = static_cast<realm::ThreadSafeReference<Object> *>(reference.get());
- Object object = realm->_realm->resolve_thread_safe_reference(std::move(*object_reference));
- if (!object.is_valid()) {
- return nil;
- }
- NSString *objectClassName = @(object.get_object_schema().name.c_str());
- return RLMCreateObjectAccessor(realm->_info[objectClassName], object.row().get_index());
- }
- @end
- RLMRealm *RLMObjectBaseRealm(__unsafe_unretained RLMObjectBase *object) {
- return object ? object->_realm : nil;
- }
- RLMObjectSchema *RLMObjectBaseObjectSchema(__unsafe_unretained RLMObjectBase *object) {
- return object ? object->_objectSchema : nil;
- }
- id RLMObjectBaseObjectForKeyedSubscript(RLMObjectBase *object, NSString *key) {
- if (!object) {
- return nil;
- }
- if (object->_realm) {
- return RLMDynamicGetByName(object, key);
- }
- else {
- return [object valueForKey:key];
- }
- }
- void RLMObjectBaseSetObjectForKeyedSubscript(RLMObjectBase *object, NSString *key, id obj) {
- if (!object) {
- return;
- }
- if (object->_realm || object.class == object->_objectSchema.accessorClass) {
- RLMDynamicValidatedSet(object, key, obj);
- }
- else {
- [object setValue:obj forKey:key];
- }
- }
- BOOL RLMObjectBaseAreEqual(RLMObjectBase *o1, RLMObjectBase *o2) {
- // if not the correct types throw
- if ((o1 && ![o1 isKindOfClass:RLMObjectBase.class]) || (o2 && ![o2 isKindOfClass:RLMObjectBase.class])) {
- @throw RLMException(@"Can only compare objects of class RLMObjectBase");
- }
- // if identical object (or both are nil)
- if (o1 == o2) {
- return YES;
- }
- // if one is nil
- if (o1 == nil || o2 == nil) {
- return NO;
- }
- // if not in realm or differing realms
- if (o1->_realm == nil || o1->_realm != o2->_realm) {
- return NO;
- }
- // if either are detached
- if (!o1->_row.is_attached() || !o2->_row.is_attached()) {
- return NO;
- }
- // if table and index are the same
- return o1->_row.get_table() == o2->_row.get_table()
- && o1->_row.get_index() == o2->_row.get_index();
- }
- id RLMValidatedValueForProperty(id object, NSString *key, NSString *className) {
- @try {
- return [object valueForKey:key];
- }
- @catch (NSException *e) {
- if ([e.name isEqualToString:NSUndefinedKeyException]) {
- @throw RLMException(@"Invalid value '%@' to initialize object of type '%@': missing key '%@'",
- object, className, key);
- }
- @throw;
- }
- }
|