面向对象

一. OC的本质

  • 我们平时编写的Objective-C代码,底层实现其实都是C\C++代码;
OC本质
  • Objective-C的对象、类主要是基于C\C++的结构体实现的
    【因为只有结构体才能容纳不同的东西(比如字段、方法)】

  • 将Objective-C代码转换为C\C++代码
    xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc OC源文件 -o 输出的CPP文件
    如果需要链接其他框架,使用-framework参数。比如-framework UIKit

1.NSObject的底层实现

  • OC对象本质是一个结构体;这个结构体里面有一个名叫isa的结构体指针。
  • isa指针的地址就是这个oc对象的地址。


    面向对象_第1张图片
    OC对象的本质

一个NSObject对象占用多少内存?

  • 系统分配了16个字节给NSObject对象(通过malloc_size函数获得)
  • 但NSObject对象内部只使用了8个字节的空间(64bit环境下,可以通过class_getInstanceSize函数获得)(这8个字节分配给isa指针)

2.NSObject对象的本质

NSObject里面存放了isa指针和成员变量;
没有存放方法;

面向对象_第2张图片
创建student对象继承NSObject
面向对象_第3张图片
NSObject对象
  • student对象的大小占用16个字节:8个字节(父类的大小:isa指针大小)+4个字节*2(两个int类型,每个int类型占用4个字节)。
  • 一个对象占用的大小 = 父类的大小+子类里面字段的大小。
  • 占用的最小是16个字节,如果不够16,系统会自动补齐为16个字节
  • 为了内存对齐。结构体的大小,占用的字节必须是最大成员(8个字节)的倍数。配合iOS操作系统,分配的内存空间是16的倍数

思考:创建一个Person:NSObeject,Student:Person。一个Person对象、一个Student对象占用多少内存空间?

面向对象_第4张图片
继承结构
面向对象_第5张图片
继承结构
  1. 一个NSObject分配了16个字节,使用了8个字节;
  2. Person对象大小为NSobect使用字节的大小8 + _age大小 4 = 12,为了对齐,所以结构体占用了16个字节;
  3. Student对象大小为Person使用的大小12个字节+_no大小4 = 16个字节,刚好是16的倍数,所以不进行补齐。所以Student占用了16个字节。

思考:创建一个Person:NSObeject,里面创建三个int类型的成员变量。一个Persont对象占用多少内存空间?

面向对象_第6张图片
image.png
面向对象_第7张图片
image.png
  1. 8个字节+ 4个字节 * 3 = 20 个字节。为了结构体对齐,需要是8的倍数。所以是占用了24个字节;
    2.但是为了操作系统的对齐,分配的空间需要是16的倍数,所以是32个字节。
  • 创建一个实例对象,至少需要多少内存?(结构体至少需要的内存,8的倍数;占用的字节)
    -#import
    class_getInstanceSize([NSObject class]);
  • 创建一个实例对象,实际上分配了多少内存?(分配的空间,16的倍数)
    -#import
    malloc_size((__bridge const void *)obj);

二.OC对象的分类

  • Objective-C中的对象,简称OC对象,主要可以分为3种
    1.instance对象(实例对象)
    2.class对象(类对象)
    3.meta-class对象(元类对象)

1. instance对象(实例对象)

instance对象就是通过类alloc出来的对象,每次调用alloc都会产生新的instance对象

instance对象在内存中存储的信息包括:
1.isa指针
2.其他成员变量

两个NSObject对象

object1、object2是NSObject的instance对象(实例对象)

  • 它们是不同的两个对象,分别占据着两块不同的内存

2. class对象(类对象)

面向对象_第8张图片
class对象的获取
  • objectClass1 ~ objectClass5都是NSObject的class对象(类对象)
  • 它们是同一个对象。每个类在内存中有且只有一个class对象

class对象在内存中存储的信息主要包括:

  1. isa指针
  2. superclass指针
  3. 类的属性信息(@property)、类的对象方法信息(instance method)
  4. 类的协议信息(protocol)、类的成员变量信息(ivar)
    这里的属性是指它的类型、名等信息;
面向对象_第9张图片
class类对象.png

3. meta-class对象(元类对象)

把类对象传进去,就可以获得元类对象


获取元类
  • objectMetaClass是NSObject的meta-class对象(元类对象)

每个类在内存中有且只有一个meta-class对象

meta-class对象和class对象的内存结构是一样的,但是用途不一样,在内存中存储的信息主要包括
1.isa指针
2.superclass指针
3.类的类方法信息(class method)
......

面向对象_第10张图片
元类对象.png

以下代码获取的objectClass是class对象,并不是meta-class对象(通过class获得的都是类对象)

获取类对象

面向对象_第11张图片
判断是否为元类

三.isa指针

1. isa指针

面向对象_第12张图片
isa指针

instance的isa指向class(类对象)
当调用对象方法时,通过instance的isa找到class(类对象),最后找到对象方法的实现进行调用

class(类对象)的isa指向meta-class(元类)
当调用类方法时,通过class(类对象)的isa找到meta-class(元类),最后找到类方法的实现进行调用

2. superclass

2.1 类对象的superclass指针

类对象的superclass指向其父类的类对象
如下:创建Person:NSObject;Student:Person
Student类对象superclass指向Person的类对象,Person类对象的superclass指向NSObject的类对象。

面向对象_第13张图片
superclass指向

  • superclass用处:
    当Student的instance对象要调用Person的对象方法时,会先通过isa找到Student的class,然后通过superclass找到Person的class,最后找到对象方法的实现进行调用;

如下:student实例对象调用父类person的test方法。
student实例对象通过isa指针找到student类对象;
student类对象通过superclass找到person类对象;
调用person类对象里面的test方法。

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        Student *student = [[Student alloc] init];
        [student test];
        [student personInstanceMethod];
        [student init];
        
        [Student studentClassMethod];
        [Student personClassMethod];
        [Student load];
        [Student abc];
      
    }
Person:
@interface Person : NSObject 
{
    @public
    int _age;
}
@property (nonatomic, assign) int no;
- (void)personInstanceMethod;
+ (void)personClassMethod;
@end

@implementation Person

- (void)test
{
    
}

- (void)personInstanceMethod
{
    
}
+ (void)personClassMethod
{
    
}
- (id)copyWithZone:(NSZone *)zone
{
    return nil;
}
@end

Student:
@interface Student : Person 
{
@public
    int _weight;
}
@property (nonatomic, assign) int height;
- (void)studentInstanceMethod;
+ (void)studentClassMethod;
@end

@implementation Student
- (void)test
{
    
}
- (void)studentInstanceMethod
{
    
}
+ (void)studentClassMethod
{
    
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
    return nil;
}

- (void)encodeWithCoder:(NSCoder *)aCoder
{
    
}
@end
    return 0;
}

2.2 元类对象的superclass指针

meta-class(元类)对象的superclass指针,指向父类的元类对象

面向对象_第14张图片
元类对象的isa指针

-superclass指针用处:
当Student的class要调用Person的类方法时,会先通过isa找到Student的meta-class,然后通过superclass找到Person的meta-class,最后找到类方法的实现进行调用

如上面的例子中:
Student调用Person的类方法personClassMethod: [Student personClassMethod];

Student类对象通过isa指针找到Student的元类对象;
Student的元类对象通过superclass指针找到Person的元类对象;

3. 方法的调用流程:

面向对象_第15张图片
isa-superclass.png

isa:

  • instance(实例对象)的isa指向class(类对象)
  • class(类对象)的isa指向meta-class(元类对象)
  • meta-class(元类对象)的isa指向基类的meta-class(元类对象)
  • 基类的元类对象的isa指向自己(基类的元类对象)

superclass:

  • class(类对象)的superclass指向父类的class(类对象)
    如果没有父类,superclass指针为nil
  • meta-class(元类)的superclass指向父类的meta-class(元类)
    -基类的meta-class(元类对象)的superclass指向基类的class(类对象)

instance调用对象方法的轨迹
isa找到class,方法不存在,就通过superclass找父类

class调用类方法的轨迹
isa找meta-class,方法不存在,就通过superclass找父类

四. Class结构

  • 类对象的地址:实例对象的isa指针,指向类对象。那么isa的地址就是类对象的地址。但是从64bit开始,isa需要进行一次位运算(isa的地址&ISA_MASK),才能计算出类对象的真实地址。

  • 元类对象的地址:类对象的isa指针,指向元类对象。但是从64bit开始,isa需要进行一次位运算(isa的地址&ISA_MASK),才能计算出元类对象的真实地址。

面向对象_第16张图片
class结构
面向对象_第17张图片
ISA_MASK

struct objc_class

  • class、meta-class对象的本质结构都是struct objc_class
    objc4源码下载:https://opensource.apple.com/tarballs/objc4/
面向对象_第18张图片
struct objc_class

五:总结

  • 一个NSObject对象占用多少内存?
    系统分配了16个字节给NSObject对象(通过malloc_size函数获得)
    但NSObject对象内部只使用了8个字节的空间(64bit环境下,可以通过class_getInstanceSize函数获得)
  • 对象的isa指针指向哪里?
    instance对象的isa指向class对象
    class对象的isa指向meta-class对象
    meta-class对象的isa指向基类的meta-class对象
  • OC的类信息存放在哪里?
    instance对象:存放成员变量的具体值
    类对象中:对象方法、属性、成员变量、协议信息
    元类对象中:类方法

方法的调用轨迹:

  • instance调用对象方法的轨迹
    isa找到class,方法不存在,就通过superclass找父类
  • class调用类方法的轨迹
    isa找meta-class,方法不存在,就通过superclass找父类

你可能感兴趣的:(面向对象)