RLMObject.mm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  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.h"
  21. #import "RLMCollection_Private.hpp"
  22. #import "RLMObjectBase_Private.h"
  23. #import "RLMObjectSchema_Private.hpp"
  24. #import "RLMObjectStore.h"
  25. #import "RLMProperty.h"
  26. #import "RLMQueryUtil.hpp"
  27. #import "RLMRealm_Private.hpp"
  28. #import "RLMSchema_Private.h"
  29. #import "collection_notifications.hpp"
  30. #import "object.hpp"
  31. @interface RLMPropertyChange ()
  32. @property (nonatomic, readwrite, strong) NSString *name;
  33. @property (nonatomic, readwrite, strong, nullable) id previousValue;
  34. @property (nonatomic, readwrite, strong, nullable) id value;
  35. @end
  36. // We declare things in RLMObject which are actually implemented in RLMObjectBase
  37. // for documentation's sake, which leads to -Wunimplemented-method warnings.
  38. // Other alternatives to this would be to disable -Wunimplemented-method for this
  39. // file (but then we could miss legitimately missing things), or declaring the
  40. // inherited things in a category (but they currently aren't nicely grouped for
  41. // that).
  42. @implementation RLMObject
  43. // synthesized in RLMObjectBase
  44. @dynamic invalidated, realm, objectSchema;
  45. #pragma mark - Designated Initializers
  46. - (instancetype)init {
  47. return [super init];
  48. }
  49. - (instancetype)initWithValue:(id)value schema:(RLMSchema *)schema {
  50. return [super initWithValue:value schema:schema];
  51. }
  52. - (instancetype)initWithRealm:(__unsafe_unretained RLMRealm *const)realm schema:(RLMObjectSchema *)schema {
  53. return [super initWithRealm:realm schema:schema];
  54. }
  55. #pragma mark - Convenience Initializers
  56. - (instancetype)initWithValue:(id)value {
  57. return [super initWithValue:value schema:RLMSchema.partialPrivateSharedSchema];
  58. }
  59. #pragma mark - Class-based Object Creation
  60. + (instancetype)createInDefaultRealmWithValue:(id)value {
  61. return (RLMObject *)RLMCreateObjectInRealmWithValue([RLMRealm defaultRealm], [self className], value, RLMUpdatePolicyError);
  62. }
  63. + (instancetype)createInRealm:(RLMRealm *)realm withValue:(id)value {
  64. return (RLMObject *)RLMCreateObjectInRealmWithValue(realm, [self className], value, RLMUpdatePolicyError);
  65. }
  66. + (instancetype)createOrUpdateInDefaultRealmWithValue:(id)value {
  67. return [self createOrUpdateInRealm:[RLMRealm defaultRealm] withValue:value];
  68. }
  69. + (instancetype)createOrUpdateModifiedInDefaultRealmWithValue:(id)value {
  70. return [self createOrUpdateModifiedInRealm:[RLMRealm defaultRealm] withValue:value];
  71. }
  72. + (instancetype)createOrUpdateInRealm:(RLMRealm *)realm withValue:(id)value {
  73. RLMVerifyHasPrimaryKey(self);
  74. return (RLMObject *)RLMCreateObjectInRealmWithValue(realm, [self className], value, RLMUpdatePolicyUpdateAll);
  75. }
  76. + (instancetype)createOrUpdateModifiedInRealm:(RLMRealm *)realm withValue:(id)value {
  77. RLMVerifyHasPrimaryKey(self);
  78. return (RLMObject *)RLMCreateObjectInRealmWithValue(realm, [self className], value, RLMUpdatePolicyUpdateChanged);
  79. }
  80. #pragma mark - Subscripting
  81. - (id)objectForKeyedSubscript:(NSString *)key {
  82. return RLMObjectBaseObjectForKeyedSubscript(self, key);
  83. }
  84. - (void)setObject:(id)obj forKeyedSubscript:(NSString *)key {
  85. RLMObjectBaseSetObjectForKeyedSubscript(self, key, obj);
  86. }
  87. #pragma mark - Getting & Querying
  88. + (RLMResults *)allObjects {
  89. return RLMGetObjects(RLMRealm.defaultRealm, self.className, nil);
  90. }
  91. + (RLMResults *)allObjectsInRealm:(__unsafe_unretained RLMRealm *const)realm {
  92. return RLMGetObjects(realm, self.className, nil);
  93. }
  94. + (RLMResults *)objectsWhere:(NSString *)predicateFormat, ... {
  95. va_list args;
  96. va_start(args, predicateFormat);
  97. RLMResults *results = [self objectsWhere:predicateFormat args:args];
  98. va_end(args);
  99. return results;
  100. }
  101. + (RLMResults *)objectsWhere:(NSString *)predicateFormat args:(va_list)args {
  102. return [self objectsWithPredicate:[NSPredicate predicateWithFormat:predicateFormat arguments:args]];
  103. }
  104. + (RLMResults *)objectsInRealm:(RLMRealm *)realm where:(NSString *)predicateFormat, ... {
  105. va_list args;
  106. va_start(args, predicateFormat);
  107. RLMResults *results = [self objectsInRealm:realm where:predicateFormat args:args];
  108. va_end(args);
  109. return results;
  110. }
  111. + (RLMResults *)objectsInRealm:(RLMRealm *)realm where:(NSString *)predicateFormat args:(va_list)args {
  112. return [self objectsInRealm:realm withPredicate:[NSPredicate predicateWithFormat:predicateFormat arguments:args]];
  113. }
  114. + (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate {
  115. return RLMGetObjects(RLMRealm.defaultRealm, self.className, predicate);
  116. }
  117. + (RLMResults *)objectsInRealm:(RLMRealm *)realm withPredicate:(NSPredicate *)predicate {
  118. return RLMGetObjects(realm, self.className, predicate);
  119. }
  120. + (instancetype)objectForPrimaryKey:(id)primaryKey {
  121. return RLMGetObject(RLMRealm.defaultRealm, self.className, primaryKey);
  122. }
  123. + (instancetype)objectInRealm:(RLMRealm *)realm forPrimaryKey:(id)primaryKey {
  124. return RLMGetObject(realm, self.className, primaryKey);
  125. }
  126. #pragma mark - Other Instance Methods
  127. - (BOOL)isEqualToObject:(RLMObject *)object {
  128. return [object isKindOfClass:RLMObject.class] && RLMObjectBaseAreEqual(self, object);
  129. }
  130. - (RLMNotificationToken *)addNotificationBlock:(RLMObjectChangeBlock)block {
  131. return RLMObjectAddNotificationBlock(self, ^(NSArray<NSString *> *propertyNames,
  132. NSArray *oldValues, NSArray *newValues, NSError *error) {
  133. if (error) {
  134. block(false, nil, error);
  135. }
  136. else if (!propertyNames) {
  137. block(true, nil, nil);
  138. }
  139. else {
  140. auto properties = [NSMutableArray arrayWithCapacity:propertyNames.count];
  141. for (NSUInteger i = 0, count = propertyNames.count; i < count; ++i) {
  142. auto prop = [RLMPropertyChange new];
  143. prop.name = propertyNames[i];
  144. prop.previousValue = RLMCoerceToNil(oldValues[i]);
  145. prop.value = RLMCoerceToNil(newValues[i]);
  146. [properties addObject:prop];
  147. }
  148. block(false, properties, nil);
  149. }
  150. });
  151. }
  152. + (NSString *)className {
  153. return [super className];
  154. }
  155. #pragma mark - Default values for schema definition
  156. + (NSArray *)indexedProperties {
  157. return @[];
  158. }
  159. + (NSDictionary *)linkingObjectsProperties {
  160. return @{};
  161. }
  162. + (NSDictionary *)defaultPropertyValues {
  163. return nil;
  164. }
  165. + (NSString *)primaryKey {
  166. return nil;
  167. }
  168. + (NSArray *)ignoredProperties {
  169. return nil;
  170. }
  171. + (NSArray *)requiredProperties {
  172. return @[];
  173. }
  174. @end
  175. @implementation RLMDynamicObject
  176. + (BOOL)shouldIncludeInDefaultSchema {
  177. return NO;
  178. }
  179. - (id)valueForUndefinedKey:(NSString *)key {
  180. return RLMDynamicGetByName(self, key, false);
  181. }
  182. - (void)setValue:(id)value forUndefinedKey:(NSString *)key {
  183. RLMDynamicValidatedSet(self, key, value);
  184. }
  185. @end
  186. @implementation RLMWeakObjectHandle {
  187. realm::Row _row;
  188. RLMClassInfo *_info;
  189. Class _objectClass;
  190. }
  191. - (instancetype)initWithObject:(RLMObjectBase *)object {
  192. if (!(self = [super init])) {
  193. return nil;
  194. }
  195. _row = object->_row;
  196. _info = object->_info;
  197. _objectClass = object.class;
  198. return self;
  199. }
  200. - (RLMObjectBase *)object {
  201. RLMObjectBase *object = RLMCreateManagedAccessor(_objectClass, _info->realm, _info);
  202. object->_row = std::move(_row);
  203. return object;
  204. }
  205. - (id)copyWithZone:(__unused NSZone *)zone {
  206. RLMWeakObjectHandle *copy = [[RLMWeakObjectHandle alloc] init];
  207. copy->_row = _row;
  208. copy->_info = _info;
  209. copy->_objectClass = _objectClass;
  210. return copy;
  211. }
  212. @end
  213. static bool treatFakeObjectAsRLMObject = false;
  214. void RLMSetTreatFakeObjectAsRLMObject(BOOL flag) {
  215. treatFakeObjectAsRLMObject = flag;
  216. }
  217. BOOL RLMIsObjectOrSubclass(Class klass) {
  218. if (RLMIsKindOfClass(klass, RLMObjectBase.class)) {
  219. return YES;
  220. }
  221. if (treatFakeObjectAsRLMObject) {
  222. static Class FakeObjectClass = NSClassFromString(@"FakeObject");
  223. return RLMIsKindOfClass(klass, FakeObjectClass);
  224. }
  225. return NO;
  226. }
  227. BOOL RLMIsObjectSubclass(Class klass) {
  228. auto isSubclass = [](Class class1, Class class2) {
  229. class1 = class_getSuperclass(class1);
  230. return RLMIsKindOfClass(class1, class2);
  231. };
  232. if (isSubclass(class_getSuperclass(klass), RLMObjectBase.class)) {
  233. return YES;
  234. }
  235. if (treatFakeObjectAsRLMObject) {
  236. static Class FakeObjectClass = NSClassFromString(@"FakeObject");
  237. return isSubclass(klass, FakeObjectClass);
  238. }
  239. return NO;
  240. }
  241. @interface RLMObjectNotificationToken : RLMCancellationToken
  242. @end
  243. @implementation RLMObjectNotificationToken {
  244. @public
  245. realm::Object _object;
  246. }
  247. @end
  248. RLMNotificationToken *RLMObjectAddNotificationBlock(RLMObjectBase *obj, RLMObjectNotificationCallback block) {
  249. if (!obj->_realm) {
  250. @throw RLMException(@"Only objects which are managed by a Realm support change notifications");
  251. }
  252. [obj->_realm verifyNotificationsAreSupported:true];
  253. struct {
  254. void (^block)(NSArray<NSString *> *, NSArray *, NSArray *, NSError *);
  255. RLMObjectBase *object;
  256. NSArray<NSString *> *propertyNames = nil;
  257. NSArray *oldValues = nil;
  258. bool deleted = false;
  259. void populateProperties(realm::CollectionChangeSet const& c) {
  260. if (propertyNames) {
  261. return;
  262. }
  263. if (!c.deletions.empty()) {
  264. deleted = true;
  265. return;
  266. }
  267. if (c.columns.empty()) {
  268. return;
  269. }
  270. auto properties = [NSMutableArray new];
  271. for (size_t i = 0; i < c.columns.size(); ++i) {
  272. if (c.columns[i].empty()) {
  273. continue;
  274. }
  275. if (auto prop = object->_info->propertyForTableColumn(i)) {
  276. [properties addObject:prop.name];
  277. }
  278. }
  279. if (properties.count) {
  280. propertyNames = properties;
  281. }
  282. }
  283. NSArray *readValues(realm::CollectionChangeSet const& c) {
  284. if (c.empty()) {
  285. return nil;
  286. }
  287. populateProperties(c);
  288. if (!propertyNames) {
  289. return nil;
  290. }
  291. auto values = [NSMutableArray arrayWithCapacity:propertyNames.count];
  292. for (NSString *name in propertyNames) {
  293. id value = [object valueForKey:name];
  294. if (!value || [value isKindOfClass:[RLMArray class]]) {
  295. [values addObject:NSNull.null];
  296. }
  297. else {
  298. [values addObject:value];
  299. }
  300. }
  301. return values;
  302. }
  303. void before(realm::CollectionChangeSet const& c) {
  304. @autoreleasepool {
  305. oldValues = readValues(c);
  306. }
  307. }
  308. void after(realm::CollectionChangeSet const& c) {
  309. @autoreleasepool {
  310. auto newValues = readValues(c);
  311. if (deleted) {
  312. block(nil, nil, nil, nil);
  313. }
  314. else if (newValues) {
  315. block(propertyNames, oldValues, newValues, nil);
  316. }
  317. propertyNames = nil;
  318. oldValues = nil;
  319. }
  320. }
  321. void error(std::exception_ptr err) {
  322. @autoreleasepool {
  323. try {
  324. rethrow_exception(err);
  325. }
  326. catch (...) {
  327. NSError *error = nil;
  328. RLMRealmTranslateException(&error);
  329. block(nil, nil, nil, error);
  330. }
  331. }
  332. }
  333. } callback{block, obj};
  334. realm::Object object(obj->_realm->_realm, *obj->_info->objectSchema, obj->_row);
  335. auto token = [[RLMObjectNotificationToken alloc] initWithToken:object.add_notification_callback(callback) realm:obj->_realm];
  336. token->_object = std::move(object);
  337. return token;
  338. }
  339. @implementation RLMPropertyChange
  340. @end