iOS ------ Block的总结

前面看了Block的基本知识,和一些源码。但对于block怎么用的还不了解,代码中出现block会看不懂,现在来具体看一下Block的用法并做个总结。

1.Block是什么

block对象是一个C语言结构体,可以并入C和OC的代码中,Block本质是一个匿名函数,以及与该函数一起使用的数据,其他语言有时称为闭包或ambda。Block特别适合于回调,或者是在你为了让代码看起来具有更清晰的逻辑进行代码的组合时使用。

上面是苹果官方的解释,告诉我们:首先Block是一个OC中的对象,并且这个对象是一个C语言的结构体,它可以使用在C语言和OC中;同时,Block本质是一个匿名函数和其包含的数据集中

2.为什么用Block

苹果官方的文档的描述

  1. 代替代理和委托方法
  2. 作为回调函数的代替
  3. 一次性编写多次处理的任务
  4. 方便对集合中的所有项执行任务
  5. 与GCD队列一起执行异步任务

以上这些情况,我们都能够使用Block。我感受最深刻的是使用Block回调,很多情况下,我们可能只需要对某个事件进行一个简单的回调,也许就仅仅一次,如果我们使用代理的话,我们需要创建类,编写协议,仅仅对一个小地方的回调成本很高,那么Block的使用就恰到好处。除此之外,Block的特性还可以让代理集中在某处,我们只需要在一个地方就可以完成回调之前和回调时的代码,相比,使用回调函数和代理都没有这个优势。另外,我门可以想到,OC中封装了一些集合分方法,比如数组的排序,这里就使用Block进行回调操作的。

简单讲一下回调的概念

将一段代码和一个特定的事件联系在一起,当特定事件发生后,这段代码才会被执行。
OC的几种回调

  • Targe-action回调
  • delegate方式
  • NOtification方式
  • block方式

OC的的回调几种方式

3.怎么使用Block

我们先创建一个Block,并简单的使用
在这里插入图片描述

运行结果
在这里插入图片描述
这里是为了强调block的回调效果。可以发现,尽管block的代码早就声明了,但是没用立即调用,而是在block的调用的时候,才被执行。到这里应该对Block的回调有一定的理解。

这好像并没有什么用处,下面看一看另外两种Block的使用。前面说block是OC的一个对象,既然是对象,我们可以把它当做一个类分属性,应该也可以更其他属性一样,被当作一个方法的参数吧。这也是block被大家认可的地方。

假设我们有这样一个类:包含一个block属性testBlock,包含一个调用自己的block属性的方法blockDo。

@interface Computer : NSObject
@property (nonatomic, copy) NSString* (^testBlock)(NSString*);//将block作为computer的属性
- (void)blockDo;
@end

#import "Computer.h"
@implementation Computer
- (void)blockDo {
    NSString* testString = @"textData_old";
    if (self.testBlock) {
        NSLog(@"%@", self.testBlock(testString));//调用并打印
    }
}
@end

在其他地方写下这些代码

Computer* computer = [[Computer alloc] init];
        computer.testBlock =  ^(NSString* parStr){
            NSLog(@"%@",parStr);
            parStr = @"testData_New";
            return parStr;
        };
        [computer blockDo]; //执行Block

运行结果
在这里插入图片描述

这里的调用就比前面的复杂了。因为我给Computer添加了一个方法,并且将block的调用交给了Computer,我只是实现了block而已,最后启动调用它的方法。我们在另一个地方对Computer类模拟了一个方法(也就是块,这个方法没有在Computer类中实现,我们甚至可以在任何地方实现它,最后我们可以在其他地方调用。这就是Block的神奇的地方。

再来看一个

@interface Computer2 : NSObject
- (void)doSomthingFeedBakck:(NSString* (^)(NSString*))handle;
@end
#import "Computer2.h"

@implementation Computer2
-(void)doSomthingFeedBakck:(NSString * _Nonnull (^)(NSString * _Nonnull))handle {
    NSString* handleStr = @"Old";
    sleep(3.0);
    NSLog(@"%@", handle(handleStr));
}
@end

其他地方调用

Computer2* computer2 = [[Computer2 alloc] init];
        [computer2 doSomthingFeedBakck:^NSString * _Nonnull(NSString * parStr) {
            NSLog(@"%@", parStr);
            NSString* returnstr = [NSString stringWithFormat:@"add %@", parStr];
            return returnstr;
        }];

运行结果
在这里插入图片描述

这里讲Block作为一个参数放在doSomthingFeedBakck函数面。体现了block的对象本质,相比之下,代码很是简洁。这种实现回调的方法逻辑更加清晰,明朗。

上面三个例子,展示了block的三种不同的使用方式。它们分别是:

  • 将block定义成员变量
  • 将block定义成属性
  • 将block作为参数

4.总结

通过上面的block的额用法发现,block每次回调是通过它的匿名函数进行的,也就是每次最多执行一个回调,在需要进行大批量的回调的时候,就需要写很多不同的block回调,这样就不合适这时使用协议和代理的方式就自然多了。除此之外,block还比较适合线程之间的切换回调,GCD就是采用了多线程结合block来做的。

5.下面我们记录一些block的原理性知识

  • 为什么说block是一个结构体,也是一个对象,同时还是携带数据的匿名函数
  • 全局block,栈block,以及堆区block的区别和他们之间的联系,探究block的内存管理
  • 为什么使用_block就可以使block可以修改外部变量
  • 引起强引用的原因是什么,我们解决的方法和原理是什么

1,为什么说block是一个结构体,也是一个对象,同时还是携带数据的匿名函数

使用Mac终端,cd将图中的第一个文件拖入

在这里插入图片描述

文件写以下内容

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSLog(@"Hello, World!");
        void (^block)(void) = ^ {
            printf("a block");
        };
        block();
    }
    return 0;
}

查看clang中间文件

clang -rewrite-objc main.m

在这里插入图片描述

回车会在刚拖入的文件中生成.cpp文件

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

//block 结构体
//这一部分在.cpp文件中没有找到
struct __block_impl {
  void *isa;//block 的类型
  int Flags;
  int Reserved;
  void *FuncPtr;// block的执行函数指针,指向__main_block_func_0
};
                        
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {

            printf("a block");
        }

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
    }
    return 0;
}

到这里,我们可以看到,在编译之前,block复原成以上四个结构体。 它们分别是:

  • __main_block_impl_0
  • __block_impl
  • __main_block_desc_0
  • __main_block_func_0

我们暂时不管这些结构体代表的都是什么。但可以说明block是结构体。仔细观察__main_block_impl_0 的机构中,有isa指针一项(黄色标出)。看到这里理解为什么苹果强调block也是一个对象了。再看还有 __main_block_func_0 ,这里其实就是我们对block函数体的实现,它实际上是一个匿名函数,作为block的众多结构体的一部分。

全局block,栈block,以及堆区block的区别和他们之间的联系,探究block的内存管理

在OC中用三种不同的block类型。它们分别是全局block _NSConcretionGlobalBlock, 栈block _NSConcretionStackBlock,以及堆block _NSConcretionMallocBlock.

全局block

假如我们这样创建一个block并使用
在这里插入图片描述

通过clang 命令获得中间编译内容

int GlobalInt = 0;

struct __getGlobalInt_block_impl_0 {
  struct __block_impl impl;
  struct __getGlobalInt_block_desc_0* Desc;
  __getGlobalInt_block_impl_0(void *fp, struct __getGlobalInt_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteGlobalBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static int __getGlobalInt_block_func_0(struct __getGlobalInt_block_impl_0 *__cself) {

    return GlobalInt;
}

static struct __getGlobalInt_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __getGlobalInt_block_desc_0_DATA = { 0, sizeof(struct __getGlobalInt_block_impl_0)};
static __getGlobalInt_block_impl_0 __global_getGlobalInt_block_impl_0((void *)__getGlobalInt_block_func_0, &__getGlobalInt_block_desc_0_DATA);
int (*getGlobalInt)(void) = ((int (*)())&__global_getGlobalInt_block_impl_0);
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_46_qzlmlhgd0xd2xjp590bfb5_00000gn_T_main_14343d_mi_0, ((int (*)(__block_impl *))((__block_impl *)getGlobalInt)->FuncPtr)((__block_impl *)getGlobalInt));
    }
    return 0;
}

_NSConcretionGlobalBlock代表这是一个全局的block。全局block和全局变量一样,可以在整个数据域中使用。 这里的其他的任何retain, copy对它是没有影响的。它存储在静态区域,基本可以理解,在APP运行期间,它是一直存在的。

很明显看到,这个block作为全局变量的形式被创建出来的。还有一种更加隐秘的方式, 像下面这样。

在这里插入图片描述

打印
在这里插入图片描述

同样是一个全局的block。

所以全局block的生成有两种不同的情况,一个是直接将block创建成一个全局变量。这是苹果官方的用法。另一种是创建一个局部变量的block,在block的函数体中,不使用任何外部的全局变量。但这个block和全局变量的block是有所不同的。我们将这个block的cpp中间文件打开。

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {

            printf("a block");
        }

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_46_qzlmlhgd0xd2xjp590bfb5_00000gn_T_main_c071cc_mi_0, block);
    }
    return 0;
}

竟然是 _NSConcreteStackBlock! 也即是我们所说的栈block! 这是为什么呢? 我个人的理解是,对于CPP文件中的指示,它是告诉用户这个结构体的存储的位置。 而在nslog打印中显示不一样是因为因为没有包含局部变量,所以block本身不需要携带上下文环境,系统在编译的时候,默认Block是全局环境。 这才导致两种展示的方式不一样。

栈block

刚才说了,全局block的其中创建方式是作为一个不包含外部变量的局部变量block。 那如果这个block变量包含了外部变量那又会怎样呢。没错,当包含了外部变量的时候,它是一个栈Blobk。

在这里插入图片描述

clang查看代码生成的中间文件。 发现是存在于栈中的。

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int a;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int flags=0) : a(_a) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

打印结果,在ARC环境中
在这里插入图片描述

在MRC环境下
在这里插入图片描述

为什么在ARC的环境下的打印结果不同呢?

正常情况下,block的申明都是在栈中的,如果需要将它转到堆上,需要进行block_copy,或者其他发送copy消息。
如果在MRC没有进行copy的话,那么当处于栈中的block的环境被销毁的时候,block也等同被销毁了。
而在ARC中 ,因为系统会自动对block发送copy消息(原因是为了确保在块作为对象时,其生命周期得到正确管理)所以我们打印的时候看到block是mallco类型,即位于堆上的。在没有进行copy 之前,栈上的Block使用retain 等操作都是没有实际作用的。

堆上的block

block的申明的时候都是在栈上的,如果发送了copy消息,那么block才会被复制到上。

当复制到堆上之后,我们使用block就可以像使用普通的属性一样,可以进行retain等。注意,重复发送copy 消息,也只会在堆上保留一份blcok。在block所在的栈中的内容没有被销毁之前,这个栈中的block还依然存在的。但是它多了一条跟堆中block的联系。我们回过头看他的一个结构体:

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {

printf(“a block”);

}

在这个block的结构体中,结构体本身的类型是__main_block_func_0 。 内部有一个参数指针指向了一个__main_block_impl_0,cself 结构体,这个指针实际上指向的是自己,如果block接受了copy 消息之后,那么这个指针将指向堆上的那份block,而堆上的那份的block的cself 还是指向堆上的blobk结构体,这就是为什么在复制到堆上之后,当栈上的内容被销毁时,block调用不会crash的原因了。

三,为什么使用_block就可以使block可以修改外部变量

我们先看一看当block对外部变量使用__block修饰局部变量前后的clang
使用前
在这里插入图片描述

clang

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int a;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int flags=0) : a(_a) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int a = __cself->a; // bound by copy

            printf("%d", a);
        }```

使用后
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/a25a4dc29d7b4f81a7539af9c9fe2dad.png#pic_center)

clang

```objectivec
struct __Block_byref_a_0 {
  void *__isa;
__Block_byref_a_0 *__forwarding;
 int __flags;
 int __size;
 int a;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_a_0 *a; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_a_0 *_a, int flags=0) : a(_a->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_a_0 *a = __cself->a; // bound by ref

            printf("%d", (a->__forwarding->a));
        }

我们可以看到区别:

当使用不加block修饰的局部变量时,直接通过__cself指针找到__main_block_impl_0结构体中自动生成的一个相同类型的age变量(为值传递),所以不能修改捕获的变量。

但使用block修饰的block变量时,通过__cself指针找到__main_block_impl_0结构体中__Block_byref_a_0 *的a结构体(_block修饰的局部变量),再在a中找到__forwarding指针,可以看到它的类型同样为__Block_byref_a_0 *类型,它指向结构体本身(block修饰的局部变量),最后通过_forwarding指针找到局部变量a本身,这样就可以进行修改外部变量。

简单讲,就是一个修改的是值传递过来的外部变量,一个是通过指针找到外部变量本身,修改实际上就是对外部变量的修改

四,引起强引用的原因是什么,我们解决的方法和原理是什么

什么情况下block会造成循环引用

block为了保证代码块内部对象不被提前释放,会对block中的对象进行强引用,就相当于持有了其中的对象,如果此时block中的对象有持有了该block,就会造成循环引用。

在这里插入图片描述

block作为self的一个属性,表明self是持有block的,这样,如果需要释放block,至少需要先把self对它的持有释放,而block是self的属性,要让self不在持有它,只有一种情况,self已经被释放。ARC会自动处理块对self的持有关系。当块被复制到堆上时,会自动将self的引用计数加1,以确保在块的生命周期内,self对象不会被提前释放,也即是持有了self.

那么这样的情况下,如果要释放self,至少要做的一件事情是让block不再持有self,显然,上面的这种情况,要不持有slef,只能等待block被销毁。 这时候,block和slef相互等待着对方先被释放,才能释放自己,一直矛盾着两个对象都得不到释放,就会造成循环引用

解决方法
1,OC提供了weak修饰符
我们会申请一个weak self对象参与block的copy使用。

在这里插入图片描述

这样就不会发生警报。

2.使用弱引用和强引用的结合
当使用__weak修饰后,如果外部对象为空了,那么block的内部对象也将为空,这样有时候并不是我们想要的。AFNetWorking中使用了一个办法解决这一问题。就是在block内部继续使用__Strong来修饰带进来的weakself。
在这里插入图片描述

这样做的好处就是避免了循环引用,同时保证对象在block中的持续存在,而不会因为block外的变量因为被释放掉使block内部的变量也为空了。

3.使用__block修饰符

在这里插入图片描述

当一个对象被__block修饰时,在块内部对该对象的引用会变为一个弱引用。这样,在块内部对该对象的使用不会阻止对象的释放,从而打破了循环引用。

需要注意的是,__block修饰符只在块内部起作用,不会改变对象在块之外的引用语义。因此,即使对象被__block修饰,如果在块外部仍然存在其他强引用,循环引用仍然可能发生。因此,使用__block修饰符时,需要综合考虑对象在块内外的引用关系,以避免潜在的循环引用问题。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

每日OJ题_多源BFS①_力扣542. 01 矩阵(多源BFS解决最短路原理)

目录 多源BFS解决最短路算法原理 力扣542. 01 矩阵 解析代码 多源BFS解决最短路算法原理 什么是单源最短路 / 多源最短路&#xff1f; 之前的BFS解决最短路都是解决的单源最短路。 画图来说&#xff0c;单源最短路问题即为&#xff1a; 而对于多源最短路问题: 如何解决此…

微信小程序之点击事件

微信小程序中常用的点击事件主要是 tap&#xff0c;但除此之外还有其他的触摸类事件&#xff0c;用于不同的交互场景。以下是一些常见的点击和触摸相关的事件及其区别&#xff1a; 1、tap——最基本的点击事件&#xff0c;适用于一般的轻触交互&#xff0c;类似于 HTML 中的 c…

Pixverse:开启文生视频与图生视频新纪元

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

NPU流式输出-torch_npu和transformers框架-多线程Streamer-昇腾910B-EE1001

前情提要 torch_npu框架不支持多线程自动set_device 报错详情 直接使用transformers的TextIteratorStreamer进行流式推理&#xff0c;会报错 Exception in thread Thread-6: Traceback (most recent call last):File "/root/anaconda3/envs/AI/lib/python3.9/threadin…

Linux shell 脚本基础与部署SpringCloud实战

博主介绍&#xff1a;✌全网粉丝5W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战&#xff0c;博主也曾写过优秀论文&#xff0c;查重率极低&#xff0c;在这方面有丰富的经验…

L2正则化——解释为什么可以减少模型的复杂度

L2正则化是一种用于机器学习模型的技术&#xff0c;旨在减少模型的复杂度&#xff0c;从而提高其泛化能力。在L2正则化中&#xff0c;通过添加一个惩罚项&#xff0c;模型的权重被迫保持较小的值&#xff0c;这有助于防止过拟合&#xff0c;即模型在训练数据上表现良好但在未见…

python学习笔记B-07:序列结构之列表--列表的常用函数和方法

以xx_函数名(列表名)的形式出现的是函数&#xff1b;以xx_列表名.xx_方法名的形式出现的是方法。 列表常用函数如下&#xff1a; len()&#xff1a;计算列表元素数量 max()&#xff1a;获取列表元素最大值 min():获取列表元素最小值 sum():计算列表中各元素之和 列表常用方法如…

【Java EE】文件操作

目录 1.认识文件 2.树型结构组织和目录 3.文件路径&#xff08;Path&#xff09; 4.其他知识 5.Java中操作文件 5.1File概述 5.1.1属性 5.1.2构造方法 5.1.3方法 5.2代码示例 1.认识文件 我们先来认识狭义的文件&#xff08;file&#xff09;。针对1硬盘这种持久化存…

HTML重要标签梳理学习

1、HTML文件的框架 使用VS Code编码时&#xff0c;输入!选中第一个&#xff01;就可以快速生成一个HTML文件框架。 2、标签 <hr> <!--下划线--> <br> <!--换行--> <strong>加粗</strong> &…

MySQL行级锁——技术深度+1

引言 本文是对MySQL行级锁的学习&#xff0c;MySQL一直停留在会用的阶段&#xff0c;需要弄清楚锁和事务的原理并DEBUG查看。 PS:本文涉及到的表结构均可从https://github.com/WeiXiao-Hyy/blog中获取&#xff0c;欢迎Star&#xff01; MySQL行级锁 行级锁&#xff08;Row-…

案例研究 | JumpServer助力天虹股份构建可靠的运维安全审计平台

天虹数科商业股份有限公司&#xff08;以下简称为“天虹股份”&#xff09;成立于1984年&#xff0c;是国有控股的上市公司。通过人本、科学的管理&#xff0c;以及专业、高效的运营&#xff0c;天虹股份连续多年入围中国连锁百强企业&#xff0c;拥有全国领先的零售技术研发和…

vue3 项目启动时vite版本问题报错

背景&#xff1a; 我是在项目迁移过程中遇到的这个问题&#xff0c;前提可以看下面这篇 http://t.csdnimg.cn/g70Eq 问题描述 迁移项目时&#xff0c;将项目整体升级到了vue3版本&#xff0c;启动项目时出现下列报错&#xff1a; npm ERR! Found: vite5.1.4 npm ERR! node_…

2024-2.基础操作-Python

Jupiter基本使用 cell有两种模式&#xff1a; codemarkdown 快捷键 新建cell&#xff1a;a,b删除cell&#xff1a;dd&#xff0c;x运行cell&#xff1a;shiftenter切换cell模式&#xff1a; m&#xff1a;将code模式的cell切换到mdy:将md模式的cell切换到code 智能补全&#x…

QT Webengine开发过程报错qml: Render process exited with code 159 (killed)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、解决方法二、补充说明总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 基于QT的Webengine开发过程中&#xff0c;QT的官方示例…

【算法】反转链表

本题来源---《反转链表》 题目描述&#xff1a; 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1]示例 2&#xff1a; 输入&#xff1a;head [1,2] 输…

22长安杯电子取证复现(检材一,二)

检材一 先用VC容器挂载&#xff0c;拿到完整的检材 从检材一入手&#xff0c;火眼创建案件&#xff0c;打开检材一 1.检材1的SHA256值为 计算SHA256值&#xff0c;直接用火眼计算哈希计算 9E48BB2CAE5C1D93BAF572E3646D2ECD26080B70413DC7DC4131F88289F49E34 2.分析检材1&am…

50.HarmonyOS鸿蒙系统 App(ArkUI)web组件实现简易浏览器

50.HarmonyOS鸿蒙系统 App(ArkUI)web组件实现简易浏览器 配置网络访问权限&#xff1a; 跳转任务&#xff1a; Button(转到).onClick(() > {try {// 点击按钮时&#xff0c;通过loadUrl&#xff0c;跳转到www.example1.comthis.webviewController.loadUrl(this.get_url);} …

Root mapping definition has unsupported parameters: [all : {analyzer=ik_max_wor

你们好&#xff0c;我是金金金。 场景 我正在使用Springboot整合elasticsearch&#xff0c;在创建索引(分词器) 运行报错&#xff0c;如下 排查 排查之前我先贴一下代码 import org.elasticsearch.action.admin.indices.create.CreateIndexRequest; // 注意这个包SpringBootTe…

Linux中如何安装ImageMagick及其常规使用命令

在Linux中安装ImageMagick可以通过包管理工具进行安装。具体步骤如下&#xff1a; 打开终端&#xff08;Terminal&#xff09;。 使用以下命令更新系统软件包列表&#xff1a; sudo apt update使用以下命令安装ImageMagick&#xff1a; sudo apt install imagemagick安装完…

物理机安装centos7并配置基本环境,网络配置,docker配置

1.首先下载镜像Download 2.下载UltraISO 安装docker 第1步&#xff1a;卸载当前版本docker yum erase docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-selinux \docker-engine-selinux \do…