iOS-底层原理05-类的结构分析

《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
iOS-底层原理05-类的结构分析_第1张图片
成员变量、setter和getter方法@2x.png

属性=带下划线的成员变量+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.
12.gif
  • 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中查看编译结果
iOS-底层原理05-类的结构分析_第5张图片
MachOView查看内存@2x.png
13.gif

若不再编译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);
14.gif
  • 2.若是App呢,能读取到App中所有的方法
15.gif
App获取所有的方法@2x.png

类方法到底存在哪儿呢???

  • 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.
iOS-底层原理05-类的结构分析_第6张图片
16.gif
  • 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();
    }

如果是元类,返回本身的实例方法,不会往下一直递归,更加说明类方法为元类的实例方法

17.gif

面试题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类方法中

18.gif

发现走入了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;
}
19.gif

你可能感兴趣的:(iOS-底层原理05-类的结构分析)