123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815 |
- ////////////////////////////////////////////////////////////////////////////
- //
- // 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 "RLMAccessor.hpp"
- #import "RLMArray_Private.hpp"
- #import "RLMListBase.h"
- #import "RLMObjectSchema_Private.hpp"
- #import "RLMObjectStore.h"
- #import "RLMObject_Private.hpp"
- #import "RLMObservation.hpp"
- #import "RLMProperty_Private.h"
- #import "RLMRealm_Private.hpp"
- #import "RLMResults_Private.hpp"
- #import "RLMSchema_Private.h"
- #import "RLMUtil.hpp"
- #import "results.hpp"
- #import "property.hpp"
- #import <objc/runtime.h>
- #import <objc/message.h>
- #import <realm/descriptor.hpp>
- #pragma mark - Helper functions
- namespace {
- template<typename T>
- T get(__unsafe_unretained RLMObjectBase *const obj, NSUInteger index) {
- RLMVerifyAttached(obj);
- return obj->_row.get<T>(obj->_info->objectSchema->persisted_properties[index].table_column);
- }
- template<typename T>
- id getBoxed(__unsafe_unretained RLMObjectBase *const obj, NSUInteger index) {
- RLMVerifyAttached(obj);
- auto& prop = obj->_info->objectSchema->persisted_properties[index];
- auto col = prop.table_column;
- if (obj->_row.is_null(col)) {
- return nil;
- }
- RLMAccessorContext ctx(obj, &prop);
- return ctx.box(obj->_row.get<T>(col));
- }
- template<typename T>
- void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, T val) {
- RLMVerifyInWriteTransaction(obj);
- obj->_row.set(colIndex, val);
- }
- template<typename Fn>
- auto translateError(Fn&& fn) {
- try {
- return fn();
- }
- catch (std::exception const& e) {
- @throw RLMException(e);
- }
- }
- void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
- __unsafe_unretained NSString *const val) {
- RLMVerifyInWriteTransaction(obj);
- translateError([&] {
- obj->_row.set(colIndex, RLMStringDataWithNSString(val));
- });
- }
- [[gnu::noinline]]
- void setNull(realm::Row& row, size_t col) {
- translateError([&] { row.set_null(col); });
- }
- void setValue(__unsafe_unretained RLMObjectBase *const obj,
- NSUInteger colIndex, __unsafe_unretained NSDate *const date) {
- RLMVerifyInWriteTransaction(obj);
- if (date) {
- obj->_row.set(colIndex, RLMTimestampForNSDate(date));
- }
- else {
- setNull(obj->_row, colIndex);
- }
- }
- void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
- __unsafe_unretained NSData *const data) {
- RLMVerifyInWriteTransaction(obj);
- translateError([&] {
- obj->_row.set(colIndex, RLMBinaryDataForNSData(data));
- });
- }
- void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
- __unsafe_unretained RLMObjectBase *const val) {
- RLMVerifyInWriteTransaction(obj);
- if (!val) {
- obj->_row.nullify_link(colIndex);
- return;
- }
- RLMAddObjectToRealm(val, obj->_realm, RLMUpdatePolicyError);
- // make sure it is the correct type
- if (val->_row.get_table() != obj->_row.get_table()->get_link_target(colIndex)) {
- @throw RLMException(@"Can't set object of type '%@' to property of type '%@'",
- val->_objectSchema.className,
- obj->_info->propertyForTableColumn(colIndex).objectClassName);
- }
- obj->_row.set_link(colIndex, val->_row.get_index());
- }
- // array getter/setter
- RLMArray *getArray(__unsafe_unretained RLMObjectBase *const obj, NSUInteger propIndex) {
- RLMVerifyAttached(obj);
- auto prop = obj->_info->rlmObjectSchema.properties[propIndex];
- return [[RLMManagedArray alloc] initWithParent:obj property:prop];
- }
- void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
- __unsafe_unretained id<NSFastEnumeration> const value) {
- RLMVerifyInWriteTransaction(obj);
- auto prop = obj->_info->propertyForTableColumn(colIndex);
- RLMValidateValueForProperty(value, obj->_info->rlmObjectSchema, prop, true);
- realm::List list(obj->_realm->_realm, *obj->_row.get_table(), colIndex, obj->_row.get_index());
- RLMClassInfo *info = obj->_info;
- if (list.get_type() == realm::PropertyType::Object) {
- info = &obj->_info->linkTargetType(prop.index);
- }
- RLMAccessorContext ctx(obj->_realm, *info);
- translateError([&] { list.assign(ctx, value, false); });
- }
- void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
- __unsafe_unretained NSNumber<RLMInt> *const intObject) {
- RLMVerifyInWriteTransaction(obj);
- if (intObject) {
- obj->_row.set(colIndex, intObject.longLongValue);
- }
- else {
- setNull(obj->_row, colIndex);
- }
- }
- void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
- __unsafe_unretained NSNumber<RLMFloat> *const floatObject) {
- RLMVerifyInWriteTransaction(obj);
- if (floatObject) {
- obj->_row.set(colIndex, floatObject.floatValue);
- }
- else {
- setNull(obj->_row, colIndex);
- }
- }
- void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
- __unsafe_unretained NSNumber<RLMDouble> *const doubleObject) {
- RLMVerifyInWriteTransaction(obj);
- if (doubleObject) {
- obj->_row.set(colIndex, doubleObject.doubleValue);
- }
- else {
- setNull(obj->_row, colIndex);
- }
- }
- void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
- __unsafe_unretained NSNumber<RLMBool> *const boolObject) {
- RLMVerifyInWriteTransaction(obj);
- if (boolObject) {
- obj->_row.set(colIndex, (bool)boolObject.boolValue);
- }
- else {
- setNull(obj->_row, colIndex);
- }
- }
- RLMLinkingObjects *getLinkingObjects(__unsafe_unretained RLMObjectBase *const obj,
- __unsafe_unretained RLMProperty *const property) {
- RLMVerifyAttached(obj);
- auto& objectInfo = obj->_realm->_info[property.objectClassName];
- auto& linkOrigin = obj->_info->objectSchema->computed_properties[property.index].link_origin_property_name;
- auto linkingProperty = objectInfo.objectSchema->property_for_name(linkOrigin);
- auto backlinkView = obj->_row.get_table()->get_backlink_view(obj->_row.get_index(),
- objectInfo.table(),
- linkingProperty->table_column);
- realm::Results results(obj->_realm->_realm, std::move(backlinkView));
- return [RLMLinkingObjects resultsWithObjectInfo:objectInfo results:std::move(results)];
- }
- // any getter/setter
- template<typename Type, typename StorageType=Type>
- id makeGetter(NSUInteger index) {
- return ^(__unsafe_unretained RLMObjectBase *const obj) {
- return static_cast<Type>(get<StorageType>(obj, index));
- };
- }
- template<typename Type>
- id makeBoxedGetter(NSUInteger index) {
- return ^(__unsafe_unretained RLMObjectBase *const obj) {
- return getBoxed<Type>(obj, index);
- };
- }
- template<typename Type>
- id makeOptionalGetter(NSUInteger index) {
- return ^(__unsafe_unretained RLMObjectBase *const obj) {
- return getBoxed<realm::util::Optional<Type>>(obj, index);
- };
- }
- template<typename Type>
- id makeNumberGetter(NSUInteger index, bool boxed, bool optional) {
- if (optional) {
- return makeOptionalGetter<Type>(index);
- }
- if (boxed) {
- return makeBoxedGetter<Type>(index);
- }
- return makeGetter<Type>(index);
- }
- // dynamic getter with column closure
- id managedGetter(RLMProperty *prop, const char *type) {
- NSUInteger index = prop.index;
- if (prop.array && prop.type != RLMPropertyTypeLinkingObjects) {
- return ^id(__unsafe_unretained RLMObjectBase *const obj) {
- return getArray(obj, index);
- };
- }
- bool boxed = *type == '@';
- switch (prop.type) {
- case RLMPropertyTypeInt:
- if (prop.optional || boxed) {
- return makeNumberGetter<long long>(index, boxed, prop.optional);
- }
- switch (*type) {
- case 'c': return makeGetter<char, int64_t>(index);
- case 's': return makeGetter<short, int64_t>(index);
- case 'i': return makeGetter<int, int64_t>(index);
- case 'l': return makeGetter<long, int64_t>(index);
- case 'q': return makeGetter<long long, int64_t>(index);
- default:
- @throw RLMException(@"Unexpected property type for Objective-C type code");
- }
- case RLMPropertyTypeFloat:
- return makeNumberGetter<float>(index, boxed, prop.optional);
- case RLMPropertyTypeDouble:
- return makeNumberGetter<double>(index, boxed, prop.optional);
- case RLMPropertyTypeBool:
- return makeNumberGetter<bool>(index, boxed, prop.optional);
- case RLMPropertyTypeString:
- return makeBoxedGetter<realm::StringData>(index);
- case RLMPropertyTypeDate:
- return makeBoxedGetter<realm::Timestamp>(index);
- case RLMPropertyTypeData:
- return makeBoxedGetter<realm::BinaryData>(index);
- case RLMPropertyTypeObject:
- return makeBoxedGetter<realm::RowExpr>(index);
- case RLMPropertyTypeAny:
- @throw RLMException(@"Cannot create accessor class for schema with Mixed properties");
- case RLMPropertyTypeLinkingObjects:
- return ^(__unsafe_unretained RLMObjectBase *const obj) {
- return getLinkingObjects(obj, prop);
- };
- }
- }
- template<typename ArgType, typename StorageType=ArgType>
- id makeSetter(__unsafe_unretained RLMProperty *const prop) {
- NSUInteger index = prop.index;
- NSString *name = prop.name;
- if (prop.isPrimary) {
- return ^(__unused RLMObjectBase *obj, __unused ArgType val) {
- @throw RLMException(@"Primary key can't be changed after an object is inserted.");
- };
- }
- return ^(__unsafe_unretained RLMObjectBase *const obj, ArgType val) {
- auto set = [&] {
- setValue(obj, obj->_info->objectSchema->persisted_properties[index].table_column,
- static_cast<StorageType>(val));
- };
- if (RLMObservationInfo *info = RLMGetObservationInfo(obj->_observationInfo,
- obj->_row.get_index(), *obj->_info)) {
- info->willChange(name);
- set();
- info->didChange(name);
- }
- else {
- set();
- }
- };
- }
- // dynamic setter with column closure
- id managedSetter(RLMProperty *prop, const char *type) {
- if (prop.array && prop.type != RLMPropertyTypeLinkingObjects) {
- return makeSetter<id<NSFastEnumeration>>(prop);
- }
- bool boxed = prop.optional || *type == '@';
- switch (prop.type) {
- case RLMPropertyTypeInt:
- if (boxed) {
- return makeSetter<NSNumber<RLMInt> *>(prop);
- }
- switch (*type) {
- case 'c': return makeSetter<char, long long>(prop);
- case 's': return makeSetter<short, long long>(prop);
- case 'i': return makeSetter<int, long long>(prop);
- case 'l': return makeSetter<long, long long>(prop);
- case 'q': return makeSetter<long long>(prop);
- default:
- @throw RLMException(@"Unexpected property type for Objective-C type code");
- }
- case RLMPropertyTypeFloat:
- return boxed ? makeSetter<NSNumber<RLMFloat> *>(prop) : makeSetter<float>(prop);
- case RLMPropertyTypeDouble:
- return boxed ? makeSetter<NSNumber<RLMDouble> *>(prop) : makeSetter<double>(prop);
- case RLMPropertyTypeBool:
- return boxed ? makeSetter<NSNumber<RLMBool> *>(prop) : makeSetter<BOOL, bool>(prop);
- case RLMPropertyTypeString: return makeSetter<NSString *>(prop);
- case RLMPropertyTypeDate: return makeSetter<NSDate *>(prop);
- case RLMPropertyTypeData: return makeSetter<NSData *>(prop);
- case RLMPropertyTypeAny: return nil;
- case RLMPropertyTypeLinkingObjects: return nil;
- case RLMPropertyTypeObject: return makeSetter<RLMObjectBase *>(prop);
- }
- }
- // call getter for superclass for property at colIndex
- id superGet(RLMObjectBase *obj, NSString *propName) {
- typedef id (*getter_type)(RLMObjectBase *, SEL);
- RLMProperty *prop = obj->_objectSchema[propName];
- Class superClass = class_getSuperclass(obj.class);
- getter_type superGetter = (getter_type)[superClass instanceMethodForSelector:prop.getterSel];
- return superGetter(obj, prop.getterSel);
- }
- // call setter for superclass for property at colIndex
- void superSet(RLMObjectBase *obj, NSString *propName, id val) {
- typedef void (*setter_type)(RLMObjectBase *, SEL, RLMArray *ar);
- RLMProperty *prop = obj->_objectSchema[propName];
- Class superClass = class_getSuperclass(obj.class);
- setter_type superSetter = (setter_type)[superClass instanceMethodForSelector:prop.setterSel];
- superSetter(obj, prop.setterSel, val);
- }
- // getter/setter for unmanaged object
- id unmanagedGetter(RLMProperty *prop, const char *) {
- // only override getters for RLMArray and linking objects properties
- if (prop.type == RLMPropertyTypeLinkingObjects) {
- return ^(RLMObjectBase *) { return [RLMResults emptyDetachedResults]; };
- }
- if (prop.array) {
- NSString *propName = prop.name;
- if (prop.type == RLMPropertyTypeObject) {
- NSString *objectClassName = prop.objectClassName;
- return ^(RLMObjectBase *obj) {
- id val = superGet(obj, propName);
- if (!val) {
- val = [[RLMArray alloc] initWithObjectClassName:objectClassName];
- superSet(obj, propName, val);
- }
- return val;
- };
- }
- auto type = prop.type;
- auto optional = prop.optional;
- return ^(RLMObjectBase *obj) {
- id val = superGet(obj, propName);
- if (!val) {
- val = [[RLMArray alloc] initWithObjectType:type optional:optional];
- superSet(obj, propName, val);
- }
- return val;
- };
- }
- return nil;
- }
- id unmanagedSetter(RLMProperty *prop, const char *) {
- // Only RLMArray needs special handling for the unmanaged setter
- if (!prop.array) {
- return nil;
- }
- NSString *propName = prop.name;
- return ^(RLMObjectBase *obj, id<NSFastEnumeration> values) {
- auto prop = obj->_objectSchema[propName];
- RLMValidateValueForProperty(values, obj->_objectSchema, prop, true);
- // make copy when setting (as is the case for all other variants)
- RLMArray *ar;
- if (prop.type == RLMPropertyTypeObject)
- ar = [[RLMArray alloc] initWithObjectClassName:prop.objectClassName];
- else
- ar = [[RLMArray alloc] initWithObjectType:prop.type optional:prop.optional];
- [ar addObjects:values];
- superSet(obj, propName, ar);
- };
- }
- void addMethod(Class cls, __unsafe_unretained RLMProperty *const prop,
- id (*getter)(RLMProperty *, const char *),
- id (*setter)(RLMProperty *, const char *)) {
- SEL sel = prop.getterSel;
- auto getterMethod = class_getInstanceMethod(cls, sel);
- if (!getterMethod) {
- return;
- }
- const char *getterType = method_getTypeEncoding(getterMethod);
- if (id block = getter(prop, getterType)) {
- class_addMethod(cls, sel, imp_implementationWithBlock(block), getterType);
- }
- if (!(sel = prop.setterSel)) {
- return;
- }
- auto setterMethod = class_getInstanceMethod(cls, sel);
- if (!setterMethod) {
- return;
- }
- if (id block = setter(prop, getterType)) { // note: deliberately getterType as it's easier to grab the relevant type from
- class_addMethod(cls, sel, imp_implementationWithBlock(block), method_getTypeEncoding(setterMethod));
- }
- }
- Class createAccessorClass(Class objectClass,
- RLMObjectSchema *schema,
- const char *accessorClassName,
- id (*getterGetter)(RLMProperty *, const char *),
- id (*setterGetter)(RLMProperty *, const char *)) {
- REALM_ASSERT_DEBUG(RLMIsObjectOrSubclass(objectClass));
- // create and register proxy class which derives from object class
- Class accClass = objc_allocateClassPair(objectClass, accessorClassName, 0);
- if (!accClass) {
- // Class with that name already exists, so just return the pre-existing one
- // This should only happen for our standalone "accessors"
- return objc_lookUpClass(accessorClassName);
- }
- // override getters/setters for each propery
- for (RLMProperty *prop in schema.properties) {
- addMethod(accClass, prop, getterGetter, setterGetter);
- }
- for (RLMProperty *prop in schema.computedProperties) {
- addMethod(accClass, prop, getterGetter, setterGetter);
- }
- objc_registerClassPair(accClass);
- return accClass;
- }
- } // anonymous namespace
- #pragma mark - Public Interface
- Class RLMManagedAccessorClassForObjectClass(Class objectClass, RLMObjectSchema *schema, const char *name) {
- return createAccessorClass(objectClass, schema, name, managedGetter, managedSetter);
- }
- Class RLMUnmanagedAccessorClassForObjectClass(Class objectClass, RLMObjectSchema *schema) {
- return createAccessorClass(objectClass, schema,
- [@"RLM:Unmanaged " stringByAppendingString:schema.className].UTF8String,
- unmanagedGetter, unmanagedSetter);
- }
- // implement the class method className on accessors to return the className of the
- // base object
- void RLMReplaceClassNameMethod(Class accessorClass, NSString *className) {
- Class metaClass = object_getClass(accessorClass);
- IMP imp = imp_implementationWithBlock(^(Class){ return className; });
- class_addMethod(metaClass, @selector(className), imp, "@@:");
- }
- // implement the shared schema method
- void RLMReplaceSharedSchemaMethod(Class accessorClass, RLMObjectSchema *schema) {
- Class metaClass = object_getClass(accessorClass);
- IMP imp = imp_implementationWithBlock(^(Class cls) {
- if (cls == accessorClass) {
- return schema;
- }
- // If we aren't being called directly on the class this was overriden
- // for, the class is either a subclass which we haven't initialized yet,
- // or it's a runtime-generated class which should use the parent's
- // schema. We check for the latter by checking if the immediate
- // descendent of the desired class is a class generated by us (there
- // may be further subclasses not generated by us for things like KVO).
- Class parent = class_getSuperclass(cls);
- while (parent != accessorClass) {
- cls = parent;
- parent = class_getSuperclass(cls);
- }
- static const char accessorClassPrefix[] = "RLM:";
- if (!strncmp(class_getName(cls), accessorClassPrefix, sizeof(accessorClassPrefix) - 1)) {
- return schema;
- }
- return [RLMSchema sharedSchemaForClass:cls];
- });
- class_addMethod(metaClass, @selector(sharedSchema), imp, "@@:");
- }
- void RLMDynamicValidatedSet(RLMObjectBase *obj, NSString *propName, id val) {
- RLMVerifyAttached(obj);
- RLMObjectSchema *schema = obj->_objectSchema;
- RLMProperty *prop = schema[propName];
- if (!prop) {
- @throw RLMException(@"Invalid property name '%@' for class '%@'.",
- propName, obj->_objectSchema.className);
- }
- if (prop.isPrimary) {
- @throw RLMException(@"Primary key can't be changed to '%@' after an object is inserted.", val);
- }
- RLMValidateValueForProperty(val, schema, prop, true);
- RLMDynamicSet(obj, prop, RLMCoerceToNil(val));
- }
- // Precondition: the property is not a primary key
- void RLMDynamicSet(__unsafe_unretained RLMObjectBase *const obj,
- __unsafe_unretained RLMProperty *const prop,
- __unsafe_unretained id const val) {
- REALM_ASSERT_DEBUG(!prop.isPrimary);
- realm::Object o(obj->_info->realm->_realm, *obj->_info->objectSchema, obj->_row);
- RLMAccessorContext c(obj);
- translateError([&] {
- o.set_property_value(c, prop.columnName.UTF8String, val ?: NSNull.null, false);
- });
- }
- id RLMDynamicGet(__unsafe_unretained RLMObjectBase *const obj, __unsafe_unretained RLMProperty *const prop) {
- realm::Object o(obj->_realm->_realm, *obj->_info->objectSchema, obj->_row);
- RLMAccessorContext c(obj);
- c.currentProperty = prop;
- return translateError([&] {
- return RLMCoerceToNil(o.get_property_value<id>(c, prop.columnName.UTF8String));
- });
- }
- id RLMDynamicGetByName(__unsafe_unretained RLMObjectBase *const obj,
- __unsafe_unretained NSString *const propName, bool asList) {
- RLMProperty *prop = obj->_objectSchema[propName];
- if (!prop) {
- @throw RLMException(@"Invalid property name '%@' for class '%@'.",
- propName, obj->_objectSchema.className);
- }
- if (asList && prop.array && prop.swiftIvar) {
- RLMListBase *list = object_getIvar(obj, prop.swiftIvar);
- if (prop.type != RLMPropertyTypeLinkingObjects && !list._rlmArray) {
- list._rlmArray = RLMDynamicGet(obj, prop);
- }
- return list;
- }
- return RLMDynamicGet(obj, prop);
- }
- RLMAccessorContext::RLMAccessorContext(RLMAccessorContext& parent, realm::Property const& property)
- : _realm(parent._realm)
- , _info(property.type == realm::PropertyType::Object ? parent._info.linkTargetType(property) : parent._info)
- , _promote_existing(parent._promote_existing)
- {
- }
- RLMAccessorContext::RLMAccessorContext(RLMRealm *realm, RLMClassInfo& info, bool promote)
- : _realm(realm), _info(info), _promote_existing(promote)
- {
- }
- RLMAccessorContext::RLMAccessorContext(__unsafe_unretained RLMObjectBase *const parent,
- const realm::Property *prop)
- : _realm(parent->_realm)
- , _info(prop && prop->type == realm::PropertyType::Object ? parent->_info->linkTargetType(*prop)
- : *parent->_info)
- , _parentObject(parent)
- {
- }
- id RLMAccessorContext::defaultValue(__unsafe_unretained NSString *const key) {
- if (!_defaultValues) {
- _defaultValues = RLMDefaultValuesForObjectSchema(_info.rlmObjectSchema);
- }
- return _defaultValues[key];
- }
- id RLMAccessorContext::propertyValue(__unsafe_unretained id const obj, size_t propIndex,
- __unsafe_unretained RLMProperty *const prop) {
- // Property value from an NSArray
- if ([obj respondsToSelector:@selector(objectAtIndex:)]) {
- return propIndex < [obj count] ? [obj objectAtIndex:propIndex] : nil;
- }
- // Property value from an NSDictionary
- if ([obj respondsToSelector:@selector(objectForKey:)]) {
- return [obj objectForKey:prop.name];
- }
- // Property value from an instance of this object type
- id value;
- if ([obj isKindOfClass:_info.rlmObjectSchema.objectClass] && prop.swiftIvar) {
- if (prop.array) {
- return static_cast<RLMListBase *>(object_getIvar(obj, prop.swiftIvar))._rlmArray;
- }
- else if (prop.swiftIvar == RLMDummySwiftIvar) {
- // FIXME: An invalid property which we're pretending is nil until 4.0
- // https://github.com/realm/realm-cocoa/issues/5784
- return NSNull.null;
- }
- else { // optional
- value = RLMGetOptional(static_cast<RLMOptionalBase *>(object_getIvar(obj, prop.swiftIvar)));
- }
- }
- else {
- // Property value from some object that's KVC-compatible
- value = RLMValidatedValueForProperty(obj, [obj respondsToSelector:prop.getterSel] ? prop.getterName : prop.name,
- _info.rlmObjectSchema.className);
- }
- return value ?: NSNull.null;
- }
- id RLMAccessorContext::box(realm::List&& l) {
- REALM_ASSERT(_parentObject);
- REALM_ASSERT(currentProperty);
- return [[RLMManagedArray alloc] initWithList:std::move(l) realm:_realm
- parentInfo:_parentObject->_info
- property:currentProperty];
- }
- id RLMAccessorContext::box(realm::Object&& o) {
- REALM_ASSERT(currentProperty);
- return RLMCreateObjectAccessor(_realm, _info.linkTargetType(currentProperty.index), o.row());
- }
- id RLMAccessorContext::box(realm::RowExpr r) {
- return RLMCreateObjectAccessor(_realm, _info, r);
- }
- id RLMAccessorContext::box(realm::Results&& r) {
- REALM_ASSERT(currentProperty);
- return [RLMResults resultsWithObjectInfo:_realm->_info[currentProperty.objectClassName]
- results:std::move(r)];
- }
- template<>
- realm::Timestamp RLMAccessorContext::unbox(__unsafe_unretained id const value, bool, bool, bool, size_t) {
- id v = RLMCoerceToNil(value);
- return RLMTimestampForNSDate(v);
- }
- template<>
- bool RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool, bool, size_t) {
- return [v boolValue];
- }
- template<>
- double RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool, bool, size_t) {
- return [v doubleValue];
- }
- template<>
- float RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool, bool, size_t) {
- return [v floatValue];
- }
- template<>
- long long RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool, bool, size_t) {
- return [v longLongValue];
- }
- template<>
- realm::BinaryData RLMAccessorContext::unbox(id v, bool, bool, bool, size_t) {
- v = RLMCoerceToNil(v);
- return RLMBinaryDataForNSData(v);
- }
- template<>
- realm::StringData RLMAccessorContext::unbox(id v, bool, bool, bool, size_t) {
- v = RLMCoerceToNil(v);
- return RLMStringDataWithNSString(v);
- }
- template<typename Fn>
- static auto to_optional(__unsafe_unretained id const value, Fn&& fn) {
- id v = RLMCoerceToNil(value);
- return v && v != NSNull.null ? realm::util::make_optional(fn(v)) : realm::util::none;
- }
- template<>
- realm::util::Optional<bool> RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool, bool, size_t) {
- return to_optional(v, [&](__unsafe_unretained id v) { return (bool)[v boolValue]; });
- }
- template<>
- realm::util::Optional<double> RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool, bool, size_t) {
- return to_optional(v, [&](__unsafe_unretained id v) { return [v doubleValue]; });
- }
- template<>
- realm::util::Optional<float> RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool, bool, size_t) {
- return to_optional(v, [&](__unsafe_unretained id v) { return [v floatValue]; });
- }
- template<>
- realm::util::Optional<int64_t> RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool, bool, size_t) {
- return to_optional(v, [&](__unsafe_unretained id v) { return [v longLongValue]; });
- }
- template<>
- realm::RowExpr RLMAccessorContext::unbox(__unsafe_unretained id const v, bool create, bool update, bool diff, size_t) {
- RLMUpdatePolicy policy = !update ? RLMUpdatePolicyError
- : !diff ? RLMUpdatePolicyUpdateAll
- : RLMUpdatePolicyUpdateChanged;
- RLMObjectBase *link = RLMDynamicCast<RLMObjectBase>(v);
- if (!link) {
- if (!create)
- return realm::RowExpr();
- return RLMCreateObjectInRealmWithValue(_realm, _info.rlmObjectSchema.className, v, policy)->_row;
- }
- if (link.isInvalidated) {
- if (create) {
- @throw RLMException(@"Adding a deleted or invalidated object to a Realm is not permitted");
- }
- else {
- @throw RLMException(@"Object has been invalidated");
- }
- }
- if (![link->_objectSchema.className isEqualToString:_info.rlmObjectSchema.className]) {
- if (create && !_promote_existing)
- return RLMCreateObjectInRealmWithValue(_realm, _info.rlmObjectSchema.className, link, policy)->_row;
- return link->_row;
- }
- if (!link->_realm) {
- if (!create)
- return realm::RowExpr();
- if (!_promote_existing)
- return RLMCreateObjectInRealmWithValue(_realm, _info.rlmObjectSchema.className, link, policy)->_row;
- RLMAddObjectToRealm(link, _realm, policy);
- }
- else if (link->_realm != _realm) {
- if (_promote_existing)
- @throw RLMException(@"Object is already managed by another Realm. Use create instead to copy it into this Realm.");
- return RLMCreateObjectInRealmWithValue(_realm, _info.rlmObjectSchema.className, v, policy)->_row;
- }
- return link->_row;
- }
- void RLMAccessorContext::will_change(realm::Row const& row, realm::Property const& prop) {
- _observationInfo = RLMGetObservationInfo(nullptr, row.get_index(), _info);
- if (_observationInfo) {
- _kvoPropertyName = _info.propertyForTableColumn(prop.table_column).name;
- _observationInfo->willChange(_kvoPropertyName);
- }
- }
- void RLMAccessorContext::did_change() {
- if (_observationInfo) {
- _observationInfo->didChange(_kvoPropertyName);
- _kvoPropertyName = nil;
- _observationInfo = nullptr;
- }
- }
- RLMOptionalId RLMAccessorContext::value_for_property(__unsafe_unretained id const obj,
- realm::Property const&, size_t propIndex) {
- auto prop = _info.rlmObjectSchema.properties[propIndex];
- id value = propertyValue(obj, propIndex, prop);
- if (value) {
- RLMValidateValueForProperty(value, _info.rlmObjectSchema, prop);
- }
- if (_promote_existing && [obj isKindOfClass:_info.rlmObjectSchema.objectClass] && !prop.swiftIvar) {
- // set the ivars for object and array properties to nil as otherwise the
- // accessors retain objects that are no longer accessible via the properties
- // this is mainly an issue when the object graph being added has cycles,
- // as it's not obvious that the user has to set the *ivars* to nil to
- // avoid leaking memory
- if (prop.type == RLMPropertyTypeObject) {
- ((void(*)(id, SEL, id))objc_msgSend)(obj, prop.setterSel, nil);
- }
- }
- return RLMOptionalId{value};
- }
- RLMOptionalId RLMAccessorContext::default_value_for_property(realm::ObjectSchema const&,
- realm::Property const& prop)
- {
- return RLMOptionalId{defaultValue(@(prop.name.c_str()))};
- }
- bool RLMAccessorContext::is_same_list(realm::List const& list, __unsafe_unretained id const v) const noexcept {
- return [v respondsToSelector:@selector(isBackedByList:)] && [v isBackedByList:list];
- }
|