【iOS】对象的本质探索

news2024/12/1 0:25:56

OC对象的底层结构

    • 问题:一个NSObject对象在内存中是如何布局的?
    • NSObject的内存布局
      • 1 通过 lldb命令 窥探NSObject内存布局
      • 2 通过 View Memory 窥探NSObject内存布局
      • 3 通过 底层函数API 窥探NSObject内存布局
      • 总结
    • 通过继承关系进一步了解NSObject
      • 1 运行项目,通过系统函数打印一下:
      • 2 打断点,通过ViewMemory查看一下内存
    • 几种OC对象
      • Class对象
        • 1 每个类 在内存中 有且只有一个 Class对象
        • 2 .Class对象在内存中存储的信息
        • 3 meta-class
      • 总结
    • isa和superclass
      • isa
      • superclass指针

生成C++文件的一些命令:
clang -rewrite-objc main.m -o main.cpp:(无法区分平台 不建议使用)
xcrun -sdk iphonesimulator clang -rewrite-objc main.m -o main.cpp:(模拟器)
xcrun -sdk iphoneos clang -rewrite-objc main.m -o main.cpp:(真机)
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc 源文件名 -o 输出的cpp文件名:(arm64架构 最常用)

当把生成的C++文件放到Xcode之后,当然是不能直接运行了,这时候点击左侧栏的项目,然后再在右边的Build Phases里找到Compile Sources把不需要运行的C++源代码文件删掉,编译时就会自动忽略咯。

在这里插入图片描述

问题:一个NSObject对象在内存中是如何布局的?

NSObject *objc = [[NSObject alloc] init];

底层实现:

NSObject的底层是一个结构体类型,内部有一个Class类型的成员名叫isa:

//impl就是implementaion实现
struct NSObject_IMPL {
	Class isa;
};

那么isa是什么?
右键点击jump to definition,可以找到:

typedef struct objc_class *Class;

那么我们可以知道,isa是一个指针,那么在64位下占8位内存,32位机下就占4位内存。

我们可以通过.h文件看到,NSObject内部确实只有一个Class类型的isa成员,其余皆是一些方法。

在这里插入图片描述

若我们了解内存分配就知道,我们探索一个类的内部结构的内存布局情况,关注其成员对象即可,不需要关注其方法(方法存储在公共内存位置,提供给所有的实例对象使用、类对象使用)。
因此,我们进一步去看看Class是什么东西即可:

在这里插入图片描述

我们查看源码得知,Class本质是一个 objc_class 类型的结构体指针

我们进一步去看一下 objc_class这个结构体:

在这里插入图片描述

我们发现objc_class结构体继承自objc_object结构体,且objc_class内部有若干成员如下(忽略其函数、方法):

struct objc_class : objc_object {

    Class superclass;

    const char *name;

    uint32_t version;

    uint32_t info;

    uint32_t instance_size;

    struct old_ivar_list *ivars;

    struct old_method_list **methodLists;

    Cache cache;

    struct old_protocol_list *protocols;

    // CLS_EXT only

    const uint8_t *ivar_layout;

    struct old_class_ext *ext;
    ....    
}

我们跳进去查看 objc_object发现,其本身也只有一个 isa_t类型的成员

struct objc_object {
private:
	isa_t isa;

最终 isa_t类型的成员是一个联合体:

在这里插入图片描述

通过前面的介绍,我们可以将NSObject的定义简写为:

目前官方暴露的头文件中的格式:
@interface NSObject <NSObject> { 
    Class isa   ; 
}
@end

简写格式:

@interface NSObject <NSObject> { 
    objc_class isa   ; 
}
@end

结构体objc_class的实现:

struct objc_class  { 
    private: 
    isa_t isa; 
    public:
    Class superclass;

    const char *name;

    uint32_t version;

    uint32_t info;

    uint32_t instance_size;

    struct old_ivar_list *ivars;

    struct old_method_list **methodLists;

    Cache cache;

    struct old_protocol_list *protocols;

    // CLS_EXT only

    const uint8_t *ivar_layout;

    struct old_class_ext *ext;
    
    ....    
}

OC对象的本质

  • Objective-C的对象、类主要是基于C\C++的结构体实现的
  • 凡是继承自NSObject的对象,都会自带一个类型是Classisa的成员变量
  • 将其转成C++,就可以看到NSObject本质上是一个叫做NSObject_IMPL的结构体
  • 其成员变量isa本质上也是一个指向objc_class结构体的指针(objc_class继承自objc_object结构体,内部有一个isa成员)

在这里插入图片描述

NSObject的内存布局

1 通过 lldb命令 窥探NSObject内存布局

  1. 添加断点
  2. 打印内存地址: 通过 po 命令打印出 对象的内存地址
  3. 打印内存布局: 通过 memory read + 内存地址值 命令打印出 对象的内存布局情况:

在这里插入图片描述

我们从打印结果中可以得出结论:一个NSObject对象,系统给其分配了16个字节

2 通过 View Memory 窥探NSObject内存布局

在这里插入图片描述

  • 从截图上我们可以看到,地址101323a20101323a4F之间差48,刚好显示差了一行
  • 101323a20的整个存储空间为绿色框框出来的一部分,蓝色框为101323a30开始的部分了
  • 从内存布局中我们看到,NSObject中有十六个字节,但是只用了八个字节来存储内容。与前面的方式是得到的结论是相符合的。

3 通过 底层函数API 窥探NSObject内存布局

我们通过阅读苹果官方开源的源码,我们可以看到runtime中有一个函数:

/**  
 * Returns the size of instances of a class. 
 *  
 * **@param** cls A class object.

 * 

 * **@return** The size in bytes of instances of the class \e *cls,* or \c 0 if \e *cls* is \c Nil.

 */

OBJC_EXPORT size_t

class_getInstanceSize(Class _Nullable cls) 

    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

我们通过调用可以看到结果:

#import <objc/runtime.h>
class_getInstanceSize([NSObject class]);

在这里插入图片描述

结果是8。
为什么是8??跟我们前面得到的结论不一致!!!我们继续往下探索!!
我们知道,OC中创建对象分配内存是通过 alloc方法,我们直接去看官方开源的程序中alloc的实现即可(本质上最终alloc的实现就会调用allocWithZone:方法):

在这里插入图片描述

OBJC_SWIFT_UNAVAILABLE这段宏是限制Swift语言分配内存(因为目前存在OC+Swift混编的情况,这个不是本篇幅谈论的范畴,咱们只关注 纯OC 环境即可)

在这里插入图片描述

我们从NSObject.mm文件可以看到其实现调用的函数:

在这里插入图片描述

从图上我们清晰看见,最终其调用的是objc里面的 _objc_rootAllocWithZone函数:

我们通过全局搜索找到函数的内部实现:

在这里插入图片描述

在这里插入图片描述

我们可以看到语言逻辑,在其首次分配内存的时候,调用了函数: class_createInstance class_createInstance函数最终也是调用了 class_createInstanceFromZone函数

obj = class_createInstance(cls, 0);

在这里插入图片描述

在这里插入图片描述

从函数的实现中我们也看到了,CF框架要求:所有对象至少分配16个字节内存。这涉及到内存对齐的概念。

  • 系统底层是早已开辟了16个长度、32个长度、48个长度、64个长度、128个长度…(16的倍数)的内存块的
  • 当分配内存给对象时,是按照对象需要内存,能容纳其所需且最接近其的16的最小公倍数来分配内存块的
  • 结合前面探索,我们不难猜出这段代码得出的8个字节,是用来存储isa指针的,我们来验证一下:

在这里插入图片描述

如图所示,我们无法直接访问isa私有成员变量(苹果设计不可以直接访问),但是我们窥探过其开源代码,知道其数据结构,我们可以自己在外部写一个类似的结构体对象进行调试打印!且通过验证得出结论,我们的猜想是正确的!
那么我们可以得出结论:class_getInstanceSize这个runtime函数是用来获取,创建一个对象的实例,至少得给其分配多少内存的!(也就是其本身的数据结构需要多少内存)


  #import <objc/runtime.h>

  class_getInstanceSize([NSObject class]);

我们进一步去找一找源码,看看系统底层对内存对齐方面的处理:

在这里插入图片描述

我们可以看到红框框中框出来的部分,为分配内存的代码!在objc这份源码中已经看不到了,我们要重新去苹果的OpenSource去下载libmalloc这个开源文件进行探索: 我们查找到 _malloc_zone_calloc的实现:

在这里插入图片描述

从上图,我们不难得知,本质上还是调了calloc函数

其中zone->calloc传入的zone 就是 上一步中的 default_zone
这个关键代码的目的就是申请一个指针,并将指针地址返回 calloc这个函数的实现,苹果官方没有开源

在这里插入图片描述

但是我们在系统暴露的malloc.h头文件中找到了一个函数:

在这里插入图片描述

extern size_t malloc_size(const void *ptr);  /* Returns size of given ptr */

其解释是指,创建对象时,分配多少内存,并把分配的内存地址返回给指针ptr。我们试着用一下这个函数去获取一下实际分配的内存:

在这里插入图片描述

从打印结果我们可以看到 实际分配了16个字节,与前面的几种方式得到的结论一致!!

总结

创建一个实例对象,至少需要多少内存?
#import <objc/runtime.h>
class_getInstanceSize([NSObject class]);


创建一个实例对象,实际上分配了多少内存?
#import <malloc/malloc.h>
malloc_size((__bridge const void *)obj);

创建一个NSObject对象:

  • 至少需要8个字节的内存(用于存放isa指针)
  • 实际分配了16个字节的内存(因为系统开发者在设计的时候,规定了内存分配的规则)
  • OC对象的内存对齐参数为 16:
    若需要分配的内存不够16,则以给16个字节
    若对象需要分配的内存超过16,则以能容纳对象的数据结构为前提,以最接近其的16最小公倍数为最终分配大小进行分配
  • 一个OC对象在内存中的布局:
    系统会在堆中开辟一块内存空间存放该对象
    这块空间里还包含成员变量和isa指针
    然后栈里的 局部变量 指向这块存储空间 的地址

通过继承关系进一步了解NSObject

随手写两个类:

  • Car继承自NSObject
  • BBA_BMW继承自Car
// 
//  main.m 
//  窥探iOS底层原理 
// 
//  Created by VanZhang on 2022/5/6. 
//
 

#import <Foundation/Foundation.h> 
#import <objc/runtime.h> 
#import <malloc/malloc.h>

struct  { 
    Class isa; 
}NSObject_ISA;
 

 

@interface Car :NSObject{

@public

    int _year;//多少年了 4个字节

    int _kilometres;//多少公里数  4个字节

}//int 4+ int 4+  isa 8 = 24 ;需要24

//内存对齐参数为16 ;总共分配了能容纳其需要的16的最小公倍数:32

-(void)run;

@end
 

@implementation Car

- (instancetype)init{

    self = [super init];

    if (self) {

        _year = 1;

        _kilometres = 2;

    }

    return self;

}

- (void)run{

    NSLog(@"%s",__func__);

}

@end
 

@interface BBA_BMW :Car{

@public

    NSString*_nameplate;//汽车铭牌 8个字节

}//int 8+ double 8+  isa 8 + _nameplate 8= 32 ;需要32

//内存对齐参数为16 ;总共分配了能容纳其需要的16的最小公倍数:32

-(void)runFaster;

@end
 


@implementation BBA_BMW

- (void)runFaster{

   NSLog(@"%s",__func__);

}

@end
 

void testFunc(void){

    Car *c = [[Car alloc]init];

    c->_year = 18;

    c->_kilometres = 890123;

    NSLog(@"Car_class_getInstanceSize:%zd",class_getInstanceSize([c class]));

    NSLog(@"Car_size:%zd",malloc_size((__bridge const void *)(c)));

    BBA_BMW *bba = [[BBA_BMW alloc]init];

    bba->_nameplate = @"宝马七系";

    NSLog(@"BBA_BMW_class_getInstanceSize:%zd",class_getInstanceSize([bba class]));

    NSLog(@"BBA_BMW_size:%zd",malloc_size((__bridge const void *)(bba)));

    

}

int main(int argc, const char * argv[]) {

    @autoreleasepool {

        // insert code here...

        NSObject *obj = [[NSObject alloc]init];

        // NSLog(@"%@",obj);

        NSLog(@"class_getInstanceSize:%zd",class_getInstanceSize([obj class]));

       // NSLog(@"isa:%zd",sizeof(obj->isa));

        NSLog(@"isa:%zd",sizeof(NSObject_ISA));

        NSLog(@"malloc_size:%zd",malloc_size((__bridge const void *)(obj)));

        testFunc();
    }
    return 0;
}

1 运行项目,通过系统函数打印一下:

在这里插入图片描述

在这里插入图片描述

2 打断点,通过ViewMemory查看一下内存

car:

  • 我们前面将_year设置为18,在16进制中,0x12=十进制的18

  • 实际分配了32,真正用到了24

在这里插入图片描述

bba:

  • 我们前面将_year默认设置为1,将 _kilometres改为int类型 默认设置为 2

  • 内存分布情况如下:

在这里插入图片描述

几种OC对象

OC对象主要分为三种:

  • instance对象(实例对象)
  • class对象(类对象)
  • meta-class对象(元类对象) 并且,我们要探索 类对象(Class) 的补充和扩展的语法:Category(分类)

通过OC语言编写的程序中,instance对象就是通过类alloc出来的对象,每次调用alloc都会产生新的instance对象。

在上文中,通过讨论 NSObject对象的底层实现、内存布局、通过继承关系了解 子类、孙子类的 内存布局…就是探索instance对象 的底层过程的开端。

Class对象

Class对象其实是一个指向objc_class结构体的指针。因此我们可以说类对象或元类对象在内存中其实就是objc_class结构体。

1 每个类 在内存中 有且只有一个 Class对象

我们在探索NSObject对象的时候,看到了底层代码:

    struct objc_class  { 
        private: 
        isa_t isa; 
        public:
        Class superclass;

        const char *name;

        uint32_t version;

        uint32_t info;

        uint32_t instance_size;

        struct old_ivar_list *ivars;

        struct old_method_list **methodLists;

        Cache cache;

        struct old_protocol_list *protocols;

        // CLS_EXT only

        const uint8_t *ivar_layout;

        struct old_class_ext *ext;

        ....    
    }

结合 NSObject.h文件:

@interface NSObject <NSObject> {

#pragma clang diagnostic push

#pragma clang diagnostic ignored "-Wobjc-interface-ivars"

    Class isa  OBJC_ISA_AVAILABILITY;

#pragma clang diagnostic pop
...
}

从前面两段代码,我们不难得知基类NSObject中有一个Class类型的对象:isa指针
我们把前面创建了四个类,且有继承关系的代码,通过clang指令转换成c++:

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

结合前面的继承关系,和转换成c++底层代码之后的具体情况(如上图),我们不难得出一个结论:

每个类在内存中有且只有一个class对象

还可以通过打印内存地址证明:

	Class objectClass1 = [object1 class];
    Class objectClass2 = [object2 class];
    Class objectClass3 = object_getClass(object1);
  	Class objectClass4 = object_getClass(object2);
	Class objectClass5 = [NSObject class];
  // 通过打印可以看出,上面几个方法返回的都是同一个类对象,内存地址都一样
    NSLog(@"class - %p %p %p %p %p %d",objectClass1,objectClass2,objectClass3,objectClass4,objectClass5;

在这里插入图片描述

我们可以通过NSObject的类方法实例方法 获得 类对象:

在这里插入图片描述

  • (Class)class
  • (Class)class

我们还可以通过 Runtime的API获得 类对象:

Class _Nullable object_getClass(id _Nullable obj)

该API效力等同于+ (Class)class、- (Class)class

在这里插入图片描述

注意: class方法返回的一直是类对象,所以哪怕这样写还是会返回类对象

Class objectMetaClass2 = [[[NSObject class] class] class];

2 .Class对象在内存中存储的信息

Class底层代码如下:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

    struct objc_class  { 
        private: 
        isa_t isa; 
        public:
        Class superclass;

        const char *name;

        uint32_t version;

        uint32_t info;

        uint32_t instance_size;

        struct old_ivar_list *ivars;

        struct old_method_list **methodLists;

        Cache cache;

        struct old_protocol_list *protocols;

        // CLS_EXT only

        const uint8_t *ivar_layout;

        //struct old_class_ext *ext;
        uint32_t size;

        const uint8_t *weak_ivar_layout;

        struct old_property_list **propertyLists;
        ....    
    }

查看源码之后,我们不难得出结论,class对象在内存中存储的信息:

  1. isa指针
  2. superclass指针
  3. 类的属性信息(@property)、类的对象方法信息(instance method

我们通过runtimeAPI遍历一下methodLists内部的信息,得知:struct old_method_list **methodLists内部存储的都是 类的对象方法信息(instance method

在这里插入图片描述

类的协议信息(protocol)、类的成员变量信息(ivar

在这里插入图片描述

3 meta-class

meta-class对象和class对象的内存结构是一样的,但是用途不一样,在内存中存储的信息主要包括:

  • isa指针
  • superclass指针
  • 类的类方法信息(class method

  • 在这里插入图片描述

总结

对instance对象和Class对象的总结:

isa指针: 每个类 在内存中 有且只有一个 Class对象,isa指针
class对象在内存中存储的信息:

  • isa指针
  • superclass指针
  • 类的属性信息(@property)、类的对象方法信息(instance method)
  • 类的协议信息(protocol)、类的成员变量信息(ivar)

成员变量的值 存储在实例对象中: 是存储在实例对象中的,因为只有当我们创建实例对象的时候才为成员变赋值
成员对象的类型、值、名称,存储在在class对象中: 但是 成员变量叫什么名字,是什么类型,只需要有一份就可以了。所以存储在class对象中

在这里插入图片描述

isa和superclass

isa

通过前面篇幅的介绍,我们得知:每个类的实例对象、类对象、元类对象都有一个isa指针

void test11(void){

    Class animalMetaCls =  getMetaClassFromClass([BBA_BMW_RunFaster class]);

    NSLog(@"superclass:%@",[BBA_BMW_RunFaster superclass]);

    NSLog(@"superclass-superclass:%@",[[BBA_BMW_RunFaster superclass] superclass]);

    NSLog(@"superclass-superclass-superclass:%@",[[[BBA_BMW_RunFaster superclass] superclass] superclass]);

    NSLog(@"superclass-superclass-superclass-superclass:%@",[[[[BBA_BMW_RunFaster superclass] superclass] superclass] superclass]);

    NSLog(@"superclass-superclass-superclass-superclass-superclass:%@",[[[[[BBA_BMW_RunFaster superclass] superclass] superclass] superclass] superclass]);

    NSLog(@"====================");

    NSLog(@"metaClass:%@",animalMetaCls);

    NSLog(@"metaClass-superclass:%@======metaClass-superclass_class_isMetaClass:%d",[animalMetaCls superclass],class_isMetaClass([animalMetaCls superclass]));

    NSLog(@"metaClass-superclass-superclass:%@======metaClass-superclass-superclass_class_isMetaClass:%d",[[animalMetaCls superclass]superclass],class_isMetaClass([[animalMetaCls superclass]superclass]));

    NSLog(@"====================");

    NSLog(@"metaClass-superclass-superclass-superclass:%@======metaClass-superclass-superclass-superclass_class_isMetaClass:%d",[[[animalMetaCls superclass]superclass]superclass],class_isMetaClass([[[animalMetaCls superclass]superclass]superclass]));

    NSLog(@"metaClass-superclass-superclass-superclass-superclass:%@======metaClass-superclass-superclasss-superclass-superclass_class_isMetaClass:%d",[[[[animalMetaCls superclass]superclass]superclass]superclass],class_isMetaClass([[[[animalMetaCls superclass]superclass]superclass]superclass]));

    NSLog(@"metaClass-superclass-superclass-superclass-superclass-superclass:%@======metaClass-superclass-superclass-superclasss-superclass-superclass_class_isMetaClass:%d",[[[[[animalMetaCls superclass]superclass]superclass]superclass] superclass],class_isMetaClass([[[[[animalMetaCls superclass]superclass]superclass]superclass] superclass]));

    NSLog(@"metaClass-superclass-superclass-superclass-superclass-superclass-superclass:%@======metaClass-superclass-superclass--superclass-superclasss-superclass-superclass_class_isMetaClass:%d",[[[[[[animalMetaCls superclass]superclass]superclass]superclass] superclass] superclass],class_isMetaClass([[[[[[animalMetaCls superclass]superclass]superclass]superclass] superclass] superclass]));
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        test11();
    }
    return 0;
}

在这里插入图片描述

  • instance的isa指向class
    当调用对象方法时,通过instance的isa找到class,最后找到对象方法的实现进行调用
  • class的isa指向meta-class
    当调用类方法时,通过class的isa找到meta-class,最后找到类方法的实现进行调用
  • meta-class的isa指向基类的meta-class
  • 基类的isa指向自己

superclass指针

通过前面篇幅的介绍,我们得知:每个类的类对象、元类对象都有一个superclass指针

  • classsuperclass指针指向父类的class
    如果没有父类,superclass指针为nil
  • meta-classsuperclass指向父类的meta-class
    基类的meta-classsuperclass指向基类的class

在这里插入图片描述

在这里插入图片描述

参考博客:
07-探究iOS底层原理|几种OC对象【实例对象、类对象、元类】、对象的isa指针、superclass、对象的方法调用、Class的底层本质

06-探究iOS底层原理|OC对象的本质【底层实现、内存布局、继承关系】

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/768461.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【SwitchyOmega】SwitchyOmega 安装及使用

文章目录 安装教程使用教程 安装教程 SwitchyOmega 谷歌商店下载链接&#xff1a;https://chrome.google.com/webstore/detail/proxy-switchyomega/padekgcemlokbadohgkifijomclgjgif?hlen-US 在谷歌商店搜索 SwitchyOmega&#xff0c; 选择 Proxy SwitchyOmega 点击 Add t…

刷题记录-1蓝桥公园

蓝桥杯刷题记录 记录蓝桥杯刷题每一天 python解题 import sysn, m, q map(int, input().split()) dp [[sys.maxsize]*(n1) for _ in range(n1)]for _ in range(m):u, v, w map(int, input().split())dp[u][v] dp[v][u] min(dp[u][v], w)for k in range(1, n1):for i in …

常见的网络攻击

​ 1.僵木蠕毒 攻击业内习惯把僵尸网络、木马、蠕虫、感染型病毒合称为僵木蠕毒。从攻击路径来看&#xff0c;蠕虫和感染型病毒通过自身的能力进行主动传播&#xff0c;木马则需要渠道来进行投放&#xff0c;而由后门木马&#xff08;部分具备蠕虫或感染传播能力&#xff09;构…

我在VScode学Java类与对象(Java构造方法 、JavaBean)第二辑 + VScode怎么在预览模式中点击另外一个文件,不会被替换掉

我的个人博客主页&#xff1a;如果’真能转义1️⃣说1️⃣的博客主页 关于Java基本语法学习---->可以参考我的这篇博客&#xff1a;《我在VScode学Java》 关于Java数组学习、JVM中的堆和栈—>可以参考我的这篇文章我在VScode学Java(Java一维数组、二维数组、JVM中的堆和栈…

VSCode种git rebase分支冲突解决无法继续rebase

情景&#xff1a; 常规来说我们git开分支开发完新功能之后&#xff0c;提交之前rebase dev分支&#xff0c;然后合并到dev上算是开发完成。 问题还原&#xff1a; 在开发完之后执行如下指令&#xff1a; 1.执行变基操作&#xff1a;git rebase dev。 //这一步出现冲突vscode上…

SQL中为何时常见到 where 1=1?

你是否曾在 SELECT 查询中看到过 WHERE 11 条件。我在许多不同的查询和许多 SQL 引擎中都有看过。这条件显然意味着 WHERE TRUE&#xff0c;所以它只是返回与没有 WHERE 子句时相同的查询结果。此外&#xff0c;由于查询优化器几乎肯定会删除它&#xff0c;因此对查询执行时间没…

AtcoderABC250场

A - Adjacent SquaresA - Adjacent Squares 题目大意 给定一个由H行W列组成的网格。令(i,j)表示从上到下第i行&#xff0c;从左到右第j列的方块。找出与方块(R,C)共享边的方块数量。 这里&#xff0c;方块(a,b)和方块(c,d)被称为共享边&#xff0c;当且仅当|a-c||b-d|1。 思路…

网站测试自动化系统

首先先分解一下执行测试用例的步骤&#xff0c;编码实现每一个步骤&#xff0c;然后使用批处理的形式将工作流串起来&#xff1a;&#xff08;当然啦&#xff0c;我们也可以使用.NET里面的Workflow来实现&#xff0c;只不过那样的话我们需要格外添加一个命令—安装.NET Framewo…

数据排布与跨距对齐

1 数据排布 1.1 数据排布的概念 在深度学习框架中&#xff0c;特征图通常以四维数组的形式呈现&#xff0c;这四个维度分别是&#xff1a;批量大小N&#xff0c;特征图通道数C&#xff0c;特征图高度H&#xff0c;特征图宽度W。数据排布&#xff08;Layout&#xff09;指的就…

数学建模的赛题类型

一、预测类 指通过分析已有的数据或者现象&#xff0c;找出其内在发展规律&#xff0c;然后对未来情形做出预测的过程。 根据已知条件和求解目的&#xff0c;往往将预测类问题分为&#xff1a;小样本内部预测&#xff0c;大样本内部预测。 解决预测类赛题的一般步骤&#xff…

Minio桶复制(Bucket Replication)入门

文章目录 前言一、部署下载用户组添加 minio.service 文件新建配置文件 minio启动 二、安装 mc三、桶复制1.权限建立 Replication Admin 用户建立 Replication Remote User 用户 2.建立桶3建立桶复制4.验证 总结遇到问题 前言 桶复制&#xff1a; 可以理解像 mysql 主从备份&a…

基于linux下的高并发服务器开发(第二章)- 2.10 进程间通信简介

面试官经常问&#xff1a; ① 你知道进程间通信的方式有哪几种吗&#xff1f; ② 进程间通信当中某一个方式一个具体的原理是什么&#xff1f;怎么去实现呢&#xff1f; 01 / 进程间通讯概念 02 / Linux进程间通信的方式 怎样理解阻塞非阻塞与同步异步的区别&#xff1f; 怎样…

SpringCloud Gateway网关

文章目录 SpringCloud Gateway1.1 网关架构1.2微服务网关介绍1.3Spring Cloud Gateway(技术选型)1.4依赖1.5yaml配置(包含gateway相关配置,实现转发的功能)1.6断言案例:1.7断言详细介绍1.8 整合nacos1.9 nacos整合网关案例1.10动态路由 SpringCloud Gateway 1.1 网关架构 (dub…

专题-【哈夫曼树】

14年三-1&#xff09; 已知电文信息为“PEFFQ RQRFE QFPQR FPER” &#xff08;1&#xff09;请按此信息构造哈夫曼树&#xff0c;求出每个字符的最优编码&#xff1b; &#xff08;2&#xff09;若对每个字符进行等长编码&#xff0c;至少需要几位二进制数&#xff1f;哈夫…

小研究 - 面向 Java 的高对抗内存型 Webshell 检测技术(二)

由于 Web 应用程序的复杂性和重要性, 导致其成为网络攻击的主要目标之一。攻击者在入侵一个网站后, 通常会植入一个 Webshell, 来持久化控制网站。但随着攻防双方的博弈, 各种检测技术、终端安全产品被广泛应用, 使得传统的以文件形式驻留的 Webshell 越来越容易被检测到, 内存…

# Linux终端控制字符详解以及简单应用实践

Linux终端控制字符详解以及简单应用实践 文章目录 Linux终端控制字符详解以及简单应用实践1 控制字符表2 控制字符 ESC &#xff08;0x1B&#xff0c;^[&#xff09;子参数表3 控制字符 ESC &#xff08;0x1B&#xff0c;^[&#xff09;子参数表 - 字符颜色参照表4 实践&#x…

Windows 如何锁定文件

一、背景 如果应用程序有操作本地文件的功能&#xff08;如&#xff1a;读、写、复制、移动、删除等等&#xff09;&#xff0c;那么在测试或调试该应用程序时&#xff0c;肯定需要测试文件被其他应用程序锁定时&#xff0c;你的应用程序是如何处理的。 那么如何在本地模拟文件…

抓包工具Fiddler的下载安装使用

文章目录 Fiddler1. 安装与使用教程2. 抓包工具的原理 Fiddler 1. 安装与使用教程 下载地址: https://www.telerik.com/fiddler/ 安装过程只用一路next即可&#xff1b; 下图是我们安装好Fiddler打开的界面&#xff1a; 右侧显示就是我们主机发送http/https请求的记录。如果…

黑马头条 分布式任务调度 定时计算热点文章、xxl-job、kafkaStream

xxl-Job分布式任务调度 1 今日内容 1.1 需求分析 目前实现的思路&#xff1a;从数据库直接按照发布时间倒序查询 问题1&#xff1a; 如何访问量较大&#xff0c;直接查询数据库&#xff0c;压力较大问题2&#xff1a; 新发布的文章会展示在前面&#xff0c;并不是热点文章 1.2 …

Flutter——最详细(NavigationRail)使用教程

NavigationRail 简介 一个 Material Design 小部件&#xff0c;旨在显示在应用程序的左侧或右侧&#xff0c;以便在少量视图&#xff08;通常在三到五个视图之间&#xff09;之间导航。 使用场景&#xff1a; 通过Row属性&#xff0c;左侧或右侧菜单栏按钮 属性作用onDestinati…