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 copyprintf("%d", a);}```

使用后
在这里插入图片描述

clang

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 refprintf("%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修饰符时,需要综合考虑对象在块内外的引用关系,以避免潜在的循环引用问题。

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

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

相关文章

探索以太坊世界:使用Geth打造你的私人网络

文章目录 概要名词解释Geth&#xff08;Go Ethereum&#xff09;区块链网络种类 具体流程下载geth客户端配置私链新建创世区块启动私链 连接MetaMask钱包小结 概要 在区块链领域&#xff0c;以太坊私链的搭建是学习和开发智能合约的重要一步。私链允许开发者在独立的环境中进行…

地图图源#ESRI ArcGIS XYZ Tiles系列(TMS)

目录 1、前言 2、地图图源网址 2.1、Satellite 卫星图源 2.2、Terrain 地形图源 2.3、Street 路网/标注图源 2.4、Specifity 特色设计图源 3、专业推荐”穿搭“ 4、图源配置下载及使用 图源名称图层类别特别注意谷歌 Google①地形 ②影像 ③矢量及标注 ④特色图源国内大…

python3如何提取汉字

采用正则表达式的方法对字符串进行处理。 str1 "&#xff5b;我%$是&#xff0c;《速$.度\发》中 /国、人"&#xff08;1&#xff09;提取汉字 汉字的范围为”\u4e00-\u9fa5“&#xff0c;这个是用Unicode表示的。 import re res1 .join(re.findall([\u4e00-\u9fa…

sso-oauth2单点登录功能笔记

场景&#xff1a;最近公司2个系统需要做单点登录&#xff0c;A系统作为服务器&#xff0c;认证方式是sso-oauth2方式&#xff0c;B系统作为客户端&#xff0c;token方式是ta-token&#xff0c;先来张sso-oauth2认证方式的图 前置准备工作 第一步&#xff1a;要确认谁是服务提…

kubernetes的网络通信实现原理

网络原理 Kubernetes网络原理详解&#xff1a;一、Kubernetes 网络实现1.容器到容器&#xff08;同一Pod内&#xff09;通信流程&#xff1a;2. pod之间的通信&#xff08;以Calico为例&#xff09;&#xff1a; 二、CNI 网络模型三、网络策略四、开源的容器网络方案五、 常见网…

2024年腾讯云免费服务器最新申请入口链接

腾讯云免费服务器申请入口 txybk.com/go/free 免费服务器可选轻量应用服务器和云服务器CVM&#xff0c;轻量配置可选2核2G3M、2核8G7M和4核8G12M&#xff0c;CVM云服务器可选2核2G3M和2核4G3M配置&#xff0c;腾讯云百科txybk.com分享2024年最新腾讯云免费服务器申请入口、限制…

【JavaEE初阶】网络原理|认识协议|协议分层|TCP/IP模型|封装和分用

一、认识协议 1.概念 简单来说&#xff1a;就是一种通信双方&#xff0c;对于通信规则的约定&#xff08;标准&#xff09;&#xff0c;一定是通信双方都认可的 但是这个协议不一定是认可面非常广的&#xff0c;即使是两个人之间的也可叫做协议 就好⽐⻅⽹友&#xff0c;彼此…

(十三)C++自制植物大战僵尸游戏多用户存档实现(二)

植物大战僵尸游戏开发教程专栏地址http://t.csdnimg.cn/8UFMs UserData.h 在头文件中定义了枚举类型openUserDataReturnType&#xff0c;用于表示打开用户数据文件的返回状态。FileExistError表示文件存在但是打开错误&#xff0c;FileExistCorrect表示文件在且正确&#xff0…

如何进行景气分析

景气分析是一种短期经济分析方法。主要分析短时间内&#xff08;一般指一年内&#xff0c; 或几个月内&#xff09;经济运行的态势&#xff0c;包括当前的状态和未来的趋势。景气分析可以为宏观经济政策提供重要的决策与参考信息&#xff0c;例如根据经济运行的方向、强弱可建议…

概念理解: DDR的寻址容量设计

主要内容&#xff1a;DDR寻址容量设计的概念理解示例。 DDR的数据存储在不同的bank上&#xff0c;你知道具体是如何通过数据总线和地址总线的配合&#xff0c;拿到所需的数据吗&#xff1f;通过下面这样一个简单的问题&#xff0c;我们来逐渐了解这个过程的实现。 DDR…

LeetCode 热题 100 题解:普通数组部分

文章目录 题目一&#xff1a;最大子数组和&#xff08;No. 53&#xff09;题解 题目二&#xff1a;合并区间&#xff08;No. 56&#xff09;题解 题目三&#xff1a;轮转数组&#xff08;No. 189&#xff09;题解 题目四&#xff1a;除自身以外数组的乘积&#xff08;No. 238&a…

MySql运维篇

目录 一.日志 1.1日志分类 1.2Error Log 1.3BinaryLog 1.4SlowQuery Log 二.备份 2.1备份原因 2.2备份目标 2.3备份技术 2.3.1物理备份 2.3.2逻辑备份 2.4备份方式 2.4.1完全备份 2.4.2增量备份 2.4.3差异备份 2.5备份环境准备 2.6完全备份实验 2.6.1完全备…

https协议的加密方式详解

各位大佬能多多点赞关注评论收藏&#xff0c;球球各位大佬们了&#xff01;&#xff01; &#xff01; 目录 1.为什么要加密&#xff1f; 2.如何加密 1.密钥&#xff08;yue,第四声&#xff09; 2.对称加密 3.非对称加密 4.公证机构 3.总结 1.为什么要加密&#xff1f;…

趋势分析 一元线性回归及显著性检验 GIS方法 Slope

slope斜率计算公式&#xff1a; 假设有三年的NDVI图像 加载3年栅格数据&#xff0c;公式中n取3计算分子左边&#xff1a; 3*(1*"1990.tif"2*"1991.tif"3*"1992.tif") 计算分子右边&#xff1a; 6*("1990.tif""1991.tif"&q…

算法课程笔记——STL题目

长度为2的字符串&#xff0c;当in下标为一&#xff0c;也就是\n,当i&#xff01;n&#xff0c;就是输出空格 &&且 city从citys里面取 加速后就不能混用scanf

CoFSM基于共现尺度空间的多模态遥感图像匹配方法--论文阅读记录

目录 论文 Multi-Modal Remote Sensing Image Matching Considering Co-Occurrence Filter 参考论文&#xff1a;SIFT系列论文&#xff0c; SIFT Distinctive Image Features from Scale-Invariant Keypoints&#xff0c;作者&#xff1a;David G. Lowe 快速样本共识算法…

汇智知了堂晨会聚焦:NAS应用如何赋能网络安全实战

在近期汇智知了堂网络安全75班的晨会上&#xff0c;一场关于NAS应用的深入分享完美展开。学员们以饱满的热情投入到这场安全讨论中&#xff0c;共同探索网络安全的新天地。 此次分享会聚焦于NAS的应用&#xff0c;旨在帮助学员们更好地了解NAS的定义与功能&#xff0c;掌握其在…

52.基于SpringBoot + Vue实现的前后端分离-房屋租赁系统(项目 + 论文)

项目介绍 本站是一个B/S模式系统&#xff0c;采用SpringBoot Vue框架&#xff0c;MYSQL数据库设计开发&#xff0c;充分保证系统的稳定性。系统具有界面清晰、操作简单&#xff0c;功能齐全的特点&#xff0c;使得基于SpringBoot Vue技术的房屋租赁系统设计与实现管理工作系统…

【Linux系统化学习】线程控制

目录 前言 POSIX线程库 线程控制 创建线程 线程终止 pthread_exit()函数 pthread_cancel()函数&#xff08;会在下面线程等待部分详解&#xff09; 线程等待 pthread_join()函数 获取线程退出码 分离线程 线程取消(pthread_cancel()函数&#xff09; 线程ID及进程…

open Gauss 数据库-06 openGauss数据库安全指导手册5.0.0

发文章是为了证明自己真的掌握了一个知识&#xff0c;同时给他人带来帮助&#xff0c;如有问题&#xff0c;欢迎指正&#xff0c;祝大家万事胜意&#xff01; 目录 前言 openGauss数据库安全指导 1 用户权限控制 1.1 实验介绍 1.1.1 关于本实验 1.1.2 实验目的 1.2 用户…