OC 底层

day1 day 2

OC 面向对象的本质

  • OC实现面向对象的本质是 C++的结构体
@interface MJPerson : NSObject{
    int _age;
    int _height;
    int _no;
}
@end
@interface MJStudent : MJPerson{
    int _weight;
}

struct NSObject_IMPL {
    Class isa;
};
struct MJPerson_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    int _age;
    int _height;
    int _no;
}; 
struct MJStudent_IMPL
{
    struct MJPerson_IMPL MJPerson_IVARS;
    int _weight;
};

通过使用 xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc Person.m 命令查看编译后的文件, 我们知道当使用 @dynamic 关键字修饰属性时候, 编译器不会默认帮我们生成 对应的setter 和getter和成员变量, 只有一个属性, 如果我们使用@synthesize 或者默认情况, 编译器会帮我们添加对应的setter和getter和成员变量.

内存对齐 :

  • 初始化的时候系统分给每个对象的内存 : 16的倍数
  • 初始化之后每个对象那个真的占用的内存 : 对象成员变量中占用的内存最大的成员变量的占用内存数的的倍数(剩余的系统分配的但是没有使用的 用 0 填充)

对象的分类

  • 实例对象 类对象 元类对象
  • 实例对象存储 : isa + 其他成员变量的具体值
  • 类对象存储 : isa + superclass + 属性信息 + 对象方法 + 协议 + 成员变量信息(名称, 类型等) + 类方法(null)
  • 元类对象存 : isa + superclass + 属性信息(null) + 对象方法(null) + 协议(null) + 成员变量信息(null) + 类方法
NSObject *object1 = [[NSObject alloc] init];
Class objectClass2 = [NSObject class];

// - 获取一个对象的类
Class objectClass3 = object_getClass(object1);

// - 获取一个类的元类
Class objectMetaClass = object_getClass(objectClass2);

// - 说明 : arg 为类时 返回元类, arg 为对象时 返回类
object_getClass(arg)

方法调用的本质

  • 在方法调用的时候 会先去对象的 isa 指针所指向的对象中找方法, 如果没有找到这个方法的话,就会去对象的父类中去找方法,如果父类对象中没有这个方法,就会去父类的父类中去找这个方法
    在这里插入图片描述
  • 因为有以上的图 才会出现 以下的状况 - 因为在 MJPerson 这个类中去调用 test 这个类方法时候 会去 MJPerson 的元类中去这个类方法, 发现MJPerson 的元类中没有这个类方法,然后就会去 MJPerson 的元类 的父类 即 NSObject 的元类 中去找这个类方法,发现 NSObject 的元类中也没有这个类方法, 就会去 NSObject 中去找名为 test 的方法 然后在 NSObject 中找到名为 test 的对象方法,所以这个可以调用成功
@interface MJPerson : NSObject
+ (void)test;
@end

@implementation MJPerson
@end

@implementation NSObject (Test)
- (void)test{
    NSLog(@"-[NSObject test] - %p", self);
}
@end
  • 在 OC 中 对象(类对象, 实例对象, 元类对象)的 superClass指针 都是直接指向父类对象的地址, 但是 isa指针不同 isa & ISA_MASK 才是指向元类对象的地址

由于现在 xcode 中的 runtime 是已经老的版本 现在都开始用了新版本 这里引用小码哥的代码 窥探下新的 runtime 的代码

//
//  MJClassInfo.h
//  TestClass
//
//  Created by MJ Lee on 2018/3/8.
//  Copyright © 2018年 MJ Lee. All rights reserved.
//

#import <Foundation/Foundation.h>

#ifndef MJClassInfo_h
#define MJClassInfo_h

# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL
# elif __x86_64__
#   define ISA_MASK        0x00007ffffffffff8ULL
# endif

#if __LP64__
typedef uint32_t mask_t;
#else
typedef uint16_t mask_t;
#endif
typedef uintptr_t cache_key_t;

struct bucket_t {
    cache_key_t _key;
    IMP _imp;
};

struct cache_t {
    bucket_t *_buckets;
    mask_t _mask;
    mask_t _occupied;
};

struct entsize_list_tt {
    uint32_t entsizeAndFlags;
    uint32_t count;
};

struct method_t {
    SEL name;
    const char *types;
    IMP imp;
};

struct method_list_t : entsize_list_tt {
    method_t first;
};

struct ivar_t {
    int32_t *offset;
    const char *name;
    const char *type;
    uint32_t alignment_raw;
    uint32_t size;
};

struct ivar_list_t : entsize_list_tt {
    ivar_t first;
};

struct property_t {
    const char *name;
    const char *attributes;
};

struct property_list_t : entsize_list_tt {
    property_t first;
};

struct chained_property_list {
    chained_property_list *next;
    uint32_t count;
    property_t list[0];
};

typedef uintptr_t protocol_ref_t;
struct protocol_list_t {
    uintptr_t count;
    protocol_ref_t list[0];
};

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;  // instance对象占用的内存空间
#ifdef __LP64__
    uint32_t reserved;
#endif
    const uint8_t * ivarLayout;
    const char * name;  // 类名
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;  // 成员变量列表
    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;
};

struct class_rw_t {
    uint32_t flags;
    uint32_t version;
    const class_ro_t *ro;
    method_list_t * methods;    // 方法列表
    property_list_t *properties;    // 属性列表
    const protocol_list_t * protocols;  // 协议列表
    Class firstSubclass;
    Class nextSiblingClass;
    char *demangledName;
};

#define FAST_DATA_MASK          0x00007ffffffffff8UL
struct class_data_bits_t {
    uintptr_t bits;
public:
    class_rw_t* data() {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }
};

/* OC对象 */
struct mj_objc_object {
    void *isa;
};

/* 类对象 */
struct mj_objc_class : mj_objc_object {
    Class superclass;
    cache_t cache;
    class_data_bits_t bits;
public:
    class_rw_t* data() {
        return bits.data();
    }
    
    mj_objc_class* metaClass() {
        return (mj_objc_class *)((long long)isa & ISA_MASK);
    }
};

#endif /* MJClassInfo_h */

使用和窥探

MJStudent *stu = [[MJStudent alloc] init];
stu->_weight = 10;        
mj_objc_class *studentClass = (__bridge mj_objc_class *)([MJStudent class]);
mj_objc_class *personClass = (__bridge mj_objc_class *)([MJPerson class]);
        
class_rw_t *studentClassData = studentClass->data();
class_rw_t *personClassData = personClass->data();
  
class_rw_t *studentMetaClassData = studentClass->metaClass()->data();
class_rw_t *personMetaClassData = personClass->metaClass()->data();

主动调用KVO

[self.model.name willChangeValueForKey:@"now"];
[self.model.name didChangeValueForKey:@"now"];

KVC 的 setValue:forKey: 和 ValueForKey 的底层原理

  • setValue:forKey
    在这里插入图片描述
  • valueForKey在这里插入图片描述
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值