RACEXTRuntimeExtensions
RACEXTRuntimeExtensions 中定义了两个函数用来进行运行时的操作,并且声明了一个结构体用来描述属性的特征。
typedef enum {
rac_propertyMemoryManagementPolicyAssign = 0,
rac_propertyMemoryManagementPolicyRetain,
rac_propertyMemoryManagementPolicyCopy
} rac_propertyMemoryManagementPolicy;
rac_propertyMemoryManagementPolicy
是枚举类型,其表示属性在内存中的管理策略。
rac_propertyAttributes
是一个结构体,描述属性的特征信息。
typedef struct {
BOOL readonly;
BOOL nonatomic;
BOOL weak;
BOOL canBeCollected;
BOOL dynamic;
rac_propertyMemoryManagementPolicy memoryManagementPolicy;
SEL getter;
SEL setter;
const char *ivar;
Class objectClass;
char type[];
} rac_propertyAttributes;
结构体成员变量 | 含义 |
---|---|
readonly | 表示属性是否是只读的 |
nonatomic | 表示属性是否是非线程安全的 |
weak | 表示属性是否是弱引用的 |
canBeCollected | 表示属性是否支持垃圾回收 |
dynamic | 表示属性是否使用了 @dynamic 进行定义 |
memoryManagementPolicy | 属性的内存管理方式 |
getter | 属性的获取选择器 |
setter | 属性的设置选择器 |
ivar | 属性所对应的成员变量 |
objectClass | 属性所属的类 |
type | 属性的类型编码 |
Method rac_getImmediateInstanceMethod (Class aClass, SEL aSelector) {
unsigned methodCount = 0;
Method *methods = class_copyMethodList(aClass, &methodCount);
Method foundMethod = NULL;
for (unsigned methodIndex = 0;methodIndex < methodCount;++methodIndex) {
if (method_getName(methods[methodIndex]) == aSelector) {
foundMethod = methods[methodIndex];
break;
}
}
free(methods);
return foundMethod;
}
rac_getImmediateInstanceMethod
函数可以用来查询指定类中是否含有指定的方法,如果有就返回,否则返回 NULL ,而该函数不同于 class_getInstanceMethod()
函数,其并不会去检查父类是否包含指定的方法。
class_copyMethodList()
函数是不会去获取父类中声明的方法的。
rac_propertyAttributes *rac_copyPropertyAttributes (objc_property_t property);
rac_copyPropertyAttributes()
函数是用来获取属性的相关信息的,如下面的伪代码:
@property (nonatomic, strong) Person *person;
objc_property_t property = class_getProperty(object_getClass(self), @"person".UTF8String);
if (property != NULL) {
rac_propertyAttributes *attributes = rac_copyPropertyAttributes(property);
}
调用 rac_copyPropertyAttributes()
函数的执行步骤如下:
-
使用原生函数获取属性的编码
const char * const attrString = property_getAttributes(property); if (!attrString) { fprintf(stderr, "ERROR: Could not get attribute string from property %s\n", property_getName(property)); return NULL; } if (attrString[0] != 'T') { fprintf(stderr, "ERROR: Expected attribute string \"%s\" for property %s to start with 'T'\n", attrString, property_getName(property)); return NULL; }
这里会判断 attrString 的值是否为空以及是否是以
T
开头,正常情况下 attrString 的值为:T@\"Person\",&,N,V_person
,@"Person"
表示该属性是类 Person 的实例对象。 -
typeString 是 attrString 向后移动一个字符,所以其值为:
@\"Person\",&,N,V_person
,其包含属性所有的声明特征。函数 NSGetSizeAndAlignment() 会返回下一个特征编码的位置,所以 next 的值为:,&,N,V_person
。const char *typeString = attrString + 1; const char *next = NSGetSizeAndAlignment(typeString, NULL, NULL); if (!next) { fprintf(stderr, "ERROR: Could not read past type in attribute string \"%s\" for property %s\n", attrString, property_getName(property)); return NULL; }
-
上一步判断声明的属性特征是否存在,而该步骤则判断第一个特征,即属性所属类型是否存在。
size_t typeLength = (size_t)(next - typeString); if (!typeLength) { fprintf(stderr, "ERROR: Invalid type in attribute string \"%s\" for property %s\n", attrString, property_getName(property)); return NULL; }
typeLength 的值是 9 ,即
@"Person"
的长度。 -
接着动态申请一片内存,用来保存接下来读取的信息,并判断是否分配成功。
rac_propertyAttributes *attributes = calloc(1, sizeof(rac_propertyAttributes) + typeLength + 1); if (!attributes) { fprintf(stderr, "ERROR: Could not allocate rac_propertyAttributes structure for attribute string \"%s\" for property %s\n", attrString, property_getName(property)); return NULL; }
attributes 是指向
rac_propertyAttributes
结构体的指针,在使用 calloc() 函数为其分配内存空间时,指定了两个参数,1 表示分配一个连续内存,而要分配的内存长度则是第二个参数指定的,包含结构体的长度、所属类型的编码长度以及一个结束符号(1+48+1)。 -
接着为 attributes 中的 type 成员变量拷贝所属类型的编码
strncpy(attributes->type, typeString, typeLength); attributes->type[typeLength] = '\0';
-
接着判断传递的属性是否是 OC 类对象
if (typeString[0] == *(@encode(id)) && typeString[1] == '"') { const char *className = typeString + 2; next = strchr(className, '"'); //提取类名,此时 //className 的值为 Person",&,N,V_person //next 的值为 ",&,N,V_person if (!next) { fprintf(stderr, "ERROR: Could not read class name in attribute string \"%s\" for property %s\n", attrString, property_getName(property)); return NULL; } if (className != next) { size_t classNameLength = (size_t)(next - className); //计算类名的长度,classNameLength 的值为 6 //接着根据该长度并加上一个结束字符,申请空间并赋值给结构体的 objectClass char trimmedName[classNameLength + 1]; strncpy(trimmedName, className, classNameLength); trimmedName[classNameLength] = '\0'; attributes->objectClass = objc_getClass(trimmedName); } }
此时,next 的值为
",&,N,V_person
-
判断 next ,如果还有属性特征,那么跳到分隔的
,
位置。if (*next != '\0') { next = strchr(next, ','); }
此时,next 的值为
,&,N,V_person
-
接着,遍历剩下所有的属性编码,并为结构体中的成员变量赋值。
while (next && *next == ',') { char flag = next[1]; next += 2; switch (flag) { case '\0': break; case 'R': attributes->readonly = YES; break; case 'C': attributes->memoryManagementPolicy = rac_propertyMemoryManagementPolicyCopy; break; case '&': attributes->memoryManagementPolicy = rac_propertyMemoryManagementPolicyRetain; break; case 'N': attributes->nonatomic = YES; break; case 'G': case 'S': { const char *nextFlag = strchr(next, ','); SEL name = NULL; if (!nextFlag) { // assume that the rest of the string is the selector const char *selectorString = next; next = ""; name = sel_registerName(selectorString); } else { size_t selectorLength = (size_t)(nextFlag - next); if (!selectorLength) { fprintf(stderr, "ERROR: Found zero length selector name in attribute string \"%s\" for property %s\n", attrString, property_getName(property)); goto errorOut; } char selectorString[selectorLength + 1]; strncpy(selectorString, next, selectorLength); selectorString[selectorLength] = '\0'; name = sel_registerName(selectorString); next = nextFlag; } if (flag == 'G') attributes->getter = name; else attributes->setter = name; } break; case 'D': attributes->dynamic = YES; attributes->ivar = NULL; break; case 'V': // assume that the rest of the string (if present) is the ivar name if (*next == '\0') { // if there's nothing there, let's assume this is dynamic attributes->ivar = NULL; } else { attributes->ivar = next; next = ""; } break; case 'W': attributes->weak = YES; break; case 'P': attributes->canBeCollected = YES; break; case 't': fprintf(stderr, "ERROR: Old-style type encoding is unsupported in attribute string \"%s\" for property %s\n", attrString, property_getName(property)); // skip over this type encoding while (*next != ',' && *next != '\0') ++next; break; default: fprintf(stderr, "ERROR: Unrecognized attribute string flag '%c' in attribute string \"%s\" for property %s\n", flag, attrString, property_getName(property)); } }
-
设置属性的默认获取方法
if (!attributes->getter) { // use the property name as the getter by default attributes->getter = sel_registerName(property_getName(property)); }
-
设置属性的默认设置方法
if (!attributes->setter) { const char *propertyName = property_getName(property); size_t propertyNameLength = strlen(propertyName); // we want to transform the name to setProperty: style size_t setterLength = propertyNameLength + 4; char setterName[setterLength + 1]; strncpy(setterName, "set", 3); strncpy(setterName + 3, propertyName, propertyNameLength); // capitalize property name for the setter setterName[3] = (char)toupper(setterName[3]); setterName[setterLength - 1] = ':'; setterName[setterLength] = '\0'; attributes->setter = sel_registerName(setterName); }
-
返回保存有属性信息的结构体指针
return attributes;
-
这里有个错误出口,在第 8 步时,若发生错误则跳转到这里,释放申请的空间并返回 NULL 。
errorOut: free(attributes); return NULL;
编码字符串是以
T
开头,所有特征的编码以,
隔开,最后以V
+属性所对应的成员变量名
结尾,参见运行时。