iOS底层-类的探索分析之类的属性及类的方法
序
iOS底层-类的探索分析之isa及继承链中,我们介绍了isa,继承链,类的结构,现在我们接着分析OC的类。
类的属性与变量
我们运行一下iOS底层-类的探索分析之isa及继承链中的Demo工程,lldb调试下,如图:


从上图中,我们拿到了三个ivars,iOS底层-类的探索分析之isa及继承链中我们说过,subject是没有取到,现在取到了,是通过在ro(class_ro_t结构体)里面找到了,那么subject跟hobby,name有什么区别呢,我们通过它的定义可以看到,subject是成员变量,而hobby,name是属性,那么它们又有什么不同呢,我们接着往下分析。
我们先贴下代码:
@interface RoPerson : NSObject
{
NSString *hobby;
NSObject *objc;
}
@property (nonatomic, copy) NSString *nickName;
@property (nonatomic, strong) NSString *name;
@property (atomic, strong) NSString *aname;
@end
@implementation RoPerson
@end
@interface RoTeacher : NSObject
@end
@implementation RoTeacher
@end
上述代码中,在RoPerson中,我们定义了nickName,name,aname的属性和hobby(字符串类型,基本数据类型是成员变量)的成员变量以及objc(对象类型,也是成员变量,是特殊的一种,叫实例变量)实例变量。
接着我们,执行clang -rewrite-objc -fobjc-arc -fobjc-runtime=ios-14.5.0 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator14.5.sdk main.m命令编译成c++代码看下底层代码结构,我们打开main.cpp并搜索RoPerson,如图所示:


这些属性在底层代码中全部被注释掉了,而属性除了生成了成员变量以外还生成了setter和getter方法。从上图中,我们可以看到nickName的getter方法用的是内存平移,setter方法用的是objc_setProperty,而name使用的也不同,这又是为什么,我们等下再分析。
我们在man.cpp再搜下static struct 找到如下图的代码:

我们看下**{{(struct objc_selector )"nickName", "@16@0:8", (void )_I_RoPerson_nickName}代码
我们解释下@16@0:8(编码,文档参考[Type Encodings]
这个
1 @ id类型
2 16:占用内存
3 @:参数 id
4 0:从0号位置
5 :代表SEL
6 8:从8号位置
void RoObjc_copyIvar_copyProperies(Class pClass){
unsigned int count = 0;
Ivar *ivars = class_copyIvarList(pClass, &count);
for (unsigned int i=0; i < count; i++) {
Ivar const ivar = ivars[i];
//获取实例变量名
const char*cName = ivar_getName(ivar);
NSString *ivarName = [NSString stringWithUTF8String:cName];
LGLog(@"class_copyIvarList:%@",ivarName);
}
free(ivars);
unsigned int pCount = 0;
objc_property_t *properties = class_copyPropertyList(pClass, &pCount);
for (unsigned int i=0; i < pCount; i++) {
objc_property_t const property = properties[i];
//获取属性名
NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)];
//获取属性值
LGLog(@"class_copyProperiesList:%@",propertyName);
}
free(properties);
}
上述代码可以用来区分哪些是属性,哪些是成员变量,不再赘述。
现在我们来分析下为什么nickName的属性是objc_setProperty(抽象方法)和内存平移,
先贴下main.m的代码
int main(int argc, const char * argv[]) {
@autoreleasepool {
RoPerson *person = [RoPerson alloc];
person.name = @"Ro";
RoTeacher *teacher = [RoTeacher alloc];
Class pClass = object_getClass(person);
}
return 0;
}
属性&成员变量&实例变量的区别
属性 = 带下划线成员变量 + setter + getter ⽅法
实例变量 : 特殊的成员变量 (类的实例化)
上文我们说到nickName使用的是objc_setProperty和内存平移,而name不是,我们通过llvm源码分析(这里源码比较难懂,不贴出来了),是因为copy与strong的区别,copy做了特殊处理,默认是strong。
类方法归属分析
iOS底层-类的探索分析之isa及继承链中的我们知道类的方法+ (void)say666没找到,我们通过MachoView分析下Demo的macho文件,如图:

我们从这里可以看到+ (void)say666这个类方法,- (void)sayNB是实例方法,也就是对象方法,是在类里面,避免浪费内存,类方法存储在元类中,比如+ (void)say666,我在定义一个- (void)say666,这时候怎么处理,这也是为什么存在元类中,我们验证下,如图:


我们从以下两幅图中,可以看出,类方法就是存储在元类中。
void RoObjc_copyMethodList(Class pClass){
unsigned int count = 0;
Method *methods = class_copyMethodList(pClass, &count);
for (unsigned int i=0; i < count; i++) {
Method const method = methods[i];
//获取方法名
NSString *key = NSStringFromSelector(method_getName(method));
LGLog(@"Method, name: %@", key);
}
free(methods);
}
void RoInstanceMethod_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
Method method1 = class_getInstanceMethod(pClass, @selector(sayHello));
Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello));
Method method3 = class_getInstanceMethod(pClass, @selector(sayHappy));
Method method4 = class_getInstanceMethod(metaClass, @selector(sayHappy));
LGLog(@"%s - %p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
void RoClassMethod_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
Method method1 = class_getClassMethod(pClass, @selector(sayHello));
Method method2 = class_getClassMethod(metaClass, @selector(sayHello));
// - (void)sayHello;
// + (void)sayHappy;
Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));
LGLog(@"%s-%p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
以上代码(objc的Api)也可以获取对象方法,类方法的存储。
结语
这篇文章补充了iOS底层-类的探索分析之isa及继承链中的两个疑问点,如有错误,敬请批评指正。