RLMObject.mm 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  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_Private.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. #pragma mark - Convenience Initializers
  50. - (instancetype)initWithValue:(id)value {
  51. if (!(self = [self init])) {
  52. return nil;
  53. }
  54. RLMInitializeWithValue(self, value, RLMSchema.partialPrivateSharedSchema);
  55. return self;
  56. }
  57. #pragma mark - Class-based Object Creation
  58. + (instancetype)createInDefaultRealmWithValue:(id)value {
  59. return (RLMObject *)RLMCreateObjectInRealmWithValue([RLMRealm defaultRealm], [self className], value, RLMUpdatePolicyError);
  60. }
  61. + (instancetype)createInRealm:(RLMRealm *)realm withValue:(id)value {
  62. return (RLMObject *)RLMCreateObjectInRealmWithValue(realm, [self className], value, RLMUpdatePolicyError);
  63. }
  64. + (instancetype)createOrUpdateInDefaultRealmWithValue:(id)value {
  65. return [self createOrUpdateInRealm:[RLMRealm defaultRealm] withValue:value];
  66. }
  67. + (instancetype)createOrUpdateModifiedInDefaultRealmWithValue:(id)value {
  68. return [self createOrUpdateModifiedInRealm:[RLMRealm defaultRealm] withValue:value];
  69. }
  70. + (instancetype)createOrUpdateInRealm:(RLMRealm *)realm withValue:(id)value {
  71. RLMVerifyHasPrimaryKey(self);
  72. return (RLMObject *)RLMCreateObjectInRealmWithValue(realm, [self className], value, RLMUpdatePolicyUpdateAll);
  73. }
  74. + (instancetype)createOrUpdateModifiedInRealm:(RLMRealm *)realm withValue:(id)value {
  75. RLMVerifyHasPrimaryKey(self);
  76. return (RLMObject *)RLMCreateObjectInRealmWithValue(realm, [self className], value, RLMUpdatePolicyUpdateChanged);
  77. }
  78. #pragma mark - Subscripting
  79. - (id)objectForKeyedSubscript:(NSString *)key {
  80. return RLMObjectBaseObjectForKeyedSubscript(self, key);
  81. }
  82. - (void)setObject:(id)obj forKeyedSubscript:(NSString *)key {
  83. RLMObjectBaseSetObjectForKeyedSubscript(self, key, obj);
  84. }
  85. #pragma mark - Getting & Querying
  86. + (RLMResults *)allObjects {
  87. return RLMGetObjects(RLMRealm.defaultRealm, self.className, nil);
  88. }
  89. + (RLMResults *)allObjectsInRealm:(__unsafe_unretained RLMRealm *const)realm {
  90. return RLMGetObjects(realm, self.className, nil);
  91. }
  92. + (RLMResults *)objectsWhere:(NSString *)predicateFormat, ... {
  93. va_list args;
  94. va_start(args, predicateFormat);
  95. RLMResults *results = [self objectsWhere:predicateFormat args:args];
  96. va_end(args);
  97. return results;
  98. }
  99. + (RLMResults *)objectsWhere:(NSString *)predicateFormat args:(va_list)args {
  100. return [self objectsWithPredicate:[NSPredicate predicateWithFormat:predicateFormat arguments:args]];
  101. }
  102. + (RLMResults *)objectsInRealm:(RLMRealm *)realm where:(NSString *)predicateFormat, ... {
  103. va_list args;
  104. va_start(args, predicateFormat);
  105. RLMResults *results = [self objectsInRealm:realm where:predicateFormat args:args];
  106. va_end(args);
  107. return results;
  108. }
  109. + (RLMResults *)objectsInRealm:(RLMRealm *)realm where:(NSString *)predicateFormat args:(va_list)args {
  110. return [self objectsInRealm:realm withPredicate:[NSPredicate predicateWithFormat:predicateFormat arguments:args]];
  111. }
  112. + (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate {
  113. return RLMGetObjects(RLMRealm.defaultRealm, self.className, predicate);
  114. }
  115. + (RLMResults *)objectsInRealm:(RLMRealm *)realm withPredicate:(NSPredicate *)predicate {
  116. return RLMGetObjects(realm, self.className, predicate);
  117. }
  118. + (instancetype)objectForPrimaryKey:(id)primaryKey {
  119. return RLMGetObject(RLMRealm.defaultRealm, self.className, primaryKey);
  120. }
  121. + (instancetype)objectInRealm:(RLMRealm *)realm forPrimaryKey:(id)primaryKey {
  122. return RLMGetObject(realm, self.className, primaryKey);
  123. }
  124. #pragma mark - Other Instance Methods
  125. - (BOOL)isEqualToObject:(RLMObject *)object {
  126. return [object isKindOfClass:RLMObject.class] && RLMObjectBaseAreEqual(self, object);
  127. }
  128. - (RLMNotificationToken *)addNotificationBlock:(RLMObjectChangeBlock)block {
  129. return RLMObjectAddNotificationBlock(self, ^(NSArray<NSString *> *propertyNames,
  130. NSArray *oldValues, NSArray *newValues, NSError *error) {
  131. if (error) {
  132. block(false, nil, error);
  133. }
  134. else if (!propertyNames) {
  135. block(true, nil, nil);
  136. }
  137. else {
  138. auto properties = [NSMutableArray arrayWithCapacity:propertyNames.count];
  139. for (NSUInteger i = 0, count = propertyNames.count; i < count; ++i) {
  140. auto prop = [RLMPropertyChange new];
  141. prop.name = propertyNames[i];
  142. prop.previousValue = RLMCoerceToNil(oldValues[i]);
  143. prop.value = RLMCoerceToNil(newValues[i]);
  144. [properties addObject:prop];
  145. }
  146. block(false, properties, nil);
  147. }
  148. });
  149. }
  150. + (NSString *)className {
  151. return [super className];
  152. }
  153. #pragma mark - Default values for schema definition
  154. + (NSArray *)indexedProperties {
  155. return @[];
  156. }
  157. + (NSDictionary *)linkingObjectsProperties {
  158. return @{};
  159. }
  160. + (NSDictionary *)defaultPropertyValues {
  161. return nil;
  162. }
  163. + (NSString *)primaryKey {
  164. return nil;
  165. }
  166. + (NSArray *)ignoredProperties {
  167. return nil;
  168. }
  169. + (NSArray *)requiredProperties {
  170. return @[];
  171. }
  172. + (bool)_realmIgnoreClass {
  173. return false;
  174. }
  175. @end
  176. @implementation RLMDynamicObject
  177. + (bool)_realmIgnoreClass {
  178. return true;
  179. }
  180. + (BOOL)shouldIncludeInDefaultSchema {
  181. return NO;
  182. }
  183. - (id)valueForUndefinedKey:(NSString *)key {
  184. return RLMDynamicGetByName(self, key);
  185. }
  186. - (void)setValue:(id)value forUndefinedKey:(NSString *)key {
  187. RLMDynamicValidatedSet(self, key, value);
  188. }
  189. + (RLMObjectSchema *)sharedSchema {
  190. return nil;
  191. }
  192. @end
  193. BOOL RLMIsObjectOrSubclass(Class klass) {
  194. return RLMIsKindOfClass(klass, RLMObjectBase.class);
  195. }
  196. BOOL RLMIsObjectSubclass(Class klass) {
  197. return RLMIsKindOfClass(class_getSuperclass(class_getSuperclass(klass)), RLMObjectBase.class);
  198. }
  199. @interface RLMObjectNotificationToken : RLMCancellationToken
  200. @end
  201. @implementation RLMObjectNotificationToken {
  202. @public
  203. realm::Object _object;
  204. }
  205. @end
  206. RLMNotificationToken *RLMObjectAddNotificationBlock(RLMObjectBase *obj, RLMObjectNotificationCallback block) {
  207. if (!obj->_realm) {
  208. @throw RLMException(@"Only objects which are managed by a Realm support change notifications");
  209. }
  210. [obj->_realm verifyNotificationsAreSupported:true];
  211. struct {
  212. void (^block)(NSArray<NSString *> *, NSArray *, NSArray *, NSError *);
  213. RLMObjectBase *object;
  214. NSArray<NSString *> *propertyNames = nil;
  215. NSArray *oldValues = nil;
  216. bool deleted = false;
  217. void populateProperties(realm::CollectionChangeSet const& c) {
  218. if (propertyNames) {
  219. return;
  220. }
  221. if (!c.deletions.empty()) {
  222. deleted = true;
  223. return;
  224. }
  225. if (c.columns.empty()) {
  226. return;
  227. }
  228. auto properties = [NSMutableArray new];
  229. for (size_t i = 0; i < c.columns.size(); ++i) {
  230. if (c.columns[i].empty()) {
  231. continue;
  232. }
  233. if (auto prop = object->_info->propertyForTableColumn(i)) {
  234. [properties addObject:prop.name];
  235. }
  236. }
  237. if (properties.count) {
  238. propertyNames = properties;
  239. }
  240. }
  241. NSArray *readValues(realm::CollectionChangeSet const& c) {
  242. if (c.empty()) {
  243. return nil;
  244. }
  245. populateProperties(c);
  246. if (!propertyNames) {
  247. return nil;
  248. }
  249. auto values = [NSMutableArray arrayWithCapacity:propertyNames.count];
  250. for (NSString *name in propertyNames) {
  251. id value = [object valueForKey:name];
  252. if (!value || [value isKindOfClass:[RLMArray class]]) {
  253. [values addObject:NSNull.null];
  254. }
  255. else {
  256. [values addObject:value];
  257. }
  258. }
  259. return values;
  260. }
  261. void before(realm::CollectionChangeSet const& c) {
  262. @autoreleasepool {
  263. oldValues = readValues(c);
  264. }
  265. }
  266. void after(realm::CollectionChangeSet const& c) {
  267. @autoreleasepool {
  268. auto newValues = readValues(c);
  269. if (deleted) {
  270. block(nil, nil, nil, nil);
  271. }
  272. else if (newValues) {
  273. block(propertyNames, oldValues, newValues, nil);
  274. }
  275. propertyNames = nil;
  276. oldValues = nil;
  277. }
  278. }
  279. void error(std::exception_ptr err) {
  280. @autoreleasepool {
  281. try {
  282. rethrow_exception(err);
  283. }
  284. catch (...) {
  285. NSError *error = nil;
  286. RLMRealmTranslateException(&error);
  287. block(nil, nil, nil, error);
  288. }
  289. }
  290. }
  291. } callback{block, obj};
  292. realm::Object object(obj->_realm->_realm, *obj->_info->objectSchema, obj->_row);
  293. auto token = [[RLMObjectNotificationToken alloc] initWithToken:object.add_notification_callback(callback) realm:obj->_realm];
  294. token->_object = std::move(object);
  295. return token;
  296. }
  297. @implementation RLMPropertyChange
  298. @end