本打算这个系列结束的,但是由于疫情影响在家的这几天有空闲时间,想着把这块也整理下,今天这篇文章打算带大家熟悉下Runtime API.其这部分和Runtime重要的数据结构紧密相关,通过objc/runtime.h无非就是获得类,对象,Method,SEL,IMP,Ivar,Property,Protocal相关类以及对应的属性。这块还是蛮有规律的,最早接触Runtime还是蛮抗拒的,后面才慢慢觉得它的强大。废话不多说这篇博客主要分两大部分,一部分介绍上面提到的这些数据结构相关的数据,另一部分介绍十分重要的类型编码和属性编码。搞定这两部分估计大家在使用上就会游刃有余了,但是还是建立在理解Runtime各个环节的基础上。

这里推荐大家用Cooci可编译苹果官方源码objc来写demo这样可以比较方便得调试整个过程,这也是最近才发现的一个很棒的开源库,之前研究底层代码的时候要是早点发现这个就不会那么纠结了。好东西不私藏。https://github.com/LGCooci/objc4_debug:

在开始之前建议大家通读下我的《iOS Runtime源码分析系列》的博客,当然在这篇博客介绍的时候还会简要提一下相关内容。好了我们开始我们的API之旅:

首先是类相关的,这些都以****class_****开头:

类相关:

//获取类名
OBJC_EXPORT const char * _Nonnull class_getName(Class _Nullable cls)

//获取isa
OBJC_EXPORT Class _Nullable object_getClass(id _Nullable obj)

//获取父类
OBJC_EXPORT Class _Nullable class_getSuperclass(Class _Nullable cls)

获取实例变量的信息
1.实例变量是指变量不是属性.每个属性都有对应的实例变量,比如下面例子中包含属性name,那么它对应的实例变量为_name
2.这个方法可以获取属性的变量,也可以获取私有变量
3.如果没有找到对应的实例变量,就会返回nil
4.返回的列表不包含父类的实例变量
Ivar class_getClassVariable(Class cls, const char *name)

获取属性:
这个方法只能获得属性,不能获得变量
objc_property_t class_getProperty(Class cls, const char *name)

获取实例方法信息
OBJC_EXPORT Method _Nullable class_getInstanceMethod(Class _Nullable cls, SEL _Nonnull name);

获取类方法信息
OBJC_EXPORT Method _Nullable class_getClassMethod(Class _Nullable cls, SEL _Nonnull name)

获取SEL对应的实现体,也就是IMP
IMP class_getMethodImplementation(Class cls, SEL name)

通过这部分接口可以获得类名,父类,实例变量,属性,实例方法,类方法和IMP,注意这里只是获取单个的并不是全部的数据。如果要获得全部的就需要class_copyXXXX了。

获取数据列表类:

//获取实例变量列表
//1.获取所有[==私有变量==]和属性对应的变量
//2.获取的私有变量的名和定义的名一样
//3.获取的属性的名前面都添加了[下划线]
//4.注意这里[不包括父类的实例变量]
OBJC_EXPORT Ivar _Nonnull * _Nullable class_copyIvarList(Class _Nullable cls, unsigned int * _Nullable outCount);

//获取方法列表
//1.获取所有实例方法,[==不包含静态方法==]
//2.不获取[父类的方法]
//3.[隐式的get set 方法也能获取到]
//4.[==可以获取分类和动态添加的方法==]。
OBJC_EXPORT Method _Nonnull * _Nullable class_copyMethodList(Class _Nullable cls, unsigned int * _Nullable outCount)

//获取协议列表
//不能获取分类实现的协议
OBJC_EXPORT Protocol * __unsafe_unretained _Nonnull * _Nullable
class_copyProtocolList(Class _Nullable cls, unsigned int * _Nullable outCount)

//获取属性列表
//1.获取的属性名不自动添加下划线
//2.[不能获取Category添加的属性]。
OBJC_EXPORT objc_property_t _Nonnull * _Nullable
class_copyPropertyList(Class _Nullable cls, unsigned int * _Nullable outCount)

这些方法都需要注意最后调用free释放掉存放copy来的空间

添加方法
OBJC_EXPORT BOOL
class_addMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp,
const char * _Nullable types)

添加实例变量
//1.只能给动态创建的类添加变量也就是用 objc_allocateClassPair 创建的类
//2.添加变量只能在函数 objc_allocateClassPair 和 class_getInstanceVariable 之间添加才有效
OBJC_EXPORT BOOL
class_addIvar(Class _Nullable cls, const char * _Nonnull name, size_t size,
uint8_t alignment, const char * _Nullable types)

添加协议
OBJC_EXPORT BOOL
class_addProtocol(Class _Nullable cls, Protocol * _Nonnull protocol)

//添加属性
//1. 添加属性不用在objc_registerClassPair之前,因为添加属性其实就是添加变量的set 和 get方法而已
//2. 添加的属性和变量不能用kvc设置值和取值
OBJC_EXPORT BOOL
class_addProperty(Class _Nullable cls, const char * _Nonnull name,
const objc_property_attribute_t * _Nullable attributes,
unsigned int attributeCount)

替换方法
OBJC_EXPORT IMP _Nullable
class_replaceMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp, const char * _Nullable types)

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

创建实例
OBJC_EXPORT id _Nullable
class_createInstance(Class _Nullable cls, size_t extraBytes)

遵循协议
OBJC_EXPORT BOOL
class_conformsToProtocol(Class _Nullable cls, Protocol * _Nullable protocol)

判断是否响应某个SEL
OBJC_EXPORT BOOL
class_respondsToSelector(Class _Nullable cls, SEL _Nonnull sel)

下面是简要的实例:

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface IDLTestObject : NSObject {
NSString *privateInstanceVar;
}

@property(nonatomic, strong, readwrite) NSString *name;

@property(nonatomic, assign, readwrite) NSInteger age;

- (void)testInstanceMethod;

@end

NS_ASSUME_NONNULL_END
#import "IDLTestObject.h"

@implementation IDLTestObject

- (void)testInstanceMethod {
NSLog(@"run testInstanceMethod");
}

+ (void)testClassMethod {
NSLog(@"testclassMethod");
}

@end
//获取类名
const char *className = class_getName([IDLTestObject class]);
NSLog(@"className : %@",[NSString stringWithUTF8String:className]);

//获取父类
Class superClass = class_getSuperclass([IDLTestObject class]);
NSLog(@"superClass : %@", NSStringFromClass(superClass));

//获取实例对象的大小
size_t instanceSize = class_getInstanceSize([IDLTestObject class]);
NSLog(@"instanceSize = %zu",instanceSize);

//获取实例变量信息 如果没有找到会返回nil
Ivar nameVar = class_getInstanceVariable([IDLTestObject class], [@"_name" UTF8String]);
NSLog(@"InstanceVariable :%@",[NSString stringWithUTF8String:ivar_getName(nameVar)]);
NSLog(@"Instance Variable TypeEncoding:%@",[NSString stringWithUTF8String:ivar_getTypeEncoding(nameVar)]);
NSLog(@"Instance Variable Offset:%td",ivar_getOffset(nameVar));

//获取实例变量列表
unsigned int outCount = 0;
Ivar *varList = class_copyIvarList([IDLTestObject class], &outCount);
for (unsigned int index = 0 ;index < outCount; index++) {
NSLog(@"var %u = %@",index,[NSString stringWithUTF8String:ivar_getName(varList[index])]);
}
free(varList);

//获取对象属性
objc_property_t property = class_getProperty([IDLTestObject class], [@"age" UTF8String]);
NSLog(@"Instance Property Name :%@",[NSString stringWithUTF8String:property_getName(property)]);
NSLog(@"Instance Property Attribute:%@",[NSString stringWithUTF8String:property_getAttributes(property)]);

//获取实例方法
Method method = class_getInstanceMethod([IDLTestObject class], @selector(testInstanceMethod));
NSLog(@"get InstanceMethod %@",NSStringFromSelector(method_getName(method)));

//获取方法列表
Method *methodList = class_copyMethodList([IDLTestObject class], &outCount);
for (unsigned int index = 0; index < outCount; index++) {
NSLog(@"method %u = %@",index,NSStringFromSelector(method_getName(methodList[index])));
}
free(methodList);

//获取类方法
Method classMethod = class_getClassMethod([IDLTestObject class], @selector(testClassMethod));
NSLog(@"get classMethod %@",NSStringFromSelector(method_getName(classMethod)));

IMP testMethod = class_getMethodImplementation([IDLTestObject class], @selector(testInstanceMethod));
testMethod();


//给某个类添加方法
class_addMethod([IDLTestObject class],
@selector(addedMethod),
(IMP)addedMethodIMP,
"v@:");

[[IDLTestObject new] performSelector:@selector(addedMethod)];

objc_property_attribute_t type = { "T", "@\"NSString\"" }; //T:类型
objc_property_attribute_t ownership = { "C", "" }; // C:copy
objc_property_attribute_t nonatomic = { "N", "" }; // N:nonatomic
objc_property_attribute_t ivar = { "V", "_addedMethod" }; //V: 实例变量变量名
objc_property_attribute_t attributs[] = { type, ownership,nonatomic, ivar };
class_addProperty([IDLTestObject class], "addedMethod", attributs, 4);

//输出添加的成员变量
unsigned int count;
objc_property_t *propertyList = class_copyPropertyList([IDLTestObject class], &count);
for (unsigned int i = 0; i< count; i++) {
const char *name = property_getName(propertyList[i]);
NSLog(@"属性%@ 属性信息%@",[NSString stringWithUTF8String:name],
[NSString stringWithUTF8String:property_getAttributes(propertyList[i])]);
}
free(propertyList);

//替换方法
class_replaceMethod([IDLTestObject class],
@selector(testInstanceMethod),
(IMP)addedMethodIMP,
"v@:");
[[[IDLTestObject alloc] init] testInstanceMethod];

//创建实例
IDLTestObject *testClass = class_createInstance([IDLTestObject class], 0);
[testClass performSelector:@selector(testInstanceMethod)];

Class DynamicClass = objc_allocateClassPair([NSObject class], "DynamicClass", 0);
class_addIvar(DynamicClass, "_attribute0", sizeof(NSString *), log(sizeof(NSString *)), "i");
Ivar ivar = class_getInstanceVariable(DynamicClass, "_attribute0");
NSLog(@"DynamicClass var:%@",[NSString stringWithUTF8String:ivar_getName(ivar)]);
objc_registerClassPair(DynamicClass);

输出结果如下:

2020-02-08 19:05:51.294372+0800 objc-debug[43702:387877] className : IDLTestObject
2020-02-08 19:05:51.296220+0800 objc-debug[43702:387877] superClass : NSObject
2020-02-08 19:05:51.297028+0800 objc-debug[43702:387877] instanceSize = 32
2020-02-08 19:05:51.299444+0800 objc-debug[43702:387877] InstanceVariable :_name
2020-02-08 19:05:51.306411+0800 objc-debug[43702:387877] Instance Variable TypeEncoding:@"NSString"
2020-02-08 19:05:51.306766+0800 objc-debug[43702:387877] Instance Variable Offset:16
2020-02-08 19:05:51.307020+0800 objc-debug[43702:387877] var 0 = privateInstanceVar
2020-02-08 19:05:51.307322+0800 objc-debug[43702:387877] var 1 = _name
2020-02-08 19:05:51.307559+0800 objc-debug[43702:387877] var 2 = _age
2020-02-08 19:05:51.308250+0800 objc-debug[43702:387877] Instance Property Name :age
2020-02-08 19:05:51.308553+0800 objc-debug[43702:387877] Instance Property Attribute:Tq,N,V_age
2020-02-08 19:05:51.308878+0800 objc-debug[43702:387877] get InstanceMethod testInstanceMethod
2020-02-08 19:05:51.309351+0800 objc-debug[43702:387877] method 0 = testInstanceMethod
2020-02-08 19:05:51.309624+0800 objc-debug[43702:387877] method 1 = .cxx_destruct
2020-02-08 19:05:51.334783+0800 objc-debug[43702:387877] method 2 = name
2020-02-08 19:05:51.335325+0800 objc-debug[43702:387877] method 3 = setName:
2020-02-08 19:05:51.335856+0800 objc-debug[43702:387877] method 4 = age
2020-02-08 19:05:51.336313+0800 objc-debug[43702:387877] method 5 = setAge:
2020-02-08 19:05:51.336745+0800 objc-debug[43702:387877] get classMethod testClassMethod
2020-02-08 19:05:51.336909+0800 objc-debug[43702:387877] run testInstanceMethod
2020-02-08 19:05:51.340473+0800 objc-debug[43702:387877] run addedMethodIMP
2020-02-08 19:05:51.351977+0800 objc-debug[43702:387877] 属性addedMethod 属性信息T@"NSString",C,N,V_addedMethod
2020-02-08 19:05:51.352879+0800 objc-debug[43702:387877] 属性name 属性信息T@"NSString",&,N,V_name
2020-02-08 19:05:51.353429+0800 objc-debug[43702:387877] 属性age 属性信息Tq,N,V_age
2020-02-08 19:05:51.353906+0800 objc-debug[43702:387877] run addedMethodIMP
2020-02-08 19:05:51.354370+0800 objc-debug[43702:387877] run addedMethodIMP
2020-02-08 19:05:51.354378+0800 objc-debug[71958:585781] DynamicClass var:_attribute0
Ivar实例变量相关:
//获取变量名
OBJC_EXPORT const char * _Nullable
ivar_getName(Ivar _Nonnull v)

//获取变量类型描述
OBJC_EXPORT const char * _Nullable
ivar_getTypeEncoding(Ivar _Nonnull v)

//获取变量基地址偏移量
OBJC_EXPORT ptrdiff_t
ivar_getOffset(Ivar _Nonnull v)

上面的三个方法基本上对应于IVar的数据结构的各个字段,后面会单独介绍TypeEncoding和属性编码。

Property属性相关:
//获取属性名
OBJC_EXPORT const char * _Nonnull
property_getName(objc_property_t _Nonnull property)

//获取属性编码
OBJC_EXPORT const char * _Nullable
property_getAttributes(objc_property_t _Nonnull property)

//获取属性列表返回的objc_property_attribute_t的结构如下:
//typedef struct {
// const char *name;
// const char *value;
//} objc_property_attribute_t;
OBJC_EXPORT objc_property_attribute_t * _Nullable
property_copyAttributeList(objc_property_t _Nonnull property,
unsigned int * _Nullable outCount)

//获取属性的属性值
OBJC_EXPORT char * _Nullable
property_copyAttributeValue(objc_property_t _Nonnull property,
const char * _Nonnull attributeName)
Method方法相关:
//获得方法名
OBJC_EXPORT SEL _Nonnull
method_getName(Method _Nonnull m)

//获得方法的实现
OBJC_EXPORT IMP _Nonnull
method_getImplementation(Method _Nonnull m)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

//获得参数和返回值的类型编码
OBJC_EXPORT const char * _Nullable
method_getTypeEncoding(Method _Nonnull m)

//获得参数数量
OBJC_EXPORT unsigned int
method_getNumberOfArguments(Method _Nonnull m)

//获得返回类型
OBJC_EXPORT char * _Nonnull
method_copyReturnType(Method _Nonnull m)

//获得参数类型
OBJC_EXPORT char * _Nullable
method_copyArgumentType(Method _Nonnull m, unsigned int index)

//获得返回类型,指定存储空间
OBJC_EXPORT void
method_getReturnType(Method _Nonnull m, char * _Nonnull dst, size_t dst_len)

//获得参数类型,指定存储空间
OBJC_EXPORT void
method_getArgumentType(Method _Nonnull m, unsigned int index,
char * _Nullable dst, size_t dst_len)

//获得方法描述
OBJC_EXPORT struct objc_method_description * _Nonnull
method_getDescription(Method _Nonnull m)

设置方法实现
OBJC_EXPORT IMP _Nonnull
method_setImplementation(Method _Nonnull m, IMP _Nonnull imp)

//交换两个方法的实现
OBJC_EXPORT void
method_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2)

之前博客已经介绍过,方法主要有方法名SEL,包含方法参数,返回值类型的编码,以及实现体IMP.这里最重要的是类型编码在后面统一介绍。这里还增加了可以分开获取方法参数和返回值类型编码的方法,以及方法参数数量。

SEL相关:
//返回SEL名字
OBJC_EXPORT const char * _Nonnull sel_getName(SEL _Nonnull sel)

//在系统中注册一个方法,将方法名映射到一个选择器,并返回这个选择器
OBJC_EXPORT SEL _Nonnull sel_registerName(const char * _Nonnull str)

//对比方法选择器
OBJC_EXPORT BOOL sel_isEqual(SEL _Nonnull lhs, SEL _Nonnull rhs)
协议Protocal相关:
//返回一个指定的协议
OBJC_EXPORT Protocol * _Nullable objc_getProtocol(const char * _Nonnull name);

//返回运行时所有的协议
OBJC_EXPORT Protocol * __unsafe_unretained _Nonnull * _Nullable
objc_copyProtocolList(unsigned int * _Nullable outCount);

//返回某个协议是否遵循另一个协议
OBJC_EXPORT BOOL
protocol_conformsToProtocol(Protocol * _Nullable proto,
Protocol * _Nullable other)

//返回协议是否相等
OBJC_EXPORT BOOL
protocol_isEqual(Protocol * _Nullable proto, Protocol * _Nullable other)

//返回协议名
OBJC_EXPORT const char * _Nonnull
protocol_getName(Protocol * _Nonnull proto)

//返回协议的某个方法描述
OBJC_EXPORT struct objc_method_description
protocol_getMethodDescription(Protocol * _Nonnull proto, SEL _Nonnull aSel,
BOOL isRequiredMethod, BOOL isInstanceMethod)

//返回协议的所有方法描述
OBJC_EXPORT struct objc_method_description * _Nullable
protocol_copyMethodDescriptionList(Protocol * _Nonnull proto,
BOOL isRequiredMethod,
BOOL isInstanceMethod,
unsigned int * _Nullable outCount)

//返回协议指定的属性
OBJC_EXPORT objc_property_t _Nullable
protocol_getProperty(Protocol * _Nonnull proto,
const char * _Nonnull name,
BOOL isRequiredProperty, BOOL isInstanceProperty)

//返回协议的所有属性
OBJC_EXPORT objc_property_t _Nonnull * _Nullable
protocol_copyPropertyList(Protocol * _Nonnull proto,
unsigned int * _Nullable outCount)

OBJC_EXPORT objc_property_t _Nonnull * _Nullable
protocol_copyPropertyList2(Protocol * _Nonnull proto,
unsigned int * _Nullable outCount,
BOOL isRequiredProperty, BOOL isInstanceProperty)
//返回某个协议遵循的协议列表
OBJC_EXPORT Protocol * __unsafe_unretained _Nonnull * _Nullable
protocol_copyProtocolList(Protocol * _Nonnull proto,
unsigned int * _Nullable outCount)

动态创建协议

//创建协议
OBJC_EXPORT Protocol * _Nullable
objc_allocateProtocol(const char * _Nonnull name)

//向Runtime注册协议
OBJC_EXPORT void
objc_registerProtocol(Protocol * _Nonnull proto)
OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0, 2.0);

//向协议添加方法描述
OBJC_EXPORT void
protocol_addMethodDescription(Protocol * _Nonnull proto, SEL _Nonnull name,
const char * _Nullable types,
BOOL isRequiredMethod, BOOL isInstanceMethod)

//添加协议
OBJC_EXPORT void
protocol_addProtocol(Protocol * _Nonnull proto, Protocol * _Nonnull addition)
OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0, 2.0);

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

类型编码与属性编码

编译器将每个方法的返回值和参数类型编码为一个字符串,并将其与方法的selector关联在一起。同时每个属性的类型也进行了编码,下面是这两部分的官方文档,供大家查阅使用。

我们给出一个例子,下面的例子会将方法的返回值以及参数编码打印出来:

这个方法如下所示:

+ (IDLReturnType *)testTypeEncoding:(NSString *)strValue intValue:(NSInteger)intValue {
return [IDLReturnType new];
}
Method testMethods = class_getClassMethod([IDLTestObject class], @selector(testTypeEncoding:intValue:));
NSLog(@"Type Encoding : %@",[NSString stringWithUTF8String:method_getTypeEncoding(testMethods)]);
char *returnType = method_copyReturnType(testMethods);
NSLog(@"Return Type: %@",[NSString stringWithUTF8String:returnType]);
free(returnType);

NSInteger numberOfArguments = method_getNumberOfArguments(testMethods);
char *argument = NULL;
for (unsigned int index = 0; index < numberOfArguments; index++) {
argument = method_copyArgumentType(testMethods, index);
NSLog(@"Index %d Return Type: %@", index,[NSString stringWithUTF8String:argument]);
}
free(argument);

输出内容为:

2020-02-09 10:21:22.231254+0800 objc-debug[84523:1305673] Type Encoding : @32@0:8@16q24
2020-02-09 10:21:22.231343+0800 objc-debug[84523:1305673] Return Type: @
2020-02-09 10:21:22.231425+0800 objc-debug[84523:1305673] Index 0 Return Type: @
2020-02-09 10:21:22.231503+0800 objc-debug[84523:1305673] Index 1 Return Type: :
2020-02-09 10:21:22.231579+0800 objc-debug[84523:1305673] Index 2 Return Type: @
2020-02-09 10:21:22.231653+0800 objc-debug[84523:1305673] Index 3 Return Type: q

我们看****@32@0:8@16q24****这实际上就是方法的类型编码。我们先看下类型编码表:


这里每个部分都包含两位,前面一个表示编码,后面表示这个数据对应的偏移量,返回的类型数据会将返回值放在最后面,所以****@32@0:8@16q24****对应部分的意义如下:

@32 返回值编码为@,也就是id类型,所处的偏移量为32。也就是q24的后面
@0 第一个参数的编码为@,也就是id类型,所处的偏移量为0,由于第一个参数恒为方法的接收者,所以是id类型。
:8 第二个参数的编码为: 也就是SEL类型,所处的偏移量为8,由于第二个参数恒为_cmd所以是SEL类型。
@16 第三个参数的编码为@ 也就是id类型,所处的偏移量为16,上面第三个参数为NSString*
q24 第四个参数的编码为q 也就是long long类型,由于用的是64位机器所以对得上。

Runtime为了我们方便,为我们提供了@encode编译器指令来获取它,当给定一个类型时,@encode返回这个类型的字符串编码,如下所示:

NSLog(@"char     : %s, %lu", @encode(char), sizeof(char));
NSLog(@"short : %s, %lu", @encode(short), sizeof(short));
NSLog(@"int : %s, %lu", @encode(int), sizeof(int));
NSLog(@"long : %s, %lu", @encode(long), sizeof(long));
NSLog(@"long long: %s, %lu", @encode(long long), sizeof(long long));
NSLog(@"float : %s, %lu", @encode(float), sizeof(float));
NSLog(@"double : %s, %lu", @encode(double), sizeof(double));
NSLog(@"NSInteger: %s, %lu", @encode(NSInteger), sizeof(NSInteger));
NSLog(@"CGFloat : %s, %lu", @encode(CGFloat), sizeof(CGFloat));
NSLog(@"int32_t : %s, %lu", @encode(int32_t), sizeof(int32_t));
NSLog(@"int64_t : %s, %lu", @encode(int64_t), sizeof(int64_t))

2020-02-09 11:04:28.116484+0800 objc-debug[91476:1363542] char     : c, 1
2020-02-09 11:04:28.116991+0800 objc-debug[91476:1363542] short : s, 2
2020-02-09 11:04:28.117745+0800 objc-debug[91476:1363542] int : i, 4
2020-02-09 11:04:28.118195+0800 objc-debug[91476:1363542] long : q, 8
2020-02-09 11:04:28.118943+0800 objc-debug[91476:1363542] long long: q, 8
2020-02-09 11:04:28.121076+0800 objc-debug[91476:1363542] float : f, 4
2020-02-09 11:04:28.123510+0800 objc-debug[91476:1363542] double : d, 8
2020-02-09 11:04:28.124016+0800 objc-debug[91476:1363542] NSInteger: q, 8
2020-02-09 11:04:28.124132+0800 objc-debug[91476:1363542] CGFloat : d, 8
2020-02-09 11:04:28.124211+0800 objc-debug[91476:1363542] int32_t : i, 4
2020-02-09 11:04:28.124283+0800 objc-debug[91476:1363542] int64_t : q, 8

一般我们会使用:

method_copyArgumentType(testMethods, index);

这种方式,避免涉及到偏移量等数据,直接获取参数类型。

  • 属性编码

前面提到一个例子:

objc_property_t property = class_getProperty([IDLTestObject class], [@"age" UTF8String]);
NSLog(@"Instance Property Name :%@",[NSString stringWithUTF8String:property_getName(property)]);
NSLog(@"Instance Property Attribute:%@",[NSString stringWithUTF8String:property_getAttributes(property)]);

age属性的声明如下所示:

@property(nonatomic, copy, readonly,getter=ageValue,setter=setAageValue:) NSString *age;

输出结果为:

2020-02-09 11:23:38.356033+0800 objc-debug[94653:1391447] Instance Property Attribute:T@"NSString",R,C,N,GageValue,SsetAageValue:,V_age

我们对照上表给大家分析:
属性编码是通过逗号进行分隔开的,T@”NSString”,R,C,N,GageValue,SsetAageValue:,V_age 表示的意义如下:

* T@"NSString" 属性类型为NSString
* R 属性类型为ReadOnly类型
* C 属性存储属性为Copy
* N 属性为nonatomic
* GageValue 属性指定getter方法为ageValue
* SsetAageValue: 属性指定setter方法为setAageValue:
* V_age 属性对应的实例变量为_age

对于类型编码和属性编码大致就这些。大家可以通过下面的表格熟悉下属性编码:

Contents
  1. 1. 类相关:
  2. 2. Ivar实例变量相关:
  3. 3. Property属性相关:
  4. 4. Method方法相关:
  5. 5. SEL相关:
  6. 6. 协议Protocal相关:
  7. 7. 类型编码与属性编码