Runtime系列:super调用函数本质、isMemberOfClass与isKindOfClass的区别、综合分析【05】

简介: Runtime系列:super调用函数本质、isMemberOfClass与isKindOfClass的区别、综合分析

1、super调用函数本质


class 方法的底层实现:


// 获取调用者对象的类
- (Class)class {
    return object_getClass(self);
}
// 获取调用者对象的夫类
- (Class)superclass {
    return class_getSuperclass(object_getClass(self));
}


super的底层实现【[super message]的底层实现】:


1、消息接收者仍然是子类对象。

2、从父类开始查找方法的实现。


示例:


#import "Person.h"
@interface Student : Person
@end


#import <Foundation/Foundation.h>
@interface Person : NSObject
- (void)run;
@end


- (instancetype)init
{
    if (self = [super init]) {
        NSLog(@"[self class] = %@", [self class]); // Student
        NSLog(@"[self superclass] = %@", [self superclass]); // Person
        NSLog(@"--------------------------------");
        /**
        * super调用实现本质:
        * objc_msgSendSuper({self, [Person class]}, @selector(class));
        */
        NSLog(@"[super class] = %@", [super class]); // Student
        NSLog(@"[super superclass] = %@", [super superclass]); // Person
    }
    return self;
}
@end


2、isMemberOfClass、isKindOfClass区别

先看下底层逻辑:


- (BOOL)isMemberOfClass:(Class)cls {
    return [self class] == cls;
}
- (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}
+ (BOOL)isMemberOfClass:(Class)cls {
    return object_getClass((id)self) == cls;
}
+ (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}


示例1:


   

id person = [[MJPerson alloc] init];
        // 判断左边的实例对象是否等于右边的类
        NSLog(@"%d", [person isMemberOfClass:[MJPerson class]]); // 1
        NSLog(@"%d", [person isMemberOfClass:[NSObject class]]); // 0
        // 判断左边的实例对象是否属于右边的类或子类
        NSLog(@"%d", [person isKindOfClass:[MJPerson class]]); // 1
        NSLog(@"%d", [person isKindOfClass:[NSObject class]]); // 1


示例2:


     

NSLog(@"%d", [NSObject isKindOfClass:object_getClass([NSObject class])]); // 1
        NSLog(@"%d", [NSObject isMemberOfClass:object_getClass([NSObject class])]); // 1
        NSLog(@"%d", [NSObject isKindOfClass:object_getClass([MJPerson class])]); // 0
        NSLog(@"%d", [MJPerson isMemberOfClass:object_getClass([MJPerson class])]); // 1
        // -------
        // 这句代码的方法调用者不管是哪个类(只要是NSObject体系下的),都返回YES
        NSLog(@"%d", [NSObject isKindOfClass:[NSObject class]]); // 1
        NSLog(@"%d", [NSObject isMemberOfClass:[NSObject class]]); // 0
        NSLog(@"%d", [MJPerson isKindOfClass:[MJPerson class]]); // 0
        NSLog(@"%d", [MJPerson isMemberOfClass:[MJPerson class]]); // 0


总结:

1、实例方法是类的判断:


1、isMemberOfClass:判断左边的实例对象是否等于右边的类

2、isKindOfClass:判断左边的实例对象是否属于右边的类或子类


2、类方法是元类的判断:


1、正常情况下传参应该取元类对象进行判断断。

2、如果是传类对象判断,返回全部为0;除非右边入参为 [NSObject class],此时方法调用者不管是哪个类(只要是NSObject体系下的),都返回YES。


3、以下代码能不能执行成功?如果可以,打印结果是什么?

a0ac85d64f544e0684e18a7af04c010f.png

考察知识点:

1、super 调用的本质

2、函数栈空间的分配问题

3、消息机制

4、访问成员变量的本质


1、指针分析

1、此时 cls 存放的是 MJPerson类的地址

2、obj 是MJPerson类的地址的指针

3、MJPerson.name 是 MJPerson 结构体中的成员变量,位于isa的下一个栈中。

4、因为MJPerson 没有初始化,所以实际情况是 name 属性没有被转成员变量,成为堆中的一个变量。

5、因此,此时 self.name 取的值是 MJPerson结构体isa的下一个栈信息。

d24d60891f124299863c98c50654f75f.png



2、[super viewDidLoad]本质分析

底层结构:


 

struct abc = {
        self,
        [ViewController class]
    };
    objc_msgSendSuper2(abc, sel_registerName("viewDidLoad"));


从这个底层结构可知,MJPerson.isa 的下一个地址是self、再下一个是 [ViewController class]。那么其堆地址信息如下:


3、堆栈分析

9ba6b0ad29d44b7786fa7c4371a354ab.png


4、打印取值

- (void)print
{
    NSLog(@"my name is %@", self.name);
}


self.name 实际是取MJPerson结构体中isa的下一个地址【self->_name】,而MJPerson结构体中isa的下一个地址是ViewController中的self的实例对象。


综上分析,最终print打印出来的值是 self 对象。


验证:

04a1a754883e4cddaa22df4b76a727b3.png

相关文章
|
索引 Python
|
8月前
|
存储 安全 前端开发
编写加密解密工具函数时,如何处理密钥的安全性?
编写加密解密工具函数时,如何处理密钥的安全性?
896 153
|
7月前
|
Java 调度 开发者
Java AQS:JUC 并发体系的底层同步框架基石
AQS(AbstractQueuedSynchronizer)是Java并发包(JUC)的底层核心,以volatile state + CLH双向队列统一实现同步控制。支持独占(如ReentrantLock)与共享(如Semaphore、CountDownLatch)两种模式,通过模板方法封装排队、阻塞/唤醒等通用逻辑,是理解与定制高性能同步组件的关键基石。(239字)
850 7
|
4月前
|
SQL 关系型数据库 MySQL
MySQL 数据恢复最佳实践:DELETE、DROP TABLE、DROP DATABASE 三种误删场景的应急与恢复方案
数据库小学妹详解MySQL误删恢复:覆盖DELETE回滚、DROP表/库的全量+Binlog时点恢复(PITR)、延迟从库急救方案,并附binlog2sql实战、止血SOP及防误删五重防护。面试必背,生产避坑指南!
|
6月前
|
数据采集 存储 人工智能
制造业生产管理怎么做?8 个核心环节与数字化落地方法
制造业生产管理是统筹“人、机、料、法、环、信、测、交付”八大要素的系统性工程,涵盖计划排程、执行监控、质量管控、物料协同、设备维保、工时核算、异常闭环与物流追踪,核心在于实现生产全过程的透明化、可追溯与动态优化。(239字)
|
9月前
|
数据采集 人工智能 搜索推荐
大模型入门指南:从看懂原理到动手微调,一步步打造你的专属AI
本文深入浅出地讲解大模型核心技术:从Token、Embedding到上下文窗口,揭秘AI如何理解语言;通过提示词工程、RAG与微调,教你打造专属智能助手。零基础也能学会,让AI真正为你所用,开启个性化智能时代。
1665 1
|
人工智能 搜索推荐 安全
正式上线!阿里云短信模板 AI 助手,10 秒生成/改写个性化、合规短信内容
阿里云短信服务 - 短信模板AI 助手已全面开放,欢迎体验!
1555 6
|
XML API 开发者
探究获取亚马逊畅销榜API接口及实战应用
亚马逊MWS(商城网络服务)提供了一系列API接口,帮助开发者获取平台数据,其中畅销榜API尤为关键。通过注册开发者账号、创建应用并申请权限,可使用HTTP POST请求获取商品的销售排名、价格等信息。Python代码示例展示了如何构建和发送请求,并处理返回的XML或JSON数据。注意遵守亚马逊的频率限制、数据准确性和合规性要求,以确保安全合法地利用这些数据支持电商业务决策。
712 1
|
JavaScript
vue2中左侧菜单和头部tab标签联动
vue2中左侧菜单和头部tab标签联动
843 0
|
计算机视觉 索引
02 OpenCV图像通道处理
在数字图像处理中,图像通道是指一个图像中的颜色信息被分离为不同的颜色分量。常见的图像通道包括RGB通道、灰度通道、HSV通道等。 RGB通道是指将图像分离为红色、绿色和蓝色三个颜色通道,每个通道表示相应颜色的亮度。这种方式是最常见的方式,它对于彩色图像的处理非常重要。

热门文章

最新文章