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机器学习】聚类算法任务,评价指标SC、DBI、ZQ等系数详解和实战演示(附源码 图文解释)
【Python机器学习】聚类算法任务,评价指标SC、DBI、ZQ等系数详解和实战演示(附源码 图文解释)
1057 0
|
弹性计算 应用服务中间件
阿里云服务器新老用户新购、续费、升级折扣汇总(最新更新)
阿里云服务器折扣分为新用户购买折扣,老用户购买折扣、老用户续费或者升级云服务器折扣和新老用户购买海外地域云服务器折扣,新用户折扣往往要比老用户低一些,下面是最新的新购和续费升级折扣优惠汇总。
阿里云服务器新老用户新购、续费、升级折扣汇总(最新更新)
|
开发框架 安全 应用服务中间件
【文件上传绕过】——解析漏洞_IIS7.0 | IIS7.5 | Nginx的解析漏洞
【文件上传绕过】——解析漏洞_IIS7.0 | IIS7.5 | Nginx的解析漏洞
602 9
|
9月前
|
弹性计算 应用服务中间件 API
AppFlow:无代码部署Dify并集成到企业微信
本文介绍如何通过计算巢AppFlow完成Dify的无代码部署,并将Dify应用集成到企业微信中使用。具体步骤包括:创建企业微信应用,获取AgentID和Secret;使用计算巢AppFlow模板创建连接流,配置Dify和企业微信的鉴权凭证;配置企业微信API接收消息和可信IP;最后测试应用确保正常运行。文中还提供了常见问题的解决方案,如域名主体校验未通过和配置企业可信IP报错等。
2278 11
AppFlow:无代码部署Dify并集成到企业微信
|
12月前
|
存储 关系型数据库 MySQL
mysql 引擎概述
MySQL存储引擎是处理不同类型表操作的组件,InnoDB是最常用的默认引擎,支持事务、行级锁定和外键。MySQL采用插件式存储引擎架构,支持多种引擎,如MyISAM、Memory、CSV等,每种引擎适用于不同的应用场景。通过`SHOW ENGINES`命令可查看当前MySQL实例支持的存储引擎及其状态。选择合适的存储引擎需根据具体业务需求和引擎特性来决定。
250 1
|
数据可视化 数据挖掘
ECharts综合案例一:近七天跑步数据
使用ECharts展示近七天跑步数据,结合雷达图和折线图揭示运动表现。雷达图多维度呈现全程距离、速度和时间,对比平均指标;折线图清晰展示里程趋势。图表具有交互性和动画效果,通过[代码地址](https://download.csdn.net/download/No_Name_Cao_Ni_Mei/89454698)可获取详情。#ECharts #跑步数据 #数据可视化 #雷达图 #折线图
333 3
ECharts综合案例一:近七天跑步数据
|
编解码 前端开发 开发者
【Web 前端】CSS常用尺寸单位有哪些?应用场景?
【4月更文挑战第22天】【Web 前端】CSS常用尺寸单位有哪些?应用场景?
|
Java 数据安全/隐私保护 Sentinel
微服务学习 | Spring Cloud 中使用 Sentinel 实现服务限流
微服务学习 | Spring Cloud 中使用 Sentinel 实现服务限流
|
前端开发
t-io websocket的聊天功能学习记录(二)
t-io websocket的聊天功能学习记录(二)
209 0
|
Linux
【专栏】在 Linux 系统中,符号链接(Symbolic Link)是一种特殊的文件类型,它指向另一个文件或目录
【4月更文挑战第28天】在Linux中,符号链接是特殊的文件类型,指向其他文件或目录。本文介绍了查找符号链接的三种方法:1) 使用`ls -l`查看文件类型为'l'的项;2) 使用`find / -type l`遍历文件系统;3) 使用`lsof -L`列出打开的链接。此外,还讨论了命令的详细用法、高级技巧和应用场景,并提供了实际案例。注意权限、系统负载和目标文件存在性等问题。掌握这些命令有助于管理和维护系统符号链接。
816 0