RLMProperty.mm 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684
  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 "RLMProperty_Private.hpp"
  19. #import "RLMArray_Private.hpp"
  20. #import "RLMListBase.h"
  21. #import "RLMObject.h"
  22. #import "RLMObjectSchema_Private.hpp"
  23. #import "RLMObject_Private.h"
  24. #import "RLMSchema_Private.h"
  25. #import "RLMSwiftSupport.h"
  26. #import "RLMUtil.hpp"
  27. #import "property.hpp"
  28. static_assert((int)RLMPropertyTypeInt == (int)realm::PropertyType::Int, "");
  29. static_assert((int)RLMPropertyTypeBool == (int)realm::PropertyType::Bool, "");
  30. static_assert((int)RLMPropertyTypeFloat == (int)realm::PropertyType::Float, "");
  31. static_assert((int)RLMPropertyTypeDouble == (int)realm::PropertyType::Double, "");
  32. static_assert((int)RLMPropertyTypeString == (int)realm::PropertyType::String, "");
  33. static_assert((int)RLMPropertyTypeData == (int)realm::PropertyType::Data, "");
  34. static_assert((int)RLMPropertyTypeDate == (int)realm::PropertyType::Date, "");
  35. static_assert((int)RLMPropertyTypeObject == (int)realm::PropertyType::Object, "");
  36. BOOL RLMPropertyTypeIsComputed(RLMPropertyType propertyType) {
  37. return propertyType == RLMPropertyTypeLinkingObjects;
  38. }
  39. // Swift obeys the ARC naming conventions for method families (except for init)
  40. // but the end result doesn't really work (using KVC on a method returning a
  41. // retained value results in a leak, but not returning a retained value results
  42. // in crashes). Objective-C makes properties with naming fitting the method
  43. // families a compile error, so we just disallow them in Swift as well.
  44. // http://clang.llvm.org/docs/AutomaticReferenceCounting.html#arc-method-families
  45. void RLMValidateSwiftPropertyName(NSString *name) {
  46. // To belong to a method family, the property name must begin with the family
  47. // name followed by a non-lowercase letter (or nothing), with an optional
  48. // leading underscore
  49. const char *str = name.UTF8String;
  50. if (str[0] == '_')
  51. ++str;
  52. auto nameSize = strlen(str);
  53. // Note that "init" is deliberately not in this list because Swift does not
  54. // infer family membership for it.
  55. for (auto family : {"alloc", "new", "copy", "mutableCopy"}) {
  56. auto familySize = strlen(family);
  57. if (nameSize < familySize || !std::equal(str, str + familySize, family)) {
  58. continue;
  59. }
  60. if (familySize == nameSize || !islower(str[familySize])) {
  61. @throw RLMException(@"Property names beginning with '%s' are not "
  62. "supported. Swift follows ARC's ownership "
  63. "rules for methods based on their name, which "
  64. "results in memory leaks when accessing "
  65. "properties which return retained values via KVC.",
  66. family);
  67. }
  68. return;
  69. }
  70. }
  71. static bool rawTypeShouldBeTreatedAsComputedProperty(NSString *rawType) {
  72. return [rawType isEqualToString:@"@\"RLMLinkingObjects\""] || [rawType hasPrefix:@"@\"RLMLinkingObjects<"];
  73. }
  74. @implementation RLMProperty
  75. + (instancetype)propertyForObjectStoreProperty:(const realm::Property &)prop {
  76. auto ret = [[RLMProperty alloc] initWithName:@(prop.name.c_str())
  77. type:static_cast<RLMPropertyType>(prop.type & ~realm::PropertyType::Flags)
  78. objectClassName:prop.object_type.length() ? @(prop.object_type.c_str()) : nil
  79. linkOriginPropertyName:prop.link_origin_property_name.length() ? @(prop.link_origin_property_name.c_str()) : nil
  80. indexed:prop.is_indexed
  81. optional:is_nullable(prop.type)];
  82. if (is_array(prop.type)) {
  83. ret->_array = true;
  84. }
  85. if (!prop.public_name.empty()) {
  86. ret->_columnName = ret->_name;
  87. ret->_name = @(prop.public_name.c_str());
  88. }
  89. return ret;
  90. }
  91. - (instancetype)initWithName:(NSString *)name
  92. type:(RLMPropertyType)type
  93. objectClassName:(NSString *)objectClassName
  94. linkOriginPropertyName:(NSString *)linkOriginPropertyName
  95. indexed:(BOOL)indexed
  96. optional:(BOOL)optional {
  97. self = [super init];
  98. if (self) {
  99. _name = name;
  100. _type = type;
  101. _objectClassName = objectClassName;
  102. _linkOriginPropertyName = linkOriginPropertyName;
  103. _indexed = indexed;
  104. _optional = optional;
  105. [self updateAccessors];
  106. }
  107. return self;
  108. }
  109. - (void)setName:(NSString *)name {
  110. _name = name;
  111. [self updateAccessors];
  112. }
  113. - (void)updateAccessors {
  114. // populate getter/setter names if generic
  115. if (!_getterName) {
  116. _getterName = _name;
  117. }
  118. if (!_setterName) {
  119. // Objective-C setters only capitalize the first letter of the property name if it falls between 'a' and 'z'
  120. int asciiCode = [_name characterAtIndex:0];
  121. BOOL shouldUppercase = asciiCode >= 'a' && asciiCode <= 'z';
  122. NSString *firstChar = [_name substringToIndex:1];
  123. firstChar = shouldUppercase ? firstChar.uppercaseString : firstChar;
  124. _setterName = [NSString stringWithFormat:@"set%@%@:", firstChar, [_name substringFromIndex:1]];
  125. }
  126. _getterSel = NSSelectorFromString(_getterName);
  127. _setterSel = NSSelectorFromString(_setterName);
  128. }
  129. static realm::util::Optional<RLMPropertyType> typeFromProtocolString(const char *type) {
  130. if (strncmp(type, "RLM", 3)) {
  131. return realm::none;
  132. }
  133. type += 3;
  134. if (strcmp(type, "Int>\"") == 0) {
  135. return RLMPropertyTypeInt;
  136. }
  137. if (strcmp(type, "Float>\"") == 0) {
  138. return RLMPropertyTypeFloat;
  139. }
  140. if (strcmp(type, "Double>\"") == 0) {
  141. return RLMPropertyTypeDouble;
  142. }
  143. if (strcmp(type, "Bool>\"") == 0) {
  144. return RLMPropertyTypeBool;
  145. }
  146. if (strcmp(type, "String>\"") == 0) {
  147. return RLMPropertyTypeString;
  148. }
  149. if (strcmp(type, "Data>\"") == 0) {
  150. return RLMPropertyTypeData;
  151. }
  152. if (strcmp(type, "Date>\"") == 0) {
  153. return RLMPropertyTypeDate;
  154. }
  155. return realm::none;
  156. }
  157. // determine RLMPropertyType from objc code - returns true if valid type was found/set
  158. - (BOOL)setTypeFromRawType:(NSString *)rawType {
  159. const char *code = rawType.UTF8String;
  160. switch (*code) {
  161. case 's': // short
  162. case 'i': // int
  163. case 'l': // long
  164. case 'q': // long long
  165. _type = RLMPropertyTypeInt;
  166. return YES;
  167. case 'f':
  168. _type = RLMPropertyTypeFloat;
  169. return YES;
  170. case 'd':
  171. _type = RLMPropertyTypeDouble;
  172. return YES;
  173. case 'c': // BOOL is stored as char - since rlm has no char type this is ok
  174. case 'B':
  175. _type = RLMPropertyTypeBool;
  176. return YES;
  177. case '@':
  178. break;
  179. default:
  180. return NO;
  181. }
  182. _optional = true;
  183. static const char arrayPrefix[] = "@\"RLMArray<";
  184. static const int arrayPrefixLen = sizeof(arrayPrefix) - 1;
  185. static const char numberPrefix[] = "@\"NSNumber<";
  186. static const int numberPrefixLen = sizeof(numberPrefix) - 1;
  187. static const char linkingObjectsPrefix[] = "@\"RLMLinkingObjects";
  188. static const int linkingObjectsPrefixLen = sizeof(linkingObjectsPrefix) - 1;
  189. if (strcmp(code, "@\"NSString\"") == 0) {
  190. _type = RLMPropertyTypeString;
  191. }
  192. else if (strcmp(code, "@\"NSDate\"") == 0) {
  193. _type = RLMPropertyTypeDate;
  194. }
  195. else if (strcmp(code, "@\"NSData\"") == 0) {
  196. _type = RLMPropertyTypeData;
  197. }
  198. else if (strncmp(code, arrayPrefix, arrayPrefixLen) == 0) {
  199. _array = true;
  200. if (auto type = typeFromProtocolString(code + arrayPrefixLen)) {
  201. _type = *type;
  202. return YES;
  203. }
  204. // get object class from type string - @"RLMArray<objectClassName>"
  205. _objectClassName = [[NSString alloc] initWithBytes:code + arrayPrefixLen
  206. length:strlen(code + arrayPrefixLen) - 2 // drop trailing >"
  207. encoding:NSUTF8StringEncoding];
  208. if ([RLMSchema classForString:_objectClassName]) {
  209. _optional = false;
  210. _type = RLMPropertyTypeObject;
  211. return YES;
  212. }
  213. @throw RLMException(@"Property '%@' is of type 'RLMArray<%@>' which is not a supported RLMArray object type. "
  214. @"RLMArrays can only contain instances of RLMObject subclasses. "
  215. @"See https://realm.io/docs/objc/latest/#to-many for more information.", _name, _objectClassName);
  216. }
  217. else if (strncmp(code, numberPrefix, numberPrefixLen) == 0) {
  218. auto type = typeFromProtocolString(code + numberPrefixLen);
  219. if (type && (*type == RLMPropertyTypeInt || *type == RLMPropertyTypeFloat || *type == RLMPropertyTypeDouble || *type == RLMPropertyTypeBool)) {
  220. _type = *type;
  221. return YES;
  222. }
  223. @throw RLMException(@"Property '%@' is of type %s which is not a supported NSNumber object type. "
  224. @"NSNumbers can only be RLMInt, RLMFloat, RLMDouble, and RLMBool at the moment. "
  225. @"See https://realm.io/docs/objc/latest for more information.", _name, code + 1);
  226. }
  227. else if (strncmp(code, linkingObjectsPrefix, linkingObjectsPrefixLen) == 0 &&
  228. (code[linkingObjectsPrefixLen] == '"' || code[linkingObjectsPrefixLen] == '<')) {
  229. _type = RLMPropertyTypeLinkingObjects;
  230. _optional = false;
  231. _array = true;
  232. if (!_objectClassName || !_linkOriginPropertyName) {
  233. @throw RLMException(@"Property '%@' is of type RLMLinkingObjects but +linkingObjectsProperties did not specify the class "
  234. "or property that is the origin of the link.", _name);
  235. }
  236. // If the property was declared with a protocol indicating the contained type, validate that it matches
  237. // the class from the dictionary returned by +linkingObjectsProperties.
  238. if (code[linkingObjectsPrefixLen] == '<') {
  239. NSString *classNameFromProtocol = [[NSString alloc] initWithBytes:code + linkingObjectsPrefixLen + 1
  240. length:strlen(code + linkingObjectsPrefixLen) - 3 // drop trailing >"
  241. encoding:NSUTF8StringEncoding];
  242. if (![_objectClassName isEqualToString:classNameFromProtocol]) {
  243. @throw RLMException(@"Property '%@' was declared with type RLMLinkingObjects<%@>, but a conflicting "
  244. "class name of '%@' was returned by +linkingObjectsProperties.", _name,
  245. classNameFromProtocol, _objectClassName);
  246. }
  247. }
  248. }
  249. else if (strcmp(code, "@\"NSNumber\"") == 0) {
  250. @throw RLMException(@"Property '%@' requires a protocol defining the contained type - example: NSNumber<RLMInt>.", _name);
  251. }
  252. else if (strcmp(code, "@\"RLMArray\"") == 0) {
  253. @throw RLMException(@"Property '%@' requires a protocol defining the contained type - example: RLMArray<Person>.", _name);
  254. }
  255. else {
  256. NSString *className;
  257. Class cls = nil;
  258. if (code[1] == '\0') {
  259. className = @"id";
  260. }
  261. else {
  262. // for objects strip the quotes and @
  263. className = [rawType substringWithRange:NSMakeRange(2, rawType.length-3)];
  264. cls = [RLMSchema classForString:className];
  265. }
  266. if (!cls) {
  267. @throw RLMException(@"Property '%@' is declared as '%@', which is not a supported RLMObject property type. "
  268. @"All properties must be primitives, NSString, NSDate, NSData, NSNumber, RLMArray, RLMLinkingObjects, or subclasses of RLMObject. "
  269. @"See https://realm.io/docs/objc/latest/api/Classes/RLMObject.html for more information.", _name, className);
  270. }
  271. _type = RLMPropertyTypeObject;
  272. _optional = true;
  273. _objectClassName = [cls className] ?: className;
  274. }
  275. return YES;
  276. }
  277. - (void)parseObjcProperty:(objc_property_t)property
  278. readOnly:(bool *)readOnly
  279. computed:(bool *)computed
  280. rawType:(NSString **)rawType {
  281. unsigned int count;
  282. objc_property_attribute_t *attrs = property_copyAttributeList(property, &count);
  283. *computed = true;
  284. for (size_t i = 0; i < count; ++i) {
  285. switch (*attrs[i].name) {
  286. case 'T':
  287. *rawType = @(attrs[i].value);
  288. break;
  289. case 'R':
  290. *readOnly = true;
  291. break;
  292. case 'G':
  293. _getterName = @(attrs[i].value);
  294. break;
  295. case 'S':
  296. _setterName = @(attrs[i].value);
  297. break;
  298. case 'V': // backing ivar name
  299. *computed = false;
  300. break;
  301. case '&':
  302. // retain/assign
  303. break;
  304. case 'C':
  305. // copy
  306. break;
  307. case 'D':
  308. // dynamic
  309. break;
  310. case 'N':
  311. // nonatomic
  312. break;
  313. case 'P':
  314. // GC'able
  315. break;
  316. case 'W':
  317. // weak
  318. break;
  319. default:
  320. break;
  321. }
  322. }
  323. free(attrs);
  324. }
  325. - (instancetype)initSwiftPropertyWithName:(NSString *)name
  326. indexed:(BOOL)indexed
  327. linkPropertyDescriptor:(RLMPropertyDescriptor *)linkPropertyDescriptor
  328. property:(objc_property_t)property
  329. instance:(RLMObject *)obj {
  330. self = [super init];
  331. if (!self) {
  332. return nil;
  333. }
  334. RLMValidateSwiftPropertyName(name);
  335. _name = name;
  336. _indexed = indexed;
  337. if (linkPropertyDescriptor) {
  338. _objectClassName = [linkPropertyDescriptor.objectClass className];
  339. _linkOriginPropertyName = linkPropertyDescriptor.propertyName;
  340. }
  341. NSString *rawType;
  342. bool readOnly = false;
  343. bool isComputed = false;
  344. [self parseObjcProperty:property readOnly:&readOnly computed:&isComputed rawType:&rawType];
  345. // Swift sometimes doesn't explicitly set the ivar name in the metadata, so check if
  346. // there's an ivar with the same name as the property.
  347. if (!readOnly && isComputed && class_getInstanceVariable([obj class], name.UTF8String)) {
  348. isComputed = false;
  349. }
  350. // Check if there's a storage ivar for a lazy property in this name. We don't honor
  351. // @lazy in managed objects, but allow it for unmanaged objects which are
  352. // subclasses of RLMObject (but not RealmSwift.Object). It's unclear if there's a
  353. // good reason for this difference.
  354. if (!readOnly && isComputed) {
  355. // Xcode 10 and earlier
  356. NSString *backingPropertyName = [NSString stringWithFormat:@"%@.storage", name];
  357. isComputed = !class_getInstanceVariable([obj class], backingPropertyName.UTF8String);
  358. }
  359. if (!readOnly && isComputed) {
  360. // Xcode 11
  361. NSString *backingPropertyName = [NSString stringWithFormat:@"$__lazy_storage_$_%@", name];
  362. isComputed = !class_getInstanceVariable([obj class], backingPropertyName.UTF8String);
  363. }
  364. if (readOnly || isComputed) {
  365. return nil;
  366. }
  367. id propertyValue = [obj valueForKey:_name];
  368. // FIXME: temporarily workaround added since Objective-C generics used in Swift show up as `@`
  369. // * broken starting in Swift 3.0 Xcode 8 b1
  370. // * tested to still be broken in Swift 3.0 Xcode 8 b6
  371. // * if the Realm Objective-C Swift tests pass with this removed, it's been fixed
  372. // * once it has been fixed, remove this entire conditional block (contents included) entirely
  373. // * Bug Report: SR-2031 https://bugs.swift.org/browse/SR-2031
  374. if ([rawType isEqualToString:@"@"]) {
  375. if (propertyValue) {
  376. rawType = [NSString stringWithFormat:@"@\"%@\"", [propertyValue class]];
  377. } else if (linkPropertyDescriptor) {
  378. // we're going to naively assume that the user used the correct type since we can't check it
  379. rawType = @"@\"RLMLinkingObjects\"";
  380. }
  381. }
  382. // convert array types to objc variant
  383. if ([rawType isEqualToString:@"@\"RLMArray\""]) {
  384. RLMArray *value = propertyValue;
  385. _type = value.type;
  386. _optional = value.optional;
  387. _array = true;
  388. _objectClassName = value.objectClassName;
  389. if (_type == RLMPropertyTypeObject && ![RLMSchema classForString:_objectClassName]) {
  390. @throw RLMException(@"Property '%@' is of type 'RLMArray<%@>' which is not a supported RLMArray object type. "
  391. @"RLMArrays can only contain instances of RLMObject subclasses. "
  392. @"See https://realm.io/docs/objc/latest/#to-many for more information.", _name, _objectClassName);
  393. }
  394. }
  395. else if ([rawType isEqualToString:@"@\"NSNumber\""]) {
  396. const char *numberType = [propertyValue objCType];
  397. if (!numberType) {
  398. @throw RLMException(@"Can't persist NSNumber without default value: use a Swift-native number type or provide a default value.");
  399. }
  400. _optional = true;
  401. switch (*numberType) {
  402. case 'i': case 'l': case 'q':
  403. _type = RLMPropertyTypeInt;
  404. break;
  405. case 'f':
  406. _type = RLMPropertyTypeFloat;
  407. break;
  408. case 'd':
  409. _type = RLMPropertyTypeDouble;
  410. break;
  411. case 'B': case 'c':
  412. _type = RLMPropertyTypeBool;
  413. break;
  414. default:
  415. @throw RLMException(@"Can't persist NSNumber of type '%s': only integers, floats, doubles, and bools are currently supported.", numberType);
  416. }
  417. }
  418. else if (![self setTypeFromRawType:rawType]) {
  419. @throw RLMException(@"Can't persist property '%@' with incompatible type. "
  420. "Add to Object.ignoredProperties() class method to ignore.",
  421. self.name);
  422. }
  423. if ([rawType isEqualToString:@"c"]) {
  424. // Check if it's a BOOL or Int8 by trying to set it to 2 and seeing if
  425. // it actually sets it to 1.
  426. [obj setValue:@2 forKey:name];
  427. NSNumber *value = [obj valueForKey:name];
  428. _type = value.intValue == 2 ? RLMPropertyTypeInt : RLMPropertyTypeBool;
  429. }
  430. // update getter/setter names
  431. [self updateAccessors];
  432. return self;
  433. }
  434. - (instancetype)initWithName:(NSString *)name
  435. indexed:(BOOL)indexed
  436. linkPropertyDescriptor:(RLMPropertyDescriptor *)linkPropertyDescriptor
  437. property:(objc_property_t)property
  438. {
  439. self = [super init];
  440. if (!self) {
  441. return nil;
  442. }
  443. _name = name;
  444. _indexed = indexed;
  445. if (linkPropertyDescriptor) {
  446. _objectClassName = [linkPropertyDescriptor.objectClass className];
  447. _linkOriginPropertyName = linkPropertyDescriptor.propertyName;
  448. }
  449. NSString *rawType;
  450. bool isReadOnly = false;
  451. bool isComputed = false;
  452. [self parseObjcProperty:property readOnly:&isReadOnly computed:&isComputed rawType:&rawType];
  453. bool shouldBeTreatedAsComputedProperty = rawTypeShouldBeTreatedAsComputedProperty(rawType);
  454. if ((isReadOnly || isComputed) && !shouldBeTreatedAsComputedProperty) {
  455. return nil;
  456. }
  457. if (![self setTypeFromRawType:rawType]) {
  458. @throw RLMException(@"Can't persist property '%@' with incompatible type. "
  459. "Add to ignoredPropertyNames: method to ignore.", self.name);
  460. }
  461. if (!isReadOnly && shouldBeTreatedAsComputedProperty) {
  462. @throw RLMException(@"Property '%@' must be declared as readonly as %@ properties cannot be written to.",
  463. self.name, RLMTypeToString(_type));
  464. }
  465. // update getter/setter names
  466. [self updateAccessors];
  467. return self;
  468. }
  469. - (instancetype)initSwiftListPropertyWithName:(NSString *)name
  470. instance:(id)object {
  471. self = [super init];
  472. if (!self) {
  473. return nil;
  474. }
  475. _name = name;
  476. _array = true;
  477. _swiftIvar = class_getInstanceVariable([object class], name.UTF8String);
  478. RLMArray *array = [object_getIvar(object, _swiftIvar) _rlmArray];
  479. _type = array.type;
  480. _optional = array.optional;
  481. _objectClassName = array.objectClassName;
  482. // no obj-c property for generic lists, and thus no getter/setter names
  483. return self;
  484. }
  485. - (instancetype)initSwiftOptionalPropertyWithName:(NSString *)name
  486. indexed:(BOOL)indexed
  487. ivar:(Ivar)ivar
  488. propertyType:(RLMPropertyType)propertyType {
  489. self = [super init];
  490. if (!self) {
  491. return nil;
  492. }
  493. _name = name;
  494. _type = propertyType;
  495. _indexed = indexed;
  496. _swiftIvar = ivar;
  497. _optional = true;
  498. // no obj-c property for generic optionals, and thus no getter/setter names
  499. return self;
  500. }
  501. - (instancetype)initSwiftLinkingObjectsPropertyWithName:(NSString *)name
  502. ivar:(Ivar)ivar
  503. objectClassName:(NSString *)objectClassName
  504. linkOriginPropertyName:(NSString *)linkOriginPropertyName {
  505. self = [super init];
  506. if (!self) {
  507. return nil;
  508. }
  509. _name = name;
  510. _type = RLMPropertyTypeLinkingObjects;
  511. _array = true;
  512. _objectClassName = objectClassName;
  513. _linkOriginPropertyName = linkOriginPropertyName;
  514. _swiftIvar = ivar;
  515. // no obj-c property for generic linking objects properties, and thus no getter/setter names
  516. return self;
  517. }
  518. - (id)copyWithZone:(NSZone *)zone {
  519. RLMProperty *prop = [[RLMProperty allocWithZone:zone] init];
  520. prop->_name = _name;
  521. prop->_columnName = _columnName;
  522. prop->_type = _type;
  523. prop->_objectClassName = _objectClassName;
  524. prop->_array = _array;
  525. prop->_indexed = _indexed;
  526. prop->_getterName = _getterName;
  527. prop->_setterName = _setterName;
  528. prop->_getterSel = _getterSel;
  529. prop->_setterSel = _setterSel;
  530. prop->_isPrimary = _isPrimary;
  531. prop->_swiftIvar = _swiftIvar;
  532. prop->_optional = _optional;
  533. prop->_linkOriginPropertyName = _linkOriginPropertyName;
  534. return prop;
  535. }
  536. - (RLMProperty *)copyWithNewName:(NSString *)name {
  537. RLMProperty *prop = [self copy];
  538. prop.name = name;
  539. return prop;
  540. }
  541. - (BOOL)isEqual:(id)object {
  542. if (![object isKindOfClass:[RLMProperty class]]) {
  543. return NO;
  544. }
  545. return [self isEqualToProperty:object];
  546. }
  547. - (BOOL)isEqualToProperty:(RLMProperty *)property {
  548. return _type == property->_type
  549. && _indexed == property->_indexed
  550. && _isPrimary == property->_isPrimary
  551. && _optional == property->_optional
  552. && [_name isEqualToString:property->_name]
  553. && (_objectClassName == property->_objectClassName || [_objectClassName isEqualToString:property->_objectClassName])
  554. && (_linkOriginPropertyName == property->_linkOriginPropertyName ||
  555. [_linkOriginPropertyName isEqualToString:property->_linkOriginPropertyName]);
  556. }
  557. - (NSString *)description {
  558. return [NSString stringWithFormat:
  559. @"%@ {\n"
  560. "\ttype = %@;\n"
  561. "\tobjectClassName = %@;\n"
  562. "\tlinkOriginPropertyName = %@;\n"
  563. "\tindexed = %@;\n"
  564. "\tisPrimary = %@;\n"
  565. "\tarray = %@;\n"
  566. "\toptional = %@;\n"
  567. "}",
  568. self.name, RLMTypeToString(self.type), self.objectClassName,
  569. self.linkOriginPropertyName,
  570. self.indexed ? @"YES" : @"NO",
  571. self.isPrimary ? @"YES" : @"NO",
  572. self.array ? @"YES" : @"NO",
  573. self.optional ? @"YES" : @"NO"];
  574. }
  575. - (NSString *)columnName {
  576. return _columnName ?: _name;
  577. }
  578. - (realm::Property)objectStoreCopy:(RLMSchema *)schema {
  579. realm::Property p;
  580. p.name = self.columnName.UTF8String;
  581. if (_objectClassName) {
  582. RLMObjectSchema *targetSchema = schema[_objectClassName];
  583. p.object_type = (targetSchema.objectName ?: _objectClassName).UTF8String;
  584. if (_linkOriginPropertyName) {
  585. p.link_origin_property_name = (targetSchema[_linkOriginPropertyName].columnName ?: _linkOriginPropertyName).UTF8String;
  586. }
  587. }
  588. p.is_indexed = static_cast<bool>(_indexed);
  589. p.type = static_cast<realm::PropertyType>(_type);
  590. if (_array) {
  591. p.type |= realm::PropertyType::Array;
  592. }
  593. if (_optional) {
  594. p.type |= realm::PropertyType::Nullable;
  595. }
  596. return p;
  597. }
  598. @end
  599. @implementation RLMPropertyDescriptor
  600. + (instancetype)descriptorWithClass:(Class)objectClass propertyName:(NSString *)propertyName
  601. {
  602. RLMPropertyDescriptor *descriptor = [[RLMPropertyDescriptor alloc] init];
  603. descriptor->_objectClass = objectClass;
  604. descriptor->_propertyName = propertyName;
  605. return descriptor;
  606. }
  607. @end