RLMAccessor.mm 30 KB

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