LKS_AttrGroupsMaker.m 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. #ifdef SHOULD_COMPILE_LOOKIN_SERVER
  2. //
  3. // LKS_AttrGroupsMaker.m
  4. // LookinServer
  5. //
  6. // Created by Li Kai on 2019/6/6.
  7. // https://lookin.work
  8. //
  9. #import "LKS_AttrGroupsMaker.h"
  10. #import "LookinAttributesGroup.h"
  11. #import "LookinAttributesSection.h"
  12. #import "LookinAttribute.h"
  13. #import "LookinDashboardBlueprint.h"
  14. #import "LookinIvarTrace.h"
  15. #import "UIColor+LookinServer.h"
  16. #import "LookinServerDefines.h"
  17. @implementation LKS_AttrGroupsMaker
  18. + (NSArray<LookinAttributesGroup *> *)attrGroupsForLayer:(CALayer *)layer {
  19. if (!layer) {
  20. NSAssert(NO, @"");
  21. return nil;
  22. }
  23. NSArray<LookinAttributesGroup *> *groups = [[LookinDashboardBlueprint groupIDs] lookin_map:^id(NSUInteger idx, LookinAttrGroupIdentifier groupID) {
  24. LookinAttributesGroup *group = [LookinAttributesGroup new];
  25. group.identifier = groupID;
  26. NSArray<LookinAttrSectionIdentifier> *secIDs = [LookinDashboardBlueprint sectionIDsForGroupID:groupID];
  27. group.attrSections = [secIDs lookin_map:^id(NSUInteger idx, LookinAttrSectionIdentifier secID) {
  28. LookinAttributesSection *sec = [LookinAttributesSection new];
  29. sec.identifier = secID;
  30. NSArray<LookinAttrIdentifier> *attrIDs = [LookinDashboardBlueprint attrIDsForSectionID:secID];
  31. sec.attributes = [attrIDs lookin_map:^id(NSUInteger idx, LookinAttrIdentifier attrID) {
  32. NSInteger minAvailableVersion = [LookinDashboardBlueprint minAvailableOSVersionWithAttrID:attrID];
  33. if (minAvailableVersion > 0 && (NSProcessInfo.processInfo.operatingSystemVersion.majorVersion < minAvailableVersion)) {
  34. // iOS 版本过低不支持该属性
  35. return nil;
  36. }
  37. id targetObj = nil;
  38. if ([LookinDashboardBlueprint isUIViewPropertyWithAttrID:attrID]) {
  39. targetObj = layer.lks_hostView;
  40. } else {
  41. targetObj = layer;
  42. }
  43. if (targetObj) {
  44. Class targetClass = NSClassFromString([LookinDashboardBlueprint classNameWithAttrID:attrID]);
  45. if (![targetObj isKindOfClass:targetClass]) {
  46. return nil;
  47. }
  48. LookinAttribute *attr = [self _attributeWithIdentifer:attrID targetObject:targetObj];
  49. return attr;
  50. } else {
  51. return nil;
  52. }
  53. }];
  54. if (sec.attributes.count) {
  55. return sec;
  56. } else {
  57. return nil;
  58. }
  59. }];
  60. if ([groupID isEqualToString:LookinAttrGroup_AutoLayout]) {
  61. // 这里特殊处理一下,如果 AutoLayout 里面不包含 Constraints 的话(只有 Hugging 和 Resistance),就丢弃掉这整个 AutoLayout 不显示
  62. BOOL hasConstraits = [group.attrSections lookin_any:^BOOL(LookinAttributesSection *obj) {
  63. return [obj.identifier isEqualToString:LookinAttrSec_AutoLayout_Constraints];
  64. }];
  65. if (!hasConstraits) {
  66. return nil;
  67. }
  68. }
  69. if (group.attrSections.count) {
  70. return group;
  71. } else {
  72. return nil;
  73. }
  74. }];
  75. return groups;
  76. }
  77. + (LookinAttribute *)_attributeWithIdentifer:(LookinAttrIdentifier)identifier targetObject:(id)target {
  78. if (!target) {
  79. NSAssert(NO, @"");
  80. return nil;
  81. }
  82. LookinAttribute *attribute = [LookinAttribute new];
  83. attribute.identifier = identifier;
  84. SEL getter = [LookinDashboardBlueprint getterWithAttrID:identifier];
  85. if (!getter) {
  86. NSAssert(NO, @"");
  87. return nil;
  88. }
  89. if (![target respondsToSelector:getter]) {
  90. // 比如某些 QMUI 的属性,不引入 QMUI 就会走到这个分支里
  91. return nil;
  92. }
  93. NSMethodSignature *signature = [target methodSignatureForSelector:getter];
  94. if (signature.numberOfArguments > 2) {
  95. NSAssert(NO, @"getter 不可以有参数");
  96. return nil;
  97. }
  98. if (strcmp([signature methodReturnType], @encode(void)) == 0) {
  99. NSAssert(NO, @"getter 返回值不能为 void");
  100. return nil;
  101. }
  102. NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
  103. invocation.target = target;
  104. invocation.selector = getter;
  105. [invocation invoke];
  106. const char *returnType = [signature methodReturnType];
  107. if (strcmp(returnType, @encode(char)) == 0) {
  108. char targetValue;
  109. [invocation getReturnValue:&targetValue];
  110. attribute.attrType = LookinAttrTypeChar;
  111. attribute.value = @(targetValue);
  112. } else if (strcmp(returnType, @encode(int)) == 0) {
  113. int targetValue;
  114. [invocation getReturnValue:&targetValue];
  115. attribute.value = @(targetValue);
  116. if ([LookinDashboardBlueprint enumListNameWithAttrID:identifier]) {
  117. attribute.attrType = LookinAttrTypeEnumInt;
  118. } else {
  119. attribute.attrType = LookinAttrTypeInt;
  120. }
  121. } else if (strcmp(returnType, @encode(short)) == 0) {
  122. short targetValue;
  123. [invocation getReturnValue:&targetValue];
  124. attribute.attrType = LookinAttrTypeShort;
  125. attribute.value = @(targetValue);
  126. } else if (strcmp(returnType, @encode(long)) == 0) {
  127. long targetValue;
  128. [invocation getReturnValue:&targetValue];
  129. attribute.value = @(targetValue);
  130. if ([LookinDashboardBlueprint enumListNameWithAttrID:identifier]) {
  131. attribute.attrType = LookinAttrTypeEnumLong;
  132. } else {
  133. attribute.attrType = LookinAttrTypeLong;
  134. }
  135. } else if (strcmp(returnType, @encode(long long)) == 0) {
  136. long long targetValue;
  137. [invocation getReturnValue:&targetValue];
  138. attribute.attrType = LookinAttrTypeLongLong;
  139. attribute.value = @(targetValue);
  140. } else if (strcmp(returnType, @encode(unsigned char)) == 0) {
  141. unsigned char targetValue;
  142. [invocation getReturnValue:&targetValue];
  143. attribute.attrType = LookinAttrTypeUnsignedChar;
  144. attribute.value = @(targetValue);
  145. } else if (strcmp(returnType, @encode(unsigned int)) == 0) {
  146. unsigned int targetValue;
  147. [invocation getReturnValue:&targetValue];
  148. attribute.attrType = LookinAttrTypeUnsignedInt;
  149. attribute.value = @(targetValue);
  150. } else if (strcmp(returnType, @encode(unsigned short)) == 0) {
  151. unsigned short targetValue;
  152. [invocation getReturnValue:&targetValue];
  153. attribute.attrType = LookinAttrTypeUnsignedShort;
  154. attribute.value = @(targetValue);
  155. } else if (strcmp(returnType, @encode(unsigned long)) == 0) {
  156. unsigned long targetValue;
  157. [invocation getReturnValue:&targetValue];
  158. attribute.attrType = LookinAttrTypeUnsignedLong;
  159. attribute.value = @(targetValue);
  160. } else if (strcmp(returnType, @encode(unsigned long long)) == 0) {
  161. unsigned long long targetValue;
  162. [invocation getReturnValue:&targetValue];
  163. attribute.attrType = LookinAttrTypeUnsignedLongLong;
  164. attribute.value = @(targetValue);
  165. } else if (strcmp(returnType, @encode(float)) == 0) {
  166. float targetValue;
  167. [invocation getReturnValue:&targetValue];
  168. attribute.attrType = LookinAttrTypeFloat;
  169. attribute.value = @(targetValue);
  170. } else if (strcmp(returnType, @encode(double)) == 0) {
  171. double targetValue;
  172. [invocation getReturnValue:&targetValue];
  173. attribute.attrType = LookinAttrTypeDouble;
  174. attribute.value = @(targetValue);
  175. } else if (strcmp(returnType, @encode(BOOL)) == 0) {
  176. BOOL targetValue;
  177. [invocation getReturnValue:&targetValue];
  178. attribute.attrType = LookinAttrTypeBOOL;
  179. attribute.value = @(targetValue);
  180. } else if (strcmp(returnType, @encode(SEL)) == 0) {
  181. SEL targetValue;
  182. [invocation getReturnValue:&targetValue];
  183. attribute.attrType = LookinAttrTypeSel;
  184. attribute.value = NSStringFromSelector(targetValue);
  185. } else if (strcmp(returnType, @encode(Class)) == 0) {
  186. Class targetValue;
  187. [invocation getReturnValue:&targetValue];
  188. attribute.attrType = LookinAttrTypeClass;
  189. attribute.value = NSStringFromClass(targetValue);
  190. } else if (strcmp(returnType, @encode(CGPoint)) == 0) {
  191. CGPoint targetValue;
  192. [invocation getReturnValue:&targetValue];
  193. attribute.attrType = LookinAttrTypeCGPoint;
  194. attribute.value = [NSValue valueWithCGPoint:targetValue];
  195. } else if (strcmp(returnType, @encode(CGVector)) == 0) {
  196. CGVector targetValue;
  197. [invocation getReturnValue:&targetValue];
  198. attribute.attrType = LookinAttrTypeCGVector;
  199. attribute.value = [NSValue valueWithCGVector:targetValue];
  200. } else if (strcmp(returnType, @encode(CGSize)) == 0) {
  201. CGSize targetValue;
  202. [invocation getReturnValue:&targetValue];
  203. attribute.attrType = LookinAttrTypeCGSize;
  204. attribute.value = [NSValue valueWithCGSize:targetValue];
  205. } else if (strcmp(returnType, @encode(CGRect)) == 0) {
  206. CGRect targetValue;
  207. [invocation getReturnValue:&targetValue];
  208. attribute.attrType = LookinAttrTypeCGRect;
  209. attribute.value = [NSValue valueWithCGRect:targetValue];
  210. } else if (strcmp(returnType, @encode(CGAffineTransform)) == 0) {
  211. CGAffineTransform targetValue;
  212. [invocation getReturnValue:&targetValue];
  213. attribute.attrType = LookinAttrTypeCGAffineTransform;
  214. attribute.value = [NSValue valueWithCGAffineTransform:targetValue];
  215. } else if (strcmp(returnType, @encode(UIEdgeInsets)) == 0) {
  216. UIEdgeInsets targetValue;
  217. [invocation getReturnValue:&targetValue];
  218. attribute.attrType = LookinAttrTypeUIEdgeInsets;
  219. attribute.value = [NSValue valueWithUIEdgeInsets:targetValue];
  220. } else if (strcmp(returnType, @encode(UIOffset)) == 0) {
  221. UIOffset targetValue;
  222. [invocation getReturnValue:&targetValue];
  223. attribute.attrType = LookinAttrTypeUIOffset;
  224. attribute.value = [NSValue valueWithUIOffset:targetValue];
  225. } else {
  226. NSString *argType_string = [[NSString alloc] lookin_safeInitWithUTF8String:returnType];
  227. if ([argType_string hasPrefix:@"@"]) {
  228. __unsafe_unretained id returnObjValue;
  229. [invocation getReturnValue:&returnObjValue];
  230. if (!returnObjValue && [LookinDashboardBlueprint hideIfNilWithAttrID:identifier]) {
  231. // 对于某些属性,若 value 为 nil 则不显示
  232. return nil;
  233. }
  234. attribute.attrType = [LookinDashboardBlueprint objectAttrTypeWithAttrID:identifier];
  235. if (attribute.attrType == LookinAttrTypeUIColor) {
  236. if (returnObjValue == nil) {
  237. attribute.value = nil;
  238. } else if ([returnObjValue isKindOfClass:[UIColor class]] && [returnObjValue respondsToSelector:@selector(lks_rgbaComponents)]) {
  239. attribute.value = [returnObjValue lks_rgbaComponents];
  240. } else {
  241. // https://github.com/QMUI/LookinServer/issues/124
  242. return nil;
  243. }
  244. } else {
  245. attribute.value = returnObjValue;
  246. }
  247. } else {
  248. NSAssert(NO, @"不支持解析该类型的返回值");
  249. return nil;
  250. }
  251. }
  252. return attribute;
  253. }
  254. @end
  255. #endif /* SHOULD_COMPILE_LOOKIN_SERVER */