2018.8.26 更新Class定义
2019.3.25 更新设置weak关联属性

关于Runtime的简单总结。


简单总结一些比较好玩的用法,但是后来我才发现,南峰子大神的Runtime系列总结的很详细很完整了。文章舍不得删,就当抛砖引玉吧。

一、Runtime

Class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
Objective-C2.0之前Class的定义
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
// 父类
Class super_class;
const char *name;
long version;
long info;
long instance_size;
struct objc_ivar_list *ivars;
struct objc_method_list **methodLists;
// 方法缓存
struct objc_cache *cache;
struct objc_protocol_list *protocols;
#endif

};

struct objc_cache {
unsigned int mask /* total = mask + 1 */;
unsigned int occupied;
Method buckets[1];
};

根据源码,最新定义大致如下:
struct objc_class : objc_object {
// Class ISA;
Class isa;
Class superclass;
cache_t cache;// 方法缓存
class_data_bits_t bits; // 用于获取具体的类信息
};

实例演练

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
YAPerson *person = [[YAPerson alloc] init];
Class cls = [person class];

// 获取实例对象对应的Class
Class class1 = object_getClass(person);

// 获取指定名称的类对象
Class class3 = objc_getClass("YAPerson");
Class class2 = objc_getRequiredClass("YAPerson");

// 获取指定名称的元类对象
Class class4 = objc_getMetaClass("YAPerson");

// 找到指定名称的类对象
Class class5 = objc_lookUpClass("NSObject");
NSLog(@"%@%@%@%@%@",class1,class2,class3,class4,class5);

// 设置对象对应的Class,返回原先的class
Class oriClass = object_setClass(person, [NSObject class]);

// 是否是类对象或元类对象
BOOL isClass = object_isClass(person);

// 是否是元类对象
BOOL isMetaClass = class_isMetaClass(class4);

// 获取父类
Class superClass = class_getSuperclass(cls);

// 获取类的版本
int version = class_getVersion(cls);
// 设置类的版本
class_setVersion(cls, 88);

// 获取实例大小
typedef __SIZE_TYPE__ size_t;

size_t size = class_getInstanceSize(cls);

// int objc_getClassList(Class *buffer, int bufferCount)
//
// Class *objc_copyClassList(unsigned int *outCount)
//
// 获取类的名称
const char *name = class_getName(cls);


// 动态创建类
//Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes)
//void objc_registerClassPair(Class cls)

// 动态废弃类
//Class objc_duplicateClass(Class original, const char *name, size_t extraBytes)
//void objc_disposeClassPair(Class cls)


//id o = objc_storeWeak(&weakObject, weakObject);
//id result = objc_loadWeak(&weakObject);

Block

1
2
3
4
5
6
7
8
block创建一个关联的函数指针
IMP imp_implementationWithBlock(id block)

获取函数指针关联的block
id imp_getBlock(IMP anImp)

移除函数指针对应的block
BOOL imp_removeBlock(IMP anImp)

实例演练

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

// 声明block
typedef void (^Block) ();

// 定义block
Block block = ^() {
NSLog(@"哈哈哈");
};

// 给block创建一个关联的函数指针
IMP imp = imp_implementationWithBlock(block);
// 调用block
(*imp)();

// 获取函数指针关联的block
id blockObject = imp_getBlock(imp);
NSLog(@"%@",blockObject);


// 移除函数指针对应的block
BOOL isRemoveBlock = imp_removeBlock(imp);
if (isRemoveBlock) {
NSLog(@"成功移除");
}

SEL

1
2
3
4
5
6
7
8
获取SEL的名称(char *类型)
const char *sel_getName(SEL sel)

注册SEL
SEL sel_registerName(const char *str)

比较SEL
BOOL sel_isEqual(SEL lhs, SEL rhs)

实例演练

1
2
3
4
5
6
7
8
9
10
// 获取SEL的名称(char *类型)
const char *selName = sel_getName(@selector(viewWillAppear:));
printf("%s",selName);

// 注册SEL
SEL newSel = sel_registerName("haha");

// 比较SEL
BOOL isEqual = sel_isEqual(@selector(viewWillAppear:), newSel);
NSLog(@"%d",isEqual);

Ivar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
定义一个objc_ivar结构体指针Ivar
typedef struct objc_ivar *Ivar;


struct objc_ivar {
成员变量名称
char *ivar_name;
成员变量类型
char *ivar_type;
偏移量
int ivar_offset;
#ifdef __LP64__
int space;
#endif
}


关于偏移量的定义(就是整型)
#if defined(__PTRDIFF_TYPE__)
typedef __PTRDIFF_TYPE__ __darwin_ptrdiff_t; /* ptr1 - ptr2 */
#elif defined(__LP64__)
typedef long __darwin_ptrdiff_t; /* ptr1 - ptr2 */
#else
typedef int __darwin_ptrdiff_t; /* ptr1 - ptr2 */
#endif /* __GNUC__ */


#ifndef _PTRDIFF_T
#define _PTRDIFF_T
typedef __darwin_ptrdiff_t ptrdiff_t;
#endif /* _PTRDIFF_T */


成员变量列表
struct objc_ivar_list {
成员变量数量
int ivar_count;
#ifdef __LP64__
int space;
#endif
/* variable length structure */
struct objc_ivar ivar_list[1];
}


获取实例变量
Ivar class_getInstanceVariable(Class cls, const char *name)

//获取类变量
//Ivar class_getClassVariable(Class cls, const char *name)

获取成员变量列表
Ivar *class_copyIvarList(Class cls, unsigned int *outCount)

获取Ivar变量的名称
const char *ivar_getName(Ivar v)

获取Ivar变量的偏移量
ptrdiff_t ivar_getOffset(Ivar v)

获取Ivar变量的编码
const char *ivar_getTypeEncoding(Ivar v)

设置成员变量的值
void object_setIvarWithStrongDefault(id obj, Ivar ivar, id value)

设置成员变量的值
void object_setIvar(id obj, Ivar ivar, id value)

获取Ivar对应的成员变量对象
id object_getIvar(id obj, Ivar ivar)

IvarLayout相关
const uint8_t *class_getIvarLayout(Class cls)
const uint8_t *class_getWeakIvarLayout(Class cls)
void class_setIvarLayout(Class cls, const uint8_t *layout)
void class_setWeakIvarLayout(Class cls, const uint8_t *layout)

添加成员变量
BOOL class_addIvar(Class cls, const char *name, size_t size,
uint8_t alignment, const char *types)

实例演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
- (void)viewDidLoad {
YAPerson *person = [[YAPerson alloc] init];
Class cls = [person class];


// 获取实例变量
Ivar ivar_num = class_getInstanceVariable(cls, "num");

Ivar ivar_name = class_getInstanceVariable(cls, "name");

//获取类变量
//Ivar class_getClassVariable(Class cls, const char *name)

// 获取Ivar变量的名称
const char *name = ivar_getName(ivar_name);
printf("%s\n",name);

// 获取Ivar变量的偏移量
ptrdiff_t p = ivar_getOffset(ivar_num);
printf("%td",p); // 打印16

// 获取Ivar变量的编码
const char *typeEncoding = ivar_getTypeEncoding(ivar_name);
printf("%s\n",typeEncoding); // 打印 @"NSString"

// 设置成员变量的值
object_setIvarWithStrongDefault(person, ivar_name, @"haha");
NSLog(@"%@",person->name);


// 设置成员变量对应的值
object_setIvar(person, ivar_name, @"hahaaaaaa");
NSLog(@"%@",person->name);

//获取Ivar对应的成员变量对象
id object = object_getIvar(person, ivar_name);
NSLog(@"%@",object);

// 动态添加成员变量
// 必须在 objc_allocateClassPair 之后 和 在objc_registerClassPair之前调用
// 不能给一个已经存在的类添加成员变量
Class peopleClass = objc_allocateClassPair(cls, "YAPeople", 0);
class_addIvar(peopleClass, "_gayFriend", sizeof(id), log2(sizeof(id)), @encode(id));
class_addIvar(peopleClass, "_girlFriend", sizeof(id), log2(sizeof(id)), @encode(id));
class_addIvar(peopleClass, "_company", sizeof(id), log2(sizeof(id)), @encode(id));
objc_registerClassPair(peopleClass);
// 打印成员变量列表
NSLog(@"%@", [self ya_getIvarList:peopleClass]);


// IvarLayout相关
// ivarLayout 和 weakIvarLayout 分别记录了哪些 ivar 是 strong 或是 weak,都未记录的就是基本类型和 __unsafe_unretained 的对象类型
const uint8_t *ivarLayoutArray= class_getIvarLayout(cls);
const uint8_t *weakIvarLayoutArray = class_getWeakIvarLayout(cls);
if (ivarLayoutArray) {
int i = 0;
uint8_t value_s = ivarLayoutArray[i];
while (value_s != 0x0) {
printf("\\x%02x\n", value_s);
value_s = ivarLayoutArray[++i];
}
}
//void class_setIvarLayout(Class cls, const uint8_t *layout)
//void class_setWeakIvarLayout(Class cls, const uint8_t *layout)
}

根据class获取成员变量列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
根据class获取成员变量列表

@param class 类
@return 成员变量字典(名称:类型)
*/
- (NSDictionary *)ya_getIvarList:(Class)class {
// 成员变量数量
unsigned int count = 0;

// 获取成员变量列表
Ivar *ivarList = class_copyIvarList(class, &count);

// 存储成员变量
NSMutableDictionary *dict = [NSMutableDictionary dictionary];

// 获取成员变量类型与成员变量名称
for (unsigned int i = 0; i < count; i++) {
// 成员变量类型
const char *c_ivarType = ivar_getTypeEncoding(ivarList[i]);

// 成员变量名称
const char *c_ivarName = ivar_getName(ivarList[i]);

// 利用字典存储,格式为 成员变量名称:对应的成员变量类型
NSString *ivarName = [NSString stringWithUTF8String:c_ivarName];
dict[ivarName] = [NSString stringWithUTF8String:c_ivarType];

}
// 需要手动free
free(ivarList);

return [NSDictionary dictionaryWithDictionary:dict];
}

Method

值得一提的是方法编码和类型编码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#define _C_ID       '@'
#define _C_CLASS '#'
#define _C_SEL ':'
#define _C_CHR 'c'
#define _C_UCHR 'C'
#define _C_SHT 's'
#define _C_USHT 'S'
#define _C_INT 'i'
#define _C_UINT 'I'
#define _C_LNG 'l'
#define _C_ULNG 'L'
#define _C_LNG_LNG 'q'
#define _C_ULNG_LNG 'Q'
#define _C_FLT 'f'
#define _C_DBL 'd'
#define _C_BFLD 'b'
#define _C_BOOL 'B'
#define _C_VOID 'v'
#define _C_UNDEF '?'
#define _C_PTR '^'
#define _C_CHARPTR '*'
#define _C_ATOM '%'
#define _C_ARY_B '['
#define _C_ARY_E ']'
#define _C_UNION_B '('
#define _C_UNION_E ')'
#define _C_STRUCT_B '{'
#define _C_STRUCT_E '}'
#define _C_VECTOR '!'
#define _C_CONST 'r'
Code Meaning
r const
n in
N inout
o out
O bycopy
R byref
V oneway

以上信息在具体应用时,可以自定义一个枚举获取。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
typedef struct objc_method *Method;

struct objc_method {
// 方法的名称
SEL method_name;
// 方法的参数类型
char *method_types;
// 方法的实现(函数指针)
IMP method_imp;
}

根据方法名称获取Method
Method class_getInstanceMethod(Class cls, SEL name)
Method class_getClassMethod(Class cls, SEL name)

获取Method的方法名称
SEL method_getName(Method m)

获取Method的方法实现
IMP method_getImplementation(Method m)
IMP class_getMethodImplementation(Class cls, SEL name)

获取Method的返回值类型(需手动释放)
char *method_copyReturnType(Method m)
//void method_getReturnType(Method m, char *dst, size_t dst_len)

获取指定Method的类型
const char *method_getTypeEncoding(Method m)

获取Method的参数数量
unsigned int method_getNumberOfArguments(Method m)

获取Method中第i个参数的类型(Char *类型)(需手动释放)
char *method_copyArgumentType(Method m, unsigned int index)

获取方法列表
Method *class_copyMethodList(Class cls, unsigned int *outCount)

设置IMP
IMP method_setImplementation(Method m, IMP imp)

交换方法实现
void method_exchangeImplementations(Method m1, Method m2)

添加方法
BOOL class_addMethod(Class cls, SEL name, IMP imp,
const char *types)
替换方法
IMP class_replaceMethod(Class cls, SEL name, IMP imp,
const char *types)


标明需要手动释放的动态内存(如返回值类型/参数类型)
// 及时释放
if (argumentType) free(argumentType);

实例演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
- (void)viewDidLoad {
[super viewDidLoad];

YAPerson *person = [[YAPerson alloc] init];

// 获取Method
Method method = class_getInstanceMethod([person class] , NSSelectorFromString(@"nameWithArg:arg:arg:arg:arg:"));


// 获取Method的方法名称
SEL sel = method_getName(method);
NSLog(@"%@",NSStringFromSelector(sel));


// 获取Method的方法实现
IMP imp1 = method_getImplementation(method);
IMP imp2 = class_getMethodImplementation([person class],sel);


// 获取Method的返回值类型(需手动释放)
char *returnType = method_copyReturnType(method);
if (returnType) {
NSLog(@"%@",[NSString stringWithUTF8String:returnType]);
free(returnType);
}


// 获取指定Method的类型
const char *typeEncoding = method_getTypeEncoding(method);
if (typeEncoding) {
NSLog(@"%@",[NSString stringWithUTF8String:typeEncoding]);
}

// 获取Method的参数数量
unsigned int arguementCount = method_getNumberOfArguments(method);
if (arguementCount > 0) {
for (unsigned int i = 0; i < arguementCount; i++) {
//获取Method中第i个参数的类型(Char *类型)(需手动释放)
char *arguementType = method_copyArgumentType(method, i);
NSLog(@"%@",[NSString stringWithUTF8String:arguementType]);

// 手动释放
if (arguementType) {
free(arguementType);
}
}
}


// 设置新的方法实现IMP,并返回原先的IMP
IMP imp3 = method_setImplementation(method, imp2);
}

Method Swizzling
使用 Method Swizzling 的目的通常都是为了给程序增加功能,而不是完全地替换某个功能,所以我们一般都需要在自定义的实现中调用原始的实现。

Swizzling应该总是在+load中执行

在Objective-C中,运行时会自动调用每个类的两个方法。+load会在类初始加载时调用,+initialize会在第一次调用类的类方法或实例方法之前被调用。这两个方法是可选的,且只有在实现了它们时才会被调用。由于method swizzling会影响到类的全局状态,因此要尽量避免在并发处理中出现竞争的情况。+load能保证在类的初始化过程中被加载,并保证这种改变应用级别的行为的一致性。相比之下,+initialize在其执行时不提供这种保证–事实上,如果在应用中没为给这个类发送消息,则它可能永远不会被调用。

Swizzling应该总是在dispatch_once中执行

与上面相同,因为swizzling会改变全局状态,所以我们需要在运行时采取一些预防措施。原子性就是这样一种措施,它确保代码只被执行一次,不管有多少个线程。GCD的dispatch_once可以确保这种行为,我们应该将其作为method swizzling的最佳实践。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
/**
Method Swizzling
一般放在load方法中,并且使用dispatch_once,需要调用 class_addMethod 方法
*/
+ (void)load {

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class cls = [self class];

SEL oldSelector = NSSelectorFromString(@"XXXX");
SEL newSelector = NSSelectorFromString(@"XXXXXXXX");

Method oldMethod = class_getInstanceMethod(cls, oldSelector);
Method newMethod = class_getInstanceMethod(cls, newSelector);

// 尝试给旧的方法oldSelector添加新的方法newSelectot的实现,如果已经存在方法实现,则添加失败
BOOL isSuccess = class_addMethod(cls, oldSelector, method_getImplementation(newMethod), method_getTypeEncoding(newMethod));
if (isSuccess) { // 添加成功,那么用旧的方法实现替换新的方法实现
class_replaceMethod(cls, newSelector, method_getImplementation(oldMethod), method_getTypeEncoding(oldMethod));
} else { // 旧的方法已经有了实现,直接交换即可
method_exchangeImplementations(newMethod, oldMethod);
}
});
}


/**
根据class获取方法列表

@param class 类
@return 方法名称数组
*/

+ (NSArray *)ya_getMethodList:(Class)class {
// 方法列表数量
unsigned int methodCount = 0;
// 获取方法列表
Method *methodList = class_copyMethodList(class, &methodCount);

// 存储方法名称
NSMutableArray *array = [NSMutableArray array];
if (methodList) {
for (unsigned int i = 0; i < methodCount; i ++) {
// 获取方法
Method method = methodList[i];
// 获取方法名称
SEL sel = method_getName(method);

[array addObject:NSStringFromSelector(sel)];
}

// 手动释放
free(methodList);
}

return [NSArray arrayWithArray:array];
}


/**
交换实例方法实现

@param class 类
@param aSEL 方法一的名称
@param bSEL 方法二的名称
*/
+ (void)ya_exchangeInstanceMethod:(Class)class firstMethod:(SEL)aSEL secondMethod:(SEL)bSEL {
Method aMethad = class_getInstanceMethod(class, aSEL);
Method bMethod = class_getInstanceMethod(class, bSEL);

// 交换方法实现
method_exchangeImplementations(aMethad, bMethod);
}


/**
交换类方法实现

@param class 类
@param aSEL 方法一的名称
@param bSEL 方法二的名称
*/
+ (void)ya_exchangeClassMethod:(Class)class firstMethod:(SEL)aSEL secondMethod:(SEL)bSEL {
Method aMethad = class_getClassMethod(class, aSEL);
Method bMethod = class_getClassMethod(class, bSEL);

// 交换方法实现
method_exchangeImplementations(aMethad, bMethod);
}



/**
为类添加名为newSEL的(实例)方法

@param class 类
@param aSEL 新的方法名称
@param bSEL 已经存在的方法名称
@return 是否添加成功
*/
+ (BOOL)ya_addMethod:(Class)class newSEL:(SEL)aSEL existSEL:(SEL)bSEL {
// 获取已经存在的方法
Method method = class_getInstanceMethod(class, bSEL);

// 获取已经存在的方法的实现
IMP imp = class_getMethodImplementation(class, bSEL);

// 获取type
const char *type = method_getTypeEncoding(method);

// 给class添加名称为aSEL的方法实现
return class_addMethod(class, aSEL, imp, type);

/*
如果父类中已经有该名称的方法,那么调用后将重写该方法
如果本类中已经有了该名称的方法实现,那么将添加失败
*/
}

Property

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
typedef struct objc_property *objc_property_t;

属性相关信息
typedef struct {
const char *name;
const char *value;
} objc_property_attribute_t;



获取属性
objc_property_t class_getProperty(Class cls, const char *name)

获取属性列表
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)

获取属性信息列表
objc_property_attribute_t *property_copyAttributeList(objc_property_t property, unsigned int *outCount)

获取属性信息中某个Value
char *property_copyAttributeValue(objc_property_t property, const char *attributeName)

添加属性
BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount)

替换属性
void class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount)

获取属性名称
const char *property_getName(objc_property_t property)

获取属性相关信息
const char *property_getAttributes(objc_property_t property)

实例演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
- (void)viewDidLoad {

YAPerson *person = [[YAPerson alloc] init];
Class cls = [person class];

// 获取属性
objc_property_t property = class_getProperty(cls, "school");

// 获取属性名称
const char *propertyName = property_getName(property);
printf("%s\n",propertyName);

// 获取属性信息列表
// objc_property_attribute_t *property_copyAttributeList(objc_property_t property, unsigned int *outCount)

// 获取属性相关信息
const char *propertyAttributes = property_getAttributes(property);
printf("%s\n",propertyAttributes); // 打印 T@"NSObject",W,N,V_school


// 根据获取属性信息中的name获取对应的Value
char *propertyValue = property_copyAttributeValue(property, "T"); // 类型
printf("%s\n",propertyValue);

/*
T 类型 例如:NSObject
V 值(成员变量) 例如 _school
C copy
N nonatommic
*/

// 添加属性
objc_property_attribute_t type = { "T", [[NSString stringWithFormat:@"@\"%@\"",NSStringFromClass([NSString class])] UTF8String] }; //type
objc_property_attribute_t ownership0 = { "C", "" }; // C = copy
objc_property_attribute_t ownership = { "N", "" }; //N = nonatomic
objc_property_attribute_t backingivar = { "V", [[NSString stringWithFormat:@"_%s", "propertyName"] UTF8String] }; //variable name
objc_property_attribute_t attrs[] = { type, ownership0, ownership, backingivar };
if (class_addProperty(cls, "propertyName", attrs, 4)) {
NSLog(@"添加成功");
}

// 替换属性
//void class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount)
}

/**
根据class获取类的属性列表

@param class 类
@return 属性列表数组
*/
+ (NSArray *)ya_getPropertyList:(Class)class {
// 私有/公有/类扩展中的所有属性数量
unsigned int count = 0;

// 获取属性列表
objc_property_t *propertyList = class_copyPropertyList(class, &count);

// 存储属性名称
NSMutableArray *array = [NSMutableArray array];

for (unsigned int i = 0; i < count; i ++) {
const char* c_propertyName = property_getName(propertyList[i]);
[array addObject:[NSString stringWithUTF8String:c_propertyName]];
}

// 手动释放
free(propertyList);

return [NSArray arrayWithArray:array];
}

Protocol

本部分应用较少,未做详细实践。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
struct objc_protocol_list {
struct objc_protocol_list *next;
long count;
__unsafe_unretained Protocol *list[1];
};

是否遵循协议
BOOL class_conformsToProtocol(Class cls, Protocol *protocol)

协议列表
Protocol * __unsafe_unretained *class_copyProtocolList(Class cls, unsigned int *outCount)

Protocol * __unsafe_unretained *objc_copyProtocolList(unsigned int *outCount)

Protocol * __unsafe_unretained *protocol_copyProtocolList(Protocol *proto, unsigned int *outCount)

动态添加协议
BOOL class_addProtocol(Class cls, Protocol *protocol)

获取指定名称的协议
Protocol *objc_getProtocol(const char *name)

协议A是否遵循协议B
BOOL protocol_conformsToProtocol(Protocol *proto, Protocol *other)

两个协议是否相等
BOOL protocol_isEqual(Protocol *proto, Protocol *other)

获取某个协议的名称
const char *protocol_getName(Protocol *p)

动态生成协议
Protocol *objc_allocateProtocol(const char *name)
void objc_registerProtocol(Protocol *proto)

给协议添加方法
void protocol_addMethodDescription(Protocol *proto, SEL name, const char *types, BOOL isRequiredMethod, BOOL isInstanceMethod)

给协议添加协议
void protocol_addProtocol(Protocol *proto, Protocol *addition)

给协议添加属性
void protocol_addProperty(Protocol *proto, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount, BOOL isRequiredProperty, BOOL isInstanceProperty)

根据class获取遵循的协议列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
根据class获取遵循的协议列表

@param class 类
@return 协议名称列表
*/
+ (NSArray *)ya_getProtocolList:(Class)class {
// 协议列表数量
unsigned int count = 0;

// 获取协议列表
__unsafe_unretained Protocol **protocolList = class_copyProtocolList(class, &count);

// 存储协议
NSMutableArray *array = [NSMutableArray array];

for (NSInteger i = 0; i < count; i ++) {
// 获取协议
Protocol *protocol = protocolList[i];

// 获取协议名称
const char *c_protocolName = protocol_getName(protocol);

[array addObject:[NSString stringWithUTF8String:c_protocolName]];
}

// 手动释放
free(protocolList);

return [NSArray arrayWithArray:array];
}

二、技能

消息转发

第一步:

1
2
3
4
5
6
7
8
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel_isEqual(sel, NSSelectorFromString(@"ss"))) {
// 要求参数要匹配
[YARuntime ya_addMethod:[self class] newSEL:sel existSEL:@selector(printPersonalInfo)];
return YES;
}
return [super resolveInstanceMethod:sel];
}

第二步:

1
2
3
4
5
6
7
- (id)forwardingTargetForSelector:(SEL)aSelector {
if (sel_isEqual(aSelector, NSSelectorFromString(@"ss"))) {
// 转发给已经存在的对象
return [[YAPerson alloc] init];
}
return [super forwardingTargetForSelector:aSelector];
}

第三步:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];

// 没有找到signature(方法签名),只能手动提供
if (signature == nil) {
signature = [NSMethodSignature signatureWithObjCTypes:"@@:"];
}
return signature;
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
YAPerson *personClass = [[YAPerson alloc] init];
SEL sel = anInvocation.selector;
if ([personClass respondsToSelector:sel]) {
// 最后一次机会
[anInvocation invokeWithTarget:personClass];
} else {
// 回天乏力
[self doesNotRecognizeSelector:sel];
}
}

多重继承

不会做菜的程序员不是好男人。

YADeveloper 继承自 YAMan,想让YADeveloper实例对象同时具备响应 YAMan方法和 YACook方法的能力。

YAMan:

1
2
3
4
5
6
7
8
9
@interface YAMan : NSObject
- (void)printMan;
@end

@implementation YAMan
- (void)printMan {
NSLog(@"我是一个男人");
}
@end

YACook:

1
2
3
4
5
6
7
8
9
@interface YACook : NSObject 
- (void)printCook;
@end

@implementation YACook
- (void)printCook {
NSLog(@"我是个会做菜的人");
}
@end

YADeveloper:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@class YACook;

@interface YADeveloper : YAMan
@property (nonatomic,strong) YACook *cook;
- (void)printDev;

// 声明YACook的方法,使编译通过
- (void)printCook;
@end

@implementation YADeveloper
- (void)printDev {
NSLog(@"我是个会写程序的人");
}

- (id)forwardingTargetForSelector:(SEL)aSelector {
if (sel_isEqual(aSelector, NSSelectorFromString(@"printCook"))) {
// 需要预先对self.cook初始化
return self.cook;
// 或者直接创建
// return [[YACook alloc] init];
}
return [super forwardingTargetForSelector:aSelector];
}
@end

关联引用

注意点:

  1. objc_removeAssociatedObjects 函数一般不可手动调用,因为这个函数会移除一个对象的所有关联对象,将该对象恢复成“原始”状态。这样做就很有可能把别人添加的关联对象也一并移除,这并不是我们所希望的。所以一般的做法是通过给 objc_setAssociatedObject 函数传入 nil 来移除某个已有的关联对象。
  2. 关联对象与被关联对象本身的存储并没有直接的关系,它是存储在单独的哈希表中的。

    给分类添加weak属性

    给任意对象A 添加 weak属性 B

问题关键点:在属性销毁的时候,将其置为空(或者说在关联对象销毁的时候,使objc_getAssociatedObject得到的是nil)。

方法一

通过继承关联对象B ,重写其dealloc方法即可适时把A中的关联对象返回值设置为nil,达到自动置空的目的,这也是最本能的方法。

给NSObject添加分类,也即任何继承自NSObject的对象都可以有weak属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@interface NSObject (YAWeakProperty)
@property (nonatomic, weak) YAProperty *property;
@end

@implementation NSObject (YAWeakProperty)
// const static char *key = "key";
- (id)property {
return objc_getAssociatedObject(self, @selector(property));
}

- (void)setProperty:(YAProperty *)property {
objc_setAssociatedObject(self, @selector(property), property, OBJC_ASSOCIATION_ASSIGN);
[property setAssociate:self selector:@selector(setProperty:)];
}
@end

关联引用的key一般使用getter方法的selector,此时get方法中的key也可使用_cmd,二者等效。

属性的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@interface YAProperty : NSObject
- (void)setAssociate:(NSObject *)associatedObject selector:(SEL)sel;
@end

// 类扩展
@interface YAProperty()
{
__weak id _associatedObject;
SEL _sel;
}
@end

@implementation YAProperty
- (void)setAssociate:(NSObject *)associatedObject selector:(SEL)sel {
_associatedObject = associatedObject;
_sel = sel;
}

- (void)dealloc {
((void (*)(id, SEL,id)) objc_msgSend)(_associatedObject, _sel, nil);
}

@end

然而有个问题:添加的属性继承自NSObject时非常完美,但是实际项目中不可能给任意对象添加的属性都是NSObject,有可能是NSString/NSArray/NSDictionary/NSSet等等。那么就只能继承自NSString/NSArray/NSDictionary/NSSet等系统类,这样会出现一系列一系列一系列问题,苹果并不建议我们使用NSString/NSArray/NSDictionary/NSSet等的派生类(这些类已经足够好了,不需要画蛇添足)。

方法二

用一个NSPointerArray(弱引用类型的数组)包一层就可以了。虽然关联属性的policy不支持weak,但是你可以把要关联的对象放入一个弱引用数组里面,然后把这个弱引用数组设置为关联对象,每次取值的时候,只需要从这个弱引用数组里面取就可以了。一样可以达到关联弱引用对象的效果。

方法三

A 关联 C
B 关联 C
C 销毁 通知B
B 再告诉 A

方法四

阅读大神博客发现的新方法,见2019年新写的文章: Weak Associated Object

三、要点

free()

1
2
3
4
5
6
7
free() 函数用来释放动态分配的内存空间,其原型为:
void free (void* ptr);

free() 可以释放由 malloc()、calloc()、realloc() 分配的内存空间,以便其他程序再次使用。

// 及时释放字符串常量
if (argumentType) free(argumentType);

NONNULL

1
2
3
4
5
NS_ASSUME_NONNULL_BEGINNS_ASSUME_NONNULL_END
在这两个宏之间的代码,所有简单指针对象都被假定为 nonnull

#define NS_ASSUME_NONNULL_BEGIN _Pragma("clang assume_nonnull begin")
#define NS_ASSUME_NONNULL_END _Pragma("clang assume_nonnull end")

__covariant

1
2
__covariant - 协变性,子类型可以强转到父类型(里氏替换原则)
__contravariant - 逆变性,父类型可以强转到子类型(WTF)
1
__kindof

内联

1
__attribute__((always_inline)) 的意思是强制内联

参考并感谢
青玉伏案
南峰子