RLMAccessor.mm 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815
  1. ////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2014 Realm Inc.
  4. //
  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
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  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.
  16. //
  17. ////////////////////////////////////////////////////////////////////////////
  18. #import "RLMAccessor.hpp"
  19. #import "RLMArray_Private.hpp"
  20. #import "RLMListBase.h"
  21. #import "RLMObjectSchema_Private.hpp"
  22. #import "RLMObjectStore.h"
  23. #import "RLMObject_Private.hpp"
  24. #import "RLMObservation.hpp"
  25. #import "RLMProperty_Private.h"
  26. #import "RLMRealm_Private.hpp"
  27. #import "RLMResults_Private.hpp"
  28. #import "RLMSchema_Private.h"
  29. #import "RLMUtil.hpp"
  30. #import "results.hpp"
  31. #import "property.hpp"
  32. #import <objc/runtime.h>
  33. #import <objc/message.h>
  34. #import <realm/descriptor.hpp>
  35. #pragma mark - Helper functions
  36. namespace {
  37. template<typename T>
  38. T get(__unsafe_unretained RLMObjectBase *const obj, NSUInteger index) {
  39. RLMVerifyAttached(obj);
  40. return obj->_row.get<T>(obj->_info->objectSchema->persisted_properties[index].table_column);
  41. }
  42. template<typename T>
  43. id getBoxed(__unsafe_unretained RLMObjectBase *const obj, NSUInteger index) {
  44. RLMVerifyAttached(obj);
  45. auto& prop = obj->_info->objectSchema->persisted_properties[index];
  46. auto col = prop.table_column;
  47. if (obj->_row.is_null(col)) {
  48. return nil;
  49. }
  50. RLMAccessorContext ctx(obj, &prop);
  51. return ctx.box(obj->_row.get<T>(col));
  52. }
  53. template<typename T>
  54. void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, T val) {
  55. RLMVerifyInWriteTransaction(obj);
  56. obj->_row.set(colIndex, val);
  57. }
  58. template<typename Fn>
  59. auto translateError(Fn&& fn) {
  60. try {
  61. return fn();
  62. }
  63. catch (std::exception const& e) {
  64. @throw RLMException(e);
  65. }
  66. }
  67. void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
  68. __unsafe_unretained NSString *const val) {
  69. RLMVerifyInWriteTransaction(obj);
  70. translateError([&] {
  71. obj->_row.set(colIndex, RLMStringDataWithNSString(val));
  72. });
  73. }
  74. [[gnu::noinline]]
  75. void setNull(realm::Row& row, size_t col) {
  76. translateError([&] { row.set_null(col); });
  77. }
  78. void setValue(__unsafe_unretained RLMObjectBase *const obj,
  79. NSUInteger colIndex, __unsafe_unretained NSDate *const date) {
  80. RLMVerifyInWriteTransaction(obj);
  81. if (date) {
  82. obj->_row.set(colIndex, RLMTimestampForNSDate(date));
  83. }
  84. else {
  85. setNull(obj->_row, colIndex);
  86. }
  87. }
  88. void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
  89. __unsafe_unretained NSData *const data) {
  90. RLMVerifyInWriteTransaction(obj);
  91. translateError([&] {
  92. obj->_row.set(colIndex, RLMBinaryDataForNSData(data));
  93. });
  94. }
  95. void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
  96. __unsafe_unretained RLMObjectBase *const val) {
  97. RLMVerifyInWriteTransaction(obj);
  98. if (!val) {
  99. obj->_row.nullify_link(colIndex);
  100. return;
  101. }
  102. RLMAddObjectToRealm(val, obj->_realm, RLMUpdatePolicyError);
  103. // make sure it is the correct type
  104. if (val->_row.get_table() != obj->_row.get_table()->get_link_target(colIndex)) {
  105. @throw RLMException(@"Can't set object of type '%@' to property of type '%@'",
  106. val->_objectSchema.className,
  107. obj->_info->propertyForTableColumn(colIndex).objectClassName);
  108. }
  109. obj->_row.set_link(colIndex, val->_row.get_index());
  110. }
  111. // array getter/setter
  112. RLMArray *getArray(__unsafe_unretained RLMObjectBase *const obj, NSUInteger propIndex) {
  113. RLMVerifyAttached(obj);
  114. auto prop = obj->_info->rlmObjectSchema.properties[propIndex];
  115. return [[RLMManagedArray alloc] initWithParent:obj property:prop];
  116. }
  117. void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
  118. __unsafe_unretained id<NSFastEnumeration> const value) {
  119. RLMVerifyInWriteTransaction(obj);
  120. auto prop = obj->_info->propertyForTableColumn(colIndex);
  121. RLMValidateValueForProperty(value, obj->_info->rlmObjectSchema, prop, true);
  122. realm::List list(obj->_realm->_realm, *obj->_row.get_table(), colIndex, obj->_row.get_index());
  123. RLMClassInfo *info = obj->_info;
  124. if (list.get_type() == realm::PropertyType::Object) {
  125. info = &obj->_info->linkTargetType(prop.index);
  126. }
  127. RLMAccessorContext ctx(obj->_realm, *info);
  128. translateError([&] { list.assign(ctx, value, false); });
  129. }
  130. void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
  131. __unsafe_unretained NSNumber<RLMInt> *const intObject) {
  132. RLMVerifyInWriteTransaction(obj);
  133. if (intObject) {
  134. obj->_row.set(colIndex, intObject.longLongValue);
  135. }
  136. else {
  137. setNull(obj->_row, colIndex);
  138. }
  139. }
  140. void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
  141. __unsafe_unretained NSNumber<RLMFloat> *const floatObject) {
  142. RLMVerifyInWriteTransaction(obj);
  143. if (floatObject) {
  144. obj->_row.set(colIndex, floatObject.floatValue);
  145. }
  146. else {
  147. setNull(obj->_row, colIndex);
  148. }
  149. }
  150. void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
  151. __unsafe_unretained NSNumber<RLMDouble> *const doubleObject) {
  152. RLMVerifyInWriteTransaction(obj);
  153. if (doubleObject) {
  154. obj->_row.set(colIndex, doubleObject.doubleValue);
  155. }
  156. else {
  157. setNull(obj->_row, colIndex);
  158. }
  159. }
  160. void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
  161. __unsafe_unretained NSNumber<RLMBool> *const boolObject) {
  162. RLMVerifyInWriteTransaction(obj);
  163. if (boolObject) {
  164. obj->_row.set(colIndex, (bool)boolObject.boolValue);
  165. }
  166. else {
  167. setNull(obj->_row, colIndex);
  168. }
  169. }
  170. RLMLinkingObjects *getLinkingObjects(__unsafe_unretained RLMObjectBase *const obj,
  171. __unsafe_unretained RLMProperty *const property) {
  172. RLMVerifyAttached(obj);
  173. auto& objectInfo = obj->_realm->_info[property.objectClassName];
  174. auto& linkOrigin = obj->_info->objectSchema->computed_properties[property.index].link_origin_property_name;
  175. auto linkingProperty = objectInfo.objectSchema->property_for_name(linkOrigin);
  176. auto backlinkView = obj->_row.get_table()->get_backlink_view(obj->_row.get_index(),
  177. objectInfo.table(),
  178. linkingProperty->table_column);
  179. realm::Results results(obj->_realm->_realm, std::move(backlinkView));
  180. return [RLMLinkingObjects resultsWithObjectInfo:objectInfo results:std::move(results)];
  181. }
  182. // any getter/setter
  183. template<typename Type, typename StorageType=Type>
  184. id makeGetter(NSUInteger index) {
  185. return ^(__unsafe_unretained RLMObjectBase *const obj) {
  186. return static_cast<Type>(get<StorageType>(obj, index));
  187. };
  188. }
  189. template<typename Type>
  190. id makeBoxedGetter(NSUInteger index) {
  191. return ^(__unsafe_unretained RLMObjectBase *const obj) {
  192. return getBoxed<Type>(obj, index);
  193. };
  194. }
  195. template<typename Type>
  196. id makeOptionalGetter(NSUInteger index) {
  197. return ^(__unsafe_unretained RLMObjectBase *const obj) {
  198. return getBoxed<realm::util::Optional<Type>>(obj, index);
  199. };
  200. }
  201. template<typename Type>
  202. id makeNumberGetter(NSUInteger index, bool boxed, bool optional) {
  203. if (optional) {
  204. return makeOptionalGetter<Type>(index);
  205. }
  206. if (boxed) {
  207. return makeBoxedGetter<Type>(index);
  208. }
  209. return makeGetter<Type>(index);
  210. }
  211. // dynamic getter with column closure
  212. id managedGetter(RLMProperty *prop, const char *type) {
  213. NSUInteger index = prop.index;
  214. if (prop.array && prop.type != RLMPropertyTypeLinkingObjects) {
  215. return ^id(__unsafe_unretained RLMObjectBase *const obj) {
  216. return getArray(obj, index);
  217. };
  218. }
  219. bool boxed = *type == '@';
  220. switch (prop.type) {
  221. case RLMPropertyTypeInt:
  222. if (prop.optional || boxed) {
  223. return makeNumberGetter<long long>(index, boxed, prop.optional);
  224. }
  225. switch (*type) {
  226. case 'c': return makeGetter<char, int64_t>(index);
  227. case 's': return makeGetter<short, int64_t>(index);
  228. case 'i': return makeGetter<int, int64_t>(index);
  229. case 'l': return makeGetter<long, int64_t>(index);
  230. case 'q': return makeGetter<long long, int64_t>(index);
  231. default:
  232. @throw RLMException(@"Unexpected property type for Objective-C type code");
  233. }
  234. case RLMPropertyTypeFloat:
  235. return makeNumberGetter<float>(index, boxed, prop.optional);
  236. case RLMPropertyTypeDouble:
  237. return makeNumberGetter<double>(index, boxed, prop.optional);
  238. case RLMPropertyTypeBool:
  239. return makeNumberGetter<bool>(index, boxed, prop.optional);
  240. case RLMPropertyTypeString:
  241. return makeBoxedGetter<realm::StringData>(index);
  242. case RLMPropertyTypeDate:
  243. return makeBoxedGetter<realm::Timestamp>(index);
  244. case RLMPropertyTypeData:
  245. return makeBoxedGetter<realm::BinaryData>(index);
  246. case RLMPropertyTypeObject:
  247. return makeBoxedGetter<realm::RowExpr>(index);
  248. case RLMPropertyTypeAny:
  249. @throw RLMException(@"Cannot create accessor class for schema with Mixed properties");
  250. case RLMPropertyTypeLinkingObjects:
  251. return ^(__unsafe_unretained RLMObjectBase *const obj) {
  252. return getLinkingObjects(obj, prop);
  253. };
  254. }
  255. }
  256. template<typename ArgType, typename StorageType=ArgType>
  257. id makeSetter(__unsafe_unretained RLMProperty *const prop) {
  258. NSUInteger index = prop.index;
  259. NSString *name = prop.name;
  260. if (prop.isPrimary) {
  261. return ^(__unused RLMObjectBase *obj, __unused ArgType val) {
  262. @throw RLMException(@"Primary key can't be changed after an object is inserted.");
  263. };
  264. }
  265. return ^(__unsafe_unretained RLMObjectBase *const obj, ArgType val) {
  266. auto set = [&] {
  267. setValue(obj, obj->_info->objectSchema->persisted_properties[index].table_column,
  268. static_cast<StorageType>(val));
  269. };
  270. if (RLMObservationInfo *info = RLMGetObservationInfo(obj->_observationInfo,
  271. obj->_row.get_index(), *obj->_info)) {
  272. info->willChange(name);
  273. set();
  274. info->didChange(name);
  275. }
  276. else {
  277. set();
  278. }
  279. };
  280. }
  281. // dynamic setter with column closure
  282. id managedSetter(RLMProperty *prop, const char *type) {
  283. if (prop.array && prop.type != RLMPropertyTypeLinkingObjects) {
  284. return makeSetter<id<NSFastEnumeration>>(prop);
  285. }
  286. bool boxed = prop.optional || *type == '@';
  287. switch (prop.type) {
  288. case RLMPropertyTypeInt:
  289. if (boxed) {
  290. return makeSetter<NSNumber<RLMInt> *>(prop);
  291. }
  292. switch (*type) {
  293. case 'c': return makeSetter<char, long long>(prop);
  294. case 's': return makeSetter<short, long long>(prop);
  295. case 'i': return makeSetter<int, long long>(prop);
  296. case 'l': return makeSetter<long, long long>(prop);
  297. case 'q': return makeSetter<long long>(prop);
  298. default:
  299. @throw RLMException(@"Unexpected property type for Objective-C type code");
  300. }
  301. case RLMPropertyTypeFloat:
  302. return boxed ? makeSetter<NSNumber<RLMFloat> *>(prop) : makeSetter<float>(prop);
  303. case RLMPropertyTypeDouble:
  304. return boxed ? makeSetter<NSNumber<RLMDouble> *>(prop) : makeSetter<double>(prop);
  305. case RLMPropertyTypeBool:
  306. return boxed ? makeSetter<NSNumber<RLMBool> *>(prop) : makeSetter<BOOL, bool>(prop);
  307. case RLMPropertyTypeString: return makeSetter<NSString *>(prop);
  308. case RLMPropertyTypeDate: return makeSetter<NSDate *>(prop);
  309. case RLMPropertyTypeData: return makeSetter<NSData *>(prop);
  310. case RLMPropertyTypeAny: return nil;
  311. case RLMPropertyTypeLinkingObjects: return nil;
  312. case RLMPropertyTypeObject: return makeSetter<RLMObjectBase *>(prop);
  313. }
  314. }
  315. // call getter for superclass for property at colIndex
  316. id superGet(RLMObjectBase *obj, NSString *propName) {
  317. typedef id (*getter_type)(RLMObjectBase *, SEL);
  318. RLMProperty *prop = obj->_objectSchema[propName];
  319. Class superClass = class_getSuperclass(obj.class);
  320. getter_type superGetter = (getter_type)[superClass instanceMethodForSelector:prop.getterSel];
  321. return superGetter(obj, prop.getterSel);
  322. }
  323. // call setter for superclass for property at colIndex
  324. void superSet(RLMObjectBase *obj, NSString *propName, id val) {
  325. typedef void (*setter_type)(RLMObjectBase *, SEL, RLMArray *ar);
  326. RLMProperty *prop = obj->_objectSchema[propName];
  327. Class superClass = class_getSuperclass(obj.class);
  328. setter_type superSetter = (setter_type)[superClass instanceMethodForSelector:prop.setterSel];
  329. superSetter(obj, prop.setterSel, val);
  330. }
  331. // getter/setter for unmanaged object
  332. id unmanagedGetter(RLMProperty *prop, const char *) {
  333. // only override getters for RLMArray and linking objects properties
  334. if (prop.type == RLMPropertyTypeLinkingObjects) {
  335. return ^(RLMObjectBase *) { return [RLMResults emptyDetachedResults]; };
  336. }
  337. if (prop.array) {
  338. NSString *propName = prop.name;
  339. if (prop.type == RLMPropertyTypeObject) {
  340. NSString *objectClassName = prop.objectClassName;
  341. return ^(RLMObjectBase *obj) {
  342. id val = superGet(obj, propName);
  343. if (!val) {
  344. val = [[RLMArray alloc] initWithObjectClassName:objectClassName];
  345. superSet(obj, propName, val);
  346. }
  347. return val;
  348. };
  349. }
  350. auto type = prop.type;
  351. auto optional = prop.optional;
  352. return ^(RLMObjectBase *obj) {
  353. id val = superGet(obj, propName);
  354. if (!val) {
  355. val = [[RLMArray alloc] initWithObjectType:type optional:optional];
  356. superSet(obj, propName, val);
  357. }
  358. return val;
  359. };
  360. }
  361. return nil;
  362. }
  363. id unmanagedSetter(RLMProperty *prop, const char *) {
  364. // Only RLMArray needs special handling for the unmanaged setter
  365. if (!prop.array) {
  366. return nil;
  367. }
  368. NSString *propName = prop.name;
  369. return ^(RLMObjectBase *obj, id<NSFastEnumeration> values) {
  370. auto prop = obj->_objectSchema[propName];
  371. RLMValidateValueForProperty(values, obj->_objectSchema, prop, true);
  372. // make copy when setting (as is the case for all other variants)
  373. RLMArray *ar;
  374. if (prop.type == RLMPropertyTypeObject)
  375. ar = [[RLMArray alloc] initWithObjectClassName:prop.objectClassName];
  376. else
  377. ar = [[RLMArray alloc] initWithObjectType:prop.type optional:prop.optional];
  378. [ar addObjects:values];
  379. superSet(obj, propName, ar);
  380. };
  381. }
  382. void addMethod(Class cls, __unsafe_unretained RLMProperty *const prop,
  383. id (*getter)(RLMProperty *, const char *),
  384. id (*setter)(RLMProperty *, const char *)) {
  385. SEL sel = prop.getterSel;
  386. auto getterMethod = class_getInstanceMethod(cls, sel);
  387. if (!getterMethod) {
  388. return;
  389. }
  390. const char *getterType = method_getTypeEncoding(getterMethod);
  391. if (id block = getter(prop, getterType)) {
  392. class_addMethod(cls, sel, imp_implementationWithBlock(block), getterType);
  393. }
  394. if (!(sel = prop.setterSel)) {
  395. return;
  396. }
  397. auto setterMethod = class_getInstanceMethod(cls, sel);
  398. if (!setterMethod) {
  399. return;
  400. }
  401. if (id block = setter(prop, getterType)) { // note: deliberately getterType as it's easier to grab the relevant type from
  402. class_addMethod(cls, sel, imp_implementationWithBlock(block), method_getTypeEncoding(setterMethod));
  403. }
  404. }
  405. Class createAccessorClass(Class objectClass,
  406. RLMObjectSchema *schema,
  407. const char *accessorClassName,
  408. id (*getterGetter)(RLMProperty *, const char *),
  409. id (*setterGetter)(RLMProperty *, const char *)) {
  410. REALM_ASSERT_DEBUG(RLMIsObjectOrSubclass(objectClass));
  411. // create and register proxy class which derives from object class
  412. Class accClass = objc_allocateClassPair(objectClass, accessorClassName, 0);
  413. if (!accClass) {
  414. // Class with that name already exists, so just return the pre-existing one
  415. // This should only happen for our standalone "accessors"
  416. return objc_lookUpClass(accessorClassName);
  417. }
  418. // override getters/setters for each propery
  419. for (RLMProperty *prop in schema.properties) {
  420. addMethod(accClass, prop, getterGetter, setterGetter);
  421. }
  422. for (RLMProperty *prop in schema.computedProperties) {
  423. addMethod(accClass, prop, getterGetter, setterGetter);
  424. }
  425. objc_registerClassPair(accClass);
  426. return accClass;
  427. }
  428. } // anonymous namespace
  429. #pragma mark - Public Interface
  430. Class RLMManagedAccessorClassForObjectClass(Class objectClass, RLMObjectSchema *schema, const char *name) {
  431. return createAccessorClass(objectClass, schema, name, managedGetter, managedSetter);
  432. }
  433. Class RLMUnmanagedAccessorClassForObjectClass(Class objectClass, RLMObjectSchema *schema) {
  434. return createAccessorClass(objectClass, schema,
  435. [@"RLM:Unmanaged " stringByAppendingString:schema.className].UTF8String,
  436. unmanagedGetter, unmanagedSetter);
  437. }
  438. // implement the class method className on accessors to return the className of the
  439. // base object
  440. void RLMReplaceClassNameMethod(Class accessorClass, NSString *className) {
  441. Class metaClass = object_getClass(accessorClass);
  442. IMP imp = imp_implementationWithBlock(^(Class){ return className; });
  443. class_addMethod(metaClass, @selector(className), imp, "@@:");
  444. }
  445. // implement the shared schema method
  446. void RLMReplaceSharedSchemaMethod(Class accessorClass, RLMObjectSchema *schema) {
  447. Class metaClass = object_getClass(accessorClass);
  448. IMP imp = imp_implementationWithBlock(^(Class cls) {
  449. if (cls == accessorClass) {
  450. return schema;
  451. }
  452. // If we aren't being called directly on the class this was overriden
  453. // for, the class is either a subclass which we haven't initialized yet,
  454. // or it's a runtime-generated class which should use the parent's
  455. // schema. We check for the latter by checking if the immediate
  456. // descendent of the desired class is a class generated by us (there
  457. // may be further subclasses not generated by us for things like KVO).
  458. Class parent = class_getSuperclass(cls);
  459. while (parent != accessorClass) {
  460. cls = parent;
  461. parent = class_getSuperclass(cls);
  462. }
  463. static const char accessorClassPrefix[] = "RLM:";
  464. if (!strncmp(class_getName(cls), accessorClassPrefix, sizeof(accessorClassPrefix) - 1)) {
  465. return schema;
  466. }
  467. return [RLMSchema sharedSchemaForClass:cls];
  468. });
  469. class_addMethod(metaClass, @selector(sharedSchema), imp, "@@:");
  470. }
  471. void RLMDynamicValidatedSet(RLMObjectBase *obj, NSString *propName, id val) {
  472. RLMVerifyAttached(obj);
  473. RLMObjectSchema *schema = obj->_objectSchema;
  474. RLMProperty *prop = schema[propName];
  475. if (!prop) {
  476. @throw RLMException(@"Invalid property name '%@' for class '%@'.",
  477. propName, obj->_objectSchema.className);
  478. }
  479. if (prop.isPrimary) {
  480. @throw RLMException(@"Primary key can't be changed to '%@' after an object is inserted.", val);
  481. }
  482. RLMValidateValueForProperty(val, schema, prop, true);
  483. RLMDynamicSet(obj, prop, RLMCoerceToNil(val));
  484. }
  485. // Precondition: the property is not a primary key
  486. void RLMDynamicSet(__unsafe_unretained RLMObjectBase *const obj,
  487. __unsafe_unretained RLMProperty *const prop,
  488. __unsafe_unretained id const val) {
  489. REALM_ASSERT_DEBUG(!prop.isPrimary);
  490. realm::Object o(obj->_info->realm->_realm, *obj->_info->objectSchema, obj->_row);
  491. RLMAccessorContext c(obj);
  492. translateError([&] {
  493. o.set_property_value(c, prop.columnName.UTF8String, val ?: NSNull.null, false);
  494. });
  495. }
  496. id RLMDynamicGet(__unsafe_unretained RLMObjectBase *const obj, __unsafe_unretained RLMProperty *const prop) {
  497. realm::Object o(obj->_realm->_realm, *obj->_info->objectSchema, obj->_row);
  498. RLMAccessorContext c(obj);
  499. c.currentProperty = prop;
  500. return translateError([&] {
  501. return RLMCoerceToNil(o.get_property_value<id>(c, prop.columnName.UTF8String));
  502. });
  503. }
  504. id RLMDynamicGetByName(__unsafe_unretained RLMObjectBase *const obj,
  505. __unsafe_unretained NSString *const propName, bool asList) {
  506. RLMProperty *prop = obj->_objectSchema[propName];
  507. if (!prop) {
  508. @throw RLMException(@"Invalid property name '%@' for class '%@'.",
  509. propName, obj->_objectSchema.className);
  510. }
  511. if (asList && prop.array && prop.swiftIvar) {
  512. RLMListBase *list = object_getIvar(obj, prop.swiftIvar);
  513. if (prop.type != RLMPropertyTypeLinkingObjects && !list._rlmArray) {
  514. list._rlmArray = RLMDynamicGet(obj, prop);
  515. }
  516. return list;
  517. }
  518. return RLMDynamicGet(obj, prop);
  519. }
  520. RLMAccessorContext::RLMAccessorContext(RLMAccessorContext& parent, realm::Property const& property)
  521. : _realm(parent._realm)
  522. , _info(property.type == realm::PropertyType::Object ? parent._info.linkTargetType(property) : parent._info)
  523. , _promote_existing(parent._promote_existing)
  524. {
  525. }
  526. RLMAccessorContext::RLMAccessorContext(RLMRealm *realm, RLMClassInfo& info, bool promote)
  527. : _realm(realm), _info(info), _promote_existing(promote)
  528. {
  529. }
  530. RLMAccessorContext::RLMAccessorContext(__unsafe_unretained RLMObjectBase *const parent,
  531. const realm::Property *prop)
  532. : _realm(parent->_realm)
  533. , _info(prop && prop->type == realm::PropertyType::Object ? parent->_info->linkTargetType(*prop)
  534. : *parent->_info)
  535. , _parentObject(parent)
  536. {
  537. }
  538. id RLMAccessorContext::defaultValue(__unsafe_unretained NSString *const key) {
  539. if (!_defaultValues) {
  540. _defaultValues = RLMDefaultValuesForObjectSchema(_info.rlmObjectSchema);
  541. }
  542. return _defaultValues[key];
  543. }
  544. id RLMAccessorContext::propertyValue(__unsafe_unretained id const obj, size_t propIndex,
  545. __unsafe_unretained RLMProperty *const prop) {
  546. // Property value from an NSArray
  547. if ([obj respondsToSelector:@selector(objectAtIndex:)]) {
  548. return propIndex < [obj count] ? [obj objectAtIndex:propIndex] : nil;
  549. }
  550. // Property value from an NSDictionary
  551. if ([obj respondsToSelector:@selector(objectForKey:)]) {
  552. return [obj objectForKey:prop.name];
  553. }
  554. // Property value from an instance of this object type
  555. id value;
  556. if ([obj isKindOfClass:_info.rlmObjectSchema.objectClass] && prop.swiftIvar) {
  557. if (prop.array) {
  558. return static_cast<RLMListBase *>(object_getIvar(obj, prop.swiftIvar))._rlmArray;
  559. }
  560. else if (prop.swiftIvar == RLMDummySwiftIvar) {
  561. // FIXME: An invalid property which we're pretending is nil until 4.0
  562. // https://github.com/realm/realm-cocoa/issues/5784
  563. return NSNull.null;
  564. }
  565. else { // optional
  566. value = RLMGetOptional(static_cast<RLMOptionalBase *>(object_getIvar(obj, prop.swiftIvar)));
  567. }
  568. }
  569. else {
  570. // Property value from some object that's KVC-compatible
  571. value = RLMValidatedValueForProperty(obj, [obj respondsToSelector:prop.getterSel] ? prop.getterName : prop.name,
  572. _info.rlmObjectSchema.className);
  573. }
  574. return value ?: NSNull.null;
  575. }
  576. id RLMAccessorContext::box(realm::List&& l) {
  577. REALM_ASSERT(_parentObject);
  578. REALM_ASSERT(currentProperty);
  579. return [[RLMManagedArray alloc] initWithList:std::move(l) realm:_realm
  580. parentInfo:_parentObject->_info
  581. property:currentProperty];
  582. }
  583. id RLMAccessorContext::box(realm::Object&& o) {
  584. REALM_ASSERT(currentProperty);
  585. return RLMCreateObjectAccessor(_realm, _info.linkTargetType(currentProperty.index), o.row());
  586. }
  587. id RLMAccessorContext::box(realm::RowExpr r) {
  588. return RLMCreateObjectAccessor(_realm, _info, r);
  589. }
  590. id RLMAccessorContext::box(realm::Results&& r) {
  591. REALM_ASSERT(currentProperty);
  592. return [RLMResults resultsWithObjectInfo:_realm->_info[currentProperty.objectClassName]
  593. results:std::move(r)];
  594. }
  595. template<>
  596. realm::Timestamp RLMAccessorContext::unbox(__unsafe_unretained id const value, bool, bool, bool, size_t) {
  597. id v = RLMCoerceToNil(value);
  598. return RLMTimestampForNSDate(v);
  599. }
  600. template<>
  601. bool RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool, bool, size_t) {
  602. return [v boolValue];
  603. }
  604. template<>
  605. double RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool, bool, size_t) {
  606. return [v doubleValue];
  607. }
  608. template<>
  609. float RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool, bool, size_t) {
  610. return [v floatValue];
  611. }
  612. template<>
  613. long long RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool, bool, size_t) {
  614. return [v longLongValue];
  615. }
  616. template<>
  617. realm::BinaryData RLMAccessorContext::unbox(id v, bool, bool, bool, size_t) {
  618. v = RLMCoerceToNil(v);
  619. return RLMBinaryDataForNSData(v);
  620. }
  621. template<>
  622. realm::StringData RLMAccessorContext::unbox(id v, bool, bool, bool, size_t) {
  623. v = RLMCoerceToNil(v);
  624. return RLMStringDataWithNSString(v);
  625. }
  626. template<typename Fn>
  627. static auto to_optional(__unsafe_unretained id const value, Fn&& fn) {
  628. id v = RLMCoerceToNil(value);
  629. return v && v != NSNull.null ? realm::util::make_optional(fn(v)) : realm::util::none;
  630. }
  631. template<>
  632. realm::util::Optional<bool> RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool, bool, size_t) {
  633. return to_optional(v, [&](__unsafe_unretained id v) { return (bool)[v boolValue]; });
  634. }
  635. template<>
  636. realm::util::Optional<double> RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool, bool, size_t) {
  637. return to_optional(v, [&](__unsafe_unretained id v) { return [v doubleValue]; });
  638. }
  639. template<>
  640. realm::util::Optional<float> RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool, bool, size_t) {
  641. return to_optional(v, [&](__unsafe_unretained id v) { return [v floatValue]; });
  642. }
  643. template<>
  644. realm::util::Optional<int64_t> RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool, bool, size_t) {
  645. return to_optional(v, [&](__unsafe_unretained id v) { return [v longLongValue]; });
  646. }
  647. template<>
  648. realm::RowExpr RLMAccessorContext::unbox(__unsafe_unretained id const v, bool create, bool update, bool diff, size_t) {
  649. RLMUpdatePolicy policy = !update ? RLMUpdatePolicyError
  650. : !diff ? RLMUpdatePolicyUpdateAll
  651. : RLMUpdatePolicyUpdateChanged;
  652. RLMObjectBase *link = RLMDynamicCast<RLMObjectBase>(v);
  653. if (!link) {
  654. if (!create)
  655. return realm::RowExpr();
  656. return RLMCreateObjectInRealmWithValue(_realm, _info.rlmObjectSchema.className, v, policy)->_row;
  657. }
  658. if (link.isInvalidated) {
  659. if (create) {
  660. @throw RLMException(@"Adding a deleted or invalidated object to a Realm is not permitted");
  661. }
  662. else {
  663. @throw RLMException(@"Object has been invalidated");
  664. }
  665. }
  666. if (![link->_objectSchema.className isEqualToString:_info.rlmObjectSchema.className]) {
  667. if (create && !_promote_existing)
  668. return RLMCreateObjectInRealmWithValue(_realm, _info.rlmObjectSchema.className, link, policy)->_row;
  669. return link->_row;
  670. }
  671. if (!link->_realm) {
  672. if (!create)
  673. return realm::RowExpr();
  674. if (!_promote_existing)
  675. return RLMCreateObjectInRealmWithValue(_realm, _info.rlmObjectSchema.className, link, policy)->_row;
  676. RLMAddObjectToRealm(link, _realm, policy);
  677. }
  678. else if (link->_realm != _realm) {
  679. if (_promote_existing)
  680. @throw RLMException(@"Object is already managed by another Realm. Use create instead to copy it into this Realm.");
  681. return RLMCreateObjectInRealmWithValue(_realm, _info.rlmObjectSchema.className, v, policy)->_row;
  682. }
  683. return link->_row;
  684. }
  685. void RLMAccessorContext::will_change(realm::Row const& row, realm::Property const& prop) {
  686. _observationInfo = RLMGetObservationInfo(nullptr, row.get_index(), _info);
  687. if (_observationInfo) {
  688. _kvoPropertyName = _info.propertyForTableColumn(prop.table_column).name;
  689. _observationInfo->willChange(_kvoPropertyName);
  690. }
  691. }
  692. void RLMAccessorContext::did_change() {
  693. if (_observationInfo) {
  694. _observationInfo->didChange(_kvoPropertyName);
  695. _kvoPropertyName = nil;
  696. _observationInfo = nullptr;
  697. }
  698. }
  699. RLMOptionalId RLMAccessorContext::value_for_property(__unsafe_unretained id const obj,
  700. realm::Property const&, size_t propIndex) {
  701. auto prop = _info.rlmObjectSchema.properties[propIndex];
  702. id value = propertyValue(obj, propIndex, prop);
  703. if (value) {
  704. RLMValidateValueForProperty(value, _info.rlmObjectSchema, prop);
  705. }
  706. if (_promote_existing && [obj isKindOfClass:_info.rlmObjectSchema.objectClass] && !prop.swiftIvar) {
  707. // set the ivars for object and array properties to nil as otherwise the
  708. // accessors retain objects that are no longer accessible via the properties
  709. // this is mainly an issue when the object graph being added has cycles,
  710. // as it's not obvious that the user has to set the *ivars* to nil to
  711. // avoid leaking memory
  712. if (prop.type == RLMPropertyTypeObject) {
  713. ((void(*)(id, SEL, id))objc_msgSend)(obj, prop.setterSel, nil);
  714. }
  715. }
  716. return RLMOptionalId{value};
  717. }
  718. RLMOptionalId RLMAccessorContext::default_value_for_property(realm::ObjectSchema const&,
  719. realm::Property const& prop)
  720. {
  721. return RLMOptionalId{defaultValue(@(prop.name.c_str()))};
  722. }
  723. bool RLMAccessorContext::is_same_list(realm::List const& list, __unsafe_unretained id const v) const noexcept {
  724. return [v respondsToSelector:@selector(isBackedByList:)] && [v isBackedByList:list];
  725. }