Objective-C对象模型

深度探索C对象模型 资源名称:深度探索C 对象模型资源截图: 资源太大,传百度网盘了,链接在附件中,有需要的同学自取。 立即下载

这里写图片描述

网上好多英文版的。看的累。自己写个中文的。
一个箭头方向代表指向, 虚线代表isa指向,实线代表父类指向

注意几个细节

所有元类的isa 都指向根元类,根元类指向他自个

注意到根元类的父类是根类。。。就连起来了

根类的父类是nil ,所以找不到就nil了

参考文章

类的成员变量 这些是在rutime 源码里的,所以方法列表什么的我都没找到。

(Child) $0 = {
  Father = (_father = 0)
  _child = 0
}

我打印的结果也不太一样

无法给类动态增加成员变量的原因:

因为对象在内存中的排布可以看成一个结构体,该结构体的大小并不能动态变化。所以无法在运行时动态给对象增加成员变量。

可以给分类添加方法的原因:

相对的,对象的方法定义都保存在类的可变区域中。 Objective-C 1.0 中,我们可以看到方法的定义列表是一个名为 methodLists的指针的指针(如下图所示)。通过修改该指针指向的指针的值,就可以实现动态地为某一个类增加成员方法。这也是Category实现的原理。同时也说明了为什么Category只可为对象增加成员方法,却不能增加成员变量。

这里写图片描述

注意点:

需要特别说明一下,通过objc_setAssociatedObject 和 objc_getAssociatedObject方法可以变相地给对象增加成员变量,但由于实现机制不一样,所以并不是真正改变了对象的内存结构。

这段内容简直贯穿OC的整个使用。

参考一篇文章

struct objc_object {  
    Class isa  OBJC_ISA_AVAILABILITY;
};

struct objc_class {  
    Class isa  OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
    Class super_class;
    const char *name;
    long version;
    long info;
    long instance_size;
    struct objc_ivar_list *ivars;
    **struct objc_method_list **methodLists**;
    **struct objc_cache *cache**;
    struct objc_protocol_list *protocols;
#endif
};

struct objc_method_list {  
    struct objc_method_list *obsolete;
    int method_count;

#ifdef __LP64__
    int space;
#endif

    /* variable length structure */
    struct objc_method method_list[1];
};

struct objc_method {  
    SEL method_name;
    char *method_types;    /* a string representing argument/return types */
    IMP method_imp;
};

objc_method_list 本质是一个有 objc_method 元素的可变长度的数组。一个 objc_method 结构体中有函数名,也就是SEL,有表示函数类型的字符串 (见 Type Encoding) ,以及函数的实现IMP。

从这些定义中可以看出发送一条消息也就 objc_msgSend 做了什么事。举 objc_msgSend(obj, foo) 这个例子来说:

1、首先,通过 obj 的 isa 指针找到它的 class ;
2、在 class 的 method list 找 foo ;
3、如果 class 中没到 foo,继续往它的 superclass 中找 ;
4、一旦找到 foo 这个函数,就去执行它的实现IMP .

但这种实现有个问题,效率低。但一个 class 往往只有 20% 的函数会被经常调用,可能占总调用次数的 80% 。每个消息都需要遍历一次 objc_method_list 并不合理。如果把经常被调用的函数缓存下来,那可以大大提高函数查询的效率。这也就是 objc_class 中另一个重要成员 objc_cache 做的事情 - 再找到 foo 之后,把 foo 的 method_name 作为 key ,method_imp 作为 value 给存起来。当再次收到 foo 消息的时候,可以直接在 cache 里找到,避免去遍历 objc_method_list.

动态方法解析和转发

在上面的例子中,如果 foo 没有找到会发生什么?通常情况下,程序会在运行时挂掉并抛出 unrecognized selector sent to … 的异常。但在异常抛出前,Objective-C 的运行时会给你三次拯救程序的机会:

1.Method resolution
2.Fast forwarding
3.Normal forwarding

贴一段消息转发的代码

@interface TestClass : NSObject
@end

#import "TestClass.h"
#import <objc/runtime.h>
#import <objc/message.h>

@interface SecondClass : NSObject
- (void)noThisMethod:(NSString *)value;
@end

@implementation SecondClass
- (void)noThisMethod:(NSString *)value {
    NSLog(@"SecondClass中的方法实现 ===== %@", value);
}
@end

@implementation TestClass

//运行时方法拦截
- (void)dynamicAddMethod: (NSString *) value {
    NSLog(@"调用不存在的方法   ===== 参数:%@", value);
}

/**
 没有找到SEL的IML实现时会执行下方的方法
 @param sel 当前对象调用并且找不到IML的SEL
 @return 找到其他的执行方法,并返回yes
 */
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    return NO;    //当返回NO时,会接着执行forwordingTargetForSelector:方法,
    [TestClass addMethod:[self class] method:sel method:@selector(dynamicAddMethod:)];
    return YES;
}


/**
 将当前对象不存在的SEL传给其他存在该SEL的对象
 @param aSelector 当前类中不存在的SEL
 @return 存在该SEL的对象
 */
- (id)forwardingTargetForSelector:(SEL)aSelector {
//    return self;
    return nil;
    return [SecondClass new];   //让SecondClass中相应的SEL去执行该方法
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
    //消息获得函数的参数和返回值类型
    //如果 -methodSignatureForSelector: 返回 nil ,Runtime 则会发出 -doesNotRecognizeSelector: 消息,程序这时也就挂掉了。如    //查找父类的方法签名
    NSMethodSignature *signature = [super methodSignatureForSelector:selector];
    if(signature == nil) {
        signature = [NSMethodSignature signatureWithObjCTypes:"@@:"];
    }
    //果返回了一个函数签名,Runtime 就会创建一个 NSInvocation 对象并发送 -forwardInvocation: 消息给目标对象。
    return signature;
}

- (void)forwardInvocation:(NSInvocation *)invocation {
    SecondClass * forwardClass = [SecondClass new];
    SEL sel = invocation.selector;
    if ([forwardClass respondsToSelector:sel]) {
        [invocation invokeWithTarget:forwardClass];
    } else {
        [self doesNotRecognizeSelector:sel];
    }
}


+ (void)addMethod:(Class)class method:(SEL)methodSel method:(SEL)methodSelImpl {
    Method method = class_getInstanceMethod(class, methodSelImpl);
    IMP methodIMP = method_getImplementation(method);
    const char *types = method_getTypeEncoding(method);
    class_addMethod(class, methodSel, methodIMP, types);
}
@end

这个是消息转发的三次机会

第一次是 resolveInstanceMethod 这时候可以写个方法交换
这样会重发一次消息,因为方法交换了,所以就不会失败了

第二次是快速转发forwardingTargetForSelector 可以把消息转发
给别的对象。

第三次是普通转发,首先要在这个methodSignatureForSelector 方法中返回NSMethodSignature 不过没返回就挂了,如果返回了就会继续走forwardInvocation 这个方法 然后在这个方法里面做处理。

图片增加一下说服力
这里写图片描述
这是上面使用的源码

然后讲到KVO

当一个观察者为一个对象的属性注册时,被观察对象的isa指针会被修改,指向一个中间类,而不是真正的类。
因此,isa指针的值不一定反映实例的实际类。绝不应依靠isa指针来确定类的成员资格。 相反,您应该使用类方法来确定对象实例的类。

这段没看太懂。比较晕

我找了一段源码:
iOS_模拟KVO的底层实现、手动实现KVO(附源码)

这里就分析别人的源码吧
这里写图片描述

可以看到项目结构

修改isa指针,就是把当前对象指向一个新类、 Person’s isa -> Person_KVO
使当前对象的isa 指向新的派生类(Person_KVO),就会调用派生类的set方法

因为所有的对象都继承自NSObject,给NSObject 添加 (NSObject+KVO)
分类就可以增加新的方法

- (void)ml_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context{

    //修改isa指针,就是把`当前对象`指向一个`新类`
    // Person's isa ->  Person_KVO
    // Class object_setClass(id obj, Class cls)
    //使当前对象的isa指向新的派生类(Person_KVO),就会调用派生类的set方法
    object_setClass(self, [Person_KVO class]);

    //给对象绑定观察者对象
    objc_setAssociatedObject(self, @"observer", observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

//然后在控制器调用


- (void)viewDidLoad {
    [super viewDidLoad];
    Person *person = [[Person alloc] init];
    _person = person;
    //手写KVO
    [person ml_addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];
}

注意 这个self 是控制器,控制器在这里充当观察者

这段内容是指当调用这个方法的时候,就把Person 类的isa指针指向Person_KVO

    //使当前对象的isa指向新的派生类(Person_KVO),就会调用派生类的set方法
    object_setClass(self, [Person_KVO class]);

所以set方法被重写

再看Person_KVO 类实现

-(void)setAge:(int)age{
    [super setAge:age]; //修改了子类的值后,父类的值也修改了
    //调用KVO
    //获取观察者
    id observer = objc_getAssociatedObject(self, @"observer");
    //调用观察者的方法
    //就是验证手写KVO的方法  observer其实这个VC中的self
    [observer ml_observeValueForKeyPath:@"age" ofObject:self change:nil context:nil];
}

所以只要子类Personset值修改了,父类person 也值也修改了。
然后调用观察者 这个观察者是 vc控制器

所以就调用vc控制器的方法,

    [observer ml_observeValueForKeyPath:@"age" ofObject:self change:nil context:nil];

自然 控制器就收到了这个消息。

总结:

所谓的观察属性的实时变化,就是重写isa类,让其指向他的子类。
子类重写需要观察的属性值的set方法,当外界改变a.b = c 这样的
b的属性值时,会调用子类set方法,子类就会告知观察者数值变化了

看了一下他的源码
觉得最后personKVO还引用了控制器的方法,不太好,这样有种循环引用的
感觉。我改了一下他的代码,具体改的地方是增加一个代理。让控制器去实现这个协议就好。

#import <Foundation/Foundation.h>
#import "Person.h"
@protocol observeValueDelegate <NSObject>
- (void)ml_observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context;

@end

@interface Person_KVO : Person
@property(nonatomic, weak) id<observeValueDelegate> delegate;
@end


#import "Person_KVO.h"
#import <objc/runtime.h>

@implementation Person_KVO

-(void)setAge:(int)age{
    [super setAge:age]; //修改了子类的值后,父类的值也修改了
    [self observeChange:@"age"];
}

- (void)setName:(NSString *)name {
    [super setName:name];
    [self observeChange:@"name"];
}

- (void)observeChange:(NSString *)keypath {
    //调用KVO
    //获取观察者
    id observer = objc_getAssociatedObject(self, @"observer");
    self.delegate = observer;
    SEL selector = @selector(ml_observeValueForKeyPath:ofObject: change: context:);
    if ([self.delegate respondsToSelector:selector]) {
        //调用观察者的方法
        //就是验证手写KVO的方法  observer其实这个VC中的self
        [self.delegate ml_observeValueForKeyPath:keypath ofObject:self change:nil context:nil];
    }
}

@end

这样就好很多viewcontroll也不需要这个方法了。

这里是我改好的代码

继续往下看
runtime的黑魔法

method_setImplementation 设置 1 个方法的实现

IMP method_setImplementation(Method m, IMP imp) 

m代表方法 imp 代表实例,就是给实例添加一个方法

method_exchangeImplementations,当需要交换 2 个方法的实现时使用。

原理

IMP imp1 = method_getImplementation(m1);
IMP imp2 = method_getImplementation(m2);
method_setImplementation(m1, imp2);
method_setImplementation(m2, imp1);
//应该就是拿到实例1  在拿到实例2  给实例2 方法1  给实例1 方法2

class_replaceMethod, 当需要替换的方法可能有不存在的情况时,可以考虑使用该方法。

class_replaceMethod在苹果的文档(如下图所示)中能看到,它有两种不同的行为。当类中没有想替换的原方法时,该方法会调用class_addMethod来为该类增加一个新方法,也因为如此,class_replaceMethod在调用时需要传入types参数

我在aop里面有应用这些

关键代码
上面是交换实例方法,下面是交换类方法的

CG_INLINE void
ReplaceMethod(Class _class, SEL _originSelector, SEL _newSelector) {
    Method oriMethod = class_getInstanceMethod(_class, _originSelector);//类的实例方法
    Method newMethod = class_getInstanceMethod(_class, _newSelector);
    BOOL isAddedMethod = class_addMethod(_class, _originSelector, method_getImplementation(newMethod), method_getTypeEncoding(newMethod));
    if (isAddedMethod) {
        class_replaceMethod(_class, _newSelector, method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod));
    } else {
        method_exchangeImplementations(oriMethod, newMethod);
    }
}

CG_INLINE void
ReplaceCLASSMethod(Class _class, SEL _originSelector, SEL _newSelector) {
    Method oriMethod = class_getClassMethod(_class, _originSelector);//类方法
    Method newMethod = class_getClassMethod(_class, _newSelector);
    method_exchangeImplementations(oriMethod, newMethod);
}

应用的时候 首先是实例方法
获取 控制器的viewappear时机,可以给viewappear添加需要的代码,

#import "UIViewController+swizzlingLogCurrentVC.h"

@implementation UIViewController (swizzlingLogCurrentVC)
+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        ReplaceMethod([self class], @selector(viewWillAppear:), @selector(yyb_viewWillAppear:));
    });
}
- (void)yyb_viewWillAppear:(BOOL)animated{
    [self yyb_viewWillAppear:animated];
//    NSLog(@"%@ viewWillAppear",[self class]);
}
@end

然后是类方法 这是把image 从内存读取换成从文件读写其他细节就不讲了

#import <UIKit/UIKit.h>
@interface UIImage (swizzlingImageName)
@property (nonatomic, copy) NSString *imageName;
@end

#import "UIImage+swizzlingImageName.h"
#import <objc/runtime.h>

@implementation UIImage (swizzlingImageName)
+ (void)load
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        ReplaceCLASSMethod([self class], @selector(imageNamed:), @selector(yyb_imageNamed:));
        ReplaceCLASSMethod([self class], @selector(imageWithContentsOfFile:), @selector(yyb_imageWithContentsOfFile:));
    });
}
+ (nullable UIImage *)yyb_imageNamed:(NSString *)name
{
    UIImage *image = [UIImage yyb_imageNamed:name];
    image.imageName = name;
    return image;
}

+ (nullable UIImage *)yyb_imageWithContentsOfFile:(NSString *)path
{
    UIImage *image = [UIImage yyb_imageWithContentsOfFile:path];
    NSURL *urlPath = [NSURL fileURLWithPath:path];
    NSString *imageName = [[urlPath.lastPathComponent componentsSeparatedByString:@"."] firstObject];
    image.imageName = imageName;
    return image;
}


- (NSString *)imageName
{
    return objc_getAssociatedObject(self, _cmd);//_cmd相当于当前方法的指针,类似self
}

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

@end

参考文章列表:

参考文章1
iOS动态性(一) 一行代码实现iOS序列化与反序列化(runtime)

在Docker中集成GPU监控体系 先上效果图 等文档整理出来再更 阅读详情

相关推荐

【数学模型】灰色关联分析

数学模型-灰色关联分析

suic009的博客 1万+

c++对象模型

一、什么是c++对象模型 语言中直接支持面向对象程序设计的部分。 对于各种支持的底层实现机制。 二、c++对象的布局成本 成员函数不占用成本 member functions虽然再class的声明之内,却不在object之中。每一个non-inline member function只会诞生一个函数实例。 每一个“拥有零个或一个定义”的inline function则会在其每一个使用者(模块)身上产生一个函数实例。 c++在布局以及存取时间上主要的额外负担是由virtual引起的 virtual

gb173652770的专栏 2272

无人机芯片模块技术要点分析

恶劣环境下信号稳定性:无人机在恶劣环境下工作时,遥感系统采集的信号往往很不稳定,视频信号处理芯片需要具备强抗干扰能力,能辨别脉冲脉宽为50ns以上、幅度为0V~4.5V的输入信号。这种架构能兼顾强大的运算能力和灵活的接口控制。芯片小型化:麻省理工学院的研究人员已设计出仅20平方毫米的微型芯片,可处理无人机飞行所需的惯性数据和相机图像,为微型无人机的发展奠定了基础。光子集成芯片(PIC) :研究显示,与电子芯片相比,光子计算芯片在延迟、计算容量和能效上都有数量级的提升,有望解决当前无人机系统的计算瓶颈。

YUNZHUO666的博客 1404

深入C++对象模型(1) -- C++的三种模型

从本篇博客开始,根据《深入C++对象模型》这本书,加上自己的理解,探讨C++的对象模型以及相关的问题。博客尽量使用书中的名词、图例和例程。 C++是面向对象的语言,类是C++与C的区别。在C++中,有两种class data members:static和nonstatic,以及三种class member function:static、nonstatic和virtual。已知有下面这个class Point声明: class Point { public: Point(float x); v.

Fisher 1164

AWS 小白书 [1] 全球架构

小白应该知道的 AWS 知识

boshansolo的博客 704

用RK3588 NPU加速人脸识别:从本地测试到服务器部署的完整流程(含ONNX模型)

本文详细介绍了如何利用RK3588 NPU加速人脸识别系统,从模型优化到高并发服务部署的全流程。通过RetinaFace和FaceNet模型的协同推理,结合ONNX模型转换和NPU特有优化技巧,显著提升识别效率。文章还涵盖了gRPC通信架构设计、性能压测及实际部署经验,为开发者提供了一套完整的解决方案。

weixin_29058331的博客 465

NB-IoT开发避坑指南:STM32下AT指令状态机的设计与优化

本文深入探讨了在STM32平台上为NB-IoT模组开发时,如何用状态机(FSM)设计替代传统的阻塞式AT指令处理方法。通过构建非阻塞、事件驱动的AT指令引擎,有效解决了系统卡顿、响应迟缓等问题,并详细介绍了状态机的设计、实现与高级优化技巧,助力开发者打造健壮可靠的物联网设备通信核心。

q5r6s7的博客 758

c++对象模型系列

一、指针与引用 一 概括 指针和引用,在C++的软件开发中非常常见,如果能恰当的使用它们能够极大的提 高整个软件的效率,但是很多的C++学习者对它们的各种使用情况并不是都了解,这就导致了实际的软件开发中经常会内存泄漏,异常抛出,程序崩溃等问题。对 于C和C++的初学者,那更是被它们搞的迷迷糊糊。本篇作为[深入C++]系列的第一节,我们就带领大家把指针和引用这个基本功练好。 二 指针 指

tanglanting12的专栏 1454

PCL RANSAC拟合分割多条直线【2026最新版】

使用RANSAC算法拟合分割多条直线,输出每一条直线的拟合参数到控制台,并保存直线点云到本地文件夹。博客长期更新,本文最新更新时间为:2026年5月24日。

点云侠的博客 7974

Coze工作流的代码无法安装第三方模块(依赖)怎么办?(如何解决Coze工作流代码缺少模块(依赖)的问题)

Coze工作流的代码无法安装第三方模块(依赖)怎么办?(如何解决Coze工作流代码缺少模块(依赖)的问题)

XiaoqiangClub的博客 4365

objective c下的对象模型

通过clang将objective c代码翻译成cpp代码,阅读代码的实现去理解objc中对象模型。

crossgowhite的博客 748

3.摄像头组件- CVP (ISP)

本文介绍 车载摄像头组件-ISP

2401_83458819的博客 1617

Objective-C对象模型及应用

前言 原创文章,转载请注明出自唐巧的技术博客。 本文主要介绍Objective-C对象模型的实现细节,以及Objective-C语言对象模型中对isa swizzling和method swizzling的支持。希望本文能加深你对Objective-C对象的理解。 ISA指针 Objective-C是一门面向对象的编程语言。每一个对象都是一个类的实例。在Object

我爱大馅饼 1091

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

objective C 对象模型

第一、在OC中关于对象模型,有五个基本概念,Object(实例),Class(类),Metaclass(元类),Rootclass(根类),Rootclass‘s metaclass(根元类) 第二、每一个object都有一个隐藏的isa指针,指向class,对象结构如下所示: typedef struct objc_class *Class; typedef struct objc_obj

richard_如风的专栏 1438

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

Objective-C的对象模型和runtime机制

对象模型和runtime 内容列表 对象模型(结构定义,类对象、元类和实例对象的关系) 消息传递和转发机制 runtime系统功能理解

jin的专栏 1859

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

Objective-C的对象模型与运行时

原文发布于http://www.cocoachina.com/ios/20141218/10679.html 0. Objective-C与C 从Objective-C的名字上我们就能看出来它和C语言有着很密切的关系,是“面向对象的C语言”。Objective-C诞生于上世纪80年代,那个时候面向对象的理念已经得到了初步发展,C语言当时也已经是很成熟的语言。和C++类似,Obj

小号鱼头 767
上一篇: block
下一篇: runloop 笔记
csdn_Damon
博客等级 码龄10年 37粉丝 143原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值