RLMObjectBase.mm 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710
  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 "RLMObject_Private.hpp"
  19. #import "RLMAccessor.h"
  20. #import "RLMArray_Private.hpp"
  21. #import "RLMListBase.h"
  22. #import "RLMObjectSchema_Private.hpp"
  23. #import "RLMObjectStore.h"
  24. #import "RLMObservation.hpp"
  25. #import "RLMOptionalBase.h"
  26. #import "RLMProperty_Private.h"
  27. #import "RLMRealm_Private.hpp"
  28. #import "RLMSchema_Private.h"
  29. #import "RLMSwiftSupport.h"
  30. #import "RLMThreadSafeReference_Private.hpp"
  31. #import "RLMUtil.hpp"
  32. #import "object.hpp"
  33. #import "object_schema.hpp"
  34. #import "shared_realm.hpp"
  35. using namespace realm;
  36. const NSUInteger RLMDescriptionMaxDepth = 5;
  37. static bool isManagedAccessorClass(Class cls) {
  38. const char *className = class_getName(cls);
  39. const char accessorClassPrefix[] = "RLM:Managed";
  40. return strncmp(className, accessorClassPrefix, sizeof(accessorClassPrefix) - 1) == 0;
  41. }
  42. static bool maybeInitObjectSchemaForUnmanaged(RLMObjectBase *obj) {
  43. Class cls = obj.class;
  44. if (isManagedAccessorClass(cls)) {
  45. return false;
  46. }
  47. obj->_objectSchema = [cls sharedSchema];
  48. if (!obj->_objectSchema) {
  49. return false;
  50. }
  51. // set default values
  52. if (!obj->_objectSchema.isSwiftClass) {
  53. NSDictionary *dict = RLMDefaultValuesForObjectSchema(obj->_objectSchema);
  54. for (NSString *key in dict) {
  55. [obj setValue:dict[key] forKey:key];
  56. }
  57. }
  58. // set unmanaged accessor class
  59. object_setClass(obj, obj->_objectSchema.unmanagedClass);
  60. return true;
  61. }
  62. @interface RLMObjectBase () <RLMThreadConfined, RLMThreadConfined_Private>
  63. @end
  64. @implementation RLMObjectBase
  65. // unmanaged init
  66. - (instancetype)init {
  67. if ((self = [super init])) {
  68. maybeInitObjectSchemaForUnmanaged(self);
  69. }
  70. return self;
  71. }
  72. - (void)dealloc {
  73. // This can't be a unique_ptr because associated objects are removed
  74. // *after* c++ members are destroyed and dealloc is called, and we need it
  75. // to be in a validish state when that happens
  76. delete _observationInfo;
  77. _observationInfo = nullptr;
  78. }
  79. static id coerceToObjectType(id obj, Class cls, RLMSchema *schema) {
  80. if ([obj isKindOfClass:cls]) {
  81. return obj;
  82. }
  83. id value = [[cls alloc] init];
  84. RLMInitializeWithValue(value, obj, schema);
  85. return value;
  86. }
  87. static id validatedObjectForProperty(__unsafe_unretained id const obj,
  88. __unsafe_unretained RLMObjectSchema *const objectSchema,
  89. __unsafe_unretained RLMProperty *const prop,
  90. __unsafe_unretained RLMSchema *const schema) {
  91. RLMValidateValueForProperty(obj, objectSchema, prop);
  92. if (!obj || obj == NSNull.null) {
  93. return nil;
  94. }
  95. if (prop.type == RLMPropertyTypeObject) {
  96. Class objectClass = schema[prop.objectClassName].objectClass;
  97. if (prop.array) {
  98. NSMutableArray *ret = [[NSMutableArray alloc] init];
  99. for (id el in obj) {
  100. [ret addObject:coerceToObjectType(el, objectClass, schema)];
  101. }
  102. return ret;
  103. }
  104. return coerceToObjectType(obj, objectClass, schema);
  105. }
  106. return obj;
  107. }
  108. void RLMInitializeWithValue(RLMObjectBase *self, id value, RLMSchema *schema) {
  109. if (!value || value == NSNull.null) {
  110. @throw RLMException(@"Must provide a non-nil value.");
  111. }
  112. RLMObjectSchema *objectSchema = self->_objectSchema;
  113. if (!objectSchema) {
  114. // Will be nil if we're called during schema init, when we don't want
  115. // to actually populate the object anyway
  116. return;
  117. }
  118. NSArray *properties = objectSchema.properties;
  119. if (NSArray *array = RLMDynamicCast<NSArray>(value)) {
  120. if (array.count > properties.count) {
  121. @throw RLMException(@"Invalid array input: more values (%llu) than properties (%llu).",
  122. (unsigned long long)array.count, (unsigned long long)properties.count);
  123. }
  124. NSUInteger i = 0;
  125. for (id val in array) {
  126. RLMProperty *prop = properties[i++];
  127. [self setValue:validatedObjectForProperty(RLMCoerceToNil(val), objectSchema, prop, schema)
  128. forKey:prop.name];
  129. }
  130. }
  131. else {
  132. // assume our object is an NSDictionary or an object with kvc properties
  133. for (RLMProperty *prop in properties) {
  134. id obj = RLMValidatedValueForProperty(value, prop.name, objectSchema.className);
  135. // don't set unspecified properties
  136. if (!obj) {
  137. continue;
  138. }
  139. [self setValue:validatedObjectForProperty(RLMCoerceToNil(obj), objectSchema, prop, schema)
  140. forKey:prop.name];
  141. }
  142. }
  143. }
  144. id RLMCreateManagedAccessor(Class cls, RLMClassInfo *info) {
  145. RLMObjectBase *obj = [[cls alloc] init];
  146. obj->_info = info;
  147. obj->_realm = info->realm;
  148. obj->_objectSchema = info->rlmObjectSchema;
  149. return obj;
  150. }
  151. - (id)valueForKey:(NSString *)key {
  152. if (_observationInfo) {
  153. return _observationInfo->valueForKey(key);
  154. }
  155. return [super valueForKey:key];
  156. }
  157. // Generic Swift properties can't be dynamic, so KVO doesn't work for them by default
  158. - (id)valueForUndefinedKey:(NSString *)key {
  159. RLMProperty *prop = _objectSchema[key];
  160. if (Class accessor = prop.swiftAccessor) {
  161. return [accessor get:(char *)(__bridge void *)self + ivar_getOffset(prop.swiftIvar)];
  162. }
  163. if (Ivar ivar = prop.swiftIvar) {
  164. return RLMCoerceToNil(object_getIvar(self, ivar));
  165. }
  166. return [super valueForUndefinedKey:key];
  167. }
  168. - (void)setValue:(id)value forUndefinedKey:(NSString *)key {
  169. value = RLMCoerceToNil(value);
  170. RLMProperty *property = _objectSchema[key];
  171. if (Ivar ivar = property.swiftIvar) {
  172. if (property.array) {
  173. value = RLMAsFastEnumeration(value);
  174. RLMArray *array = [object_getIvar(self, ivar) _rlmArray];
  175. [array removeAllObjects];
  176. if (value) {
  177. [array addObjects:validatedObjectForProperty(value, _objectSchema, property,
  178. RLMSchema.partialPrivateSharedSchema)];
  179. }
  180. }
  181. else if (property.optional) {
  182. RLMSetOptional(object_getIvar(self, ivar), value);
  183. }
  184. return;
  185. }
  186. [super setValue:value forUndefinedKey:key];
  187. }
  188. // overridden at runtime per-class for performance
  189. + (NSString *)className {
  190. NSString *className = NSStringFromClass(self);
  191. if ([RLMSwiftSupport isSwiftClassName:className]) {
  192. className = [RLMSwiftSupport demangleClassName:className];
  193. }
  194. return className;
  195. }
  196. // overridden at runtime per-class for performance
  197. + (RLMObjectSchema *)sharedSchema {
  198. return [RLMSchema sharedSchemaForClass:self.class];
  199. }
  200. + (void)initializeLinkedObjectSchemas {
  201. for (RLMProperty *prop in self.sharedSchema.properties) {
  202. if (prop.type == RLMPropertyTypeObject && !RLMSchema.partialPrivateSharedSchema[prop.objectClassName]) {
  203. [[RLMSchema classForString:prop.objectClassName] initializeLinkedObjectSchemas];
  204. }
  205. }
  206. }
  207. + (nullable NSArray<RLMProperty *> *)_getPropertiesWithInstance:(__unused id)obj {
  208. return nil;
  209. }
  210. - (NSString *)description {
  211. if (self.isInvalidated) {
  212. return @"[invalid object]";
  213. }
  214. return [self descriptionWithMaxDepth:RLMDescriptionMaxDepth];
  215. }
  216. - (NSString *)descriptionWithMaxDepth:(NSUInteger)depth {
  217. if (depth == 0) {
  218. return @"<Maximum depth exceeded>";
  219. }
  220. NSString *baseClassName = _objectSchema.className;
  221. NSMutableString *mString = [NSMutableString stringWithFormat:@"%@ {\n", baseClassName];
  222. for (RLMProperty *property in _objectSchema.properties) {
  223. id object = [(id)self objectForKeyedSubscript:property.name];
  224. NSString *sub;
  225. if ([object respondsToSelector:@selector(descriptionWithMaxDepth:)]) {
  226. sub = [object descriptionWithMaxDepth:depth - 1];
  227. }
  228. else if (property.type == RLMPropertyTypeData) {
  229. static NSUInteger maxPrintedDataLength = 24;
  230. NSData *data = object;
  231. NSUInteger length = data.length;
  232. if (length > maxPrintedDataLength) {
  233. data = [NSData dataWithBytes:data.bytes length:maxPrintedDataLength];
  234. }
  235. NSString *dataDescription = [data description];
  236. sub = [NSString stringWithFormat:@"<%@ — %lu total bytes>", [dataDescription substringWithRange:NSMakeRange(1, dataDescription.length - 2)], (unsigned long)length];
  237. }
  238. else {
  239. sub = [object description];
  240. }
  241. [mString appendFormat:@"\t%@ = %@;\n", property.name, [sub stringByReplacingOccurrencesOfString:@"\n" withString:@"\n\t"]];
  242. }
  243. [mString appendString:@"}"];
  244. return [NSString stringWithString:mString];
  245. }
  246. - (RLMRealm *)realm {
  247. return _realm;
  248. }
  249. - (RLMObjectSchema *)objectSchema {
  250. return _objectSchema;
  251. }
  252. - (BOOL)isInvalidated {
  253. // if not unmanaged and our accessor has been detached, we have been deleted
  254. return self.class == _objectSchema.accessorClass && !_row.is_valid();
  255. }
  256. - (BOOL)isEqual:(id)object {
  257. if (RLMObjectBase *other = RLMDynamicCast<RLMObjectBase>(object)) {
  258. if (_objectSchema.primaryKeyProperty || _realm.isFrozen) {
  259. return RLMObjectBaseAreEqual(self, other);
  260. }
  261. }
  262. return [super isEqual:object];
  263. }
  264. - (NSUInteger)hash {
  265. if (_objectSchema.primaryKeyProperty) {
  266. // If we have a primary key property, that's an immutable value which we
  267. // can use as the identity of the object.
  268. id primaryProperty = [self valueForKey:_objectSchema.primaryKeyProperty.name];
  269. // modify the hash of our primary key value to avoid potential (although unlikely) collisions
  270. return [primaryProperty hash] ^ 1;
  271. }
  272. else if (_realm.isFrozen) {
  273. // The object key can never change for frozen objects, so that's usable
  274. // for objects without primary keys
  275. return _row.get_key().value;
  276. }
  277. else {
  278. // Non-frozen objects without primary keys don't have any immutable
  279. // concept of identity that we can hash so we have to fall back to
  280. // pointer equality
  281. return [super hash];
  282. }
  283. }
  284. + (BOOL)shouldIncludeInDefaultSchema {
  285. return RLMIsObjectSubclass(self);
  286. }
  287. + (NSString *)_realmObjectName {
  288. return nil;
  289. }
  290. + (NSDictionary *)_realmColumnNames {
  291. return nil;
  292. }
  293. + (bool)_realmIgnoreClass {
  294. return false;
  295. }
  296. - (id)mutableArrayValueForKey:(NSString *)key {
  297. id obj = [self valueForKey:key];
  298. if ([obj isKindOfClass:[RLMArray class]]) {
  299. return obj;
  300. }
  301. return [super mutableArrayValueForKey:key];
  302. }
  303. - (void)addObserver:(id)observer
  304. forKeyPath:(NSString *)keyPath
  305. options:(NSKeyValueObservingOptions)options
  306. context:(void *)context {
  307. if (!_observationInfo) {
  308. _observationInfo = new RLMObservationInfo(self);
  309. }
  310. _observationInfo->recordObserver(_row, _info, _objectSchema, keyPath);
  311. [super addObserver:observer forKeyPath:keyPath options:options context:context];
  312. }
  313. - (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath {
  314. [super removeObserver:observer forKeyPath:keyPath];
  315. if (_observationInfo)
  316. _observationInfo->removeObserver();
  317. }
  318. + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
  319. if (isManagedAccessorClass(self) && [class_getSuperclass(self.class) sharedSchema][key]) {
  320. return NO;
  321. }
  322. return [super automaticallyNotifiesObserversForKey:key];
  323. }
  324. #pragma mark - Thread Confined Protocol Conformance
  325. - (realm::ThreadSafeReference)makeThreadSafeReference {
  326. return Object(_realm->_realm, *_info->objectSchema, _row);
  327. }
  328. - (id)objectiveCMetadata {
  329. return nil;
  330. }
  331. + (instancetype)objectWithThreadSafeReference:(realm::ThreadSafeReference)reference
  332. metadata:(__unused id)metadata
  333. realm:(RLMRealm *)realm {
  334. Object object = reference.resolve<Object>(realm->_realm);
  335. if (!object.is_valid()) {
  336. return nil;
  337. }
  338. NSString *objectClassName = @(object.get_object_schema().name.c_str());
  339. return RLMCreateObjectAccessor(realm->_info[objectClassName], object.obj());
  340. }
  341. @end
  342. RLMRealm *RLMObjectBaseRealm(__unsafe_unretained RLMObjectBase *object) {
  343. return object ? object->_realm : nil;
  344. }
  345. RLMObjectSchema *RLMObjectBaseObjectSchema(__unsafe_unretained RLMObjectBase *object) {
  346. return object ? object->_objectSchema : nil;
  347. }
  348. id RLMObjectBaseObjectForKeyedSubscript(RLMObjectBase *object, NSString *key) {
  349. if (!object) {
  350. return nil;
  351. }
  352. if (object->_realm) {
  353. return RLMDynamicGetByName(object, key);
  354. }
  355. else {
  356. return [object valueForKey:key];
  357. }
  358. }
  359. void RLMObjectBaseSetObjectForKeyedSubscript(RLMObjectBase *object, NSString *key, id obj) {
  360. if (!object) {
  361. return;
  362. }
  363. if (object->_realm || object.class == object->_objectSchema.accessorClass) {
  364. RLMDynamicValidatedSet(object, key, obj);
  365. }
  366. else {
  367. [object setValue:obj forKey:key];
  368. }
  369. }
  370. BOOL RLMObjectBaseAreEqual(RLMObjectBase *o1, RLMObjectBase *o2) {
  371. // if not the correct types throw
  372. if ((o1 && ![o1 isKindOfClass:RLMObjectBase.class]) || (o2 && ![o2 isKindOfClass:RLMObjectBase.class])) {
  373. @throw RLMException(@"Can only compare objects of class RLMObjectBase");
  374. }
  375. // if identical object (or both are nil)
  376. if (o1 == o2) {
  377. return YES;
  378. }
  379. // if one is nil
  380. if (o1 == nil || o2 == nil) {
  381. return NO;
  382. }
  383. // if not in realm or differing realms
  384. if (o1->_realm == nil || o1->_realm != o2->_realm) {
  385. return NO;
  386. }
  387. // if either are detached
  388. if (!o1->_row.is_valid() || !o2->_row.is_valid()) {
  389. return NO;
  390. }
  391. // if table and index are the same
  392. return o1->_row.get_table() == o2->_row.get_table()
  393. && o1->_row.get_key() == o2->_row.get_key();
  394. }
  395. id RLMObjectFreeze(RLMObjectBase *obj) {
  396. if (!obj->_realm && !obj.isInvalidated) {
  397. @throw RLMException(@"Unmanaged objects cannot be frozen.");
  398. }
  399. RLMVerifyAttached(obj);
  400. if (obj->_realm.frozen) {
  401. return obj;
  402. }
  403. RLMRealm *frozenRealm = [obj->_realm freeze];
  404. RLMObjectBase *frozen = RLMCreateManagedAccessor(obj.class, &frozenRealm->_info[obj->_info->rlmObjectSchema.className]);
  405. frozen->_row = frozenRealm->_realm->import_copy_of(obj->_row);
  406. if (!frozen->_row.is_valid()) {
  407. @throw RLMException(@"Cannot freeze an object in the same write transaction as it was created in.");
  408. }
  409. RLMInitializeSwiftAccessorGenerics(frozen);
  410. return frozen;
  411. }
  412. id RLMValidatedValueForProperty(id object, NSString *key, NSString *className) {
  413. @try {
  414. return [object valueForKey:key];
  415. }
  416. @catch (NSException *e) {
  417. if ([e.name isEqualToString:NSUndefinedKeyException]) {
  418. @throw RLMException(@"Invalid value '%@' to initialize object of type '%@': missing key '%@'",
  419. object, className, key);
  420. }
  421. @throw;
  422. }
  423. }
  424. #pragma mark - Notifications
  425. namespace {
  426. struct ObjectChangeCallbackWrapper {
  427. RLMObjectNotificationCallback block;
  428. RLMObjectBase *object;
  429. NSArray<NSString *> *propertyNames = nil;
  430. NSArray *oldValues = nil;
  431. bool deleted = false;
  432. void populateProperties(realm::CollectionChangeSet const& c) {
  433. if (propertyNames) {
  434. return;
  435. }
  436. if (!c.deletions.empty()) {
  437. deleted = true;
  438. return;
  439. }
  440. if (c.columns.empty()) {
  441. return;
  442. }
  443. auto properties = [NSMutableArray new];
  444. for (RLMProperty *property in object->_info->rlmObjectSchema.properties) {
  445. if (c.columns.count(object->_info->tableColumn(property).value)) {
  446. [properties addObject:property.name];
  447. }
  448. }
  449. if (properties.count) {
  450. propertyNames = properties;
  451. }
  452. }
  453. NSArray *readValues(realm::CollectionChangeSet const& c) {
  454. if (c.empty()) {
  455. return nil;
  456. }
  457. populateProperties(c);
  458. if (!propertyNames) {
  459. return nil;
  460. }
  461. auto values = [NSMutableArray arrayWithCapacity:propertyNames.count];
  462. for (NSString *name in propertyNames) {
  463. id value = [object valueForKey:name];
  464. if (!value || [value isKindOfClass:[RLMArray class]]) {
  465. [values addObject:NSNull.null];
  466. }
  467. else {
  468. [values addObject:value];
  469. }
  470. }
  471. return values;
  472. }
  473. void before(realm::CollectionChangeSet const& c) {
  474. @autoreleasepool {
  475. oldValues = readValues(c);
  476. }
  477. }
  478. void after(realm::CollectionChangeSet const& c) {
  479. @autoreleasepool {
  480. auto newValues = readValues(c);
  481. if (deleted) {
  482. block(nil, nil, nil, nil, nil);
  483. }
  484. else if (newValues) {
  485. block(object, propertyNames, oldValues, newValues, nil);
  486. }
  487. propertyNames = nil;
  488. oldValues = nil;
  489. }
  490. }
  491. void error(std::exception_ptr err) {
  492. @autoreleasepool {
  493. try {
  494. rethrow_exception(err);
  495. }
  496. catch (...) {
  497. NSError *error = nil;
  498. RLMRealmTranslateException(&error);
  499. block(nil, nil, nil, nil, error);
  500. }
  501. }
  502. }
  503. };
  504. } // anonymous namespace
  505. @interface RLMPropertyChange ()
  506. @property (nonatomic, readwrite, strong) NSString *name;
  507. @property (nonatomic, readwrite, strong, nullable) id previousValue;
  508. @property (nonatomic, readwrite, strong, nullable) id value;
  509. @end
  510. @implementation RLMPropertyChange
  511. - (NSString *)description {
  512. return [NSString stringWithFormat:@"<RLMPropertyChange: %p> %@ %@ -> %@",
  513. (__bridge void *)self, _name, _previousValue, _value];
  514. }
  515. @end
  516. @interface RLMObjectNotificationToken : RLMNotificationToken
  517. @end
  518. @implementation RLMObjectNotificationToken {
  519. std::mutex _mutex;
  520. __unsafe_unretained RLMRealm *_realm;
  521. realm::Object _object;
  522. realm::NotificationToken _token;
  523. }
  524. - (RLMRealm *)realm {
  525. return _realm;
  526. }
  527. - (void)suppressNextNotification {
  528. std::lock_guard<std::mutex> lock(_mutex);
  529. if (_object.is_valid()) {
  530. _token.suppress_next();
  531. }
  532. }
  533. - (void)invalidate {
  534. std::lock_guard<std::mutex> lock(_mutex);
  535. _realm = nil;
  536. _token = {};
  537. _object = {};
  538. }
  539. - (void)addNotificationBlock:(RLMObjectNotificationCallback)block
  540. threadSafeReference:(RLMThreadSafeReference *)tsr
  541. config:(RLMRealmConfiguration *)config
  542. queue:(dispatch_queue_t)queue {
  543. std::lock_guard<std::mutex> lock(_mutex);
  544. if (!_realm) {
  545. // Token was invalidated before we got this far
  546. return;
  547. }
  548. NSError *error;
  549. RLMRealm *realm = _realm = [RLMRealm realmWithConfiguration:config queue:queue error:&error];
  550. if (!realm) {
  551. block(nil, nil, nil, nil, error);
  552. return;
  553. }
  554. RLMObjectBase *obj = [realm resolveThreadSafeReference:tsr];
  555. _object = realm::Object(obj->_realm->_realm, *obj->_info->objectSchema, obj->_row);
  556. _token = _object.add_notification_callback(ObjectChangeCallbackWrapper{block, obj});
  557. }
  558. - (void)addNotificationBlock:(RLMObjectNotificationCallback)block object:(RLMObjectBase *)obj {
  559. _object = realm::Object(obj->_realm->_realm, *obj->_info->objectSchema, obj->_row);
  560. _realm = obj->_realm;
  561. _token = _object.add_notification_callback(ObjectChangeCallbackWrapper{block, obj});
  562. }
  563. RLMNotificationToken *RLMObjectBaseAddNotificationBlock(RLMObjectBase *obj, dispatch_queue_t queue,
  564. RLMObjectNotificationCallback block) {
  565. if (!obj->_realm) {
  566. @throw RLMException(@"Only objects which are managed by a Realm support change notifications");
  567. }
  568. if (!queue) {
  569. [obj->_realm verifyNotificationsAreSupported:true];
  570. auto token = [[RLMObjectNotificationToken alloc] init];
  571. token->_realm = obj->_realm;
  572. [token addNotificationBlock:block object:obj];
  573. return token;
  574. }
  575. RLMThreadSafeReference *tsr = [RLMThreadSafeReference referenceWithThreadConfined:(id)obj];
  576. auto token = [[RLMObjectNotificationToken alloc] init];
  577. token->_realm = obj->_realm;
  578. RLMRealmConfiguration *config = obj->_realm.configuration;
  579. dispatch_async(queue, ^{
  580. @autoreleasepool {
  581. [token addNotificationBlock:block threadSafeReference:tsr config:config queue:queue];
  582. }
  583. });
  584. return token;
  585. }
  586. @end
  587. RLMNotificationToken *RLMObjectAddNotificationBlock(RLMObjectBase *obj, RLMObjectChangeBlock block, dispatch_queue_t queue) {
  588. return RLMObjectBaseAddNotificationBlock(obj, queue, ^(RLMObjectBase *, NSArray<NSString *> *propertyNames,
  589. NSArray *oldValues, NSArray *newValues, NSError *error) {
  590. if (error) {
  591. block(false, nil, error);
  592. }
  593. else if (!propertyNames) {
  594. block(true, nil, nil);
  595. }
  596. else {
  597. auto properties = [NSMutableArray arrayWithCapacity:propertyNames.count];
  598. for (NSUInteger i = 0, count = propertyNames.count; i < count; ++i) {
  599. auto prop = [RLMPropertyChange new];
  600. prop.name = propertyNames[i];
  601. prop.previousValue = RLMCoerceToNil(oldValues[i]);
  602. prop.value = RLMCoerceToNil(newValues[i]);
  603. [properties addObject:prop];
  604. }
  605. block(false, properties, nil);
  606. }
  607. });
  608. }
  609. uint64_t RLMObjectBaseGetCombineId(__unsafe_unretained RLMObjectBase *const obj) {
  610. if (obj.invalidated) {
  611. RLMVerifyAttached(obj);
  612. }
  613. if (obj->_realm) {
  614. return obj->_row.get_key().value;
  615. }
  616. return reinterpret_cast<uint64_t>((__bridge void *)obj);
  617. }