FLEXPropertyAttributes.m 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. //
  2. // FLEXPropertyAttributes.m
  3. // FLEX
  4. //
  5. // Derived from MirrorKit.
  6. // Created by Tanner on 7/5/15.
  7. // Copyright (c) 2020 FLEX Team. All rights reserved.
  8. //
  9. #import "FLEXPropertyAttributes.h"
  10. #import "FLEXRuntimeUtility.h"
  11. #import "NSString+ObjcRuntime.h"
  12. #import "NSDictionary+ObjcRuntime.h"
  13. #pragma mark FLEXPropertyAttributes
  14. @interface FLEXPropertyAttributes ()
  15. @property (nonatomic) NSString *backingIvar;
  16. @property (nonatomic) NSString *typeEncoding;
  17. @property (nonatomic) NSString *oldTypeEncoding;
  18. @property (nonatomic) SEL customGetter;
  19. @property (nonatomic) SEL customSetter;
  20. @property (nonatomic) BOOL isReadOnly;
  21. @property (nonatomic) BOOL isCopy;
  22. @property (nonatomic) BOOL isRetained;
  23. @property (nonatomic) BOOL isNonatomic;
  24. @property (nonatomic) BOOL isDynamic;
  25. @property (nonatomic) BOOL isWeak;
  26. @property (nonatomic) BOOL isGarbageCollectable;
  27. - (NSString *)buildFullDeclaration;
  28. @end
  29. @implementation FLEXPropertyAttributes
  30. @synthesize list = _list;
  31. #pragma mark Initializers
  32. + (instancetype)attributesForProperty:(objc_property_t)property {
  33. return [self attributesFromDictionary:[NSDictionary attributesDictionaryForProperty:property]];
  34. }
  35. + (instancetype)attributesFromDictionary:(NSDictionary *)attributes {
  36. return [[self alloc] initWithAttributesDictionary:attributes];
  37. }
  38. - (id)initWithAttributesDictionary:(NSDictionary *)attributes {
  39. NSParameterAssert(attributes);
  40. self = [super init];
  41. if (self) {
  42. _dictionary = attributes;
  43. _string = attributes.propertyAttributesString;
  44. _count = attributes.count;
  45. _typeEncoding = attributes[kFLEXPropertyAttributeKeyTypeEncoding];
  46. _backingIvar = attributes[kFLEXPropertyAttributeKeyBackingIvarName];
  47. _oldTypeEncoding = attributes[kFLEXPropertyAttributeKeyOldStyleTypeEncoding];
  48. _customGetterString = attributes[kFLEXPropertyAttributeKeyCustomGetter];
  49. _customSetterString = attributes[kFLEXPropertyAttributeKeyCustomSetter];
  50. _customGetter = NSSelectorFromString(_customGetterString);
  51. _customSetter = NSSelectorFromString(_customSetterString);
  52. _isReadOnly = attributes[kFLEXPropertyAttributeKeyReadOnly] != nil;
  53. _isCopy = attributes[kFLEXPropertyAttributeKeyCopy] != nil;
  54. _isRetained = attributes[kFLEXPropertyAttributeKeyRetain] != nil;
  55. _isNonatomic = attributes[kFLEXPropertyAttributeKeyNonAtomic] != nil;
  56. _isWeak = attributes[kFLEXPropertyAttributeKeyWeak] != nil;
  57. _isGarbageCollectable = attributes[kFLEXPropertyAttributeKeyGarbageCollectable] != nil;
  58. _fullDeclaration = [self buildFullDeclaration];
  59. }
  60. return self;
  61. }
  62. #pragma mark Misc
  63. - (NSString *)description {
  64. return [NSString
  65. stringWithFormat:@"<%@ \"%@\", ivar=%@, readonly=%d, nonatomic=%d, getter=%@, setter=%@>",
  66. NSStringFromClass(self.class),
  67. self.string,
  68. self.backingIvar ?: @"none",
  69. self.isReadOnly,
  70. self.isNonatomic,
  71. NSStringFromSelector(self.customGetter) ?: @"none",
  72. NSStringFromSelector(self.customSetter) ?: @"none"
  73. ];
  74. }
  75. - (objc_property_attribute_t *)copyAttributesList:(unsigned int *)attributesCount {
  76. NSDictionary *attrs = self.string.propertyAttributes;
  77. objc_property_attribute_t *propertyAttributes = malloc(attrs.count * sizeof(objc_property_attribute_t));
  78. if (attributesCount) {
  79. *attributesCount = (unsigned int)attrs.count;
  80. }
  81. NSUInteger i = 0;
  82. for (NSString *key in attrs.allKeys) {
  83. FLEXPropertyAttribute c = (FLEXPropertyAttribute)[key characterAtIndex:0];
  84. switch (c) {
  85. case FLEXPropertyAttributeTypeEncoding: {
  86. objc_property_attribute_t pa = {
  87. kFLEXPropertyAttributeKeyTypeEncoding.UTF8String,
  88. self.typeEncoding.UTF8String
  89. };
  90. propertyAttributes[i] = pa;
  91. break;
  92. }
  93. case FLEXPropertyAttributeBackingIvarName: {
  94. objc_property_attribute_t pa = {
  95. kFLEXPropertyAttributeKeyBackingIvarName.UTF8String,
  96. self.backingIvar.UTF8String
  97. };
  98. propertyAttributes[i] = pa;
  99. break;
  100. }
  101. case FLEXPropertyAttributeCopy: {
  102. objc_property_attribute_t pa = {kFLEXPropertyAttributeKeyCopy.UTF8String, ""};
  103. propertyAttributes[i] = pa;
  104. break;
  105. }
  106. case FLEXPropertyAttributeCustomGetter: {
  107. objc_property_attribute_t pa = {
  108. kFLEXPropertyAttributeKeyCustomGetter.UTF8String,
  109. NSStringFromSelector(self.customGetter).UTF8String ?: ""
  110. };
  111. propertyAttributes[i] = pa;
  112. break;
  113. }
  114. case FLEXPropertyAttributeCustomSetter: {
  115. objc_property_attribute_t pa = {
  116. kFLEXPropertyAttributeKeyCustomSetter.UTF8String,
  117. NSStringFromSelector(self.customSetter).UTF8String ?: ""
  118. };
  119. propertyAttributes[i] = pa;
  120. break;
  121. }
  122. case FLEXPropertyAttributeDynamic: {
  123. objc_property_attribute_t pa = {kFLEXPropertyAttributeKeyDynamic.UTF8String, ""};
  124. propertyAttributes[i] = pa;
  125. break;
  126. }
  127. case FLEXPropertyAttributeGarbageCollectible: {
  128. objc_property_attribute_t pa = {kFLEXPropertyAttributeKeyGarbageCollectable.UTF8String, ""};
  129. propertyAttributes[i] = pa;
  130. break;
  131. }
  132. case FLEXPropertyAttributeNonAtomic: {
  133. objc_property_attribute_t pa = {kFLEXPropertyAttributeKeyNonAtomic.UTF8String, ""};
  134. propertyAttributes[i] = pa;
  135. break;
  136. }
  137. case FLEXPropertyAttributeOldTypeEncoding: {
  138. objc_property_attribute_t pa = {
  139. kFLEXPropertyAttributeKeyOldStyleTypeEncoding.UTF8String,
  140. self.oldTypeEncoding.UTF8String ?: ""
  141. };
  142. propertyAttributes[i] = pa;
  143. break;
  144. }
  145. case FLEXPropertyAttributeReadOnly: {
  146. objc_property_attribute_t pa = {kFLEXPropertyAttributeKeyReadOnly.UTF8String, ""};
  147. propertyAttributes[i] = pa;
  148. break;
  149. }
  150. case FLEXPropertyAttributeRetain: {
  151. objc_property_attribute_t pa = {kFLEXPropertyAttributeKeyRetain.UTF8String, ""};
  152. propertyAttributes[i] = pa;
  153. break;
  154. }
  155. case FLEXPropertyAttributeWeak: {
  156. objc_property_attribute_t pa = {kFLEXPropertyAttributeKeyWeak.UTF8String, ""};
  157. propertyAttributes[i] = pa;
  158. break;
  159. }
  160. }
  161. i++;
  162. }
  163. return propertyAttributes;
  164. }
  165. - (objc_property_attribute_t *)list {
  166. if (!_list) {
  167. _list = [self copyAttributesList:nil];
  168. }
  169. return _list;
  170. }
  171. - (NSString *)buildFullDeclaration {
  172. NSMutableString *decl = [NSMutableString new];
  173. [decl appendFormat:@"%@, ", _isNonatomic ? @"nonatomic" : @"atomic"];
  174. [decl appendFormat:@"%@, ", _isReadOnly ? @"readonly" : @"readwrite"];
  175. BOOL noExplicitMemorySemantics = YES;
  176. if (_isCopy) { noExplicitMemorySemantics = NO;
  177. [decl appendString:@"copy, "];
  178. }
  179. if (_isRetained) { noExplicitMemorySemantics = NO;
  180. [decl appendString:@"strong, "];
  181. }
  182. if (_isWeak) { noExplicitMemorySemantics = NO;
  183. [decl appendString:@"weak, "];
  184. }
  185. if ([_typeEncoding hasPrefix:@"@"] && noExplicitMemorySemantics) {
  186. // *probably* strong if this is an object; strong is the default.
  187. [decl appendString:@"strong, "];
  188. } else if (noExplicitMemorySemantics) {
  189. // *probably* assign if this is not an object
  190. [decl appendString:@"assign, "];
  191. }
  192. if (_customGetter) {
  193. [decl appendFormat:@"getter=%@, ", NSStringFromSelector(_customGetter)];
  194. }
  195. if (_customSetter) {
  196. [decl appendFormat:@"setter=%@, ", NSStringFromSelector(_customSetter)];
  197. }
  198. [decl deleteCharactersInRange:NSMakeRange(decl.length-2, 2)];
  199. return decl.copy;
  200. }
  201. - (void)dealloc {
  202. if (_list) {
  203. free(_list);
  204. _list = nil;
  205. }
  206. }
  207. #pragma mark Copying
  208. - (id)copyWithZone:(NSZone *)zone {
  209. return [[FLEXPropertyAttributes class] attributesFromDictionary:self.dictionary];
  210. }
  211. - (id)mutableCopyWithZone:(NSZone *)zone {
  212. return [[FLEXMutablePropertyAttributes class] attributesFromDictionary:self.dictionary];
  213. }
  214. @end
  215. #pragma mark FLEXMutablePropertyAttributes
  216. @interface FLEXMutablePropertyAttributes ()
  217. @property (nonatomic) BOOL countDelta;
  218. @property (nonatomic) BOOL stringDelta;
  219. @property (nonatomic) BOOL dictDelta;
  220. @property (nonatomic) BOOL listDelta;
  221. @property (nonatomic) BOOL declDelta;
  222. @end
  223. #define PropertyWithDeltaFlag(type, name, Name) @dynamic name; \
  224. - (void)set ## Name:(type)name { \
  225. if (name != _ ## name) { \
  226. _countDelta = _stringDelta = _dictDelta = _listDelta = _declDelta = YES; \
  227. _ ## name = name; \
  228. } \
  229. }
  230. @implementation FLEXMutablePropertyAttributes
  231. PropertyWithDeltaFlag(NSString *, backingIvar, BackingIvar);
  232. PropertyWithDeltaFlag(NSString *, typeEncoding, TypeEncoding);
  233. PropertyWithDeltaFlag(NSString *, oldTypeEncoding, OldTypeEncoding);
  234. PropertyWithDeltaFlag(SEL, customGetter, CustomGetter);
  235. PropertyWithDeltaFlag(SEL, customSetter, CustomSetter);
  236. PropertyWithDeltaFlag(BOOL, isReadOnly, IsReadOnly);
  237. PropertyWithDeltaFlag(BOOL, isCopy, IsCopy);
  238. PropertyWithDeltaFlag(BOOL, isRetained, IsRetained);
  239. PropertyWithDeltaFlag(BOOL, isNonatomic, IsNonatomic);
  240. PropertyWithDeltaFlag(BOOL, isDynamic, IsDynamic);
  241. PropertyWithDeltaFlag(BOOL, isWeak, IsWeak);
  242. PropertyWithDeltaFlag(BOOL, isGarbageCollectable, IsGarbageCollectable);
  243. + (instancetype)attributes {
  244. return [self new];
  245. }
  246. - (void)setTypeEncodingChar:(char)type {
  247. self.typeEncoding = [NSString stringWithFormat:@"%c", type];
  248. }
  249. - (NSUInteger)count {
  250. // Recalculate attribute count after mutations
  251. if (self.countDelta) {
  252. self.countDelta = NO;
  253. _count = self.dictionary.count;
  254. }
  255. return _count;
  256. }
  257. - (objc_property_attribute_t *)list {
  258. // Regenerate list after mutations
  259. if (self.listDelta) {
  260. self.listDelta = NO;
  261. if (_list) {
  262. free(_list);
  263. _list = nil;
  264. }
  265. }
  266. // Super will generate the list if it isn't set
  267. return super.list;
  268. }
  269. - (NSString *)string {
  270. // Regenerate string after mutations
  271. if (self.stringDelta || !_string) {
  272. self.stringDelta = NO;
  273. _string = self.dictionary.propertyAttributesString;
  274. }
  275. return _string;
  276. }
  277. - (NSDictionary *)dictionary {
  278. // Regenerate dictionary after mutations
  279. if (self.dictDelta || !_dictionary) {
  280. // _stringa nd _dictionary depend on each other,
  281. // so we must generate ONE by hand using our properties.
  282. // We arbitrarily choose to generate the dictionary.
  283. NSMutableDictionary *attrs = [NSMutableDictionary new];
  284. if (self.typeEncoding)
  285. attrs[kFLEXPropertyAttributeKeyTypeEncoding] = self.typeEncoding;
  286. if (self.backingIvar)
  287. attrs[kFLEXPropertyAttributeKeyBackingIvarName] = self.backingIvar;
  288. if (self.oldTypeEncoding)
  289. attrs[kFLEXPropertyAttributeKeyOldStyleTypeEncoding] = self.oldTypeEncoding;
  290. if (self.customGetter)
  291. attrs[kFLEXPropertyAttributeKeyCustomGetter] = NSStringFromSelector(self.customGetter);
  292. if (self.customSetter)
  293. attrs[kFLEXPropertyAttributeKeyCustomSetter] = NSStringFromSelector(self.customSetter);
  294. if (self.isReadOnly) attrs[kFLEXPropertyAttributeKeyReadOnly] = @YES;
  295. if (self.isCopy) attrs[kFLEXPropertyAttributeKeyCopy] = @YES;
  296. if (self.isRetained) attrs[kFLEXPropertyAttributeKeyRetain] = @YES;
  297. if (self.isNonatomic) attrs[kFLEXPropertyAttributeKeyNonAtomic] = @YES;
  298. if (self.isDynamic) attrs[kFLEXPropertyAttributeKeyDynamic] = @YES;
  299. if (self.isWeak) attrs[kFLEXPropertyAttributeKeyWeak] = @YES;
  300. if (self.isGarbageCollectable) attrs[kFLEXPropertyAttributeKeyGarbageCollectable] = @YES;
  301. _dictionary = attrs.copy;
  302. }
  303. return _dictionary;
  304. }
  305. - (NSString *)fullDeclaration {
  306. if (self.declDelta || !_fullDeclaration) {
  307. _declDelta = NO;
  308. _fullDeclaration = [self buildFullDeclaration];
  309. }
  310. return _fullDeclaration;
  311. }
  312. - (NSString *)customGetterString {
  313. return _customGetter ? NSStringFromSelector(_customGetter) : nil;
  314. }
  315. - (NSString *)customSetterString {
  316. return _customSetter ? NSStringFromSelector(_customSetter) : nil;
  317. }
  318. @end