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 "RLMAccessor.hpp"
21 #import "RLMArray_Private.hpp"
22 #import "RLMListBase.h"
23 #import "RLMObjectSchema_Private.hpp"
24 #import "RLMObjectStore.h"
25 #import "RLMObject_Private.hpp"
26 #import "RLMObservation.hpp"
27 #import "RLMProperty_Private.h"
28 #import "RLMRealm_Private.hpp"
29 #import "RLMResults_Private.hpp"
30 #import "RLMSchema_Private.h"
33 #import "property.hpp"
35 #import <objc/runtime.h>
36 #import <objc/message.h>
37 #import <realm/descriptor.hpp>
39 #pragma mark - Helper functions
43 T get(__unsafe_unretained RLMObjectBase *const obj, NSUInteger index) {
44 RLMVerifyAttached(obj);
45 return obj->_row.get<T>(obj->_info->objectSchema->persisted_properties[index].table_column);
49 id getBoxed(__unsafe_unretained RLMObjectBase *const obj, NSUInteger index) {
50 RLMVerifyAttached(obj);
51 auto& prop = obj->_info->objectSchema->persisted_properties[index];
52 auto col = prop.table_column;
53 if (obj->_row.is_null(col)) {
57 RLMAccessorContext ctx(obj, &prop);
58 return ctx.box(obj->_row.get<T>(col));
62 void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, T val) {
63 RLMVerifyInWriteTransaction(obj);
64 obj->_row.set(colIndex, val);
68 void translateError(Fn&& fn) {
72 catch (std::exception const& e) {
73 @throw RLMException(e);
77 void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
78 __unsafe_unretained NSString *const val) {
79 RLMVerifyInWriteTransaction(obj);
81 obj->_row.set(colIndex, RLMStringDataWithNSString(val));
86 void setNull(realm::Row& row, size_t col) {
87 translateError([&] { row.set_null(col); });
90 void setValue(__unsafe_unretained RLMObjectBase *const obj,
91 NSUInteger colIndex, __unsafe_unretained NSDate *const date) {
92 RLMVerifyInWriteTransaction(obj);
94 obj->_row.set(colIndex, RLMTimestampForNSDate(date));
97 setNull(obj->_row, colIndex);
101 void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
102 __unsafe_unretained NSData *const data) {
103 RLMVerifyInWriteTransaction(obj);
105 obj->_row.set(colIndex, RLMBinaryDataForNSData(data));
109 void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
110 __unsafe_unretained RLMObjectBase *const val) {
112 RLMVerifyInWriteTransaction(obj);
113 obj->_row.nullify_link(colIndex);
117 RLMAddObjectToRealm(val, obj->_realm, false);
119 // make sure it is the correct type
120 if (val->_row.get_table() != obj->_row.get_table()->get_link_target(colIndex)) {
121 @throw RLMException(@"Can't set object of type '%@' to property of type '%@'",
122 val->_objectSchema.className,
123 obj->_info->propertyForTableColumn(colIndex).objectClassName);
125 obj->_row.set_link(colIndex, val->_row.get_index());
128 // array getter/setter
129 RLMArray *getArray(__unsafe_unretained RLMObjectBase *const obj, NSUInteger propIndex) {
130 RLMVerifyAttached(obj);
131 auto prop = obj->_info->rlmObjectSchema.properties[propIndex];
132 return [[RLMManagedArray alloc] initWithParent:obj property:prop];
135 void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
136 __unsafe_unretained id<NSFastEnumeration> const value) {
137 RLMVerifyInWriteTransaction(obj);
138 auto prop = obj->_info->propertyForTableColumn(colIndex);
139 RLMValidateValueForProperty(value, obj->_info->rlmObjectSchema, prop, true);
141 realm::List list(obj->_realm->_realm, *obj->_row.get_table(), colIndex, obj->_row.get_index());
142 RLMClassInfo *info = obj->_info;
143 if (list.get_type() == realm::PropertyType::Object) {
144 info = &obj->_info->linkTargetType(prop.index);
146 RLMAccessorContext ctx(obj->_realm, *info);
147 translateError([&] { list.assign(ctx, value, false); });
150 void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
151 __unsafe_unretained NSNumber<RLMInt> *const intObject) {
152 RLMVerifyInWriteTransaction(obj);
155 obj->_row.set(colIndex, intObject.longLongValue);
158 setNull(obj->_row, colIndex);
162 void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
163 __unsafe_unretained NSNumber<RLMFloat> *const floatObject) {
164 RLMVerifyInWriteTransaction(obj);
167 obj->_row.set(colIndex, floatObject.floatValue);
170 setNull(obj->_row, colIndex);
174 void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
175 __unsafe_unretained NSNumber<RLMDouble> *const doubleObject) {
176 RLMVerifyInWriteTransaction(obj);
179 obj->_row.set(colIndex, doubleObject.doubleValue);
182 setNull(obj->_row, colIndex);
186 void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
187 __unsafe_unretained NSNumber<RLMBool> *const boolObject) {
188 RLMVerifyInWriteTransaction(obj);
191 obj->_row.set(colIndex, (bool)boolObject.boolValue);
194 setNull(obj->_row, colIndex);
198 RLMLinkingObjects *getLinkingObjects(__unsafe_unretained RLMObjectBase *const obj,
199 __unsafe_unretained RLMProperty *const property) {
200 RLMVerifyAttached(obj);
201 auto& objectInfo = obj->_realm->_info[property.objectClassName];
202 auto linkingProperty = objectInfo.objectSchema->property_for_name(property.linkOriginPropertyName.UTF8String);
203 auto backlinkView = obj->_row.get_table()->get_backlink_view(obj->_row.get_index(), objectInfo.table(), linkingProperty->table_column);
204 realm::Results results(obj->_realm->_realm, std::move(backlinkView));
205 return [RLMLinkingObjects resultsWithObjectInfo:objectInfo results:std::move(results)];
209 template<typename Type, typename StorageType=Type>
210 id makeGetter(NSUInteger index) {
211 return ^(__unsafe_unretained RLMObjectBase *const obj) {
212 return static_cast<Type>(get<StorageType>(obj, index));
216 template<typename Type>
217 id makeBoxedGetter(NSUInteger index) {
218 return ^(__unsafe_unretained RLMObjectBase *const obj) {
219 return getBoxed<Type>(obj, index);
222 template<typename Type>
223 id makeOptionalGetter(NSUInteger index) {
224 return ^(__unsafe_unretained RLMObjectBase *const obj) {
225 return getBoxed<realm::util::Optional<Type>>(obj, index);
228 template<typename Type>
229 id makeNumberGetter(NSUInteger index, bool boxed, bool optional) {
231 return makeOptionalGetter<Type>(index);
234 return makeBoxedGetter<Type>(index);
236 return makeGetter<Type>(index);
239 // dynamic getter with column closure
240 id managedGetter(RLMProperty *prop, const char *type) {
241 NSUInteger index = prop.index;
242 if (prop.array && prop.type != RLMPropertyTypeLinkingObjects) {
243 return ^id(__unsafe_unretained RLMObjectBase *const obj) {
244 return getArray(obj, index);
248 bool boxed = *type == '@';
250 case RLMPropertyTypeInt:
251 if (prop.optional || boxed) {
252 return makeNumberGetter<long long>(index, boxed, prop.optional);
255 case 'c': return makeGetter<char, int64_t>(index);
256 case 's': return makeGetter<short, int64_t>(index);
257 case 'i': return makeGetter<int, int64_t>(index);
258 case 'l': return makeGetter<long, int64_t>(index);
259 case 'q': return makeGetter<long long, int64_t>(index);
261 @throw RLMException(@"Unexpected property type for Objective-C type code");
263 case RLMPropertyTypeFloat:
264 return makeNumberGetter<float>(index, boxed, prop.optional);
265 case RLMPropertyTypeDouble:
266 return makeNumberGetter<double>(index, boxed, prop.optional);
267 case RLMPropertyTypeBool:
268 return makeNumberGetter<bool>(index, boxed, prop.optional);
269 case RLMPropertyTypeString:
270 return makeBoxedGetter<realm::StringData>(index);
271 case RLMPropertyTypeDate:
272 return makeBoxedGetter<realm::Timestamp>(index);
273 case RLMPropertyTypeData:
274 return makeBoxedGetter<realm::BinaryData>(index);
275 case RLMPropertyTypeObject:
276 return makeBoxedGetter<realm::RowExpr>(index);
277 case RLMPropertyTypeAny:
278 @throw RLMException(@"Cannot create accessor class for schema with Mixed properties");
279 case RLMPropertyTypeLinkingObjects:
280 return ^(__unsafe_unretained RLMObjectBase *const obj) {
281 return getLinkingObjects(obj, prop);
286 template<typename ArgType, typename StorageType=ArgType>
287 id makeSetter(__unsafe_unretained RLMProperty *const prop) {
288 NSUInteger index = prop.index;
289 NSString *name = prop.name;
290 if (prop.isPrimary) {
291 return ^(__unused RLMObjectBase *obj, __unused ArgType val) {
292 @throw RLMException(@"Primary key can't be changed after an object is inserted.");
296 return ^(__unsafe_unretained RLMObjectBase *const obj, ArgType val) {
298 setValue(obj, obj->_info->objectSchema->persisted_properties[index].table_column,
299 static_cast<StorageType>(val));
301 if (RLMObservationInfo *info = RLMGetObservationInfo(obj->_observationInfo,
302 obj->_row.get_index(), *obj->_info)) {
303 info->willChange(name);
305 info->didChange(name);
313 // dynamic setter with column closure
314 id managedSetter(RLMProperty *prop, const char *type) {
315 if (prop.array && prop.type != RLMPropertyTypeLinkingObjects) {
316 return makeSetter<id<NSFastEnumeration>>(prop);
319 bool boxed = prop.optional || *type == '@';
321 case RLMPropertyTypeInt:
323 return makeSetter<NSNumber<RLMInt> *>(prop);
326 case 'c': return makeSetter<char, long long>(prop);
327 case 's': return makeSetter<short, long long>(prop);
328 case 'i': return makeSetter<int, long long>(prop);
329 case 'l': return makeSetter<long, long long>(prop);
330 case 'q': return makeSetter<long long>(prop);
332 @throw RLMException(@"Unexpected property type for Objective-C type code");
334 case RLMPropertyTypeFloat:
335 return boxed ? makeSetter<NSNumber<RLMFloat> *>(prop) : makeSetter<float>(prop);
336 case RLMPropertyTypeDouble:
337 return boxed ? makeSetter<NSNumber<RLMDouble> *>(prop) : makeSetter<double>(prop);
338 case RLMPropertyTypeBool:
339 return boxed ? makeSetter<NSNumber<RLMBool> *>(prop) : makeSetter<BOOL, bool>(prop);
340 case RLMPropertyTypeString: return makeSetter<NSString *>(prop);
341 case RLMPropertyTypeDate: return makeSetter<NSDate *>(prop);
342 case RLMPropertyTypeData: return makeSetter<NSData *>(prop);
343 case RLMPropertyTypeAny: return nil;
344 case RLMPropertyTypeLinkingObjects: return nil;
345 case RLMPropertyTypeObject: return makeSetter<RLMObjectBase *>(prop);
349 // call getter for superclass for property at colIndex
350 id superGet(RLMObjectBase *obj, NSString *propName) {
351 typedef id (*getter_type)(RLMObjectBase *, SEL);
352 RLMProperty *prop = obj->_objectSchema[propName];
353 Class superClass = class_getSuperclass(obj.class);
354 getter_type superGetter = (getter_type)[superClass instanceMethodForSelector:prop.getterSel];
355 return superGetter(obj, prop.getterSel);
358 // call setter for superclass for property at colIndex
359 void superSet(RLMObjectBase *obj, NSString *propName, id val) {
360 typedef void (*setter_type)(RLMObjectBase *, SEL, RLMArray *ar);
361 RLMProperty *prop = obj->_objectSchema[propName];
362 Class superClass = class_getSuperclass(obj.class);
363 setter_type superSetter = (setter_type)[superClass instanceMethodForSelector:prop.setterSel];
364 superSetter(obj, prop.setterSel, val);
367 // getter/setter for unmanaged object
368 id unmanagedGetter(RLMProperty *prop, const char *) {
369 // only override getters for RLMArray and linking objects properties
370 if (prop.type == RLMPropertyTypeLinkingObjects) {
371 return ^(RLMObjectBase *) { return [RLMResults emptyDetachedResults]; };
374 NSString *propName = prop.name;
375 if (prop.type == RLMPropertyTypeObject) {
376 NSString *objectClassName = prop.objectClassName;
377 return ^(RLMObjectBase *obj) {
378 id val = superGet(obj, propName);
380 val = [[RLMArray alloc] initWithObjectClassName:objectClassName];
381 superSet(obj, propName, val);
386 auto type = prop.type;
387 auto optional = prop.optional;
388 return ^(RLMObjectBase *obj) {
389 id val = superGet(obj, propName);
391 val = [[RLMArray alloc] initWithObjectType:type optional:optional];
392 superSet(obj, propName, val);
400 id unmanagedSetter(RLMProperty *prop, const char *) {
401 // Only RLMArray needs special handling for the unmanaged setter
406 NSString *propName = prop.name;
407 return ^(RLMObjectBase *obj, id<NSFastEnumeration> values) {
408 auto prop = obj->_objectSchema[propName];
409 RLMValidateValueForProperty(values, obj->_objectSchema, prop, true);
411 // make copy when setting (as is the case for all other variants)
413 if (prop.type == RLMPropertyTypeObject)
414 ar = [[RLMArray alloc] initWithObjectClassName:prop.objectClassName];
416 ar = [[RLMArray alloc] initWithObjectType:prop.type optional:prop.optional];
417 [ar addObjects:values];
418 superSet(obj, propName, ar);
422 void addMethod(Class cls, __unsafe_unretained RLMProperty *const prop,
423 id (*getter)(RLMProperty *, const char *),
424 id (*setter)(RLMProperty *, const char *)) {
425 SEL sel = prop.getterSel;
426 auto getterMethod = class_getInstanceMethod(cls, sel);
431 const char *getterType = method_getTypeEncoding(getterMethod);
432 if (id block = getter(prop, getterType)) {
433 class_addMethod(cls, sel, imp_implementationWithBlock(block), getterType);
436 if (!(sel = prop.setterSel)) {
439 auto setterMethod = class_getInstanceMethod(cls, sel);
443 if (id block = setter(prop, getterType)) { // note: deliberately getterType as it's easier to grab the relevant type from
444 class_addMethod(cls, sel, imp_implementationWithBlock(block), method_getTypeEncoding(setterMethod));
448 Class createAccessorClass(Class objectClass,
449 RLMObjectSchema *schema,
450 const char *accessorClassName,
451 id (*getterGetter)(RLMProperty *, const char *),
452 id (*setterGetter)(RLMProperty *, const char *)) {
453 REALM_ASSERT_DEBUG(RLMIsObjectOrSubclass(objectClass));
455 // create and register proxy class which derives from object class
456 Class accClass = objc_allocateClassPair(objectClass, accessorClassName, 0);
458 // Class with that name already exists, so just return the pre-existing one
459 // This should only happen for our standalone "accessors"
460 return objc_lookUpClass(accessorClassName);
463 // override getters/setters for each propery
464 for (RLMProperty *prop in schema.properties) {
465 addMethod(accClass, prop, getterGetter, setterGetter);
467 for (RLMProperty *prop in schema.computedProperties) {
468 addMethod(accClass, prop, getterGetter, setterGetter);
471 objc_registerClassPair(accClass);
475 } // anonymous namespace
477 #pragma mark - Public Interface
479 Class RLMManagedAccessorClassForObjectClass(Class objectClass, RLMObjectSchema *schema, const char *name) {
480 return createAccessorClass(objectClass, schema, name, managedGetter, managedSetter);
483 Class RLMUnmanagedAccessorClassForObjectClass(Class objectClass, RLMObjectSchema *schema) {
484 return createAccessorClass(objectClass, schema,
485 [@"RLM:Unmanaged " stringByAppendingString:schema.className].UTF8String,
486 unmanagedGetter, unmanagedSetter);
489 // implement the class method className on accessors to return the className of the
491 void RLMReplaceClassNameMethod(Class accessorClass, NSString *className) {
492 Class metaClass = object_getClass(accessorClass);
493 IMP imp = imp_implementationWithBlock(^(Class){ return className; });
494 class_addMethod(metaClass, @selector(className), imp, "@@:");
497 // implement the shared schema method
498 void RLMReplaceSharedSchemaMethod(Class accessorClass, RLMObjectSchema *schema) {
499 Class metaClass = object_getClass(accessorClass);
500 IMP imp = imp_implementationWithBlock(^(Class cls) {
501 if (cls == accessorClass) {
505 // If we aren't being called directly on the class this was overriden
506 // for, the class is either a subclass which we haven't initialized yet,
507 // or it's a runtime-generated class which should use the parent's
508 // schema. We check for the latter by checking if the immediate
509 // descendent of the desired class is a class generated by us (there
510 // may be further subclasses not generated by us for things like KVO).
511 Class parent = class_getSuperclass(cls);
512 while (parent != accessorClass) {
514 parent = class_getSuperclass(cls);
517 static const char accessorClassPrefix[] = "RLM:";
518 if (!strncmp(class_getName(cls), accessorClassPrefix, sizeof(accessorClassPrefix) - 1)) {
522 return [RLMSchema sharedSchemaForClass:cls];
524 class_addMethod(metaClass, @selector(sharedSchema), imp, "@@:");
527 void RLMDynamicValidatedSet(RLMObjectBase *obj, NSString *propName, id val) {
528 RLMObjectSchema *schema = obj->_objectSchema;
529 RLMProperty *prop = schema[propName];
531 @throw RLMException(@"Invalid property name '%@' for class '%@'.",
532 propName, obj->_objectSchema.className);
534 if (prop.isPrimary) {
535 @throw RLMException(@"Primary key can't be changed to '%@' after an object is inserted.", val);
537 RLMValidateValueForProperty(val, schema, prop, true);
538 RLMDynamicSet(obj, prop, RLMCoerceToNil(val));
541 // Precondition: the property is not a primary key
542 void RLMDynamicSet(__unsafe_unretained RLMObjectBase *const obj,
543 __unsafe_unretained RLMProperty *const prop,
544 __unsafe_unretained id const val) {
545 REALM_ASSERT_DEBUG(!prop.isPrimary);
546 realm::Object o(obj->_info->realm->_realm, *obj->_info->objectSchema, obj->_row);
547 RLMAccessorContext c(obj);
549 o.set_property_value(c, prop.name.UTF8String, val ?: NSNull.null, false);
553 id RLMDynamicGet(__unsafe_unretained RLMObjectBase *const obj, __unsafe_unretained RLMProperty *const prop) {
554 realm::Object o(obj->_realm->_realm, *obj->_info->objectSchema, obj->_row);
555 RLMAccessorContext c(obj);
556 c.currentProperty = prop;
557 return RLMCoerceToNil(o.get_property_value<id>(c, prop.name.UTF8String));
560 id RLMDynamicGetByName(__unsafe_unretained RLMObjectBase *const obj,
561 __unsafe_unretained NSString *const propName, bool asList) {
562 RLMProperty *prop = obj->_objectSchema[propName];
564 @throw RLMException(@"Invalid property name '%@' for class '%@'.",
565 propName, obj->_objectSchema.className);
567 if (asList && prop.array && prop.swiftIvar) {
568 RLMListBase *list = object_getIvar(obj, prop.swiftIvar);
569 if (prop.type != RLMPropertyTypeLinkingObjects && !list._rlmArray) {
570 list._rlmArray = RLMDynamicGet(obj, prop);
575 return RLMDynamicGet(obj, prop);
578 RLMAccessorContext::RLMAccessorContext(RLMAccessorContext& parent, realm::Property const& property)
579 : _realm(parent._realm)
580 , _info(property.type == realm::PropertyType::Object ? parent._info.linkTargetType(property) : parent._info)
581 , _promote_existing(parent._promote_existing)
585 RLMAccessorContext::RLMAccessorContext(RLMRealm *realm, RLMClassInfo& info, bool promote)
586 : _realm(realm), _info(info), _promote_existing(promote)
590 RLMAccessorContext::RLMAccessorContext(__unsafe_unretained RLMObjectBase *const parent,
591 const realm::Property *prop)
592 : _realm(parent->_realm)
593 , _info(prop && prop->type == realm::PropertyType::Object ? parent->_info->linkTargetType(*prop)
595 , _parentObject(parent)
599 id RLMAccessorContext::defaultValue(__unsafe_unretained NSString *const key) {
600 if (!_defaultValues) {
601 _defaultValues = RLMDefaultValuesForObjectSchema(_info.rlmObjectSchema);
603 return _defaultValues[key];
606 id RLMAccessorContext::propertyValue(__unsafe_unretained id const obj, size_t propIndex,
607 __unsafe_unretained RLMProperty *const prop) {
608 // Property value from an NSArray
609 if ([obj respondsToSelector:@selector(objectAtIndex:)]) {
610 return propIndex < [obj count] ? [obj objectAtIndex:propIndex] : nil;
613 // Property value from an NSDictionary
614 if ([obj respondsToSelector:@selector(objectForKey:)]) {
615 return [obj objectForKey:prop.name];
618 // Property value from an instance of this object type
620 if ([obj isKindOfClass:_info.rlmObjectSchema.objectClass] && prop.swiftIvar) {
622 return static_cast<RLMListBase *>(object_getIvar(obj, prop.swiftIvar))._rlmArray;
625 value = static_cast<RLMOptionalBase *>(object_getIvar(obj, prop.swiftIvar)).underlyingValue;
629 // Property value from some object that's KVC-compatible
630 value = RLMValidatedValueForProperty(obj, [obj respondsToSelector:prop.getterSel] ? prop.getterName : prop.name,
631 _info.rlmObjectSchema.className);
633 return value ?: NSNull.null;
636 id RLMAccessorContext::box(realm::List&& l) {
637 REALM_ASSERT(_parentObject);
638 REALM_ASSERT(currentProperty);
639 return [[RLMManagedArray alloc] initWithList:std::move(l) realm:_realm
640 parentInfo:_parentObject->_info
641 property:currentProperty];
644 id RLMAccessorContext::box(realm::Object&& o) {
645 REALM_ASSERT(currentProperty);
646 return RLMCreateObjectAccessor(_realm, _info.linkTargetType(currentProperty.index), o.row());
649 id RLMAccessorContext::box(realm::RowExpr r) {
650 return RLMCreateObjectAccessor(_realm, _info, r);
653 id RLMAccessorContext::box(realm::Results&& r) {
654 REALM_ASSERT(currentProperty);
655 return [RLMResults resultsWithObjectInfo:_realm->_info[currentProperty.objectClassName]
656 results:std::move(r)];
660 realm::Timestamp RLMAccessorContext::unbox(__unsafe_unretained id const value, bool, bool) {
661 id v = RLMCoerceToNil(value);
662 return RLMTimestampForNSDate(v);
666 bool RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) {
667 return [v boolValue];
670 double RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) {
671 return [v doubleValue];
674 float RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) {
675 return [v floatValue];
678 long long RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) {
679 return [v longLongValue];
682 realm::BinaryData RLMAccessorContext::unbox(id v, bool, bool) {
683 v = RLMCoerceToNil(v);
684 return RLMBinaryDataForNSData(v);
687 realm::StringData RLMAccessorContext::unbox(id v, bool, bool) {
688 v = RLMCoerceToNil(v);
689 return RLMStringDataWithNSString(v);
692 template<typename Fn>
693 static auto to_optional(__unsafe_unretained id const value, Fn&& fn) {
694 id v = RLMCoerceToNil(value);
695 return v && v != NSNull.null ? realm::util::make_optional(fn(v)) : realm::util::none;
699 realm::util::Optional<bool> RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) {
700 return to_optional(v, [&](__unsafe_unretained id v) { return (bool)[v boolValue]; });
703 realm::util::Optional<double> RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) {
704 return to_optional(v, [&](__unsafe_unretained id v) { return [v doubleValue]; });
707 realm::util::Optional<float> RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) {
708 return to_optional(v, [&](__unsafe_unretained id v) { return [v floatValue]; });
711 realm::util::Optional<int64_t> RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) {
712 return to_optional(v, [&](__unsafe_unretained id v) { return [v longLongValue]; });
716 realm::RowExpr RLMAccessorContext::unbox(__unsafe_unretained id const v, bool create, bool update) {
717 RLMObjectBase *link = RLMDynamicCast<RLMObjectBase>(v);
720 return realm::RowExpr();
721 return RLMCreateObjectInRealmWithValue(_realm, _info.rlmObjectSchema.className, v, update)->_row;
724 if (link.isInvalidated) {
726 @throw RLMException(@"Adding a deleted or invalidated object to a Realm is not permitted");
729 @throw RLMException(@"Object has been invalidated");
733 if (![link->_objectSchema.className isEqualToString:_info.rlmObjectSchema.className]) {
734 if (create && !_promote_existing)
735 return RLMCreateObjectInRealmWithValue(_realm, _info.rlmObjectSchema.className, link, update)->_row;
741 return realm::RowExpr();
742 if (!_promote_existing)
743 return RLMCreateObjectInRealmWithValue(_realm, _info.rlmObjectSchema.className, link, update)->_row;
744 RLMAddObjectToRealm(link, _realm, update);
746 else if (link->_realm != _realm) {
747 if (_promote_existing)
748 @throw RLMException(@"Object is already managed by another Realm. Use create instead to copy it into this Realm.");
749 return RLMCreateObjectInRealmWithValue(_realm, _info.rlmObjectSchema.className, v, update)->_row;
754 void RLMAccessorContext::will_change(realm::Row const& row, realm::Property const& prop) {
755 _observationInfo = RLMGetObservationInfo(nullptr, row.get_index(), _info);
756 if (_observationInfo) {
757 _kvoPropertyName = @(prop.name.c_str());
758 _observationInfo->willChange(_kvoPropertyName);
762 void RLMAccessorContext::did_change() {
763 if (_observationInfo) {
764 _observationInfo->didChange(_kvoPropertyName);
765 _kvoPropertyName = nil;
766 _observationInfo = nullptr;
770 RLMOptionalId RLMAccessorContext::value_for_property(__unsafe_unretained id const obj,
771 std::string const&, size_t propIndex) {
772 auto prop = _info.rlmObjectSchema.properties[propIndex];
773 id value = propertyValue(obj, propIndex, prop);
775 RLMValidateValueForProperty(value, _info.rlmObjectSchema, prop);
778 if (_promote_existing && [obj isKindOfClass:_info.rlmObjectSchema.objectClass] && !prop.swiftIvar) {
779 // set the ivars for object and array properties to nil as otherwise the
780 // accessors retain objects that are no longer accessible via the properties
781 // this is mainly an issue when the object graph being added has cycles,
782 // as it's not obvious that the user has to set the *ivars* to nil to
783 // avoid leaking memory
784 if (prop.type == RLMPropertyTypeObject) {
785 ((void(*)(id, SEL, id))objc_msgSend)(obj, prop.setterSel, nil);
789 return RLMOptionalId{value};
792 RLMOptionalId RLMAccessorContext::default_value_for_property(realm::ObjectSchema const&,
793 std::string const& prop)
795 return RLMOptionalId{defaultValue(@(prop.c_str()))};
798 bool RLMAccessorContext::is_same_list(realm::List const& list, __unsafe_unretained id const v) const noexcept {
799 return [v respondsToSelector:@selector(isBackedByList:)] && [v isBackedByList:list];