Object-C底层原理总结

本文总结了Objective-C(OC)的基础知识,包括对象的本质、内存分配、对象分类、isa指针、KVO和KVC机制,以及关联对象和block等核心概念。深入探讨了load和initialize方法,多线程、内存管理和性能优化,适合进一步理解OC架构设计。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.对象的本质

  1. OC的类主要C\C++的 结构体 来实现的
  2. 一个NSObject对象占用多少内存?
  • 系统分配给NSObject对象占用16个字节(通过malloc_size函数获取);
  • 但NSObject对象只使用了8个字节(通过class_getInstanceSize函数获取),用来放isa指针,剩下8个字节空着;(64bit环境下)
  • OC中CoreFoundation框架规定对象至少16个字节。
  • iOS内存对齐分配的内存数为16个字节的倍数。
  1. class_getInstanceSize函数对象实际占用的内存,malloc_size函数获取系统为对象分配的内存

二.对象的分类

  • 对象分为:
    实例对象,
    类对象(object_getClass(对象)),
    元类对象(object_getClass(class对象))
  • 对象里存储的是什么信息?
    isa指针、自己的成员变量
  • 类对象class里存储的是什么信息?
    isa指针、superclass指针、
    类的属性信息(@property)、类的对象方法、
    类的协议信息(protocol)、类的成员变量信息(ivar)
  • 元类对象meta-class里存储的是什么信息?
    isa指针、superclass指针、类的类方法信息(class method)
HHPerson *person = [[HHPerson alloc]init];//对象 包含:isa指针,对象成员变量的值
Class personClass = object_getClass(person);//类对象 (class返回的始终都是类对象) 包含:isa指针,superClass指针,类的属性信息(@property),成员变量信息(结构体ivar),类的对象方法,协议信息 (为什么存在类中:这些信息存一份就够了)
Class personMetalClass = object_getClass(personClass);//元类对象  包含:isa指针,superClass指针,类方法信息
        
NSLog(@"%@,%@",personClass,personMetalClass);//HHPerson,HHPerson
NSLog(@"%p,%p",personClass,personMetalClass);//0x100008210,0x1000081e8
NSLog(@"%d,%d",class_isMetaClass(personClass),class_isMetaClass(personMetalClass));//0,1

三.isa superclass 指针

isa指针 、superclass指针
请添加图片描述
请添加图片描述

四.KVO分析

1.kvo代码基础写法 代码片.

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    self.person = [Preson new];
    self.person.age = 10;
    
    NSKeyValueObservingOptions options = NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew;
    [self.person addObserver:self forKeyPath:@"age" options:options context:nil]; 
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    self.person.age = 20;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    NSLog(@"%@,-%@,-%@",keyPath,object,change);
}
- (void)dealloc{
    [self.person removeObserver:self forKeyPath:@"age"];
}
//打印
age,-<Preson: 0x2804744e0>,-{
    kind = 1;
    new = 20;
    old = 10;
}

2.使用了kvo监听的对象

  • 对象的isa指针指向 -> NSKVONotifying_NSPreson(iOS动态创建的Preson的子类)
  • 调用子类NSKVONotifying_NSPreson set方法
//先调用子类的setter方法
-(void)setAge:(int)age
{
    //foundation c语言函数
    _NSSetIntValueAndNotify();
}
//相当于以下代码
-(void)setAge:(int)age
{
    [persion willChangeValueForKey:@"age"];//通知监听器
    [super setAge:age];
    [persion didChangeValueForKey:@"age"];//通知监听器
}
//手动监听属性变化
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [self.person willChangeValueForKey:@"age"];//通知监听器
    self.person -> _age = 20;
    [self.person didChangeValueForKey:@"age"];//通知监听器
}
//打印
age,-<Preson: 0x283bbc4d0>,-{
    kind = 1;
    new = 20;
    old = 0;
}
//调试lldb:p (IMP) 0x方法内存地址  -> 调用的方法信息

五.KVC分析

1.Key-Value Coding,“键值编码”
添加kvo监听,通过kvc修改属性的值,是可以触发kvo,没有set方法也是可以触发,kvc内部调用willChangeValueForKey:及didChangeValueForKey:方法进行触发。

2.setValue: forKey:原理
请添加图片描述
3.ValueForKey:原理

请添加图片描述

六.Category分类

结论:

  • 内存中所有对象方法最终都会放在类中,所有类方法都会放在元类中。
  • 通过runtime动态将分类的方法合并到类对象,元类对象中
    请添加图片描述
    将oc文件转为c++文件:
    xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc 文件名.m

1.每个分类编译完会生成c++ struct结构体_category_t

//比如Person有分类Eat
static struct _category_t _OBJC_$_CATEGORY_Person_$_Eat = {
	"Person",
	cls,
	对象方法列表,
	类方法列表,
	协议列表,
	属性列表
}

2.运行时,通过Runtime加载某个类的所有Category数据,把所有Category的方法、属性、协议数据,合并到类信息中(类对象、元类对象中)合并到一个大数组中后面参与编译的Category数据,会放在数组的前面,会被优先调用。
请添加图片描述
请添加图片描述

七.load、initialize

load方法

  • load方法会在Runtime加载类、分类时调用,通过函数指针调用

  • 每个类、分类的+load,在程序运行过程中只调用一次

  • 调用顺序

    • 1.先调用类的+load方法
      按照编译顺序调用(先编译,先调用)
      调用子类的+load方法之前会先调用父类的+load方法
    • 2.再调用分类的+load方法
      按照编译先后顺序调用(先编译,先调用)

initialize方法

  • +initialize方法会在类第一次接收到消息时调用(alloc),通过消息机制调用

  • 调用顺序

    • 先调用父类的+initialize(父类调用过不会再调用),再调用子类的+initialize;
    • 先初始化父类,再初始化子类,每个类只会初始化1次;但是如果子类没有实现+initialize,会调用父类的+initialize
  • +initialize和+load的区别是,+initialize是通过obj_msgSend进行调用,+load是通过函数进行调用

    • 如果子类没有书实现+initialize,会调用父类的+initialize(所以父类的+initialize可以调用多次)
    • 如果分类实现了+initialize,就覆盖类本身的+initialize调用

八.关联对象

如何为分类添加成员变量

  • 首先不能直接为分类添加成员变量,只能添加属性及set,get方法申明,并没有生成成员变量及set,get方法的实现
  • 使用runtime关联对象
//添加关联对象
objc_setAssociatedObject(<#id  _Nonnull object#>, <#const void * _Nonnull key#>, <#id  _Nullable value#>, <#objc_AssociationPolicy policy#>)
//获取关联对象
objc_getAssociatedObject(<#id  _Nonnull object#>, <#const void * _Nonnull key#>)
//移除所有关联对象
objc_removeAssociatedObjects(<#id  _Nonnull object#>)

//方案一:void *
#import <objc/runtime.h>

@interface Person (Test1)
@property (nonatomic,copy)NSString *name;
@end

@implementation Person (Test1)

static const void * NameKey = &NameKey;

- (void)setName:(NSString *)name
{
    objc_setAssociatedObject(self, NameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
-(NSString *)name
{
    return objc_getAssociatedObject(self, NameKey);
}
@end

//方案二:char
@interface Person (Test1)
@property (nonatomic,copy)NSString *name;
@end

@implementation Person (Test1)

static const char *NameKey;

- (void)setName:(NSString *)name
{
    objc_setAssociatedObject(self, &NameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
-(NSString *)name
{
    return objc_getAssociatedObject(self, &NameKey);
}
@end

//方案三:属性名作为key
@interface Person (Test1)
@property (nonatomic,copy)NSString *name;
@end

@implementation Person (Test1)

#define NameKey @"name"

- (void)setName:(NSString *)name
{
    objc_setAssociatedObject(self, NameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
-(NSString *)name
{
    return objc_getAssociatedObject(self, NameKey);
}
@end

//方案四:get方法@selector作为key
@interface Person (Test1)
@property (nonatomic,copy)NSString *name;
@end

@implementation Person (Test1)

- (void)setName:(NSString *)name
{
    objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

-(NSString *)name
{
//    return objc_getAssociatedObject(self, @selector(name));
//    _cmd = @selector(name)
    return objc_getAssociatedObject(self, _cmd);
}
@end
  • 底层实现原理
    请添加图片描述
    请添加图片描述

九.block

十.Runtime

十一.Runloop

十二.多线程

十三.内存管理

十四.性能优化

十五.架构设计

知识点后续有时间继续完善

以上知识点学习参考:MJ小码哥李明杰,如有疑问请留言,谢谢

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值