《iOS底层原理文章汇总》
类中为什么没有成员变量和类方法,它们在哪里获取???
属性,成员变量和实例变量的区分
- 1.clang编译main.m文件:clang -rewrite-objc main.m -o main.cpp得到main.cpp文件
@interface DCPerson :NSObject
{
NSString *hobby;
NSObject *objc;
}
@property(nonatomic,copy)NSString *nickName;
@property(nonatomic,strong)NSString *name;
@end
@implementation DCPerson
@end
属性=带下划线的成员变量+setter+getter方法
实例变量:特殊的成员变量(类的实例化)
获取成员变量(hobby)
成员变量都在class_ro_t中,class_ro_t中的ivars,获取成员变量
(lldb) p/x DCPerson.class
(Class) $0 = 0x00000001000022c0 DCPerson
(lldb) p (class_data_bits_t *)0x00000001000022e0
(class_data_bits_t *) $1 = 0x00000001000022e0
(lldb) p $1->data()
(class_rw_t *) $2 = 0x000000010067c5d0
(lldb) p *$2
(class_rw_t) $3 = {
flags = 2148007936
witness = 0
ro_or_rw_ext = {
std::__1::atomic = 4294975680
}
firstSubclass = DCTeacher
nextSiblingClass = NSUUID
}
(lldb) p $3.ro()
(const class_ro_t *) $4 = 0x00000001000020c0
(lldb) p *$4
(const class_ro_t) $5 = {
flags = 388
instanceStart = 8
instanceSize = 24
reserved = 0
ivarLayout = 0x0000000100000f70 "\x02"
name = 0x0000000100000f67 "DCPerson"
baseMethodList = {
ptr = 0x0000000100002108
}
baseProtocols = 0x0000000000000000
ivars = 0x0000000100002170
weakIvarLayout = 0x0000000000000000
baseProperties = 0x00000001000021b8
_swiftMetadataInitializer_NEVER_USE = {}
}
(lldb) p $5.ivars
(const ivar_list_t *const) $6 = 0x0000000100002170
(lldb) p *$6
(const ivar_list_t) $7 = {
entsize_list_tt = (entsizeAndFlags = 32, count = 2)
}
(lldb) p $7.get(0)
(ivar_t) $8 = {
offset = 0x0000000100002288
name = 0x0000000100000ef2 "hobby"
type = 0x0000000100000f84 "@\"NSString\""
alignment_raw = 3
size = 8
}
(lldb) p $7.get(1)
(ivar_t) $9 = {
offset = 0x0000000100002290
name = 0x0000000100000ef8 "_kc_name"
type = 0x0000000100000f84 "@\"NSString\""
alignment_raw = 3
size = 8
}
(lldb) p $7.get(2)
Assertion failed: (i < count), function get, file /Users/cloud/Documents/iOS/0914/0914练习/iOS-isa指针/runtime/objc-runtime-new.h, line 479.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
- 2.属性怎么生成setter和getter方法?调用objc_setProperty
- 3.为什么 copy和strong修饰属性有区别,设计到llvm
- 4.typeEncodeUrl表示
void lgTypes(){
NSLog(@"char --> %s",@encode(char));
NSLog(@"int --> %s",@encode(int));
NSLog(@"short --> %s",@encode(short));
NSLog(@"long --> %s",@encode(long));
NSLog(@"long long --> %s",@encode(long long));
NSLog(@"unsigned char --> %s",@encode(unsigned char));
NSLog(@"unsigned int --> %s",@encode(unsigned int));
NSLog(@"unsigned short --> %s",@encode(unsigned short));
NSLog(@"unsigned long --> %s",@encode(unsigned long));
NSLog(@"float --> %s",@encode(float));
NSLog(@"bool --> %s",@encode(bool));
NSLog(@"void --> %s",@encode(void));
NSLog(@"char * --> %s",@encode(char *));
NSLog(@"id --> %s",@encode(id));
NSLog(@"Class --> %s",@encode(Class));
NSLog(@"SEL --> %s",@encode(SEL));
int array[] = {1,2,3};
NSLog(@"int[] --> %s",@encode(typeof(array)));
typedef struct person{
char *name;
int age;
} Person;
NSLog(@"struct --> %s",@encode(Person));
typedef union union_type{
char *name;
int a;
}Union;
NSLog(@"union --> %s",@encode(Union));
int a = 2;
int *b = {&a};
NSLog(@"int[] --> %s",@encode(typeof(b)));
}
//输出
2020-10-05 20:27:27.382751+0800 iOS-isa分析[27629:3706131] char --> c
2020-10-05 20:27:27.382783+0800 iOS-isa分析[27629:3706131] int --> i
2020-10-05 20:27:27.382809+0800 iOS-isa分析[27629:3706131] short --> s
2020-10-05 20:27:27.384252+0800 iOS-isa分析[27629:3706131] long --> q
2020-10-05 20:27:27.384306+0800 iOS-isa分析[27629:3706131] long long --> q
2020-10-05 20:27:27.384342+0800 iOS-isa分析[27629:3706131] unsigned char --> C
2020-10-05 20:27:27.384373+0800 iOS-isa分析[27629:3706131] unsigned int --> I
2020-10-05 20:27:27.392859+0800 iOS-isa分析[27629:3706131] unsigned short --> S
2020-10-05 20:27:27.392903+0800 iOS-isa分析[27629:3706131] unsigned long --> Q
2020-10-05 20:27:27.392931+0800 iOS-isa分析[27629:3706131] float --> f
2020-10-05 20:27:27.392957+0800 iOS-isa分析[27629:3706131] bool --> B
2020-10-05 20:27:27.393086+0800 iOS-isa分析[27629:3706131] void --> v
2020-10-05 20:27:27.393117+0800 iOS-isa分析[27629:3706131] char * --> *
2020-10-05 20:27:27.393262+0800 iOS-isa分析[27629:3706131] id --> @
2020-10-05 20:27:27.394643+0800 iOS-isa分析[27629:3706131] Class --> #
2020-10-05 20:27:27.394705+0800 iOS-isa分析[27629:3706131] SEL --> :
2020-10-05 20:27:27.394750+0800 iOS-isa分析[27629:3706131] int[] --> [3i]
2020-10-05 20:27:27.394815+0800 iOS-isa分析[27629:3706131] struct --> {person=*i}
2020-10-05 20:27:27.394869+0800 iOS-isa分析[27629:3706131] union --> (union_type=*i)
2020-10-05 20:27:27.394990+0800 iOS-isa分析[27629:3706131] int[] --> ^i
typeEncodingUrl
nickName的getter方法@16@0:8分别表示什么???
查看nickName的getter方法得到,每个方法都默认自带了两个参数(id self,SEL _cmd)
static NSString * _I_DCPerson_nickName(DCPerson * self, SEL _cmd)
{ return (*(NSString **)((char *)self + OBJC_IVAR_$_DCPerson$_nickName)); }
@------>函数的返回值
16----->参数共占用16个字节
@------>第一个参数的类型 id 8字节
0------>第一个参数从0开始
:------>第二个参数为SEL 8字节 从8号位开始
8------>第二个参数从第8字节开始
setName的函数的类型为v24@0:8@16,分别表示如下
v------>函数类型为无返回值
24----->参数共占用24个字节
@------>第一个参数的类型 id 8字节
0------>第一个参数从0开始
:------>第二个参数为SEL 8字节 从8号位开始
8------>第二个参数从第8字节位置开始
@------>第三个参数为类型为对象 从第16字节开始
16----->第三个参数从第16字节位置开始
static void _I_DCPerson_setName_(DCPerson * self, SEL _cmd, NSString *name)
{ (*(NSString **)((char *)self + OBJC_IVAR_$_DCPerson$_name)) = name; }
为什么没有获取到类方法呢???
- 1.MachOView,通过工具查看编译后的工具,Cmd + B编译后将exe文件拖到MachOView中查看编译结果
若不再编译DCTeacher,则不回加载到内存中,注释掉DCTeacher的初始化,内存中不再有DCTeacher的类信息
//0x00007ffffffffff8ULL
//class_data_bits_t
DCPerson *person = [DCPerson alloc];
object_getClass(person);
// DCTeacher *teacher = [DCTeacher alloc];
DCNSLog(@"%@---%p-----%p",person,person,&person);
// DCNSLog(@"%@---%p-----%p",teacher,teacher,&teacher);
- 2.若是App呢,能读取到App中所有的方法
类方法到底存在哪儿呢???
- 1.通过lldb调试
lldb调试类方法-既然不存在类中,那就往上一级查找方法
(lldb) p/x DCPerson.class
(Class) $0 = 0x00000001000022c0 DCPerson
(lldb) x/4gx 0x00000001000022c0
0x1000022c0: 0x0000000100002298 0x0000000100336140
0x1000022d0: 0x00000001003303d0 0x0000802400000000
(lldb) p/x 0x0000000100002298 & 0x00007ffffffffff8ULL
(unsigned long long) $1 = 0x0000000100002298
(lldb) p (class_data_bits_t *)0x00000001000022b8
(class_data_bits_t *) $2 = 0x00000001000022b8
(lldb) p $2->data()
(class_rw_t *) $3 = 0x0000000101116e20
(lldb) p $3->methods()
(const method_array_t) $4 = {
list_array_tt = {
= {
list = {
ptr = 0x00000001000020a0
}
arrayAndFlag = 4294975648
}
}
}
(lldb) p $4.list.ptr
(method_list_t *const) $5 = 0x00000001000020a0
(lldb) p *$5
(method_list_t) $6 = {
entsize_list_tt = (entsizeAndFlags = 26, count = 1)
}
(lldb) p $6.get(0)
(method_t) $7 = {}
(lldb) p $7.name()
(SEL) $8 = "say666"
(lldb) p $7.types()
(const char *) $9 = 0x0000000100000f77 "v16@0:8"
(lldb) p $6.get(1)
Assertion failed: (i < count), function get, file /Users/cloud/Documents/iOS/0914/0914练习/iOS-isa指针/runtime/objc-runtime-new.h, line 479.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
- 2.通过方法打印
void lgObjc_copyMethodList(Class pClass){
unsigned int count = 0;
Method *methods = class_copyMethodList(pClass, &count);
for (unsigned int i=0; i
面试题:为什么元类中含有类方法呢???
Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));
返回true。
Method class_getClassMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
return class_getInstanceMethod(cls->getMeta(), sel);
}
// NOT identical to this->ISA when this is a metaclass
Class getMeta() {
if (isMetaClass()) return (Class)this;
else return this->ISA();
}
如果是元类,返回本身的实例方法,不会往下一直递归,更加说明类方法为元类的实例方法
面试题isKindOfClass和isMemberOfClass
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
BOOL re3 = [(id)[DCPerson class] isKindOfClass:[DCPerson class]];
BOOL re4 = [(id)[DCPerson class] isMemberOfClass:[DCPerson class]];
NSLog(@" re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n",re1,re2,re3,re4);
BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];
BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];
BOOL re7 = [(id)[DCPerson alloc] isKindOfClass:[DCPerson class]];
BOOL re8 = [(id)[DCPerson alloc] isMemberOfClass:[DCPerson class]];
NSLog(@" re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8);
//输出
2020-10-06 17:56:22.792804+0800 iOS-类方法归属分析[51580:4486122] re1 :1
re2 :0
re3 :0
re4 :0
2020-10-06 17:56:22.793425+0800 iOS-类方法归属分析[51580:4486122] re5 :1
re6 :1
re7 :1
re8 :1
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];分析
这儿调用的是isKindOfClass类方法
//类 VS 元类 ====> NSObject VS NSObject的元类
不相等继续找父类
//类 VS NSObject的根元类 ====> NSObject VS NSObject
循环结束返回true
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
BOOL re3 = [(id)[DCPerson class] isKindOfClass:[DCPerson class]];分析
这儿调用的也是isKindOfClass类方法
DCPerson VS DCPerson的元类 不相等
DCPerson VS DCPerson的根元类 不相等
DCPerson VS DCObject 不相等
DCPerson VS nil 循环结束 返回false
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];分析
这儿调用的是isMemberOfClass类方法
NSObject VS NSObject的元类 不相等 返回false
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
BOOL re4 = [(id)[DCPerson class] isMemberOfClass:[DCPerson class]];分析
这儿调用的也是isMemberOfClass类方法
DCPerson VS DCPerson的元类 不相等 返回false
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];
调用对象方法isKindOfClass
[NSObject class] VS [NSObject class] 返回true
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
BOOL re7 = [(id)[DCPerson alloc] isKindOfClass:[DCPerson class]];
调用对象方法isKindOfClass
[DCPerson class] VS [DCPerson class] 返回true
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];
调用对象方法isMemberOfClass
[NSObject class] VS [NSObject class] 返回true
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
BOOL re8 = [(id)[DCPerson alloc] isMemberOfClass:[DCPerson class]];
调用对象方法isMemberOfClass
[DCPerson class] VS [DCPerson class] 返回true
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
isKindOfClass类方法:类和元类的继承链进行对比
1.isKindOfClass和isMemberOfClass思维误区,断点以为会走入类方法isKindOfClass中,结果发现并没有走入isKindOfClass类方法中
发现走入了BOOL
objc_opt_isKindOfClass(id obj, Class otherClass)方法中
// Calls [obj isKindOfClass]
BOOL
objc_opt_isKindOfClass(id obj, Class otherClass)
{
#if __OBJC2__
if (slowpath(!obj)) return NO;
Class cls = obj->getIsa();
if (fastpath(!cls->hasCustomCore())) {
for (Class tcls = cls; tcls; tcls = tcls->superclass) {
if (tcls == otherClass) return YES;
}
return NO;
}
#endif
return ((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass);
}
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
BOOL re3 = [(id)[DCPerson class] isKindOfClass:[DCPerson class]];
BOOL re4 = [(id)[DCPerson class] isMemberOfClass:[DCPerson class]];
NSLog(@" re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n",re1,re2,re3,re4);
BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];
BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];
BOOL re7 = [(id)[DCPerson alloc] isKindOfClass:[DCPerson class]];
BOOL re8 = [(id)[DCPerson alloc] isMemberOfClass:[DCPerson class]];
NSLog(@" re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8);
//输出
2020-10-06 20:33:43.487320+0800 iOS-类方法归属分析[46086:4702913] re1 :1
re2 :0
re3 :0
re4 :0
2020-10-06 20:33:43.492205+0800 iOS-类方法归属分析[46086:4702913] re5 :1
re6 :1
re7 :1
re8 :1
对象方法存在类里面,类方法存在元类里面
-(void)sayHello;
+(void)sayHappy;
-(void)sayHello{
NSLog(@"%@",_cmd);
}
+(void)sayHappy{
NSLog(@"%@",_cmd);
}
void lgIMP_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
IMP imp1 = class_getMethodImplementation(pClass, @selector(sayHello));
IMP imp2 = class_getMethodImplementation(metaClass, @selector(sayHello));
IMP imp3 = class_getMethodImplementation(pClass, @selector(sayHappy));
IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayHappy));
DCNSLog(@"%s - %p-%p-%p-%p",__func__,imp1,imp2,imp3,imp4);
}
//输出
lgIMP_classToMetaclass - 0x100001cb0-0x1002c2b40-0x1002c2b40-0x100001c50
imp2和imp3按理说应该找不到为0x0,结果为什么不为0x0呢???
查看class_getMethodImplementation源码,找不到方法都会走消息转发流程,return _objc_msgForward。所以imp2和imp3两个地址打印的一样,都走消息转发流程。
IMP class_getMethodImplementation(Class cls, SEL sel)
{
IMP imp;
if (!cls || !sel) return nil;
imp = lookUpImpOrNil(nil, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER);
// Translate forwarding function to C-callable external version
if (!imp) {
return _objc_msgForward;
}
return imp;
}