RQEXTRuntimeExtensions.m 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. //
  2. // RQEXTRuntimeExtensions.m
  3. // RQCommon
  4. //
  5. // Created by 张嵘 on 2018/11/16.
  6. // Copyright © 2018 张嵘. All rights reserved.
  7. //
  8. #import "RQEXTRuntimeExtensions.h"
  9. #import <Foundation/Foundation.h>
  10. rq_propertyAttributes *rq_copyPropertyAttributes (objc_property_t property) {
  11. const char * const attrString = property_getAttributes(property);
  12. if (!attrString) {
  13. fprintf(stderr, "ERROR: Could not get attribute string from property %s\n", property_getName(property));
  14. return NULL;
  15. }
  16. if (attrString[0] != 'T') {
  17. fprintf(stderr, "ERROR: Expected attribute string \"%s\" for property %s to start with 'T'\n", attrString, property_getName(property));
  18. return NULL;
  19. }
  20. const char *typeString = attrString + 1;
  21. const char *next = NSGetSizeAndAlignment(typeString, NULL, NULL);
  22. if (!next) {
  23. fprintf(stderr, "ERROR: Could not read past type in attribute string \"%s\" for property %s\n", attrString, property_getName(property));
  24. return NULL;
  25. }
  26. size_t typeLength = next - typeString;
  27. if (!typeLength) {
  28. fprintf(stderr, "ERROR: Invalid type in attribute string \"%s\" for property %s\n", attrString, property_getName(property));
  29. return NULL;
  30. }
  31. // allocate enough space for the structure and the type string (plus a NUL)
  32. rq_propertyAttributes *attributes = calloc(1, sizeof(rq_propertyAttributes) + typeLength + 1);
  33. if (!attributes) {
  34. fprintf(stderr, "ERROR: Could not allocate rq_propertyAttributes structure for attribute string \"%s\" for property %s\n", attrString, property_getName(property));
  35. return NULL;
  36. }
  37. // copy the type string
  38. strncpy(attributes->type, typeString, typeLength);
  39. attributes->type[typeLength] = '\0';
  40. // if this is an object type, and immediately followed by a quoted string...
  41. if (typeString[0] == *(@encode(id)) && typeString[1] == '"') {
  42. // we should be able to extract a class name
  43. const char *className = typeString + 2;
  44. next = strchr(className, '"');
  45. if (!next) {
  46. fprintf(stderr, "ERROR: Could not read class name in attribute string \"%s\" for property %s\n", attrString, property_getName(property));
  47. return NULL;
  48. }
  49. if (className != next) {
  50. size_t classNameLength = next - className;
  51. char trimmedName[classNameLength + 1];
  52. strncpy(trimmedName, className, classNameLength);
  53. trimmedName[classNameLength] = '\0';
  54. // attempt to look up the class in the runtime
  55. attributes->objectClass = objc_getClass(trimmedName);
  56. }
  57. }
  58. if (*next != '\0') {
  59. // skip past any junk before the first flag
  60. next = strchr(next, ',');
  61. }
  62. while (next && *next == ',') {
  63. char flag = next[1];
  64. next += 2;
  65. switch (flag) {
  66. case '\0':
  67. break;
  68. case 'R':
  69. attributes->readonly = YES;
  70. break;
  71. case 'C':
  72. attributes->memoryManagementPolicy = rq_propertyMemoryManagementPolicyCopy;
  73. break;
  74. case '&':
  75. attributes->memoryManagementPolicy = rq_propertyMemoryManagementPolicyRetain;
  76. break;
  77. case 'N':
  78. attributes->nonatomic = YES;
  79. break;
  80. case 'G':
  81. case 'S':
  82. {
  83. const char *nextFlag = strchr(next, ',');
  84. SEL name = NULL;
  85. if (!nextFlag) {
  86. // assume that the rest of the string is the selector
  87. const char *selectorString = next;
  88. next = "";
  89. name = sel_registerName(selectorString);
  90. } else {
  91. size_t selectorLength = nextFlag - next;
  92. if (!selectorLength) {
  93. fprintf(stderr, "ERROR: Found zero length selector name in attribute string \"%s\" for property %s\n", attrString, property_getName(property));
  94. goto errorOut;
  95. }
  96. char selectorString[selectorLength + 1];
  97. strncpy(selectorString, next, selectorLength);
  98. selectorString[selectorLength] = '\0';
  99. name = sel_registerName(selectorString);
  100. next = nextFlag;
  101. }
  102. if (flag == 'G')
  103. attributes->getter = name;
  104. else
  105. attributes->setter = name;
  106. }
  107. break;
  108. case 'D':
  109. attributes->dynamic = YES;
  110. attributes->ivar = NULL;
  111. break;
  112. case 'V':
  113. // assume that the rest of the string (if present) is the ivar name
  114. if (*next == '\0') {
  115. // if there's nothing there, let's assume this is dynamic
  116. attributes->ivar = NULL;
  117. } else {
  118. attributes->ivar = next;
  119. next = "";
  120. }
  121. break;
  122. case 'W':
  123. attributes->weak = YES;
  124. break;
  125. case 'P':
  126. attributes->canBeCollected = YES;
  127. break;
  128. case 't':
  129. fprintf(stderr, "ERROR: Old-style type encoding is unsupported in attribute string \"%s\" for property %s\n", attrString, property_getName(property));
  130. // skip over this type encoding
  131. while (*next != ',' && *next != '\0')
  132. ++next;
  133. break;
  134. default:
  135. fprintf(stderr, "ERROR: Unrecognized attribute string flag '%c' in attribute string \"%s\" for property %s\n", flag, attrString, property_getName(property));
  136. }
  137. }
  138. if (next && *next != '\0') {
  139. fprintf(stderr, "Warning: Unparsed data \"%s\" in attribute string \"%s\" for property %s\n", next, attrString, property_getName(property));
  140. }
  141. if (!attributes->getter) {
  142. // use the property name as the getter by default
  143. attributes->getter = sel_registerName(property_getName(property));
  144. }
  145. if (!attributes->setter) {
  146. const char *propertyName = property_getName(property);
  147. size_t propertyNameLength = strlen(propertyName);
  148. // we want to transform the name to setProperty: style
  149. size_t setterLength = propertyNameLength + 4;
  150. char setterName[setterLength + 1];
  151. strncpy(setterName, "set", 3);
  152. strncpy(setterName + 3, propertyName, propertyNameLength);
  153. // capitalize property name for the setter
  154. setterName[3] = (char)toupper(setterName[3]);
  155. setterName[setterLength - 1] = ':';
  156. setterName[setterLength] = '\0';
  157. attributes->setter = sel_registerName(setterName);
  158. }
  159. return attributes;
  160. errorOut:
  161. free(attributes);
  162. return NULL;
  163. }