类的结构分析
-
根据底层继承找到 objc_class
-
obj_class 里面成员变量 isa, superclass,cache,bits 猜想 我们上述了解了isa superclass cache 现在探索一下bits
- 从上图可以看出cache里面结果是一个互斥的结构体 explicit_atomic
_originalPreoptCache 是一个结构指针根据上图得知cache所占的内存大小是16字节, bits 总共偏移了32字节 - bits 根据内存偏移计算 bits的内存地址 = 类的首的地址 +0x20
探索bits内部结构
- 探索class_data_bits_t 内部结构
@interface LGPerson : NSObject{
NSString *subject;
}
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *hobby;
- (void)mayNB;
+ (void)maysay;
-
lldb 调试如下
- 进入class_rw_t 查看结构体如下
const method_array_t methods() const {
auto v = get_ro_or_rwe();
if (v.is()) {
return v.get(&ro_or_rw_ext)->methods;
} else {
return method_array_t{v.get(&ro_or_rw_ext)->baseMethods()};
}
}
const property_array_t properties() const {
auto v = get_ro_or_rwe();
if (v.is()) {
return v.get(&ro_or_rw_ext)->properties;
} else {
return property_array_t{v.get(&ro_or_rw_ext)->baseProperties};
}
}
const protocol_array_t protocols() const {
auto v = get_ro_or_rwe();
if (v.is()) {
return v.get(&ro_or_rw_ext)->protocols;
} else {
return protocol_array_t{v.get(&ro_or_rw_ext)->baseProtocols};
}
}
- 在class_rw_t 找到methods(),properties(),protocols() 先探索一下properties()
(lldb) p $3.properties()
(const property_array_t) $9 = {
list_array_tt = {
= {
list = {
ptr = 0x0000000100008260
}
arrayAndFlag = 4295000672
}
}
}
Fix-it applied, fixed expression was:
$3->properties()
(lldb) p $9.list
(const RawPtr) $10 = {
ptr = 0x0000000100008260
}
(lldb) p $10.ptr
(property_list_t *const) $11 = 0x0000000100008260
(lldb) p *$11
(property_list_t) $12 = {
entsize_list_tt = (entsizeAndFlags = 16, count = 2)
}
(lldb) p $12.get(0)
(property_t) $13 = (name = "name", attributes = "T@\"NSString\",C,N,V_name")
(lldb) p $12.get(1)
(property_t) $14 = (name = "hobby", attributes = "T@\"NSString\",C,N,V_hobby")
- 从上面输出结果得到properties()里面是存放属性变量
- 探索一下methods()
(lldb) p $3.methods()
(const method_array_t) $15 = {
list_array_tt = {
= {
list = {
ptr = 0x0000000100008160
}
arrayAndFlag = 4295000416
}
}
}
Fix-it applied, fixed expression was:
$3->methods()
(lldb) p $15.list
(const method_list_t_authed_ptr) $16 = {
ptr = 0x0000000100008160
}
(lldb) p $16.ptr
(method_list_t *const) $17 = 0x0000000100008160
(lldb) p *$17
(method_list_t) $18 = {
entsize_list_tt = (entsizeAndFlags = 27, count = 6)
}
(lldb) p *$18.get(0)
error: :1:1: indirection requires pointer operand ('method_t' invalid)
*$18.get(0)
^~~~~~~~~~~
(lldb) p $18.get(0)
(method_t) $19 = {}
(lldb) p $18.get(1)
(method_t) $20 = {}
(lldb) p $18.get(2)
(method_t) $21 = {}
(lldb)
-
methods 没有存值吗?看下methods的内部结构如下图
-
method_t 的结构体中找到了big成员变量的结构体 以及big调用方法
(lldb) p $2->data()
(class_rw_t *) $24 = 0x0000000100692250
(lldb) p $24->methods()
(const method_array_t) $25 = {
list_array_tt = {
= {
list = {
ptr = 0x0000000100008160
}
arrayAndFlag = 4295000416
}
}
}
(lldb) p $25.list
(const method_list_t_authed_ptr) $26 = {
ptr = 0x0000000100008160
}
(lldb) p $26.ptr
(method_list_t *const) $27 = 0x0000000100008160
(lldb) p *$27
(method_list_t) $28 = {
entsize_list_tt = (entsizeAndFlags = 27, count = 6)
}
(lldb) p $28.get(0).big()
(method_t::big) $29 = {
name = "sayNB"
types = 0x0000000100003f77 "v16@0:8"
imp = 0x0000000100003d40 (KCObjcBuild`-[LGPerson sayNB])
}
(lldb) p $28.get(1).big()
(method_t::big) $30 = {
name = "hobby"
types = 0x0000000100003f6f "@16@0:8"
imp = 0x0000000100003db0 (KCObjcBuild`-[LGPerson hobby])
}
(lldb) p $28.get(2).big()
(method_t::big) $31 = {
name = "setHobby:"
types = 0x0000000100003f8b "v24@0:8@16"
imp = 0x0000000100003de0 (KCObjcBuild`-[LGPerson setHobby:])
}
(lldb) p $28.get(3).big()
(method_t::big) $32 = {
name = "init"
types = 0x0000000100003f6f "@16@0:8"
imp = 0x0000000100003ce0 (KCObjcBuild`-[LGPerson init])
}
(lldb) p $28.get(4).big()
(method_t::big) $33 = {
name = "name"
types = 0x0000000100003f6f "@16@0:8"
imp = 0x0000000100003d50 (KCObjcBuild`-[LGPerson name])
}
(lldb) p $28.get(5).big()
(method_t::big) $34 = {
name = "setName:"
types = 0x0000000100003f8b "v24@0:8@16"
imp = 0x0000000100003d80 (KCObjcBuild`-[LGPerson setName:])
}
- 在methods里面没有找到subject成员变量 和 + (void)maysay 类方法 只找到属性方法 那这2个在哪里呢?????
- 探索一下ro()输出如下:
(lldb) p $2->data()
(class_rw_t *) $35 = 0x0000000100692250
(lldb) p $35.ro()
(const class_ro_t *) $36 = 0x0000000100008118
Fix-it applied, fixed expression was:
$35->ro()
(lldb) p *$36
(const class_ro_t) $37 = {
flags = 0
instanceStart = 8
instanceSize = 32
reserved = 0
= {
ivarLayout = 0x0000000000000000
nonMetaclass = nil
}
name = {
std::__1::atomic = "LGPerson" {
Value = 0x0000000100003ea8 "LGPerson"
}
}
baseMethodList = 0x0000000100008160
baseProtocols = 0x0000000000000000
ivars = 0x00000001000081f8
weakIvarLayout = 0x0000000000000000
baseProperties = 0x0000000100008260
_swiftMetadataInitializer_NEVER_USE = {}
}
(lldb) p $37.ivars
(const ivar_list_t *const) $38 = 0x00000001000081f8
(lldb) p *$38
(const ivar_list_t) $39 = {
entsize_list_tt = (entsizeAndFlags = 32, count = 3)
}
(lldb) p $39.get(0)
(ivar_t) $40 = {
offset = 0x0000000100008318
name = 0x0000000100003f2f "subject"
type = 0x0000000100003f7f "@\"NSString\""
alignment_raw = 3
size = 8
}
(lldb) p $39.get(1)
(ivar_t) $41 = {
offset = 0x0000000100008320
name = 0x0000000100003f37 "_name"
type = 0x0000000100003f7f "@\"NSString\""
alignment_raw = 3
size = 8
}
(lldb) p $39.get(2)
(ivar_t) $42 = {
offset = 0x0000000100008328
name = 0x0000000100003f3d "_hobby"
type = 0x0000000100003f7f "@\"NSString\""
alignment_raw = 3
size = 8
}
从lldb打印得到成员变量subject 最后还剩类方法在哪里呢???
类方法归属分析
1.根据上述分析没有找到类方法猜想类方法是否存在元类中????
(lldb) p/x object_getClass(LGPerson.class)
(Class) $13 = 0x00000001000083a8
(lldb) p/x 0x00000001000083c8
(long) $14 = 0x00000001000083c8
(lldb) p/x (class_data_bits_t *)0x00000001000083c8
(class_data_bits_t *) $15 = 0x00000001000083c8
(lldb) p $15.data()
(class_rw_t *) $16 = 0x00000001008339c0
Fix-it applied, fixed expression was:
$15->data()
(lldb) p $16.methods()
(const method_array_t) $17 = {
list_array_tt = {
= {
list = {
ptr = 0x00000001000082d0
}
arrayAndFlag = 4295000784
}
}
}
Fix-it applied, fixed expression was:
$16->methods()
(lldb) p $17.list
(const method_list_t_authed_ptr) $18 = {
ptr = 0x00000001000082d0
}
(lldb) p $18.ptr
(method_list_t *const) $19 = 0x00000001000082d0
(lldb) p *$19
(method_list_t) $20 = {
entsize_list_tt = (entsizeAndFlags = 27, count = 1)
}
(lldb) p $20.get(0).big()
(method_t::big) $21 = {
name = "say666"
types = 0x0000000100003f77 "v16@0:8"
imp = 0x0000000100003e10 (KCObjcBuild`+[LGPerson say666])
}
- 总结: 类方法所在位置在元类中的methods()中
二.观察类的属性
- 在上面输出0x0000000100003f77 "v16@0:8" 可以看到"v16@0:8"
-
了解TypeEncoding 官方图表如下:
- 在上面输出0x0000000100003f77 "v16@0:8" 可以看到"v16@0:8"
-
创建.cpp文件中观察到一个特殊的字符串
二.分析属性的get和set方法(objc_getProperty和objc_setProperty)
- 1.clang分析属性setter方法
- 在main.m中 定义SBTeacher类如下:
@interface SBTeacher : NSObject
@property (nonatomic, strong) NSString *sbName;
@property (nonatomic, copy) NSString *name;
@end
@implementation SBTeacher
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
SBTeacher *teacher = [SBTeacher alloc];
teacher.sbName = @"SB";
teacher.name = @"Name666";
}
return 0;
}
用命令行编译成c++文件
clang -rewrite-objc main.m -o main.cpp
-
分析main.cpp 文件找到set方法
从中发现 sbName (*(NSString **) 有个指针修饰 而name 是objc_setProperty 修饰
使用llvm源码了解objc_setProperty,源码下载:[llvm] (https://github.com/llvm/llvm-project "llvm")
-
在llvm 找到如图
根源方法调用反推找到getSetPropertyFn()方法找到GetPropertySetFunction()
找到GetPropertySetFunction()调用地方
-
PropertyImplStrategy strategy(CGM, propImpl);:这是C++语法,创建了一个PropertyImplStrategy类型的变量strategy,并调用PropertyImplStrategy的构造函数初始化
根据strategy.getKind()条件判断执行switch语句case条件判断
所以分析PropertyImplStrategy的实现
namespace {
class PropertyImplStrategy {
public:
enum StrategyKind {
/// The 'native' strategy is to use the architecture's provided
/// reads and writes.
Native,
/// Use objc_setProperty and objc_getProperty.
GetSetProperty,
/// Use objc_setProperty for the setter, but use expression
/// evaluation for the getter.
SetPropertyAndExpressionGet,
/// Use objc_copyStruct.
CopyStruct,
/// The 'expression' strategy is to emit normal assignment or
/// lvalue-to-rvalue expressions.
Expression
};
StrategyKind getKind() const { return StrategyKind(Kind); }
bool hasStrongMember() const { return HasStrong; }
bool isAtomic() const { return IsAtomic; }
bool isCopy() const { return IsCopy; }
CharUnits getIvarSize() const { return IvarSize; }
CharUnits getIvarAlignment() const { return IvarAlignment; }
PropertyImplStrategy(CodeGenModule &CGM,
const ObjCPropertyImplDecl *propImpl);
private:
unsigned Kind : 8;
unsigned IsAtomic : 1;
unsigned IsCopy : 1;
unsigned HasStrong : 1;
CharUnits IvarSize;
CharUnits IvarAlignment;
};
}
/// Pick an implementation strategy for the given property synthesis.
PropertyImplStrategy::PropertyImplStrategy(CodeGenModule &CGM,
const ObjCPropertyImplDecl *propImpl) {
const ObjCPropertyDecl *prop = propImpl->getPropertyDecl();
ObjCPropertyDecl::SetterKind setterKind = prop->getSetterKind();
IsCopy = (setterKind == ObjCPropertyDecl::Copy);
IsAtomic = prop->isAtomic();
HasStrong = false; // doesn't matter here.
// Evaluate the ivar's size and alignment.
ObjCIvarDecl *ivar = propImpl->getPropertyIvarDecl();
QualType ivarType = ivar->getType();
auto TInfo = CGM.getContext().getTypeInfoInChars(ivarType);
IvarSize = TInfo.Width;
IvarAlignment = TInfo.Align;
// If we have a copy property, we always have to use getProperty/setProperty.
// TODO: we could actually use setProperty and an expression for non-atomics.
if (IsCopy) {
Kind = GetSetProperty;
return;
}
- 根据构造函数的实现苹果官方解释(If we have a copy property, we always have to use getProperty/setProperty.)
- 总结:使用copy修饰的属性,生成的setter方法会调用objc_setPropert和objc_getProperty
三.分析属性修饰的作用 strong,copy,retain,retain,atomic,nonatomic
- 根据cpp 文件分析
@interface SBTeacher : NSObject
@property (atomic, copy) NSString *sbName;
@property (atomic, strong) NSString *atsbName;
@property (atomic, retain) NSString * resbName;
@property (nonatomic, copy) NSString *nosbName;
@property (nonatomic, strong) NSString *noatsbName;
@property (nonatomic, retain) NSString * noresbName;
@property (atomic, assign) NSInteger age;
@property (nonatomic, assign) NSInteger noage;
@end
extern "C" __declspec(dllimport) id objc_getProperty(id, SEL, long, bool);
static NSString * _I_SBTeacher_sbName(SBTeacher * self, SEL _cmd) { typedef NSString * _TYPE;
return (_TYPE)objc_getProperty(self, _cmd, __OFFSETOFIVAR__(struct SBTeacher, _sbName), 1); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);
static void _I_SBTeacher_setSbName_(SBTeacher * self, SEL _cmd, NSString *sbName) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct SBTeacher, _sbName), (id)sbName, 1, 1); }
static NSString * _I_SBTeacher_atsbName(SBTeacher * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_SBTeacher$_atsbName)); }
static void _I_SBTeacher_setAtsbName_(SBTeacher * self, SEL _cmd, NSString *atsbName) { (*(NSString **)((char *)self + OBJC_IVAR_$_SBTeacher$_atsbName)) = atsbName; }
static NSString * _I_SBTeacher_resbName(SBTeacher * self, SEL _cmd) { typedef NSString * _TYPE;
return (_TYPE)objc_getProperty(self, _cmd, __OFFSETOFIVAR__(struct SBTeacher, _resbName), 1); }
static void _I_SBTeacher_setResbName_(SBTeacher * self, SEL _cmd, NSString *resbName) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct SBTeacher, _resbName), (id)resbName, 1, 0); }
static NSString * _I_SBTeacher_nosbName(SBTeacher * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_SBTeacher$_nosbName)); }
static void _I_SBTeacher_setNosbName_(SBTeacher * self, SEL _cmd, NSString *nosbName) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct SBTeacher, _nosbName), (id)nosbName, 0, 1); }
static NSString * _I_SBTeacher_noatsbName(SBTeacher * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_SBTeacher$_noatsbName)); }
static void _I_SBTeacher_setNoatsbName_(SBTeacher * self, SEL _cmd, NSString *noatsbName) { (*(NSString **)((char *)self + OBJC_IVAR_$_SBTeacher$_noatsbName)) = noatsbName; }
static NSString * _I_SBTeacher_noresbName(SBTeacher * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_SBTeacher$_noresbName)); }
static void _I_SBTeacher_setNoresbName_(SBTeacher * self, SEL _cmd, NSString *noresbName) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct SBTeacher, _noresbName), (id)noresbName, 0, 0); }
static NSInteger _I_SBTeacher_age(SBTeacher * self, SEL _cmd) { return (*(NSInteger *)((char *)self + OBJC_IVAR_$_SBTeacher$_age)); }
static void _I_SBTeacher_setAge_(SBTeacher * self, SEL _cmd, NSInteger age) { (*(NSInteger *)((char *)self + OBJC_IVAR_$_SBTeacher$_age)) = age; }
static NSInteger _I_SBTeacher_noage(SBTeacher * self, SEL _cmd) { return (*(NSInteger *)((char *)self + OBJC_IVAR_$_SBTeacher$_noage)); }
static void _I_SBTeacher_setNoage_(SBTeacher * self, SEL _cmd, NSInteger noage) { (*(NSInteger *)((char *)self + OBJC_IVAR_$_SBTeacher$_noage)) = noage; }
- 总结 :
- objc_getProperty调用如下:
- sbName 使用atomic, copy修饰
- resbName 使用atomic, retain修饰
- objc_setProperty 调用如下
- sbName 使用 atomic, copy 修饰
- resbName 使用 atomic, retain 修饰
- nosbName 使用 nonatomic, copy 修饰
- noresbName 使用 nonatomic, retain 修饰
- 根据分析得到copy,retain,retain 修饰会调用objc_setProperty方法,atomic和nonatomic会影响objc_getProperty的调用。其它的是内存地址平移取值或赋值
- objc_getProperty调用如下: