Files
YTLitePlus/Tweaks/FLEX/Utility/Runtime/Objc/Reflection/FLEXPropertyAttributes.m
2023-06-27 09:54:41 +02:00

377 lines
13 KiB
Objective-C

//
// 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