//
//  main.m
//  Objc4_Study
//
//  Created by huangyirong on 2020/5/21.
//  Copyright © 2020 Sogou. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import "custom_objc_class.h"

///OC对象分类
///instance对象、class对象、meta-class对象
///
///
///实例对象
///实例对象就是通过类alloc出来的对象，每次调用alloc都会产生新的实例对象，都会分配新的存储空间
///实例对象在内存中存储的信息包括isa和其他成员变量
///几乎所有实例对象都有isa，因为都是继承NSObject
///
///2、每一个实例对象都有isa，而且在该内存地址的最前面，也就是例如Student *stu = [[Student alloc] init]; 如果isa的地址是0x30020 所以stu指针存储的地址值也就是0x30020，指向这个Student实例对象
///实例对象没有存储方法，为什么？不可能每个alloc出来的对象都需要一份方法列表太浪费存储在对应的class中
///
///
///2、通过对一个实例对象内存结构的理解，有助于研究其他问题，如分析一个对象分配多少内存
///
///class_getInstanceSize()
/// 传入一个类，返回这个类最终创建出来的实例至少需要多少存储空间
///class_getInstanceSize([Student class])
///
///
///malloc_size() 传入指针，返回指针所指向内存大小，其实也是实际分配的大小，存在内存对齐问题，iOS系统中buket size，分成好多段，16、32，128字节等等 nano size什么的
///malloc_size((__bridge const void *)(stu))
///
///
///sizeof() 实际上是运算符（不是函数），传的是类型，比如int、float、struct或者指针，返回这个类型有多大，一编译就转成常数，类似宏,sizeof(double) ，转成汇编看实际上就是常数8，sizeof(stu) stu是指针变量则为8个字节,sizeof(struct)也为8
///
///
///笔记-其他注意点
///alloc 调用
///_objc_rootAllocWithZone
///class_createInstance
///calloc
///alignxxxx
///
///

///
///meta-class元类对象
///meta-class可以理解成用来描述类的
///

struct NSObjct_IMPL {
    Class isa;
};

struct Person_IMPL {
    struct NSObjct_IMPL NSObjct_IVARS;
    NSString *name;
    int age;
};

struct Student_IMPL {
    struct Person_IMPL NSObjct_IVARS;
    NSString *name;
    int age;
    int number;
};

@interface NSObject (Ext)

@end
@implementation NSObject(Ext)

- (void)test
{
    
    NSLog(@"[NSObject class] %p",[NSObject class]);
}

@end


@interface Person : NSObject
{
    NSString *name;
    int age;
}
- (void)say;
+ (void)fuxx;

@end

@implementation Person

- (void)say
{
    
}
+ (void)fuxx
{
    
}

@end

@interface Student : Person
{
    int number;
}
@property (nonatomic, assign) int age;

- (void)study;

@end

@implementation Student

- (void)study
{
    
}

@end

@interface Student (Ext)

@end
@implementation Student(Ext)

- (void)chuiniubi
{
    
}

@end


struct test_objc_class {
    Class isa;
    Class superclass;
};


int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
//        NSLog(@"Hello, World!");
        
        ///类对象示例代码
        Student *stu = [[Student alloc] init];
        ///类对象获取方式
        Class objClass1 = [stu class];
        Class objClass2 = [Student class];
        Class objClass3 = object_getClass(stu);
//        NSLog(@"1:%p 2:%p 3:%p",objClass1,objClass2,objClass3);
        [stu study];
   ///类对象 ///每个类再内存中有且只有一个class对象，因此objClass1、objClass2和objClass3指向的地址都是一样
        ///类对象再内存中存储的信息主要包括
        ///isa指针
        ///superclass指针
        ///类的属性信息（@property）、类的对象方法信息（instance method）
        ///类的协议信息（protocl）、类的成员变量信息（ivar）
        ///
        ///注意，类对象包含的成员变量信息并非比如Person的成员变量name的值是多少
      ///成员变量的值是由实例对象决定的，这里类对象的成员变量信息主要是这个变量的名称、类型等描述信息，只需要存储一份，不管对于哪个实例对象这些信息都是固定不变
        ///
        ///
        ///获取元类对象
        Class objectMetaClass = object_getClass([Student class]);
        ///object_getClass传入的是类对象，获得元类对象
        ///
        Class objectMetaClass2 = [objClass2 class];
        ///objectMetaClass2返回的也是元类对象吗？
        ///可以通过以下runtime api判断
        class_isMetaClass(objectMetaClass2);
        ///不是。。试了下类对象无论调用多少次class方法始终返回类对象
        ///
        ///每个类在内存有且只有一个meta-class对象
        ///meta-class对象和class对象的内存结构是一样的（类对象和元类对象指针类型都是Class，所以指向内存的结构一样），但是用途不同，
        ///在内存中存储的主要信息包括
        ///isa指针
        ///superclass指针
        ///class method类的类方法信息
        ///
        ///比起class对象包含的东西好像少了，内存结构还能一样？
        ///meta-class中那些属性信息、协议信息等等都为null
        ///两者类型一样指向的都是typedef struct objc_class *Class结构体
        ///
      
        ///
        ///思考
        ///1、对象的isa指针指向哪里？
        ///
        ///instance的isa指向class
        ///class的isa指向meta-class
        ///meta-class的isa指向基类的meta-class
        ///
        ///
        ///2、OC的类信息存在哪里？
        ///对象方法、属性、成员变量、协议信息存放在class对象中
        ///类方法存放在meta-class对象中
        ///成员变量的具体值存放在instance对象中
        ///
        ///
        ///
        ///
        ///3、object_getClass() vs objc_getClass()，内部实现
        ///
        ///Class objc_getClass(const char *aClassName) （源码在objc-runtime.mm里）
        ///传入字符串类名，返回对应的类对象
        ///
        ///Class object_getClass(id obj) (编码在objc-class.mm里)
        ///传入的obj可能是instance对象（返回class对象）
        ///class对象(返回meta-class对象)
        ///meta-class对象（返回NSObjectd meta-class对象）
        ///
        ///-（Class）class; 返回类对象
        ///
        ///
        ///xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
        ///-framework链接上其他框架 如 -framework UIKit
        ///
        ///搜 int main(int argc, const char * argv[])
        ///
        ///[stu study];
        ///
        ///objc_msgSend(stu,@selector(study))
        ///stu对象发送一条消息@selector(study)
        ///stu需要先找到实例方法才能调用成功，怎么找到？
        ///通过isa找到对应类对象，再找到实例方法
        ///同样道理，类对象调用类方法，也是类对象通过isa找到元类对象中的类方法实现
        ///
        ///总结下
        ///instance的isa指向class，当调用对象方法时，通过instance的isa找到class
        ///最后找到对象方法的实现进行调用
        ///class的isa指向meta-class，当调用类方法时，通过class的isa找到meta-class
        ///最后找到类方法的实现进行调用
        ///
        ///
        ///
        ///关于superclass指针
        ///
        ///先看class对象中的superclass指针
        ///Student的类对象的superclass指向Person的类对象，后者的superclass指针指向 NSObject的类对象
        ///也就是类对象的superclass指向它父类的类对象
        ///
        ///[stu say];、[stu init];调用过程？
        ///当Student的instance对象调用Person的对象方法时，
        ///会先通过stu对象的isa找到Student的class，
        ///然后通过superclass找到Person的class，最后找到对象方法的实现进行调用
        ///
        ///同理，元类对象的superclass指针指向它的父类的元类对象
        ///[Student fuxx]; 、[Student load];
        ///当Student的class要调用Person的类方法时，
        ///会先通过类对象的isa找到Student的meta-class,
        ///然后通过superclass找到Person的meta-class，
        ///最后找到类方法的实现进行调用
        ///
        ///小结
        ///class的superclass指向父类的类对象
        ///meta-class的superclass指向父类的meta-class对象
        ///基类的meta-class的superclass指向基类的class（特殊）
        ///
        ///
        ///isa、superclass总结 结合图
        ///
        ///1、instance实例对象的isa指向class类对象
        ///2、class类对象的isa指向meta-class元类对象
        ///3、meta-class元类对象的isa指向基类的meta-class(所有的元类对象的isa指向NSObject的元类对象)
        ///
        ///1、class类对象的superclass指向父类的class，如果没有父类，则superclass指针为nil
        ///2、meta-class的supercalss指向父类的meta-class，基类的meta-class元类对象的superclass指向基类的class
        ///
        ///
        ///实际开发的运用
        ///
        ///instance对象调用实例方法的流程
        ///例如 [stu say];
        ///先从stu的isa指针找到stu对应的类对象，看看类中的方法列表是否有say方法，找到就展开调用
        ///没有找到，则通过类对象的superclass指针找到父类的类对象查找是否有say方法
        ///一直通过superclass往父类找，一直找到基类NSObject，有就调用，没有就unreconize selector send to instance xxxx
        ///
        ///
        ///类对象调用类方法的流程
        ///[Student test];
        ///通过类对象的isa找到对应的元类对象，如果元类对象没有找到方法 ，则通过superclass往上找，一直找到基类的元类的对象
        ///找不到，再通过基类的元类对象的superclass找到基类的类对象，如果找到就调用，没有则报错
        ///测试：
            [Student test];
        ///
        ///  64bit开始，isa需要进行一次位运算（& ISA_MASK）才能计算出真实地址
     
        Student *student = [[Student alloc] init];
        Class studentClass = [Student class];
        Class studentMetaClass = object_getClass(studentClass);
        NSLog(@">>> %p %p %p",student,studentClass,studentMetaClass);

        ///         >>> 0x100506600 0x1000013d8 0x1000013b0
        ///        (lldb) p/x (long)student->isa
        ///        (long) $0 = 0x001d8001000013dd
        ///        (lldb) p/x 0x001d8001000013dd & 0x00007ffffffffff8ULL
        ///        (unsigned long long) $1 = 0x00000001000013d8
        ///
        ///控制台不能直接打  p/x (long)studentClass->isa 报错，因为Class中虽然有isa但没有把结构暴露出来,需要一个小技巧强制转换。
            struct test_objc_class *studentClass2 = (__bridge struct test_objc_class *)studentClass;
        ///(lldb) p/x (long)studentClass2->isa
        ///(long) $0 = 0x00000001000013b0
        ///(lldb) p/x 0x00000001000013b0 & 0x00007ffffffffff8ULL
        ///(unsigned long long) $1 = 0x00000001000013b0
        ///即 studentMetaClass 的地址
        ///
        ///superclass指针存储的地址值直接为父类的地址值 跟isa不一样
        ///
        ///
        ///
        ///通过源码看class的结构（objc-runtime-new.h）
        ///
        ///
        ///
        ///
        ///怎么调试看内存结构？
        ///
        
        custom_objc_class *stuClass = (__bridge custom_objc_class *)([Student class]);
        custom_objc_class *perClass = (__bridge custom_objc_class *)([Person class]);
        class_rw_t *studentClassData = stuClass->data();
        class_rw_t *personClassData = perClass->data();
        class_rw_t *studentMetaClassData = stuClass->metaClass()->data();
        class_rw_t *personMetaClassData = perClass->metaClass()->data();
        NSLog(@"%p %p",stuClass,perClass);
        ///
        ///https://juejin.im/post/5c9487a6f265da611e177614
        ///class_rw_t结构中有一个class_ro_t类型的指针ro,看一下class_ro_t结构体。
        /// class_ro_t结构体存储了类在编译期就已经确定的属性、方法以及遵循的协议
        // 因为在编译期就已经确定了，所以是ro(readonly)的，不可修改
        ///在class_ro_t结构体中，定义了方法列表、协议列表、属性列表、变量列表,也就是class_ro_t结构体存储了类在编译期间确定的属性、方法、协议以及变量。
        ///
        ///Objective-C代码经过编译之后，会生成类结构，以及根据代码生成类的属性、方法、协议、变量，这些信息在编译期间就能够完全确定，编译期间确定的信息保存在class_ro_t结构体中。因为是在编译期间确定的，所以是只读的，不可修改，ro,代表readonly。在运行时，可以往类结构中增加一些额外的方法、协议，比如在Category中写的方法，Category中的方法就是在运行时加入到类结构中的。运行时生成的类的方法、属性、协议保存在class_rw_t结构体中，rw,代表readwrite,可以修改。
        ///
        ///
        ///baseMethodList
        ///baseProperties
        ///ivars
        ///baseProtocols
        ///
        ///
        
        
        
        

        

        
        
        
        
        
        
        

    }
    return 0;
}

