【iOS】——Blocks

文章目录

  • 前言
  • 一、Blocks概要
    • 1.什么是Blocks
  • 二、Block模式
  • 1.block语法
    • 2.block类型变量
    • 3.截获自动变量值
    • 4._Block修饰符
    • 5.截获的自动变量
  • 三、Blocks的实现
    • 1.Block的实质
    • 2.截获自动变量值
    • 3._Block说明符
    • 4.Block存储域


前言

一、Blocks概要

1.什么是Blocks

Blocks是C语言的扩充功能,并且可以用一句话来表示Blocks的扩充功能:Blocks是带有自动变量(局部变量)的匿名函数。

匿名函数:Blocks是一种匿名函数,也就是没有特定名称的函数。它们可以在需要的地方定义和使用,而无需提前声明或命名。这使得Blocks非常灵活,可以作为参数传递给其他函数或方法,或者作为变量保存和执行。
自动变量(局部变量):Blocks可以捕获其定义范围内的自动变量(也称为局部变量)。当一个Block被定义时,它会在其内部创建一个副本,用于在Block执行时访问该变量的值。这意味着即使变量超出了其定义范围,Block仍然可以访问和使用该变量的值。
闭包:由于Blocks可以捕获自动变量,它们形成了一个封闭的环境,即闭包。这意味着Block可以在其定义范围之外访问和使用自动变量。当Block被传递到其他函数或方法时,它会携带其封闭环境中的自动变量,以便在执行时可以访问这些变量的值。

二、Block模式

1.block语法

完整形式的Block语法和一般的C语言函数相比,只有两点不同。第一点是没有函数名,因为它是匿名函数。第二点是返回值类型前带有“^”(插入记号)记号。下面是Block语法格式:

^ 返回值类型 参数列表 表达式

^int (int count){return count + 1;}

Block语法可以省略返回值类型

^(int count){return count + 1;}

如果不使用参数,Block语法还可以省略参数列表

^void (void){printf("Blocks\n");}

2.block类型变量

在C语言中可以将函数地址赋值给函数指针的类型变量中,同样在Block语法下,可将Block语法赋值给Block类型的变量中。示例如下:

int (^blk)(int) = ^(int count) {return count + 1;};
int (^blk_t)(int);
blk_t = blk;

Block类型变量声明
返回参数 (^变量名称)(接受参数);

同时使用typedef重命名Block类型,因为Block类型变量和平时的使用类型相同,为了方便我们使用,我们通常都是用typedef来重命名Block。

typedef 返回参数 (^该Block的名称)(接收参数)

此时就可以用该Block的名称代表该Block了。

typedef int (^blk_t)(int)blk_t blk = ^(int count) {return count + 1;};

3.截获自动变量值

Block截获自动变量值就是说在定义Block的时候,其之前的变量都会被该Block所截获,该Block后再改变变量的值然后再调用,其Block中所捕获的值不会受到改变。

int main(int argc, const char * argv[]) {int dmy = 256;int val = 10;const char *fmt = "val = %d\n";void (^blk)(void) = ^{printf(fmt, val);};val = 2;fmt = "These values were changed. val = %d\n";blk();return 0;
}

在这里插入图片描述

此时我们发现,在定义Block之后的这val,fmt两个变量被改变了,但是再次调用该Block还是定义Block之前的值,这就是捕获。我的理解就是定义Block之前的自动变量都会被该Block捕获,在定义Block后改变变量其值还是该Block之前的定义的值,不会收到影响。

4._Block修饰符

自动变量截获只能保存执行Block语法瞬间的值并且保存后就不能修改这个值,如果要修改这个自动变量的值就需要在在声明时给这个自动变量附加_Block说明符。

int main(int argc, const char * argv[]) {__block int val = 10;__block const char *fmt = "val = %d\n";void (^blk)(void) = ^{printf(fmt, val);val = 20;};val = 2;fmt = "These values were changed. val = %d\n";blk();printf(fmt, val);return 0;
}

在这里插入图片描述

5.截获的自动变量

前面提到向Block截获的自动变量赋值会报错,如果调用变更该对象的方法则不会产生编译错误。
用OC中的NSMutableArray来说,截获它其实就是截获该类对象用的结构体实例指针,就是说你只要不改变其指针的指向和其指针的值,他就不会出错。

int main(int argc, const char * argv[]) {id array = [[NSMutableArray alloc] init];void (^blk)(void) = ^{id obj = [[NSObject alloc] init];[array addObject:obj];};blk();return 0;
}

这里因为你并没有改变其array指针的指向和值,而只是在其地址后边新加了一个值,所以他就不会报错。

int main(int argc, const char * argv[]) {id array = [[NSMutableArray alloc] init];void (^blk)(void) = ^{array = [[NSMutableArray alloc] init];};blk();return 0;
}

但是你这样写就改变了array的初始地址,所以他就会报错。

另外,在使用C语言数组是必须小心使用其指针。

int main(int argc, const char * argv[]) {const char text[] = "hello";  //这里是定义一个数组void (^blk)(void) = ^{printf("%c\n", text[2]);};blk();return 0;
}

这里你看似没有什么错误,但是会出现下面的编译错误。
在这里插入图片描述
因为在现在的Blocks中,截获自动变量的方法并没有实现对C语言数组的截获,这时我们可以使用指针来解决该问题。

int main(int argc, const char * argv[]) {const char *text = "hello";  //这里是使用指针引用void (^blk)(void) = ^{printf("%c\n", text[2]);};blk();return 0;
}

三、Blocks的实现

1.Block的实质

Block实质是Objective-C对闭包的对象实现,简单说来,Block就是对象。

下面分别从表层到底层来分析一下:

表层分析Block的实质:它是一个类型

Block是一种类型,一旦使用了Block就相当于生成了可赋值给Block类型变量的值。举个例子:

int (^blk)(int) = ^(int count){return count + 1;
};

等号左侧的代码表示了这个Block的类型:它接受一个int参数,返回一个int值。
等号右侧的代码是这个Block的值:它是等号左侧定义的block类型的一种实现。

如果我们在项目中经常使用某种相同类型的block,我们可以用typedef来抽象出这种类型的Block:

typedef int(^AddOneBlock)(int count);AddOneBlock block = ^(int count){return count + 1;//具体实现代码
};

这样一来,block的赋值和传递就变得相对方便一些了, 因为block的类型已经抽象了出来。

深层分析Block的实质:它是Objective-C对象

Block其实就是Objective-C对象,因为它的结构体中含有isa指针。

下面将Objective-C的代码转化为C++的代码来看一下block的实现。

int main()
{void (^blk)(void) = ^{printf("Block\n");};return 0;
}

通过clang编辑器转换为c++:

struct __block_impl {void *isa;int Flags;int Reserved;void *FuncPtr;
};//block结构体
struct __main_block_impl_0 {struct __block_impl impl;struct __main_block_desc_0* Desc;//Block构造函数__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {impl.isa = &_NSConcreteStackBlock;//isa指针impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;}};//将来被调用的block内部的代码:block值被转换为C的函数代码
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {printf("Block\n");
}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)};//main 函数
int main()
{void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));return 0;
}

首先我们看一下从原来的block值(OC代码块)转化而来的C++代码:

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {printf("Block\n");
}

这里,*__cself 是指向Block的值的指针,也就相当于是Block的值它自己(相当于C++里的this,OC里的self)。
而且很容易看出来,cself 是指向mainblockimpl0结构体实现的指针。 结合上句话,也就是说Block结构体就是mainblockimpl0结构体。Block的值就是通过mainblockimpl_0构造出来的。


下面来看一下这个结构体的声明:

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;}
};

可以看出,_mainblockimpl0结构体有三个部分:

第一个是成员变量impl,它是实际的函数指针,它指向_mainblockfunc0。来看一下它的结构体的声明:

struct __block_impl {void *isa;int Flags;int Reserved;  //今后版本升级所需的区域void *FuncPtr; //函数指针
};

第二个是成员变量是指向_mainblockdesc0结构体的Desc指针,是用于描述当前这个block的附加信息的,包括结构体的大小等等信息

static struct __main_block_desc_0 {size_t reserved;  //今后升级版本所需区域size_t Block_size;//block的大小} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};

第三个部分是mainblockimpl0结构体的构造函数,mainblockimpl0 就是该 block 的实现

__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;}

在这个结构体的构造函数里,isa指针保持这所属类的结构体的实例的指针。_mainblockimlp0结构体就相当于Objective-C类对象的结构体,这里的_NSConcreteStackBlock相当于Block的结构体实例,也就是说block其实就是Objective-C对于闭包的对象实现。

2.截获自动变量值

使用Block的时候,不仅可以使用其内部的参数,还可以使用Block外部的局部变量。而一旦在Block内部使用了其外部变量,这些变量就会被Block保存。
通过先前的例子我们知道
Block可以截获局部变量。
修改Block外部的局部变量,Block内部被截获的局部变量不受影响。
修改Block内部到局部变量,编译不通过。

通过C++的代码来看一下Block在截获变量的时候都发生了什么:

struct __main_block_impl_0 {struct __block_impl impl;struct __main_block_desc_0* Desc;const char *fmt;  //被添加int val;          //被添加__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, const char *_fmt, int _val, int flags=0) : fmt(_fmt), val(_val) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {const char *fmt = __cself->fmt; // bound by copyint val = __cself->val; // bound by copyprintf(fmt,val);}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 dmy = 256;int val = 10;const char *fmt = "var = %d\n";void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, fmt, val));val = 2;fmt = "These values were changed. var = %d\n";((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);return 0;
}

单独抽取_mainblockimpl0来看一下:

struct __main_block_impl_0 {struct __block_impl impl;struct __main_block_desc_0* Desc;const char *fmt; //截获的自动变量int val;         //截获的自动变量__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, const char *_fmt, int _val, int flags=0) : fmt(_fmt), val(_val) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;}
};

我们可以看到,在block内部语法表达式中使用的自动变量(fmt,val)被作为成员变量追加到了_mainblockimpl0结构体中(注意:block没有使用的自动变量不会被追加,如dmy变量)。
在初始化block结构体实例时(请看mainblockimpl0的构造函数),还需要截获的自动变量fmt和val来初始化mainblockimpl0结构体实例,因为增加了被截获的自动变量,block的体积会变大。

再来看一下函数体的代码:

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {const char *fmt = __cself->fmt; // bound by copyint val = __cself->val; // bound by copyprintf(fmt,val);
}

从这里看就更明显了:fmt,var都是从__cself里面获取的,更说明了二者是属于block的。而且从注释来看(注释是由clang自动生成的),这两个变量是值传递,而不是指针传递,也就是说Block仅仅截获自动变量的值,所以这就解释了即使改变了外部的自动变量的值,也不会影响Block内部的值。

既然我们无法在Block中改变外部变量的值,所以也就没有必要在Block内部改变变量的值了,因为Block内部和外部的变量实际上是两种不同的存在:前者是Block内部结构体的一个成员变量,后者是在栈区里的临时变量。

现在我们知道:被截获的自动变量的值是无法直接修改的,但是有两个方法可以解决这个问题:

改变存储于特殊存储区域的变量。
通过__block修饰符来改变。

3._Block说明符

前面提到Block中使用自动变量后,在Blockj的结构体实例中重写该自动变量也不会改变原先截获的自动变量的值。不过这样的话就不能在Block中保存值了,不是很方便。因此下面提供两种方法来解决。
第一种方法是:C语言中有一个变量允许Block改写值,即静态变量,也就是用static修饰的变量。但是他并不太好,因为变量作用域结束的同时,原来的自动变量被废弃,因此Block 中超过变量作用域而存在的变量同静态变量一样,将不能通过指针访问原来的自动变量。
第二种方法是:使用_Block说明符。更准确的表达方式是“_Block存储域类说明符”。C语言中有以下存储域类说明符:

  • typedef
  • extern
  • static
  • auto
  • register

使用__block修饰符,它类似于static、auto、和register说明符,用于指定将变量值设置到哪个存储域中。

下面是个例子:

int main(int argc, const char * argv[]) {__block int val = 10;void (^blk)(void) = ^{val = 1;};return 0;
}

其源代码如下:

//__block说明符修饰后的变量的结构体
struct __Block_byref_val_0 {void *__isa;  //指向所属类的指针__Block_byref_val_0 *__forwarding;  //指向自己的内存地址的指针int __flags;  //标志性参数,暂时没用到所以默认为0int __size;  //该结构体所占用的大小int val;  //该结构体存储的值,即原变量的赋的值
};//block本体
struct __main_block_impl_0 {struct __block_impl impl;  //block的主体struct __main block desc 0* Desc;  //存储该block的大小__Block_byref_val_0 *val;  //__block修饰的变量的值//构造函数__main_block_impl_0(void *fp, struct __main_block_desc 0 *desc, __Block_byrefval_0 *_val, int flags=0) : val(_val->__forwarding) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;
};//封装的block逻辑,存储了block的代码块
static void __main_block_func_0(struct__main_block_impl_0 *_cself) {__Block_byref_val_0 *val =__cself->val;(val->__forwarding->val) = 1;
}
static void_main_block_copy_0(struct __main_block_impl_0* dst, struct __main_block_impl_0* src) {//根据auto变量的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用_Block_object_assign(&dst->val, src->val, BLOCK_FIELD_IS_BYREF);
}static void __main_block_dispose_0(struct __main_block_imp1_0* src) {//自动释放引用的auto变量(相当于release)_Block_object_dispose(src->val, BLOCK_FIELD_IS_BYREF);
}static struct __main_block_desc_0 {unsigned long reserved;  //保留字段unsigned long Block_size;  //block大小void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);  //copy的函数指针,下面使用构造函数进行了初始化void (*dispose)(struct __main_block_impl_0*);  //dispose的函数指针,下面使用构造函数进行了初始化
}//构造函数,初始化保留字段、block大小及两个函数__main_block_desc_0_DATA = {0,sizeof(structmain_block_impl_0),__main_block_copy_O, __main_block_dispose_0
};
int main() {//之前的 __block int val = 10;变成了结构体实例struct __Block_byref_val_0 val = {0,  //isa指针&val,  //指向自身地址的指针0,  //标志变量sizeof(__Block_byref_val_0),  //block大小10  //该数据的值};blk = &__main_block_impl_0(
__main_block_func_0, &__main_block_desc_0_DATA, &val, 0x22000000);return 0;
}

通过源代码发现__block说明符修饰的变量变成了一个结构体,它是利用这个结构体来进行存储数据的:

struct __Block_byref_val_0 val = {0,  //isa指针&val,  //指向自身地址的指针0,  //标志变量sizeof(__Block_byref_val_0),  //block大小10  //该数据的值};

那么为什么会有成员变量__forwarding呢?
因为__block变量用结构体成员变量__forwarding可以实现无论__block变量配置在栈上还是堆上时都能够正确的访问__block变量。

4.Block存储域

Block转换为Block的结构体类型的自动变量,_Block变量转换为 block变量的结构体类型的自动变量。所谓结构体类型的自动变量就是栈上生成的该结构体的实例。

Block的所属类:

_NSConcreteStackBlock //栈
_NSConcreteGlobalBlock //全局(.data区)
_NSConcreteMallocBlock //堆

全局Block:

在记述全局变量的地方使用Block语法时,生成的Block为_NSConcreteGlobalBlock类对象。

void (^blk)(void) = ^{printf("Global Block\n");};int main(void) {······
}

其isa指针初始化如下:

impl.isa = &_NSConcreteGlobalBlock;

该类Block用结构体实例设置在程序的数据区域中。因为在使用全局变量的地方不能使用自动变量,所以不存在对自动变量进行截获。只在截获自动变量时,Block 用结构体实例截获的值才会根据执行时的状态变化。也就是说,全局Block不可以截获自动变量,否则其就不可以设置为全局Block。

也就是说,即使在函数内而不在记述广域变量的地方使用Block语法时,只要Block不截获自动变量,就可以将Block用结构体实例设置在程序的数据区域。以上的这些情况下,Block为_NSConcreteGlobalBlock类对象。即Block配置在应用程序的数据区域中。

栈Block:

其isa指针初始化如下:

impl.isa = &_NSConcreteStackBlock;

除上述的设置两种全局Block之外的Block语法生成的Block为_NSConcreteStackBlock类对象,且设置在栈上。

配置在全局变量上的Block,从变量作用域外也可以通过指针安全地使用。但设置在栈上的Block,如果其所属的变量作用域结束,该Block就被废弃。由于__block变量也配置在栈上,同样地,如果其所属的变量作用域结束,则该__block变量也会被废弃。我们也就无法再访问到该变量了。

为了解决无法访问被废弃变量这个问题,就出现了堆block,同时也出现了将Block和__block变量从栈上复制到堆上的方法。这样即使Block 语法记述的变量作用域结束,堆上的Block还可以继续存在。下面我们就来说说:

堆Block:

其isa指针初始化如下:

impl.isa = &_NSConcreteMallocBlock;

堆上的Block其实就是将栈上的Block复制过来而成的,有时我们在_block变量配置在堆上的状态下,也可以访问栈上的__block变量。在此情形下,只要栈上的结构体实例成员变量__forwarding指向堆上的结构体实例,那么不管是从栈上的__block 变量还是从堆上的__block 变量都能够正确访问。

在这里插入图片描述

当ARC有效时,大多数情形下编译器会恰当地进行判断,自动生成将Block从栈上复制到堆上的代码。这是为了防止其被废弃而导致我们访问错误。
下面这个例子就是堆Block,用它来说明:

typedef int (^blk_t)(int);blk_t func(int rate) {return ^(int count) {return rate * count;};
}

源代码:

blk_t func(int rate) {//因为ARC处于有效的状态,所以blk_t tmp实际上与附有__strong 修饰符的blk_t __strong tmp 相同blk_t tmp = &__func_block_impl_0(__func_block_func_0,&__func_block_desc_0_DATA, rate);//通过 objc4运行时库的runtime/objc-arrmm可知,objc_retainBlock函数实际上就是_Block_copy 函数tmp = objc_retainBlock(tmp);//等同于 tmp = _Block_copy(tmp);//最后将tmp放入自动释放池中进行返回return objc_autoreleaseReturnValue(tmp);
}    
/**将通过Block语法生成的Block,*即配置在栈上的Block用结构体实例*赋值给相当于Block类型的变量tmp中。*/
tmp = _Block_copy(tmp);
/**_Block_copy 函数*将栈上的Block复制到堆上。*复制后,将堆上的地址作为指针赋值给变量tmp。*/return objc_autoreleaseReturnValue(tmp);
/**将堆上的Block作为Objective-c对象*注册到autoreleasepool中,然后返回该对象。*/

通过上面的例子就可以知道将 Block 作为函数返回值返回时,编译器会自动生成复制到堆上的代码。

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

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

相关文章

使用jenkins-pipeline进行利用项目文件自动化部署到k8s上

Discard old builds:丢弃旧的构建,目的是管理存储空间、提升性能以及保持环境整洁 Do not allow concurrent builds: 禁止并发构建是指同一时间内只允许一个构建任务执行,避免多个构建同时运行可能带来的问题 Do not allow the pipeline to resume if the controller resta…

DockerHub搜索并拉取一个Redis镜像

1)去DockerHub搜索Redis镜像 2)查看Redis镜像的名称和版本 3)利用docker pull命令拉取镜像 4)利用docker save命令将 redis:latest打包为一个redis.tar包 5)利用docker rmi 删除本地的redis:latest 6)利用…

Flutter-自定义图片3D画廊

效果 需求 3D画廊效果 设计内容 StackGestureDetectorTransformPositioned数学三角函数 代码实现 具体代码大概300行 import dart:math;import package:flutter/material.dart; import package:flutter_xy/widgets/xy_app_bar.dart;import ../../r.dart;class ImageSwitc…

zookeeper集群安装部署和集群异常处理

准备jdk和zookeeper安装包【官网即可下载】 zookeeper-3.5.1-alpha.tar.gz jdk1.7.0_8020200612.tar 准备三台linux虚拟机【具体以项目实际需要为准】,并安装jdk和zookeeper 虚拟机地址如下:194.1.1.86(server.1)、194.1.1.74…

Lyricsx让歌词悬浮于最顶层

欢迎来到我的博客,代码的世界里,每一行都是一个故事 Lyricsx让歌词悬浮于最顶层 前言Lyricsx的特色功能:歌词展现的新高度安装在 macOS 上安装 Lyricsx: 前言 在音符的世界里,歌词是一把打开心扉的钥匙,而…

全国各省市县统计年鉴/中国环境统计年鉴/中国工业企业数据库/中国专利数据库/污染排放数据库

统计年鉴是指以统计图表和分析说明为主,通过高度密集的统计数据来全面、系统、连续地记录年度经济、社会等各方面发展情况的大型工具书来获取统计数据资料。 统计年鉴是进行各项经济、社会研究的必要前提。而借助于统计年鉴,则是研究者常用的途径。目前国…

蓝桥之手撕排序算法——冒泡、选择、插入、快排、归并(Python版)

目录 1. 排序引言 2. 冒泡排序 2.1 算法思想 2.2 代码实现 2.3 时空复杂度分析 3. 选择排序 3.1 算法思想 3.2 代码实现 3.3 时空复杂度分析 4. 插入排序 4.1 算法思想 4.3 代码实现 4.4 时空复杂度分析 5. 快速排序 5.1 算法思想 5.2 代码实现 5.3 时空复杂度分…

考研C语言复习进阶(5)

目录 1. 为什么使用文件 2. 什么是文件 2.1 程序文件 2.2 数据文件 2.3 文件名 3. 文件的打开和关闭 3.1 文件指针 3.2 文件的打开和关闭 4. 文件的顺序读写 ​编辑 ​编辑 4.1 对比一组函数: ​编辑 5. 文件的随机读写 5.1 fseek 5.2 ftell 5.3 rewind…

深度学习实战模拟——softmax回归(图像识别并分类)

目录 1、数据集: 2、完整代码 1、数据集: 1.1 Fashion-MNIST是一个服装分类数据集,由10个类别的图像组成,分别为t-shirt(T恤)、trouser(裤子)、pullover(套衫&#xf…

论文笔记:Llama 2: Open Foundation and Fine-Tuned Chat Models

导语 Llama 2 是之前广受欢迎的开源大型语言模型 LLaMA 的新版本,该模型已公开发布,可用于研究和商业用途。本文记录了阅读该论文的一些关键笔记。 链接:https://arxiv.org/abs/2307.09288 1 引言 大型语言模型(LLMs&#xff…

深入探索C与C++的混合编程

实现混合编程的技术细节 混合使用C和C可能由多种原因驱动。一方面,现有的大量优秀C语言库为特定任务提供了高效的解决方案,将这些库直接应用于C项目中可以节省大量的开发时间和成本。另一方面,C的高级特性如类、模板和异常处理等,…

ONLYOFFICE文档8.0全新发布:私有部署、卓越安全的协同办公解决方案

ONLYOFFICE文档8.0全新发布:私有部署、卓越安全的协同办公解决方案 文章目录 ONLYOFFICE文档8.0全新发布:私有部署、卓越安全的协同办公解决方案摘要📑引言 🌟正文📚一、ONLYOFFICE文档概述 📊二、ONLYOFFI…

2024同城招标投标微网站微信小程序版本源码

2024同城招标投标微网站&微信小程序版本源码 功能介绍: 同城招投标这套程序主要是为了解决招投标问题 用户缴纳保证金发布起来招标,然后商家进行认证成功后可以对招标发起投标,投标过程也需要缴纳保证金,单招标结束或者下架保…

第五篇:数字视频广告格式概述 - IAB视频广告标准《数字视频和有线电视广告格式指南》

第五篇:第五篇:数字视频广告格式概述 - IAB视频广告标准《数字视频和有线电视广告格式指南 --- 我为什么要翻译介绍美国人工智能科技公司IAB系列技术标准(2) ​​​​​​​翻译计划 第一篇序言第二篇简介和目录第三篇概述- IA…

完成系统支持Github三方登录

文章目录 1、需求2、在对接系统中完成客户端注册3、创建客户端应用4、CommonOAuth2Provider SpringSecurity OAuth2.0文档: https://docs.spring.io/spring-security/reference/servlet/oauth2/index.html 1、需求 对接Github,在自己系统实现支持Githu…

Mit6.s081 前置开发环境: 虚拟机ubuntu + ssh + vscode

虚拟机 ssh vscode 前置条件 下载VMware Download VMware Workstation ProUbuntuUbuntu系统下载 | Ubuntu vscode Visual Studio Code - Code Editing. Redefined Ubuntu版本:20.04 Ubuntu基本操作 ubuntu 安装 ssh 服务 sudo apt-get install openssh-serv…

TT-100K数据集,YOLO格式

TT-100K数据集YOLO格式,分为train、val和test,其中train中共有6793张图片,val中共有1949张图片,test中共有996张图片。数据集只保留包含图片数超过100的类别。共计46类。

基于支持向量机(svm)的人脸识别

注意:本文引用自专业人工智能社区Venus AI 更多AI知识请参考原站 ([www.aideeplearning.cn]) 数据集加载与可视化 from sklearn.datasets import fetch_lfw_people faces fetch_lfw_people(min_faces_per_person60) # Check out sample…

MS12_020 漏洞利用与安全加固

文章目录 环境说明1 MS12_020 简介2 MS12_020 复现过程3 MS12_020 安全加固 环境说明 渗透机操作系统:kali-linux-2024.1-installer-amd64漏洞复现操作系统: cn_win_srv_2003_r2_enterprise_with_sp2_vl_cd1_X13-46432 1 MS12_020 简介 MS12_020 漏洞全称为&#x…

【视频异常检测】Delving into CLIP latent space for Video Anomaly Recognition 论文阅读

Delving into CLIP latent space for Video Anomaly Recognition 论文阅读 ABSTRACT1. Introduction2. Related Works3. Proposed approach3.1. Selector model3.2. Temporal Model3.3. Predictions Aggregation3.4. Training 4. Experiments4.1. Experiment Setup4.2. Evaluat…