【iOS】类对象的结构分析
目录
- 对象的分类
- object_getClass和class方法
- isa流程和继承链分析
- isa流程实例验证
- 类的继承链实例验证
- 类的结构
- cache_t结构
- bits分析
- 实例验证
- 属性properties
- 方法methods
- 协议protocols
- ro
- 类方法
- 类结构流程图解
对象的分类
OC中的对象主要可以分为3种:实例对象(instance)、类对象(class)和元类对象(meta-class)
实例对象
通过类alloc出来的对象,每次调用alloc都会产生新的instance对象
NSObject* obj1 = [[NSObject alloc] init];
NSObject* obj2 = [[NSObject alloc] init];
NSLog(@"%p %p", obj1, obj2);
// 打印结果:0x600000180040 0x600000180050
从运行结果可看出以上是不同的两个实例对象,分别占据着两块不同的内存
实例对象在内存中存储的信息包括:isa
指针、其他成员变量
类对象
#import <objc/runtime.h>
Class objectClass1 = [obj1 class];
Class objectClass2 = [obj2 class];
Class objectClass3 = [NSObject class];
Class objectClass4 = object_getClass(obj1); //Runtime API
Class objectClass5 = object_getClass(obj2); //Runtime API
// 打印结果:0x1d6fc6070 0x1d6fc6070 0x1d6fc6070 0x1d6fc6070 0x1d6fc6070
以上都是NSObject
的类对象,从运行结果可看出它们都是同一个对象,即这些指针指向的是同一块内存,每个类在内存中有且只有一个class对象
类对象在内存中存储的信息主要包括:isa
指针、superclass
指针、类的属性信息(@property
)、类的对象方法信息(instance method
)、类的协议信息(protocol
)、类的成员变量(ivar
,类型、名称等描述信息而不是具体的值)
元类对象
看下面如何获取元类对象(元类对象类型仍是一个类对象,底层都是struct objc_class* Class
,只是包含的信息不一样)
Class objectMetaClass = object_getClass(object_getClass(obj1));
将类对象作为参数传入,再次调用object_getClass
函数
那如果调用两次class
方法呢?
Class objectMetaClass2 = [[NSObject class] class];
NSLog(@"%p %p %d", objectMetaClass, objectMetaClass2, class_isMetaClass(objectMetaClass));
// 打印结果:0x1d6fc6020 0x1d6fc6070 1 0
从打印结果可以看出,class
不管调多少次返回的一直是类对象,不会是元类对象
每个类只有一个元类对象,元类对象在内存中存储的信息主要包括:isa
指针、superclass指针以及类方法信息
object_getClass和class方法
查看objc4源码
object_getClass
方法中传入各种对象,通过访问isa
,返回不同的类对象:
Class object_getClass(id obj)
{if (obj) return obj->getIsa();else return Nil;
}// 传入类名字符串,返回对象的类对象
Class objc_getClass(const char *aClassName)
{if (!aClassName) return Nil;// NO unconnected, YES class handlerreturn look_up_class(aClassName, NO, YES);
}
class
方法直接返回类对象:
//+ (id)self {
// return (id)self;
//}
//- (id)self {
// return self;
//}+ (Class)class {return self;
}- (Class)class {return object_getClass(self);
}//+ (Class)superclass {
// return self->getSuperclass();
//}
//- (Class)superclass {
// return [self class]->getSuperclass();
//}
isa流程和继承链分析
上面我们了解了对象的分类,认识到不同类型对象的差别,那么是什么让这些不同类型的对象联系起来从而构成OC对象体系的呢?
上经典老图:
isa指向链
实际上就是isa
指针将它们联系起来形成 isa
指向链:
- 实例对象
instance
的isa
指向类class
- 类对象
class
也有isa
指向的是元类meta
- 元类
meta
中也有isa
指向的是根元类root meta
当调用对象方法时,通过实例对象的isa
找到class
,最后找到对象方法的实现进行调用
当调用类方法时,通过类对象的isa
找到meta-class
,最后找到类方法的实现进行调用
类继承链
根据superclass
的指向,也可总结出OC类的继承链:
- 子类继承于父类,父类继承于根类,根类指向的是
nil
- 在元类中也存在继承,子类的元类继承于父类的元类,父类的元类继承于根元类,根元类又继承与根类
当Student的实例对象要调用Person的对象方法时,会先通过isa
找到Student的class
,然后通过superclass
找到Person的class
,最后找到对象方法的实现进行调用
类似地,当Student的类对象要调用Person的类方法时,会先通过isa
找到Student的meta-class
,然后通过superclass
找到Person的meta-class
,最后找到类方法的实现进行调用
isa流程实例验证
Person类继承于NSObject,Student类继承于Person
@interface Person : NSObject {@publicint _age;
}- (void)personInstanceMethod;
+ (void)personClassMethod;@end@interface Student : Person {@publicint _no;
}- (void)studentInstanceMethod;
+ (void)studentClassMethod;@end
打断点,通过LLDB查看isa关联类的地址:
// 打印出实例的地址
Person* person = [Person alloc];
NSLog(@"%@", person);
Student* student = [Student alloc];
NSLog(@"%@", student);
类对象的地址和实例对象
isa
所指向的地址有所出入,isa
需要进行一次位运算,才能计算出类对象的真实地址
在获取到对象的isa
值后,可以通过&
(按位与)一个掩码ISA_MASK 0x007ffffffffffff8ULL
来获取到对象关联的类地址:
根据student
实例的isa
地址找到关联类Student的地址0x00000001000082d8
同样地,根据Student类对象的isa
找到Student元类的地址0x00000001000082b0
根据Student元类对象的isa
找到关联类的地址0x00000001d6fc6020
找到NSObject类对象的isa
关联类地址0x00000001d6fc6020
,与Student元类对象的isa
关联类地址一致,可以验证元类的isa
指向根元类,且根元类的isa指向自己
类的继承链实例验证
Class tClass = [Student class];
Class pClass = class_getSuperclass(tClass);
Class nClass = class_getSuperclass(pClass);
Class rClass = class_getSuperclass(nClass);
NSLog(@"\n tClass-%@ \n pClass-%@ \n nClass-%@ \n rClass-%@ \n", tClass, pClass, nClass, rClass);
可看出类对象的继承链:Student->Person->NSObject->nil
Student * student = [Student alloc];
Class tClass = object_getClass(student);
Class mtClass = object_getClass(tClass);
Class mtSuperClass = class_getSuperclass(mtClass);
NSLog(@"\n student %p 实例对象 -- %p 类 -- %p 元类 -- %p 元类父类", student, tClass, mtClass, mtSuperClass);
Person * person = [Person alloc];
Class pClass = object_getClass(person);
Class mpClass = object_getClass(pClass);
Class mpSuperClass = class_getSuperclass(mpClass);
NSLog(@"\n person %p 实例对象 -- %p 类 -- %p 元类 -- %p 元类父类", person, pClass, mpClass, mpSuperClass);
NSObject * obj = [NSObject alloc];
Class objClass = object_getClass(obj);
Class mobjClass = object_getClass(objClass);
Class mobjSuperClass = class_getSuperclass(mobjClass);
NSLog(@"\n NSObject %p 实例对象 -- %p 类 -- %p 元类 -- %p 元类父类 == %p NSObject类对象", obj, objClass, mobjClass, mobjSuperClass,
[NSObject class]);
可看出元类的继承链:Student Meta-class -> Person Meta-class -> NSObject Meta-class -> NSObject class -> nil
类的结构
前面我们了解到了Class
的类型是struct objc_class*
结构体指针类型,下面就来分析一下这个结构体的定义
struct objc_object {Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};struct objc_class : objc_object {// Class ISA;Class superclass;cache_t cache; // formerly cache pointer and vtableclass_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags// ...其他代码,objc_class定义共计531行代码...
};
继承于objc_object
说明:
- 还有一个继承过来的Class类型变量
isa
superclass
:指向父类的指针cache
:缓存相关bits
:用于获取具体的类信息
cache_t结构
cache_t
是一个结构体
struct cache_t {
private:explicit_atomic<uintptr_t> _bucketsAndMaybeMask; // 8字节union {struct {explicit_atomic<mask_t> _maybeMask; // uint32_t 4字节
#if __LP64__uint16_t _flags; // 2字节
#endifuint16_t _occupied; // 2字节};explicit_atomic<preopt_cache_t *> _originalPreoptCache; // 8字节};};
// 此段为部分代码,cache_t定义总共有290行
分析整个cache_t的结构,发现cache_t的内存总共为16字节,后面会对其底层进行学习
bits分析
在objc_class
里有一段源码是data
操作
class_rw_t *data() const {return bits.data();
}
void setData(class_rw_t *newData) {bits.setData(newData);
}
data
为class_rw_t
类型,下面是其部分源码:
ro
:成员变量、methods
:方法、properties
:属性、protocols
协议
我们在类中定义的方法、属性等就是通过调取class_rw_t
结构体中的方法获取的
实例验证
下面通过实例来验证一下类的结构是否如上面一致
创建Person类继承于NSObject,定义一些属性、方法以及协议:
@protocol PersonDelegate<NSObject>- (void)personDelegateMethod;
// 让Person类遵守并实现此协议方法
@end@interface Person : NSObject<PersonDelegate> {NSString* hobby;
}@property (nonatomic, strong)NSString* name;
@property (nonatomic, assign)NSInteger age;- (void)sayHello;
+ (void)sayWorld;@end
LLDB
调试输出
第一个地址0x0000000100008470
是类的第一个成员isa
,第二个地址0x00000001d6fc6070
是类的第二个成员superclass
isa
和superclass
都是结构体指针类型,占用8字节,cache
结构体占用16字节,XYPerson的地址加上8 + 8 + 16 = 32
就可以得到bits
的地址
相加并强转为class_data_bits_t *
类型得到bits
的地址0x0000000100008270
,再调用data()
方法就得到类型为class_rw_t
的地址
属性properties
调用class_rw_t
的properties()
方法,得到property_array_t
类型的数组,继承于list_array_tt
,找到list
下的ptr
class property_array_t :public list_array_tt<property_t, property_list_t, RawPtr>
{typedef list_array_tt<property_t, property_list_t, RawPtr> Super;public:property_array_t() : Super() { }property_array_t(property_list_t *l) : Super(l) { }
};
ptr
为property_list_t
类型,继承于entsize_list_tt
struct property_list_t : entsize_list_tt<property_t, property_list_t, 0> {
};
entsize_list_tt
部分源码:
struct entsize_list_tt {uint32_t entsizeAndFlags;uint32_t count; // 数量uint32_t entsize() const {return entsizeAndFlags & ~FlagMask;}uint32_t flags() const {return entsizeAndFlags & FlagMask;}Element& getOrEnd(uint32_t i) const {ASSERT(i <= count);return *PointerModifier::modify(*(List *)this, (Element *)((uint8_t *)this + sizeof(*this) + i*entsize()));}Element& get(uint32_t i) const { // 获取元素方法ASSERT(i < count);return getOrEnd(i);}// ...其他代码...
};
通过调用get()
方法,获取元素,下面的结果就是Person类的name
、age
在properties()
里,而实例变量hobby
不在这里
方法methods
调用class_rw_t
的methods()
方法,得到method_array_t
类型的数组,继承于list_array_tt
,同样找到list
下的ptr
这里看到ptr
是method_list_t
类型,同样继承于entsize_list_tt
,其中有count
为6,调用get()
方法查看输出
这里的元素为method_t
类型,method_t
为结构体类型,其中的一个成员变量为big
的结构体,里面是方法名称等信息:
struct method_t {method_t(const method_t &other) = delete;// The representation of a "big" method. This is the traditional// representation of three pointers storing the selector, types// and implementation.struct big {SEL name;const char *types;MethodListIMP imp;};
// ...其他代码
};
调用big
方法查看输出
这6个方法分别是:
- 实例方法:
sayHello
- 属性
name
、age
的set
/get
方法 C++
析构函数:.cxx_destruct
且都是实例方法,并没有类方法sayWorld
协议protocols
调用class_rw_t
的protocols()
方法,得到protocol_array_t
类型的数组,继承于list_array_tt
,同样找到list
下的ptr
这里protocol_list_t
并没有继承于entsize_list_tt
:
struct protocol_list_t {// count is pointer-sized by accident.uintptr_t count;protocol_ref_t list[0]; // variable-sizesize_t byteSize() const {return sizeof(*this) + count*sizeof(list[0]);}protocol_list_t *duplicate() const {return (protocol_list_t *)memdup(this, this->byteSize());}typedef protocol_ref_t* iterator;typedef const protocol_ref_t* const_iterator;const_iterator begin() const {return list;}iterator begin() {return list;}const_iterator end() const {return list + count;}iterator end() {return list + count;}
};
看到protocol_list_t
的定义,我们知道count
值为1,说明是有值,但是其成员是protocol_ref_t
为uintptr_t
类型,那怎么输出查看这个count
中的1到底是什么呢
查看protocol_ref_t
的定义,通过注释信息,我们可以看到protocol_ref_t
未映射到protocol_t
类型,那我们就找protocol_t
的定义
这里看到protocol_t
中有mangledName
以及instanceMethods
等,只要得到protocol_t
就可以输出我们想要的名称方法等信息,怎么才能从protocol_ref_t
映射到protocol_t
呢,全局找一下吧
这里我们看到,protocol_ref_t
是可以强转protocol_t
的,那我们就试试:
强转成功,调用demangledName
方法,我们就得到了LGPersonDelegate
,那我们再找一下协议方法
按照method
查看输出的步骤,成功找到协议方法personDelegateMethod
ro
调用class_rw_t
的ro
方法,得到class_ro_t
的结构体
查看ivars
,也是继承于entsize_list_tt
的ivar_list_t
类型的结构体,调用get
方法查看:
这6个实例变量分别是自定义hobby
以及系统自动帮我们自动生成的带有_
的实例变量
类方法
methods
中的方法全部都存在类中,都是实例方法,那么类方法应该去在元类中找
通过类的isa
指针找到元类,再根据上面的步骤找到并输出这个元类的methods
这里我们不由地想,OC的底层是
C/C++
实现的,不存在对象方法和类方法的区分,有的都是函数实现,在OC的设计中,一个类可以new出无数个对象,因此把方法存在类中,而不是动态创建的对象中,是合理的。
因为OC的对象方法和类方法的定义是-
和+
的区分,那么方法名称就会有重名的存在,因此才会引入元类的概念,元类的存在就是解决类方法重名的问题
类结构流程图解
类的结构流程图解析:
相关文章:
【iOS】类对象的结构分析
目录 对象的分类object_getClass和class方法isa流程和继承链分析isa流程实例验证类的继承链实例验证 类的结构cache_t结构bits分析实例验证属性properties方法methods协议protocolsro类方法 类结构流程图解 对象的分类 OC中的对象主要可以分为3种:实例对象…...
接口性能优化思路
前言 日常开发中设计接口,响应时间是衡量一个接口质量的重要指标。 接口响应时间这里粗糙地分为三种: 即时响应:毫秒级,小于500毫秒快速响应:秒级,大于500毫秒且小于2秒长时间操作:大于2秒&a…...
PyQt5 多线程编程详细教程
PyQt5 多线程编程详细教程 在 PyQt5 中,多线程编程是提高应用程序性能和响应性的重要手段。本教程将详细介绍如何在 PyQt5 中使用 QThread 进行多线程编程,学习如何避免界面冻结和线程安全问题,并通过丰富的案例来展示如何实现这些功能。 Q…...
uniapp小程序上传pdf文件
<template><view class"mainInnBox"><view class"formBox"><!-- 注意,如果需要兼容微信小程序,最好通过setRules方法设置rules规则 --><u-form :model"form" ref"uForm" :rules&quo…...
Python酷库之旅-第三方库Pandas(036)
目录 一、用法精讲 111、pandas.Series.item方法 111-1、语法 111-2、参数 111-3、功能 111-4、返回值 111-5、说明 111-6、用法 111-6-1、数据准备 111-6-2、代码示例 111-6-3、结果输出 112、pandas.Series.xs方法 112-1、语法 112-2、参数 112-3、功能 112-…...
Python爬虫(2) --爬取网页页面
文章目录 爬虫URL发送请求UA伪装requests 获取想要的数据打开网页 总结完整代码 爬虫 Python 爬虫是一种自动化工具,用于从互联网上抓取网页数据并提取有用的信息。Python 因其简洁的语法和丰富的库支持(如 requests、BeautifulSoup、Scrapy 等…...
【iOS】——探究isKindOfClass和isMemberOfClass底层实现
isKindOfClass 判断该对象是否为传入的类或其子类的实例 // 类方法实现,用于检查一个类是否属于另一个类或其父类链上的任何类。(BOOL)isKindOfClass:(Class)cls {// 从当前类开始,tcls将沿着元类的继承链向上遍历。for (Class tcls self->ISA(); …...
Python 热门面试题(七)
Python中如何拷贝对象?浅拷贝和深拷贝的区别是什么? 在Python中,拷贝对象是一个常见的需求,尤其是当你需要修改一个对象但又不想影响原始对象时。Python提供了几种拷贝对象的方法,其中最重要的是浅拷贝(sh…...
STM32项目分享:智能宠物喂食系统
目录 一、前言 二、项目简介 1.功能详解 2.主要器件 三、原理图设计 四、PCB硬件设计 1.PCB图 五、程序设计 六、实验效果 七、资料内容 项目分享 一、前言 项目成品图片: 哔哩哔哩视频链接: https://www.bilibili.com/video/BV1zy411z7…...
数据结构——栈的实现(java实现)与相应的oj题
文章目录 一 栈栈的概念:栈的实现:栈的数组实现默认构造方法压栈获取栈元素的个数出栈获取栈顶元素判断当前栈是否为空 java提供的Stack类Stack实现的接口: LinkedList也可以当Stack使用虚拟机栈,栈帧,栈的三个概念 二 栈的一些算…...
linux修改时区为CST
目录 第一步: 第二步: 第三步: 第一步: 备份原来的时区信息 [rootlocalhost ~]# mv /etc/localtime localtime.bak 第二步: 通过软链接将亚洲/上海 的时区信息 指导时区信息 [rootlocalhost ~]# ln -s /usr/share…...
【Spring Security】初识Spring Security
今天晚上因为一个项目问题,而正式开始学习Spring Security。 这个问题是“APP端的操作员应仅可查看管理后台的项目负责人分配给自己的计划”。 一、Spring Security的核心组件: Spring Security的核心组件包括:SecurityContextHolder、Auth…...
配置单区域OSPF
目录 引言 一、搭建基础网络 1.1 配置网络拓扑图如下 1.2 IP地址表 二、测试每个网段都能单独连通 2.1 PC0 ping通Router1所有接口 2.2 PC1 ping通Router1所有接口 2.3 PC2 ping通Router2所有接口 2.4 PC3 ping通Router2所有接口 2.5 PC4 ping通Router3所有接口 2.…...
SQL中的游标是什么?
在 SQL 中,游标(Cursor)是一种用于遍历结果集的数据库对象。它允许开发者在 SQL 查询的结果集中逐行或逐批处理数据。 具体来说,SQL 中的游标通常用于以下目的: 遍历结果集:当一个 SQL 查询返回多行结果时…...
7. LangChain4j如何使用统一api调用?
前言 当我们对接LangChain4j的时候,面对复杂的各种各样的大模型的api的对接,让很多开发者感到力不从心。在每个大模型的api都不一样的时候?该如何快捷的切换模型的使用呢? 这时,One-API应运而生,它以其简洁…...
RPM、YUM 安装 xtrabackup 8 (mysql 热备系列一)包含rpm安装 mysql 8 配置主从
RPM安装 percona-xtrabackup-80-8.0.35-30.1.el7.x86_64.rpm 官网: https://www.percona.com/ 下载地址: https://www.percona.com/downloads wget https://downloads.percona.com/downloads/percona-distribution-mysql-ps/percona-distribution-mysq…...
maven项目打成可运行的jar及pom中的依赖一同打包
maven项目打jar及pom中的依赖一同打包 最近开发中有个需求,不部署新的服务,只jar包执行 那maven项目中,代码如何以jar的方式运行、如何把代码打成jar、pom中的依赖如何与代码一同打到jar包中? 1、代码如何以jar的方式运行&…...
Gettler‘s Screep World 笔记 Ⅰ
夏促时候刚刚入坑,写个笔记叭~ 环境配置 参考 HoPGoldy 大佬的简书,先配置下开发环境 萌新去看大佬的详细教程,我这里比较简单,有前端基础的可以直接抄 VSCode 跳过 node 我配的是v18.18.2 换源 npm config set registry h…...
联合体(union)的定义以及如何与结构体(struct)不同
联合体(Union)是一种特殊的数据类型,它允许在相同的内存位置存储不同的数据类型。但是,在任何给定的时间点,联合体只能存储其中的一个值;这意味着联合体的大小是其最大成员的大小,因为它必须足够…...
【Spark官方文档部分翻译】RDD编程指南(RDD Programming Guide)
写在前面 内容如何选择 本翻译只翻译本人认为精华的部分,本人认为的Spark的一些核心理念,编程思想。一些特别基础的操作包括但不限于搭建环境就不在此赘述了。 配套版本 本系列基于Spark 3.3.1,Scala 2.12.10,进行翻译总结 原…...
前端八股文 $set
为什么会有$set vue2中对数组中新增的属性是监听不到的 如图 vue 插件中有但是 视图中没有刷新 解决方法 解决就是 $set() 就是在数组中新增属性的时候可以重新渲染视图 具体的写法 写法 就是 第一个 是在那个对象上新增 第二个参数 是新增的属性 第三个参数是 新增的属性…...
Connecting weaviate with langflow across docker containers
题意:在Docker容器之间连接Weaviate与Langflow 问题背景: I am trying to build a local RAG application using Langflow. For my vectore store, I want to use a local Weaviate instance, hosted in a separate docker container on the same netwo…...
【linux vim使用说明】
基本概念 提示:本文是网络资源整理 模式: vim 有多种模式,每种模式都有不同的功能。 普通模式 (Normal Mode): 默认模式,用于导航和执行命令。插入模式 (Insert Mode): 用于文本输入。可以通过按 i 进入。可视模式 (Visual Mode): 用于选择…...
cocos2d-x安装和项目
首先多方查找资料发现教程很简洁,发现对自己的操作方面没多大帮助,后来干脆去官网,好像也很简洁。基于这样一个原因,加上我首次碰cocos2d-x,决定记录一下整个流程,解决实际操作上的疑惑。 涉及的方面&…...
因果推断 | 双重机器学习(DML)算法原理和实例应用
文章目录 1 引言2 DML算法原理2.1 问题阐述2.2 DML算法 3 DML代码实现3.1 策略变量为0/1变量3.2 策略变量为连续变量 4 总结5 相关阅读 1 引言 小伙伴们,好久不见呀。 距离上次更新已经过去了一个半月,上次发文章时还信誓旦旦地表达自己后续目标是3周更…...
Flutter 开源库学习
网上看了好多歌词实现逻辑相关资料,封装比较的好的 就 flutter_lyric,核心类是LyricsReader,而且如果实现逐字逐句歌词编辑功能还需要自己实现很多细节 ,网友原话是 :歌词的功能真的是不少,写起来也是挺难的…...
自主巡航,目标射击
中国机器人及人工智能大赛 参赛经验: 自主巡航赛道 【机器人和人工智能——自主巡航赛项】动手实践篇-CSDN博客 主要逻辑代码 #!/usr/bin/env python #coding: utf-8import rospy from geometry_msgs.msg import Point import threading import actionlib impor…...
MySQL中EXPLAIN关键字详解
昨天领导突然问到,MySQL中explain获取到的type字段中index和ref的区别是什么。 这两种状态都是在使用索引后产生的,但具体区别却了解不多,只知道ref相比于index效率更高。 因此,本文较为详细地记录了MySQL性能中返回字段的含义、状…...
如何理解ref toRef和toRefs
是什么 ref 生成值类型的响应式数据可用于模板和reactive通过.value修改值 ref也可以像vue2中的ref那样使用 toRef 针对一个响应式对象(reactive)的prop创建一个ref两者保持引用关系 toRefs 将响应式对象(reactive封装)转换…...
【linux】kernel-trace
文章目录 linux kernel trace配置trace内核配置trace接口使用通用配置Events配置Function配置Function graph配置Stack trace设置 跟踪器tracer功能描述 使用示例1.irqsoff2.preemptoff3.preemptirqsoff linux kernel trace 配置 源码路径: kernel/trace trace内…...
网站关键词重要性/百度联盟注册
1234567891011121314151617package practiceGO; /**5、算水仙花数(100-999):表示三位数的数字,个位的三次方十位的三次方百位的三次方这个数本身 */ public class Cto { public static void main(String[] args) { int first,sec…...
wordpress 电脑微信/建个网站需要多少钱
Application 函数 Application.LoaddLevel(“名称或者索引”); //加载场景,5.6.0以上过时,修改方法 Application.OpenURL("www.baidu.com"); //运行时自动打开网站 Application.CaptureScreenshot("1.jpg"); //保存图片在项目根目…...
建设交易网站多少钱/seo推广优化方案
步骤 配置路由规则,使用children配置项: routes:[{path:/about,component:About,},{path:/home,component:Home,children:[ //通过children配置子级路由{path:news, //此处一定不要写:/newscomponent:News},{path:message,//此处一定不要写…...
asp爆网站绝对路径/seo岗位工作内容
背景 大家都知道,我们在通过 Selenium 执行 Web 自动化测试时,每次都需要启动/关闭浏览器,如果是多线程执行还会同时打开多个,比较影响工作的正常进行。那有没有办法可以不用让浏览器的自动化执行干扰我们的工作呢? 7…...
校园二手网站设计论文/博客优化网站seo怎么写
题库来源:安全生产模拟考试一点通公众号小程序 2022年安全员-B证培训试题系安全员-B证国家题库仿真模拟预测!2022安全员-B证考试模拟100题模拟考试平台操作依据安全员-B证新版考试题库。安全员-B证试卷通过安全生产模拟考试一点通上题库学习。 1、【多选…...
后台企业网站模板/免费创建网站软件
AI在现实中的应用有很多,你有没有想过,它还可以进行文本纠错呢?传统的校对既耗时又枯燥,通过“AI纠错”,不仅能更快完成,还能提高准确度。那么AI“文本纠错”背后的原理是什么呢?和我一起看看吧…...