123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684 |
- #import "RLMProperty_Private.hpp"
- #import "RLMArray_Private.hpp"
- #import "RLMListBase.h"
- #import "RLMObject.h"
- #import "RLMObjectSchema_Private.hpp"
- #import "RLMObject_Private.h"
- #import "RLMSchema_Private.h"
- #import "RLMSwiftSupport.h"
- #import "RLMUtil.hpp"
- #import "property.hpp"
- static_assert((int)RLMPropertyTypeInt == (int)realm::PropertyType::Int, "");
- static_assert((int)RLMPropertyTypeBool == (int)realm::PropertyType::Bool, "");
- static_assert((int)RLMPropertyTypeFloat == (int)realm::PropertyType::Float, "");
- static_assert((int)RLMPropertyTypeDouble == (int)realm::PropertyType::Double, "");
- static_assert((int)RLMPropertyTypeString == (int)realm::PropertyType::String, "");
- static_assert((int)RLMPropertyTypeData == (int)realm::PropertyType::Data, "");
- static_assert((int)RLMPropertyTypeDate == (int)realm::PropertyType::Date, "");
- static_assert((int)RLMPropertyTypeObject == (int)realm::PropertyType::Object, "");
- BOOL RLMPropertyTypeIsComputed(RLMPropertyType propertyType) {
- return propertyType == RLMPropertyTypeLinkingObjects;
- }
- void RLMValidateSwiftPropertyName(NSString *name) {
-
-
-
- const char *str = name.UTF8String;
- if (str[0] == '_')
- ++str;
- auto nameSize = strlen(str);
-
-
- for (auto family : {"alloc", "new", "copy", "mutableCopy"}) {
- auto familySize = strlen(family);
- if (nameSize < familySize || !std::equal(str, str + familySize, family)) {
- continue;
- }
- if (familySize == nameSize || !islower(str[familySize])) {
- @throw RLMException(@"Property names beginning with '%s' are not "
- "supported. Swift follows ARC's ownership "
- "rules for methods based on their name, which "
- "results in memory leaks when accessing "
- "properties which return retained values via KVC.",
- family);
- }
- return;
- }
- }
- static bool rawTypeShouldBeTreatedAsComputedProperty(NSString *rawType) {
- return [rawType isEqualToString:@"@\"RLMLinkingObjects\""] || [rawType hasPrefix:@"@\"RLMLinkingObjects<"];
- }
- @implementation RLMProperty
- + (instancetype)propertyForObjectStoreProperty:(const realm::Property &)prop {
- auto ret = [[RLMProperty alloc] initWithName:@(prop.name.c_str())
- type:static_cast<RLMPropertyType>(prop.type & ~realm::PropertyType::Flags)
- objectClassName:prop.object_type.length() ? @(prop.object_type.c_str()) : nil
- linkOriginPropertyName:prop.link_origin_property_name.length() ? @(prop.link_origin_property_name.c_str()) : nil
- indexed:prop.is_indexed
- optional:is_nullable(prop.type)];
- if (is_array(prop.type)) {
- ret->_array = true;
- }
- if (!prop.public_name.empty()) {
- ret->_columnName = ret->_name;
- ret->_name = @(prop.public_name.c_str());
- }
- return ret;
- }
- - (instancetype)initWithName:(NSString *)name
- type:(RLMPropertyType)type
- objectClassName:(NSString *)objectClassName
- linkOriginPropertyName:(NSString *)linkOriginPropertyName
- indexed:(BOOL)indexed
- optional:(BOOL)optional {
- self = [super init];
- if (self) {
- _name = name;
- _type = type;
- _objectClassName = objectClassName;
- _linkOriginPropertyName = linkOriginPropertyName;
- _indexed = indexed;
- _optional = optional;
- [self updateAccessors];
- }
- return self;
- }
- - (void)setName:(NSString *)name {
- _name = name;
- [self updateAccessors];
- }
- - (void)updateAccessors {
-
- if (!_getterName) {
- _getterName = _name;
- }
- if (!_setterName) {
-
- int asciiCode = [_name characterAtIndex:0];
- BOOL shouldUppercase = asciiCode >= 'a' && asciiCode <= 'z';
- NSString *firstChar = [_name substringToIndex:1];
- firstChar = shouldUppercase ? firstChar.uppercaseString : firstChar;
- _setterName = [NSString stringWithFormat:@"set%@%@:", firstChar, [_name substringFromIndex:1]];
- }
- _getterSel = NSSelectorFromString(_getterName);
- _setterSel = NSSelectorFromString(_setterName);
- }
- static realm::util::Optional<RLMPropertyType> typeFromProtocolString(const char *type) {
- if (strncmp(type, "RLM", 3)) {
- return realm::none;
- }
- type += 3;
- if (strcmp(type, "Int>\"") == 0) {
- return RLMPropertyTypeInt;
- }
- if (strcmp(type, "Float>\"") == 0) {
- return RLMPropertyTypeFloat;
- }
- if (strcmp(type, "Double>\"") == 0) {
- return RLMPropertyTypeDouble;
- }
- if (strcmp(type, "Bool>\"") == 0) {
- return RLMPropertyTypeBool;
- }
- if (strcmp(type, "String>\"") == 0) {
- return RLMPropertyTypeString;
- }
- if (strcmp(type, "Data>\"") == 0) {
- return RLMPropertyTypeData;
- }
- if (strcmp(type, "Date>\"") == 0) {
- return RLMPropertyTypeDate;
- }
- return realm::none;
- }
- - (BOOL)setTypeFromRawType:(NSString *)rawType {
- const char *code = rawType.UTF8String;
- switch (*code) {
- case 's':
- case 'i':
- case 'l':
- case 'q':
- _type = RLMPropertyTypeInt;
- return YES;
- case 'f':
- _type = RLMPropertyTypeFloat;
- return YES;
- case 'd':
- _type = RLMPropertyTypeDouble;
- return YES;
- case 'c':
- case 'B':
- _type = RLMPropertyTypeBool;
- return YES;
- case '@':
- break;
- default:
- return NO;
- }
- _optional = true;
- static const char arrayPrefix[] = "@\"RLMArray<";
- static const int arrayPrefixLen = sizeof(arrayPrefix) - 1;
- static const char numberPrefix[] = "@\"NSNumber<";
- static const int numberPrefixLen = sizeof(numberPrefix) - 1;
- static const char linkingObjectsPrefix[] = "@\"RLMLinkingObjects";
- static const int linkingObjectsPrefixLen = sizeof(linkingObjectsPrefix) - 1;
- if (strcmp(code, "@\"NSString\"") == 0) {
- _type = RLMPropertyTypeString;
- }
- else if (strcmp(code, "@\"NSDate\"") == 0) {
- _type = RLMPropertyTypeDate;
- }
- else if (strcmp(code, "@\"NSData\"") == 0) {
- _type = RLMPropertyTypeData;
- }
- else if (strncmp(code, arrayPrefix, arrayPrefixLen) == 0) {
- _array = true;
- if (auto type = typeFromProtocolString(code + arrayPrefixLen)) {
- _type = *type;
- return YES;
- }
-
- _objectClassName = [[NSString alloc] initWithBytes:code + arrayPrefixLen
- length:strlen(code + arrayPrefixLen) - 2
- encoding:NSUTF8StringEncoding];
- if ([RLMSchema classForString:_objectClassName]) {
- _optional = false;
- _type = RLMPropertyTypeObject;
- return YES;
- }
- @throw RLMException(@"Property '%@' is of type 'RLMArray<%@>' which is not a supported RLMArray object type. "
- @"RLMArrays can only contain instances of RLMObject subclasses. "
- @"See https://realm.io/docs/objc/latest/#to-many for more information.", _name, _objectClassName);
- }
- else if (strncmp(code, numberPrefix, numberPrefixLen) == 0) {
- auto type = typeFromProtocolString(code + numberPrefixLen);
- if (type && (*type == RLMPropertyTypeInt || *type == RLMPropertyTypeFloat || *type == RLMPropertyTypeDouble || *type == RLMPropertyTypeBool)) {
- _type = *type;
- return YES;
- }
- @throw RLMException(@"Property '%@' is of type %s which is not a supported NSNumber object type. "
- @"NSNumbers can only be RLMInt, RLMFloat, RLMDouble, and RLMBool at the moment. "
- @"See https://realm.io/docs/objc/latest for more information.", _name, code + 1);
- }
- else if (strncmp(code, linkingObjectsPrefix, linkingObjectsPrefixLen) == 0 &&
- (code[linkingObjectsPrefixLen] == '"' || code[linkingObjectsPrefixLen] == '<')) {
- _type = RLMPropertyTypeLinkingObjects;
- _optional = false;
- _array = true;
- if (!_objectClassName || !_linkOriginPropertyName) {
- @throw RLMException(@"Property '%@' is of type RLMLinkingObjects but +linkingObjectsProperties did not specify the class "
- "or property that is the origin of the link.", _name);
- }
-
-
- if (code[linkingObjectsPrefixLen] == '<') {
- NSString *classNameFromProtocol = [[NSString alloc] initWithBytes:code + linkingObjectsPrefixLen + 1
- length:strlen(code + linkingObjectsPrefixLen) - 3
- encoding:NSUTF8StringEncoding];
- if (![_objectClassName isEqualToString:classNameFromProtocol]) {
- @throw RLMException(@"Property '%@' was declared with type RLMLinkingObjects<%@>, but a conflicting "
- "class name of '%@' was returned by +linkingObjectsProperties.", _name,
- classNameFromProtocol, _objectClassName);
- }
- }
- }
- else if (strcmp(code, "@\"NSNumber\"") == 0) {
- @throw RLMException(@"Property '%@' requires a protocol defining the contained type - example: NSNumber<RLMInt>.", _name);
- }
- else if (strcmp(code, "@\"RLMArray\"") == 0) {
- @throw RLMException(@"Property '%@' requires a protocol defining the contained type - example: RLMArray<Person>.", _name);
- }
- else {
- NSString *className;
- Class cls = nil;
- if (code[1] == '\0') {
- className = @"id";
- }
- else {
-
- className = [rawType substringWithRange:NSMakeRange(2, rawType.length-3)];
- cls = [RLMSchema classForString:className];
- }
- if (!cls) {
- @throw RLMException(@"Property '%@' is declared as '%@', which is not a supported RLMObject property type. "
- @"All properties must be primitives, NSString, NSDate, NSData, NSNumber, RLMArray, RLMLinkingObjects, or subclasses of RLMObject. "
- @"See https://realm.io/docs/objc/latest/api/Classes/RLMObject.html for more information.", _name, className);
- }
- _type = RLMPropertyTypeObject;
- _optional = true;
- _objectClassName = [cls className] ?: className;
- }
- return YES;
- }
- - (void)parseObjcProperty:(objc_property_t)property
- readOnly:(bool *)readOnly
- computed:(bool *)computed
- rawType:(NSString **)rawType {
- unsigned int count;
- objc_property_attribute_t *attrs = property_copyAttributeList(property, &count);
- *computed = true;
- for (size_t i = 0; i < count; ++i) {
- switch (*attrs[i].name) {
- case 'T':
- *rawType = @(attrs[i].value);
- break;
- case 'R':
- *readOnly = true;
- break;
- case 'G':
- _getterName = @(attrs[i].value);
- break;
- case 'S':
- _setterName = @(attrs[i].value);
- break;
- case 'V':
- *computed = false;
- break;
- case '&':
-
- break;
- case 'C':
-
- break;
- case 'D':
-
- break;
- case 'N':
-
- break;
- case 'P':
-
- break;
- case 'W':
-
- break;
- default:
- break;
- }
- }
- free(attrs);
- }
- - (instancetype)initSwiftPropertyWithName:(NSString *)name
- indexed:(BOOL)indexed
- linkPropertyDescriptor:(RLMPropertyDescriptor *)linkPropertyDescriptor
- property:(objc_property_t)property
- instance:(RLMObject *)obj {
- self = [super init];
- if (!self) {
- return nil;
- }
- RLMValidateSwiftPropertyName(name);
- _name = name;
- _indexed = indexed;
- if (linkPropertyDescriptor) {
- _objectClassName = [linkPropertyDescriptor.objectClass className];
- _linkOriginPropertyName = linkPropertyDescriptor.propertyName;
- }
- NSString *rawType;
- bool readOnly = false;
- bool isComputed = false;
- [self parseObjcProperty:property readOnly:&readOnly computed:&isComputed rawType:&rawType];
-
-
- if (!readOnly && isComputed && class_getInstanceVariable([obj class], name.UTF8String)) {
- isComputed = false;
- }
-
-
-
-
- if (!readOnly && isComputed) {
-
- NSString *backingPropertyName = [NSString stringWithFormat:@"%@.storage", name];
- isComputed = !class_getInstanceVariable([obj class], backingPropertyName.UTF8String);
- }
- if (!readOnly && isComputed) {
-
- NSString *backingPropertyName = [NSString stringWithFormat:@"$__lazy_storage_$_%@", name];
- isComputed = !class_getInstanceVariable([obj class], backingPropertyName.UTF8String);
- }
- if (readOnly || isComputed) {
- return nil;
- }
- id propertyValue = [obj valueForKey:_name];
-
-
-
-
-
-
- if ([rawType isEqualToString:@"@"]) {
- if (propertyValue) {
- rawType = [NSString stringWithFormat:@"@\"%@\"", [propertyValue class]];
- } else if (linkPropertyDescriptor) {
-
- rawType = @"@\"RLMLinkingObjects\"";
- }
- }
-
- if ([rawType isEqualToString:@"@\"RLMArray\""]) {
- RLMArray *value = propertyValue;
- _type = value.type;
- _optional = value.optional;
- _array = true;
- _objectClassName = value.objectClassName;
- if (_type == RLMPropertyTypeObject && ![RLMSchema classForString:_objectClassName]) {
- @throw RLMException(@"Property '%@' is of type 'RLMArray<%@>' which is not a supported RLMArray object type. "
- @"RLMArrays can only contain instances of RLMObject subclasses. "
- @"See https://realm.io/docs/objc/latest/#to-many for more information.", _name, _objectClassName);
- }
- }
- else if ([rawType isEqualToString:@"@\"NSNumber\""]) {
- const char *numberType = [propertyValue objCType];
- if (!numberType) {
- @throw RLMException(@"Can't persist NSNumber without default value: use a Swift-native number type or provide a default value.");
- }
- _optional = true;
- switch (*numberType) {
- case 'i': case 'l': case 'q':
- _type = RLMPropertyTypeInt;
- break;
- case 'f':
- _type = RLMPropertyTypeFloat;
- break;
- case 'd':
- _type = RLMPropertyTypeDouble;
- break;
- case 'B': case 'c':
- _type = RLMPropertyTypeBool;
- break;
- default:
- @throw RLMException(@"Can't persist NSNumber of type '%s': only integers, floats, doubles, and bools are currently supported.", numberType);
- }
- }
- else if (![self setTypeFromRawType:rawType]) {
- @throw RLMException(@"Can't persist property '%@' with incompatible type. "
- "Add to Object.ignoredProperties() class method to ignore.",
- self.name);
- }
- if ([rawType isEqualToString:@"c"]) {
-
-
- [obj setValue:@2 forKey:name];
- NSNumber *value = [obj valueForKey:name];
- _type = value.intValue == 2 ? RLMPropertyTypeInt : RLMPropertyTypeBool;
- }
-
- [self updateAccessors];
- return self;
- }
- - (instancetype)initWithName:(NSString *)name
- indexed:(BOOL)indexed
- linkPropertyDescriptor:(RLMPropertyDescriptor *)linkPropertyDescriptor
- property:(objc_property_t)property
- {
- self = [super init];
- if (!self) {
- return nil;
- }
- _name = name;
- _indexed = indexed;
- if (linkPropertyDescriptor) {
- _objectClassName = [linkPropertyDescriptor.objectClass className];
- _linkOriginPropertyName = linkPropertyDescriptor.propertyName;
- }
- NSString *rawType;
- bool isReadOnly = false;
- bool isComputed = false;
- [self parseObjcProperty:property readOnly:&isReadOnly computed:&isComputed rawType:&rawType];
- bool shouldBeTreatedAsComputedProperty = rawTypeShouldBeTreatedAsComputedProperty(rawType);
- if ((isReadOnly || isComputed) && !shouldBeTreatedAsComputedProperty) {
- return nil;
- }
- if (![self setTypeFromRawType:rawType]) {
- @throw RLMException(@"Can't persist property '%@' with incompatible type. "
- "Add to ignoredPropertyNames: method to ignore.", self.name);
- }
- if (!isReadOnly && shouldBeTreatedAsComputedProperty) {
- @throw RLMException(@"Property '%@' must be declared as readonly as %@ properties cannot be written to.",
- self.name, RLMTypeToString(_type));
- }
-
- [self updateAccessors];
- return self;
- }
- - (instancetype)initSwiftListPropertyWithName:(NSString *)name
- instance:(id)object {
- self = [super init];
- if (!self) {
- return nil;
- }
- _name = name;
- _array = true;
- _swiftIvar = class_getInstanceVariable([object class], name.UTF8String);
- RLMArray *array = [object_getIvar(object, _swiftIvar) _rlmArray];
- _type = array.type;
- _optional = array.optional;
- _objectClassName = array.objectClassName;
-
- return self;
- }
- - (instancetype)initSwiftOptionalPropertyWithName:(NSString *)name
- indexed:(BOOL)indexed
- ivar:(Ivar)ivar
- propertyType:(RLMPropertyType)propertyType {
- self = [super init];
- if (!self) {
- return nil;
- }
- _name = name;
- _type = propertyType;
- _indexed = indexed;
- _swiftIvar = ivar;
- _optional = true;
-
- return self;
- }
- - (instancetype)initSwiftLinkingObjectsPropertyWithName:(NSString *)name
- ivar:(Ivar)ivar
- objectClassName:(NSString *)objectClassName
- linkOriginPropertyName:(NSString *)linkOriginPropertyName {
- self = [super init];
- if (!self) {
- return nil;
- }
- _name = name;
- _type = RLMPropertyTypeLinkingObjects;
- _array = true;
- _objectClassName = objectClassName;
- _linkOriginPropertyName = linkOriginPropertyName;
- _swiftIvar = ivar;
-
- return self;
- }
- - (id)copyWithZone:(NSZone *)zone {
- RLMProperty *prop = [[RLMProperty allocWithZone:zone] init];
- prop->_name = _name;
- prop->_columnName = _columnName;
- prop->_type = _type;
- prop->_objectClassName = _objectClassName;
- prop->_array = _array;
- prop->_indexed = _indexed;
- prop->_getterName = _getterName;
- prop->_setterName = _setterName;
- prop->_getterSel = _getterSel;
- prop->_setterSel = _setterSel;
- prop->_isPrimary = _isPrimary;
- prop->_swiftIvar = _swiftIvar;
- prop->_optional = _optional;
- prop->_linkOriginPropertyName = _linkOriginPropertyName;
- return prop;
- }
- - (RLMProperty *)copyWithNewName:(NSString *)name {
- RLMProperty *prop = [self copy];
- prop.name = name;
- return prop;
- }
- - (BOOL)isEqual:(id)object {
- if (![object isKindOfClass:[RLMProperty class]]) {
- return NO;
- }
- return [self isEqualToProperty:object];
- }
- - (BOOL)isEqualToProperty:(RLMProperty *)property {
- return _type == property->_type
- && _indexed == property->_indexed
- && _isPrimary == property->_isPrimary
- && _optional == property->_optional
- && [_name isEqualToString:property->_name]
- && (_objectClassName == property->_objectClassName || [_objectClassName isEqualToString:property->_objectClassName])
- && (_linkOriginPropertyName == property->_linkOriginPropertyName ||
- [_linkOriginPropertyName isEqualToString:property->_linkOriginPropertyName]);
- }
- - (NSString *)description {
- return [NSString stringWithFormat:
- @"%@ {\n"
- "\ttype = %@;\n"
- "\tobjectClassName = %@;\n"
- "\tlinkOriginPropertyName = %@;\n"
- "\tindexed = %@;\n"
- "\tisPrimary = %@;\n"
- "\tarray = %@;\n"
- "\toptional = %@;\n"
- "}",
- self.name, RLMTypeToString(self.type), self.objectClassName,
- self.linkOriginPropertyName,
- self.indexed ? @"YES" : @"NO",
- self.isPrimary ? @"YES" : @"NO",
- self.array ? @"YES" : @"NO",
- self.optional ? @"YES" : @"NO"];
- }
- - (NSString *)columnName {
- return _columnName ?: _name;
- }
- - (realm::Property)objectStoreCopy:(RLMSchema *)schema {
- realm::Property p;
- p.name = self.columnName.UTF8String;
- if (_objectClassName) {
- RLMObjectSchema *targetSchema = schema[_objectClassName];
- p.object_type = (targetSchema.objectName ?: _objectClassName).UTF8String;
- if (_linkOriginPropertyName) {
- p.link_origin_property_name = (targetSchema[_linkOriginPropertyName].columnName ?: _linkOriginPropertyName).UTF8String;
- }
- }
- p.is_indexed = static_cast<bool>(_indexed);
- p.type = static_cast<realm::PropertyType>(_type);
- if (_array) {
- p.type |= realm::PropertyType::Array;
- }
- if (_optional) {
- p.type |= realm::PropertyType::Nullable;
- }
- return p;
- }
- @end
- @implementation RLMPropertyDescriptor
- + (instancetype)descriptorWithClass:(Class)objectClass propertyName:(NSString *)propertyName
- {
- RLMPropertyDescriptor *descriptor = [[RLMPropertyDescriptor alloc] init];
- descriptor->_objectClass = objectClass;
- descriptor->_propertyName = propertyName;
- return descriptor;
- }
- @end
|