RLMObjectBase.mm 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  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 maybeInitObjectSchemaForUnmanaged(RLMObjectBase *obj) {
  38. obj->_objectSchema = [obj.class sharedSchema];
  39. if (!obj->_objectSchema) {
  40. return false;
  41. }
  42. // set default values
  43. if (!obj->_objectSchema.isSwiftClass) {
  44. NSDictionary *dict = RLMDefaultValuesForObjectSchema(obj->_objectSchema);
  45. for (NSString *key in dict) {
  46. [obj setValue:dict[key] forKey:key];
  47. }
  48. }
  49. // set unmanaged accessor class
  50. object_setClass(obj, obj->_objectSchema.unmanagedClass);
  51. return true;
  52. }
  53. @interface RLMObjectBase () <RLMThreadConfined, RLMThreadConfined_Private>
  54. @end
  55. @implementation RLMObjectBase
  56. // unmanaged init
  57. - (instancetype)init {
  58. if ((self = [super init])) {
  59. maybeInitObjectSchemaForUnmanaged(self);
  60. }
  61. return self;
  62. }
  63. - (void)dealloc {
  64. // This can't be a unique_ptr because associated objects are removed
  65. // *after* c++ members are destroyed and dealloc is called, and we need it
  66. // to be in a validish state when that happens
  67. delete _observationInfo;
  68. _observationInfo = nullptr;
  69. }
  70. static id coerceToObjectType(id obj, Class cls, RLMSchema *schema) {
  71. return [obj isKindOfClass:cls] ? obj : [[cls alloc] initWithValue:obj schema:schema];
  72. }
  73. static id validatedObjectForProperty(__unsafe_unretained id const obj,
  74. __unsafe_unretained RLMObjectSchema *const objectSchema,
  75. __unsafe_unretained RLMProperty *const prop,
  76. __unsafe_unretained RLMSchema *const schema) {
  77. RLMValidateValueForProperty(obj, objectSchema, prop);
  78. if (!obj || obj == NSNull.null) {
  79. return nil;
  80. }
  81. if (prop.type == RLMPropertyTypeObject) {
  82. Class objectClass = schema[prop.objectClassName].objectClass;
  83. if (prop.array) {
  84. NSMutableArray *ret = [[NSMutableArray alloc] init];
  85. for (id el in obj) {
  86. [ret addObject:coerceToObjectType(el, objectClass, schema)];
  87. }
  88. return ret;
  89. }
  90. return coerceToObjectType(obj, objectClass, schema);
  91. }
  92. return obj;
  93. }
  94. - (instancetype)initWithValue:(id)value schema:(RLMSchema *)schema {
  95. if (!(self = [super init])) {
  96. return self;
  97. }
  98. if (!value || value == NSNull.null) {
  99. @throw RLMException(@"Must provide a non-nil value.");
  100. }
  101. if (!maybeInitObjectSchemaForUnmanaged(self)) {
  102. // Don't populate fields from the passed-in object if we're called
  103. // during schema init
  104. return self;
  105. }
  106. NSArray *properties = _objectSchema.properties;
  107. if (NSArray *array = RLMDynamicCast<NSArray>(value)) {
  108. if (array.count > properties.count) {
  109. @throw RLMException(@"Invalid array input: more values (%llu) than properties (%llu).",
  110. (unsigned long long)array.count, (unsigned long long)properties.count);
  111. }
  112. NSUInteger i = 0;
  113. for (id val in array) {
  114. RLMProperty *prop = properties[i++];
  115. [self setValue:validatedObjectForProperty(RLMCoerceToNil(val), _objectSchema, prop, schema)
  116. forKey:prop.name];
  117. }
  118. }
  119. else {
  120. // assume our object is an NSDictionary or an object with kvc properties
  121. for (RLMProperty *prop in properties) {
  122. id obj = RLMValidatedValueForProperty(value, prop.name, _objectSchema.className);
  123. // don't set unspecified properties
  124. if (!obj) {
  125. continue;
  126. }
  127. [self setValue:validatedObjectForProperty(RLMCoerceToNil(obj), _objectSchema, prop, schema)
  128. forKey:prop.name];
  129. }
  130. }
  131. return self;
  132. }
  133. id RLMCreateManagedAccessor(Class cls, __unsafe_unretained RLMRealm *realm, RLMClassInfo *info) {
  134. RLMObjectBase *obj = [[cls alloc] initWithRealm:realm schema:info->rlmObjectSchema];
  135. obj->_info = info;
  136. return obj;
  137. }
  138. - (instancetype)initWithRealm:(__unsafe_unretained RLMRealm *const)realm
  139. schema:(RLMObjectSchema *)schema {
  140. self = [super init];
  141. if (self) {
  142. _realm = realm;
  143. _objectSchema = schema;
  144. }
  145. return self;
  146. }
  147. - (id)valueForKey:(NSString *)key {
  148. if (_observationInfo) {
  149. return _observationInfo->valueForKey(key);
  150. }
  151. return [super valueForKey:key];
  152. }
  153. // Generic Swift properties can't be dynamic, so KVO doesn't work for them by default
  154. - (id)valueForUndefinedKey:(NSString *)key {
  155. if (Ivar ivar = _objectSchema[key].swiftIvar) {
  156. return RLMCoerceToNil(object_getIvar(self, ivar));
  157. }
  158. return [super valueForUndefinedKey:key];
  159. }
  160. - (void)setValue:(id)value forUndefinedKey:(NSString *)key {
  161. value = RLMCoerceToNil(value);
  162. RLMProperty *property = _objectSchema[key];
  163. if (Ivar ivar = property.swiftIvar) {
  164. if (property.array && (!value || [value conformsToProtocol:@protocol(NSFastEnumeration)])) {
  165. RLMArray *array = [object_getIvar(self, ivar) _rlmArray];
  166. [array removeAllObjects];
  167. if (value) {
  168. [array addObjects:validatedObjectForProperty(value, _objectSchema, property,
  169. RLMSchema.partialPrivateSharedSchema)];
  170. }
  171. }
  172. else if (property.optional) {
  173. RLMSetOptional(object_getIvar(self, ivar), value);
  174. }
  175. return;
  176. }
  177. [super setValue:value forUndefinedKey:key];
  178. }
  179. // overridden at runtime per-class for performance
  180. + (NSString *)className {
  181. NSString *className = NSStringFromClass(self);
  182. if ([RLMSwiftSupport isSwiftClassName:className]) {
  183. className = [RLMSwiftSupport demangleClassName:className];
  184. }
  185. return className;
  186. }
  187. // overridden at runtime per-class for performance
  188. + (RLMObjectSchema *)sharedSchema {
  189. return [RLMSchema sharedSchemaForClass:self.class];
  190. }
  191. + (void)initializeLinkedObjectSchemas {
  192. for (RLMProperty *prop in self.sharedSchema.properties) {
  193. if (prop.type == RLMPropertyTypeObject && !RLMSchema.partialPrivateSharedSchema[prop.objectClassName]) {
  194. [[RLMSchema classForString:prop.objectClassName] initializeLinkedObjectSchemas];
  195. }
  196. }
  197. }
  198. + (Class)objectUtilClass:(BOOL)isSwift {
  199. return RLMObjectUtilClass(isSwift);
  200. }
  201. - (NSString *)description
  202. {
  203. if (self.isInvalidated) {
  204. return @"[invalid object]";
  205. }
  206. return [self descriptionWithMaxDepth:RLMDescriptionMaxDepth];
  207. }
  208. - (NSString *)descriptionWithMaxDepth:(NSUInteger)depth {
  209. if (depth == 0) {
  210. return @"<Maximum depth exceeded>";
  211. }
  212. NSString *baseClassName = _objectSchema.className;
  213. NSMutableString *mString = [NSMutableString stringWithFormat:@"%@ {\n", baseClassName];
  214. for (RLMProperty *property in _objectSchema.properties) {
  215. id object = [(id)self objectForKeyedSubscript:property.name];
  216. NSString *sub;
  217. if ([object respondsToSelector:@selector(descriptionWithMaxDepth:)]) {
  218. sub = [object descriptionWithMaxDepth:depth - 1];
  219. }
  220. else if (property.type == RLMPropertyTypeData) {
  221. static NSUInteger maxPrintedDataLength = 24;
  222. NSData *data = object;
  223. NSUInteger length = data.length;
  224. if (length > maxPrintedDataLength) {
  225. data = [NSData dataWithBytes:data.bytes length:maxPrintedDataLength];
  226. }
  227. NSString *dataDescription = [data description];
  228. sub = [NSString stringWithFormat:@"<%@ — %lu total bytes>", [dataDescription substringWithRange:NSMakeRange(1, dataDescription.length - 2)], (unsigned long)length];
  229. }
  230. else {
  231. sub = [object description];
  232. }
  233. [mString appendFormat:@"\t%@ = %@;\n", property.name, [sub stringByReplacingOccurrencesOfString:@"\n" withString:@"\n\t"]];
  234. }
  235. [mString appendString:@"}"];
  236. return [NSString stringWithString:mString];
  237. }
  238. - (RLMRealm *)realm {
  239. return _realm;
  240. }
  241. - (RLMObjectSchema *)objectSchema {
  242. return _objectSchema;
  243. }
  244. - (BOOL)isInvalidated {
  245. // if not unmanaged and our accessor has been detached, we have been deleted
  246. return self.class == _objectSchema.accessorClass && !_row.is_attached();
  247. }
  248. - (BOOL)isEqual:(id)object {
  249. if (RLMObjectBase *other = RLMDynamicCast<RLMObjectBase>(object)) {
  250. if (_objectSchema.primaryKeyProperty) {
  251. return RLMObjectBaseAreEqual(self, other);
  252. }
  253. }
  254. return [super isEqual:object];
  255. }
  256. - (NSUInteger)hash {
  257. if (_objectSchema.primaryKeyProperty) {
  258. id primaryProperty = [self valueForKey:_objectSchema.primaryKeyProperty.name];
  259. // modify the hash of our primary key value to avoid potential (although unlikely) collisions
  260. return [primaryProperty hash] ^ 1;
  261. }
  262. else {
  263. return [super hash];
  264. }
  265. }
  266. + (BOOL)shouldIncludeInDefaultSchema {
  267. return RLMIsObjectSubclass(self);
  268. }
  269. + (NSString *)_realmObjectName {
  270. return nil;
  271. }
  272. + (NSDictionary *)_realmColumnNames {
  273. return nil;
  274. }
  275. - (id)mutableArrayValueForKey:(NSString *)key {
  276. id obj = [self valueForKey:key];
  277. if ([obj isKindOfClass:[RLMArray class]]) {
  278. return obj;
  279. }
  280. return [super mutableArrayValueForKey:key];
  281. }
  282. - (void)addObserver:(id)observer
  283. forKeyPath:(NSString *)keyPath
  284. options:(NSKeyValueObservingOptions)options
  285. context:(void *)context {
  286. if (!_observationInfo) {
  287. _observationInfo = new RLMObservationInfo(self);
  288. }
  289. _observationInfo->recordObserver(_row, _info, _objectSchema, keyPath);
  290. [super addObserver:observer forKeyPath:keyPath options:options context:context];
  291. }
  292. - (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath {
  293. [super removeObserver:observer forKeyPath:keyPath];
  294. if (_observationInfo)
  295. _observationInfo->removeObserver();
  296. }
  297. + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
  298. const char *className = class_getName(self);
  299. const char accessorClassPrefix[] = "RLM:Managed";
  300. if (!strncmp(className, accessorClassPrefix, sizeof(accessorClassPrefix) - 1)) {
  301. if ([class_getSuperclass(self.class) sharedSchema][key]) {
  302. return NO;
  303. }
  304. }
  305. return [super automaticallyNotifiesObserversForKey:key];
  306. }
  307. #pragma mark - Thread Confined Protocol Conformance
  308. - (std::unique_ptr<realm::ThreadSafeReferenceBase>)makeThreadSafeReference {
  309. Object object(_realm->_realm, *_info->objectSchema, _row);
  310. realm::ThreadSafeReference<Object> reference = _realm->_realm->obtain_thread_safe_reference(std::move(object));
  311. return std::make_unique<realm::ThreadSafeReference<Object>>(std::move(reference));
  312. }
  313. - (id)objectiveCMetadata {
  314. return nil;
  315. }
  316. + (instancetype)objectWithThreadSafeReference:(std::unique_ptr<realm::ThreadSafeReferenceBase>)reference
  317. metadata:(__unused id)metadata
  318. realm:(RLMRealm *)realm {
  319. REALM_ASSERT_DEBUG(dynamic_cast<realm::ThreadSafeReference<Object> *>(reference.get()));
  320. auto object_reference = static_cast<realm::ThreadSafeReference<Object> *>(reference.get());
  321. Object object = realm->_realm->resolve_thread_safe_reference(std::move(*object_reference));
  322. if (!object.is_valid()) {
  323. return nil;
  324. }
  325. NSString *objectClassName = @(object.get_object_schema().name.c_str());
  326. return RLMCreateObjectAccessor(realm, realm->_info[objectClassName], object.row().get_index());
  327. }
  328. @end
  329. RLMRealm *RLMObjectBaseRealm(__unsafe_unretained RLMObjectBase *object) {
  330. return object ? object->_realm : nil;
  331. }
  332. RLMObjectSchema *RLMObjectBaseObjectSchema(__unsafe_unretained RLMObjectBase *object) {
  333. return object ? object->_objectSchema : nil;
  334. }
  335. id RLMObjectBaseObjectForKeyedSubscript(RLMObjectBase *object, NSString *key) {
  336. if (!object) {
  337. return nil;
  338. }
  339. if (object->_realm) {
  340. return RLMDynamicGetByName(object, key, false);
  341. }
  342. else {
  343. return [object valueForKey:key];
  344. }
  345. }
  346. void RLMObjectBaseSetObjectForKeyedSubscript(RLMObjectBase *object, NSString *key, id obj) {
  347. if (!object) {
  348. return;
  349. }
  350. if (object->_realm || object.class == object->_objectSchema.accessorClass) {
  351. RLMDynamicValidatedSet(object, key, obj);
  352. }
  353. else {
  354. [object setValue:obj forKey:key];
  355. }
  356. }
  357. BOOL RLMObjectBaseAreEqual(RLMObjectBase *o1, RLMObjectBase *o2) {
  358. // if not the correct types throw
  359. if ((o1 && ![o1 isKindOfClass:RLMObjectBase.class]) || (o2 && ![o2 isKindOfClass:RLMObjectBase.class])) {
  360. @throw RLMException(@"Can only compare objects of class RLMObjectBase");
  361. }
  362. // if identical object (or both are nil)
  363. if (o1 == o2) {
  364. return YES;
  365. }
  366. // if one is nil
  367. if (o1 == nil || o2 == nil) {
  368. return NO;
  369. }
  370. // if not in realm or differing realms
  371. if (o1->_realm == nil || o1->_realm != o2->_realm) {
  372. return NO;
  373. }
  374. // if either are detached
  375. if (!o1->_row.is_attached() || !o2->_row.is_attached()) {
  376. return NO;
  377. }
  378. // if table and index are the same
  379. return o1->_row.get_table() == o2->_row.get_table()
  380. && o1->_row.get_index() == o2->_row.get_index();
  381. }
  382. id RLMValidatedValueForProperty(id object, NSString *key, NSString *className) {
  383. @try {
  384. return [object valueForKey:key];
  385. }
  386. @catch (NSException *e) {
  387. if ([e.name isEqualToString:NSUndefinedKeyException]) {
  388. @throw RLMException(@"Invalid value '%@' to initialize object of type '%@': missing key '%@'",
  389. object, className, key);
  390. }
  391. @throw;
  392. }
  393. }
  394. Class RLMObjectUtilClass(BOOL isSwift) {
  395. static Class objectUtilObjc = [RLMObjectUtil class];
  396. static Class objectUtilSwift = NSClassFromString(@"RealmSwiftObjectUtil");
  397. return isSwift && objectUtilSwift ? objectUtilSwift : objectUtilObjc;
  398. }
  399. @implementation RLMObjectUtil
  400. + (NSArray *)ignoredPropertiesForClass:(Class)cls {
  401. return [cls ignoredProperties];
  402. }
  403. + (NSArray *)indexedPropertiesForClass:(Class)cls {
  404. return [cls indexedProperties];
  405. }
  406. + (NSDictionary *)linkingObjectsPropertiesForClass:(Class)cls {
  407. return [cls linkingObjectsProperties];
  408. }
  409. + (NSDictionary *)linkingObjectProperties:(__unused id)object {
  410. return nil;
  411. }
  412. + (NSArray *)getSwiftProperties:(__unused id)obj {
  413. return nil;
  414. }
  415. + (NSDictionary *)getOptionalProperties:(__unused id)obj {
  416. return nil;
  417. }
  418. + (NSArray *)requiredPropertiesForClass:(Class)cls {
  419. return [cls requiredProperties];
  420. }
  421. @end
  422. @implementation RLMSwiftPropertyMetadata
  423. + (instancetype)metadataForOtherProperty:(NSString *)propertyName {
  424. RLMSwiftPropertyMetadata *md = [RLMSwiftPropertyMetadata new];
  425. md.propertyName = propertyName;
  426. md.kind = RLMSwiftPropertyKindOther;
  427. return md;
  428. }
  429. + (instancetype)metadataForListProperty:(NSString *)propertyName {
  430. RLMSwiftPropertyMetadata *md = [RLMSwiftPropertyMetadata new];
  431. md.propertyName = propertyName;
  432. md.kind = RLMSwiftPropertyKindList;
  433. return md;
  434. }
  435. + (instancetype)metadataForLinkingObjectsProperty:(NSString *)propertyName
  436. className:(NSString *)className
  437. linkedPropertyName:(NSString *)linkedPropertyName {
  438. RLMSwiftPropertyMetadata *md = [RLMSwiftPropertyMetadata new];
  439. md.propertyName = propertyName;
  440. md.className = className;
  441. md.linkedPropertyName = linkedPropertyName;
  442. md.kind = RLMSwiftPropertyKindLinkingObjects;
  443. return md;
  444. }
  445. + (instancetype)metadataForOptionalProperty:(NSString *)propertyName type:(RLMPropertyType)type {
  446. RLMSwiftPropertyMetadata *md = [RLMSwiftPropertyMetadata new];
  447. md.propertyName = propertyName;
  448. md.propertyType = type;
  449. md.kind = RLMSwiftPropertyKindOptional;
  450. return md;
  451. }
  452. + (instancetype)metadataForNilLiteralOptionalProperty:(NSString *)propertyName {
  453. RLMSwiftPropertyMetadata *md = [RLMSwiftPropertyMetadata new];
  454. md.propertyName = propertyName;
  455. md.kind = RLMSwiftPropertyKindNilLiteralOptional;
  456. return md;
  457. }
  458. @end