// // FLEXPropertyAttributes.m // FLEX // // Derived from MirrorKit. // Created by Tanner on 7/5/15. // Copyright (c) 2020 FLEX Team. All rights reserved. // #import "FLEXPropertyAttributes.h" #import "FLEXRuntimeUtility.h" #import "NSString+ObjcRuntime.h" #import "NSDictionary+ObjcRuntime.h" #pragma mark FLEXPropertyAttributes @interface FLEXPropertyAttributes () @property (nonatomic) NSString *backingIvar; @property (nonatomic) NSString *typeEncoding; @property (nonatomic) NSString *oldTypeEncoding; @property (nonatomic) SEL customGetter; @property (nonatomic) SEL customSetter; @property (nonatomic) BOOL isReadOnly; @property (nonatomic) BOOL isCopy; @property (nonatomic) BOOL isRetained; @property (nonatomic) BOOL isNonatomic; @property (nonatomic) BOOL isDynamic; @property (nonatomic) BOOL isWeak; @property (nonatomic) BOOL isGarbageCollectable; - (NSString *)buildFullDeclaration; @end @implementation FLEXPropertyAttributes @synthesize list = _list; #pragma mark Initializers + (instancetype)attributesForProperty:(objc_property_t)property { return [self attributesFromDictionary:[NSDictionary attributesDictionaryForProperty:property]]; } + (instancetype)attributesFromDictionary:(NSDictionary *)attributes { return [[self alloc] initWithAttributesDictionary:attributes]; } - (id)initWithAttributesDictionary:(NSDictionary *)attributes { NSParameterAssert(attributes); self = [super init]; if (self) { _dictionary = attributes; _string = attributes.propertyAttributesString; _count = attributes.count; _typeEncoding = attributes[kFLEXPropertyAttributeKeyTypeEncoding]; _backingIvar = attributes[kFLEXPropertyAttributeKeyBackingIvarName]; _oldTypeEncoding = attributes[kFLEXPropertyAttributeKeyOldStyleTypeEncoding]; _customGetterString = attributes[kFLEXPropertyAttributeKeyCustomGetter]; _customSetterString = attributes[kFLEXPropertyAttributeKeyCustomSetter]; _customGetter = NSSelectorFromString(_customGetterString); _customSetter = NSSelectorFromString(_customSetterString); _isReadOnly = attributes[kFLEXPropertyAttributeKeyReadOnly] != nil; _isCopy = attributes[kFLEXPropertyAttributeKeyCopy] != nil; _isRetained = attributes[kFLEXPropertyAttributeKeyRetain] != nil; _isNonatomic = attributes[kFLEXPropertyAttributeKeyNonAtomic] != nil; _isWeak = attributes[kFLEXPropertyAttributeKeyWeak] != nil; _isGarbageCollectable = attributes[kFLEXPropertyAttributeKeyGarbageCollectable] != nil; _fullDeclaration = [self buildFullDeclaration]; } return self; } #pragma mark Misc - (NSString *)description { return [NSString stringWithFormat:@"<%@ \"%@\", ivar=%@, readonly=%d, nonatomic=%d, getter=%@, setter=%@>", NSStringFromClass(self.class), self.string, self.backingIvar ?: @"none", self.isReadOnly, self.isNonatomic, NSStringFromSelector(self.customGetter) ?: @"none", NSStringFromSelector(self.customSetter) ?: @"none" ]; } - (objc_property_attribute_t *)copyAttributesList:(unsigned int *)attributesCount { NSDictionary *attrs = self.string.propertyAttributes; objc_property_attribute_t *propertyAttributes = malloc(attrs.count * sizeof(objc_property_attribute_t)); if (attributesCount) { *attributesCount = (unsigned int)attrs.count; } NSUInteger i = 0; for (NSString *key in attrs.allKeys) { FLEXPropertyAttribute c = (FLEXPropertyAttribute)[key characterAtIndex:0]; switch (c) { case FLEXPropertyAttributeTypeEncoding: { objc_property_attribute_t pa = { kFLEXPropertyAttributeKeyTypeEncoding.UTF8String, self.typeEncoding.UTF8String }; propertyAttributes[i] = pa; break; } case FLEXPropertyAttributeBackingIvarName: { objc_property_attribute_t pa = { kFLEXPropertyAttributeKeyBackingIvarName.UTF8String, self.backingIvar.UTF8String }; propertyAttributes[i] = pa; break; } case FLEXPropertyAttributeCopy: { objc_property_attribute_t pa = {kFLEXPropertyAttributeKeyCopy.UTF8String, ""}; propertyAttributes[i] = pa; break; } case FLEXPropertyAttributeCustomGetter: { objc_property_attribute_t pa = { kFLEXPropertyAttributeKeyCustomGetter.UTF8String, NSStringFromSelector(self.customGetter).UTF8String ?: "" }; propertyAttributes[i] = pa; break; } case FLEXPropertyAttributeCustomSetter: { objc_property_attribute_t pa = { kFLEXPropertyAttributeKeyCustomSetter.UTF8String, NSStringFromSelector(self.customSetter).UTF8String ?: "" }; propertyAttributes[i] = pa; break; } case FLEXPropertyAttributeDynamic: { objc_property_attribute_t pa = {kFLEXPropertyAttributeKeyDynamic.UTF8String, ""}; propertyAttributes[i] = pa; break; } case FLEXPropertyAttributeGarbageCollectible: { objc_property_attribute_t pa = {kFLEXPropertyAttributeKeyGarbageCollectable.UTF8String, ""}; propertyAttributes[i] = pa; break; } case FLEXPropertyAttributeNonAtomic: { objc_property_attribute_t pa = {kFLEXPropertyAttributeKeyNonAtomic.UTF8String, ""}; propertyAttributes[i] = pa; break; } case FLEXPropertyAttributeOldTypeEncoding: { objc_property_attribute_t pa = { kFLEXPropertyAttributeKeyOldStyleTypeEncoding.UTF8String, self.oldTypeEncoding.UTF8String ?: "" }; propertyAttributes[i] = pa; break; } case FLEXPropertyAttributeReadOnly: { objc_property_attribute_t pa = {kFLEXPropertyAttributeKeyReadOnly.UTF8String, ""}; propertyAttributes[i] = pa; break; } case FLEXPropertyAttributeRetain: { objc_property_attribute_t pa = {kFLEXPropertyAttributeKeyRetain.UTF8String, ""}; propertyAttributes[i] = pa; break; } case FLEXPropertyAttributeWeak: { objc_property_attribute_t pa = {kFLEXPropertyAttributeKeyWeak.UTF8String, ""}; propertyAttributes[i] = pa; break; } } i++; } return propertyAttributes; } - (objc_property_attribute_t *)list { if (!_list) { _list = [self copyAttributesList:nil]; } return _list; } - (NSString *)buildFullDeclaration { NSMutableString *decl = [NSMutableString new]; [decl appendFormat:@"%@, ", _isNonatomic ? @"nonatomic" : @"atomic"]; [decl appendFormat:@"%@, ", _isReadOnly ? @"readonly" : @"readwrite"]; BOOL noExplicitMemorySemantics = YES; if (_isCopy) { noExplicitMemorySemantics = NO; [decl appendString:@"copy, "]; } if (_isRetained) { noExplicitMemorySemantics = NO; [decl appendString:@"strong, "]; } if (_isWeak) { noExplicitMemorySemantics = NO; [decl appendString:@"weak, "]; } if ([_typeEncoding hasPrefix:@"@"] && noExplicitMemorySemantics) { // *probably* strong if this is an object; strong is the default. [decl appendString:@"strong, "]; } else if (noExplicitMemorySemantics) { // *probably* assign if this is not an object [decl appendString:@"assign, "]; } if (_customGetter) { [decl appendFormat:@"getter=%@, ", NSStringFromSelector(_customGetter)]; } if (_customSetter) { [decl appendFormat:@"setter=%@, ", NSStringFromSelector(_customSetter)]; } [decl deleteCharactersInRange:NSMakeRange(decl.length-2, 2)]; return decl.copy; } - (void)dealloc { if (_list) { free(_list); _list = nil; } } #pragma mark Copying - (id)copyWithZone:(NSZone *)zone { return [[FLEXPropertyAttributes class] attributesFromDictionary:self.dictionary]; } - (id)mutableCopyWithZone:(NSZone *)zone { return [[FLEXMutablePropertyAttributes class] attributesFromDictionary:self.dictionary]; } @end #pragma mark FLEXMutablePropertyAttributes @interface FLEXMutablePropertyAttributes () @property (nonatomic) BOOL countDelta; @property (nonatomic) BOOL stringDelta; @property (nonatomic) BOOL dictDelta; @property (nonatomic) BOOL listDelta; @property (nonatomic) BOOL declDelta; @end #define PropertyWithDeltaFlag(type, name, Name) @dynamic name; \ - (void)set ## Name:(type)name { \ if (name != _ ## name) { \ _countDelta = _stringDelta = _dictDelta = _listDelta = _declDelta = YES; \ _ ## name = name; \ } \ } @implementation FLEXMutablePropertyAttributes PropertyWithDeltaFlag(NSString *, backingIvar, BackingIvar); PropertyWithDeltaFlag(NSString *, typeEncoding, TypeEncoding); PropertyWithDeltaFlag(NSString *, oldTypeEncoding, OldTypeEncoding); PropertyWithDeltaFlag(SEL, customGetter, CustomGetter); PropertyWithDeltaFlag(SEL, customSetter, CustomSetter); PropertyWithDeltaFlag(BOOL, isReadOnly, IsReadOnly); PropertyWithDeltaFlag(BOOL, isCopy, IsCopy); PropertyWithDeltaFlag(BOOL, isRetained, IsRetained); PropertyWithDeltaFlag(BOOL, isNonatomic, IsNonatomic); PropertyWithDeltaFlag(BOOL, isDynamic, IsDynamic); PropertyWithDeltaFlag(BOOL, isWeak, IsWeak); PropertyWithDeltaFlag(BOOL, isGarbageCollectable, IsGarbageCollectable); + (instancetype)attributes { return [self new]; } - (void)setTypeEncodingChar:(char)type { self.typeEncoding = [NSString stringWithFormat:@"%c", type]; } - (NSUInteger)count { // Recalculate attribute count after mutations if (self.countDelta) { self.countDelta = NO; _count = self.dictionary.count; } return _count; } - (objc_property_attribute_t *)list { // Regenerate list after mutations if (self.listDelta) { self.listDelta = NO; if (_list) { free(_list); _list = nil; } } // Super will generate the list if it isn't set return super.list; } - (NSString *)string { // Regenerate string after mutations if (self.stringDelta || !_string) { self.stringDelta = NO; _string = self.dictionary.propertyAttributesString; } return _string; } - (NSDictionary *)dictionary { // Regenerate dictionary after mutations if (self.dictDelta || !_dictionary) { // _stringa nd _dictionary depend on each other, // so we must generate ONE by hand using our properties. // We arbitrarily choose to generate the dictionary. NSMutableDictionary *attrs = [NSMutableDictionary new]; if (self.typeEncoding) attrs[kFLEXPropertyAttributeKeyTypeEncoding] = self.typeEncoding; if (self.backingIvar) attrs[kFLEXPropertyAttributeKeyBackingIvarName] = self.backingIvar; if (self.oldTypeEncoding) attrs[kFLEXPropertyAttributeKeyOldStyleTypeEncoding] = self.oldTypeEncoding; if (self.customGetter) attrs[kFLEXPropertyAttributeKeyCustomGetter] = NSStringFromSelector(self.customGetter); if (self.customSetter) attrs[kFLEXPropertyAttributeKeyCustomSetter] = NSStringFromSelector(self.customSetter); if (self.isReadOnly) attrs[kFLEXPropertyAttributeKeyReadOnly] = @YES; if (self.isCopy) attrs[kFLEXPropertyAttributeKeyCopy] = @YES; if (self.isRetained) attrs[kFLEXPropertyAttributeKeyRetain] = @YES; if (self.isNonatomic) attrs[kFLEXPropertyAttributeKeyNonAtomic] = @YES; if (self.isDynamic) attrs[kFLEXPropertyAttributeKeyDynamic] = @YES; if (self.isWeak) attrs[kFLEXPropertyAttributeKeyWeak] = @YES; if (self.isGarbageCollectable) attrs[kFLEXPropertyAttributeKeyGarbageCollectable] = @YES; _dictionary = attrs.copy; } return _dictionary; } - (NSString *)fullDeclaration { if (self.declDelta || !_fullDeclaration) { _declDelta = NO; _fullDeclaration = [self buildFullDeclaration]; } return _fullDeclaration; } - (NSString *)customGetterString { return _customGetter ? NSStringFromSelector(_customGetter) : nil; } - (NSString *)customSetterString { return _customSetter ? NSStringFromSelector(_customSetter) : nil; } @end