【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 作为函数返回值返回时,编译器会自动生成复制到堆上的代码。
相关文章:
【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语言的扩…...
体验OceanBase OBD V2.5.0 组件内扩容和组件变更
背景 OBD 是OceanBase的命令行部署工具,在 obd V2.5.0 版本之前,其主要功能主要是部署各类组件,例如 oceanbase-ce,obproxy-ce,obagent 等。然而,它并不支持组件的变更操作以及组件内部的扩缩容调整。具体来说: 1、若…...
关于前端的学习
目录 前言: 1.初识HTML: 1.1超文本: 1.2标记语言: 2.关于html的基本框架: 3.HTML基本文字标签: 3.1.h标题标签: 3.3 文本内容: 3.4换行的和分割的: 3.5 特殊文字标签: 3.5.1表面上看着三对的结果呈现都是一样的: 3.5.2但是其背后的效果其实是不一样的: 3.6转义字符:…...
DataX脚本告别手动编写,用大模型或Java代码自动生成
在离线数仓开发中,DataX支持多种数据源,性能好抽取速度快。美中不足的是,对需要抽取的每一张表都需要写配置文件,这样很繁琐和耗时。可以用大模型提示词来节省这方面的工作量。ChatGPT等大模型并不能很智能提供一个完全正确的,需要提供一个模板让它参考。 方法1:用大模型…...
ASP.NET通过Appliaction和Session统计在人数和历史访问量
目录 背景: Appliaction: Session: 过程: 数据库: Application_Start: Session_Start: Session_End: Application_End: 背景: 事件何时激发Application_Start在调用当前应用…...
在基于全志V851se的TinyVision上手动构建 Linux 6.1 + Debian 12 镜像
构建 SyterKit 作为 Bootloader SyterKit 是一个纯裸机框架,用于 TinyVision 或者其他 v851se/v851s/v851s3/v853 等芯片的开发板,SyterKit 使用 CMake 作为构建系统构建,支持多种应用与多种外设驱动。同时 SyterKit 也具有启动引导的功能&a…...
使用jenkins-pipeline进行利用项目文件自动化部署到k8s上
Discard old builds:丢弃旧的构建,目的是管理存储空间、提升性能以及保持环境整洁 Do not allow concurrent builds: 禁止并发构建是指同一时间内只允许一个构建任务执行,避免多个构建同时运行可能带来的问题 Do not allow the pipeline to resume if the controller resta…...
unity发布安卓获取读取权限
一、Player Settings 设置 Player Settings>Player>Other Settings> Android > Write Permission > External (SDCard). 二、代码 using System.Collections; using System.Collections.Generic; using System.IO; using UnityEngine; using UnityEngine.Andr…...
VSCode下使用github初步
由于各种需要,现在需要统一将一些代码提交搞github,于是有了在VSCode下使用github的需求。之前只是简单的使用git clone,代码提交这些用的是其他源代码工具,于是得学习实操下,并做一记录以备后用。 安装 VSCode安装 …...
华为设备配置命令大全
目录 一、华为设备常用命令视图 二、返回命令和保存命令 三、设置设备名称 四、关闭泛洪信息 五、设置设备接口的IP地址和子网掩码 六、交换机的登录 6.1、设置Consile接口密码 6.2、设置Telent接口密码 七、VLAN配置 7.1、创建VLAN 7.2、进入vlan视图 7.3、把端口…...
详解基于快速排序算法的qsort的模拟实现
目录 1. 快速排序 1.1 快速排序理论分析 1.2 快速排序的模拟实现 2. qsort的模拟实现 2.1 qsort的理论分析 2.2 qsort的模拟实现 qsort函数是基于快速排序思想设计的可以针对任意数据类型的c语言函数。要对qsort进行模拟实现,首先就要理解快速排序。 1. 快…...
鸿蒙Harmony应用开发—ArkTS声明式开发(绘制组件:Polyline)
折线绘制组件。 说明: 该组件从API Version 7开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。 子组件 无 接口 Polyline(value?: {width?: string | number, height?: string | number}) 从API version 9开始,…...
项目风险管理
项目风险管理 1 规划风险管理2 识别风险1.2 输出 3 实施定性风险分析3.1 输入3.2 输出 4 实施定量风险分析4.1 输入4.2 输出 5 规划风险应对5.1 输入5.2 输出 6 实施风俗应对6.1 输入6.2 输出 7 监督风险7.1 输入7.2 输出 项目风险是一种不确定的事件或条件,一旦发生…...
glib交叉编译
Glib交叉编译 逸一时,误一世。 —— 田所浩二「夏夜银梦」 交叉编译 GLib 涉及到在一个平台上生成能够在另一个平台上运行的目标文件。在这种情况下,我们将会在一台主机(通常是开发机器)上使用交叉编译工具链来构建 GLib 库&#…...
Android11实现能同时开多个录屏应用(或者共享屏幕或投屏时录屏)
1.概述 Android原生对MediaProjection的管理逻辑,是如果服务端已经保存有MediaProjection的实例,那么再次创建的时候,之前的MediaProjection实例就会被暂停,并且引用指向新的实例,也就导致了当开启后一个录屏应用时&a…...
音视频实战---音频重采样
1、使用swr_alloc()创建重采样实例 2、使用av_opt_set_int函数设置重采样输入输出参数 3、使用swr_init函数初始化重采样器 4、使用av_get_channel_layout_nb_channels函数计算输入源的通道数 5、给输入源分配内存空间–av_samples_alloc_array_and_samples 6、计算输出采…...
主存中存储单元地址的分配
主存中存储单元地址的分配 为什么写这篇文章? 因为我看书中这部分时,看到下面的计算一下子没反应过来: 知识回顾(第1章) 计算机系统中,字节是最小的可寻址的存储单位,通常由8个比特(bit&…...
Python和R的区别是什么,Python与R的应用场景是什么?
如果你这么问,那么你可能正站在数据科学的起点。对于志在成为数据专业人员的你来说,学习编程是无疑的。我想行你早就听过Python 与R的比较之声,并在选择中感到困惑。在此,我想说,也算是一种安慰吧:对于语言…...
azure databricks 常用的JDBC连接
做个笔记常用的spark-jdbc连接 1、mysql 的连接 def query_mysql(database,sqlstr):jdbcUsernamejdbcHostname " "jdbcDatabase ""jdbcPort 3306mysql_df spark.read \.format("jdbc") \.option("driver","com.mysql.cj.jdb…...
功能齐全的免费 IDE Visual Studio 2022 社区版
面向学生、开放源代码和单个开发人员的功能齐全的免费 IDE 下载地址 Visual Studio 2022 社区版 - 下载最新的免费版本 Visual Studio 2022 Community Edition – Download Latest Free Version 准备安装 选择需要安装的程序 安装进行中 使用C学习程序设计相关知识并培养编程…...
FreeRTOS入门基础
RTOS是为了更好地在嵌入式系统上实现多任务处理和时间敏感任务而设计的系统。它能确保任务在指定或预期的时间内得到处理。FreeRTOS是一款免费开源的RTOS,它广泛用于需要小型、预测性强、灵活系统的嵌入式设备。 创建第一个任务 任务函数:任务是通过函数…...
蓝桥杯-24点-搜索
题目 思路 --暴力递归全组合的方法。只有4个数,4种计算方式,共有4 * 3 * 2 * 1 * 4种不同的情况,可以写递归来实现。 --每次计算都是两个数之间的运算,因此4个数需要3次计算,第一次计算前有4个数,第二次有…...
【附下载】3Ds Max从安装、配置到入门提高和高级用法
#3Ds Max 一、安装 1.1 安装说明 地址:链接:https://pan.baidu.com/s/1lwKMbgbE32wCL6PpMv706A?pwddll8 提取码:dll8 –来自百度网盘超级会员V2的分享 安装说明:文件夹里有安装说明 安装解压即可 关键就是将crack文件放到自己…...
开源堡垒机Jumpserver
开源堡垒机Jumpserver 文章目录 开源堡垒机Jumpserver1 Jumpserver介绍2 Jumpserver部署用户管理资产创建账号管理模板添加 用户组管理权限管理远程连接免密连接 1 Jumpserver介绍 Jumpserver 是全球首款完全开源的堡垒机,使用 GNU GPL v2.0 开源协议,是…...
PyTorch学习笔记之基础函数篇(十五)
文章目录 数值比较运算8.1 torch.equal()函数8.2 torch.ge()函数8.3 torch.gt()函数8.4 torch.le()函数8.5 torch.lt()函数8.6 torch.ne()函数8.7 torch.sort()函数8.8 torch.topk()函数 数值比较运算 8.1 torch.equal()函数 torch.equal(tensor1, tensor2) -> bool这个函…...
Latex插入pdf图片,去除空白部分
目录 参考链接: 流程: 参考链接: 科研锦囊之Latex-如何插入图片、表格、参考文献 http://t.csdnimg.cn/vpSJ3 流程: Latex的图片插入支持PDF文件,这里笔者建议都使用PDF文件进行图片的插入,因为PDF作…...
微服务:高并发带来的问题的容错方案
1.相关脚本(陈天狼) 启动nacos客户端: startup.cmd -m standalone 启动sentinel控制台: # 直接使⽤jar命令启动项⽬(控制台本身是⼀个SpringBoot项⽬) java -Dserver.port8080 -Dcsp.sentinel.dashboard.serverlocalhost:808…...
sqllab第35-45关通关笔记
35关知识点: 宽字节注入数值型注入错误注入 payload:id1andextractvalue(1,concat(0x7e,database(),0x7e))0--联合注入 payload:id0unionselect1,database(),version()-- 36关知识点: 字符型注入宽字节注入错误注入 payload:id1%df%27andextractvalue(…...
Jenkins流水线将制品发布到Nexus存储库
1、安装jenkins(建议别用docker安装,坑太多) docker run -d -p 8089:8080 -p 10241:50000 -v /var/jenkins_workspace:/var/jenkins_home -v /etc/localtime:/etc/localtime --name my_jenkins --userroot jenkins/jenkins:2.449 坑1 打开x…...
信息学奥赛一本通之MAC端VSCode C++环境配置
前提 安装 Visual Studio CodeVSCode 中安装 C/C扩展确保 Clang 已经安装(在终端中输入命令:clang --version 来确认是否安装)未安装,在命令行执行xcode-select --install 命令,会自行安装,安装文件有点大…...
保定网站建设价格低/站长统计app
标题的含义给出N问题。和概率P,然后给予相应的分数为每个问题x(每个问题只有两种选择,纠正错误)。两个人来回答。一个人是随机选择的答案,问:还有一个人的至少一些点的能力有保证P概率不会失败。01背包&…...
类似qq空间的网站/南昌seo推广公司
文章目录1. 数据命令1.1 启动kafka服务1.2 创建topic1.3 向topic中发送数据1.4 开启一个消费端2. check命令2.1 查看kafka topic列表2.2 查看kafka特定topic的详情2.3 查看consumer group列表2.4 查看特定consumer group 详情2.5 查看指定topic上每个partition的offset1. 数据命…...
美国做南京做网站/seo实战培训视频
1:触发条件:创建的实体类生成到数据库表时报错 报错信息:TypeError: __init__() missing 1 required positional argument: on_delete from django.db import models# Create your models here. class Classes(models.Model):# 班级表title…...
怎么做网站图片的切换图/襄阳网站seo
-- StartOracle 提供了以下四个函数用来做四舍五入。CEIL 向上四舍五入FLOOR 向下四舍五入ROUND 四舍五入TRUNC 去掉小数下面让我们通过例子来看看它们之间的不同。SELECT 5.5 NUM, CEIL(5.5) CEIL, FLOOR(5.5) FLOOR, ROUND(5.5) ROUND, TRUNC(5.5, 0) TRUNC FROM DUALUNION A…...
石家庄短视频运营/广州seo工资
1.日期与时间函数 - 日期、时间提取 A1 NOW() >>> 2019/6/16 21:31:53函数名函数功能示例输出结果YEAR从日期中提取出"年" YEAR(A1)2019MONTH从日期中提取出"月" MONTH(A1)6DAY从日期中提取出"日" DAY(A1)16WEEKDAY计算出与日期相…...
erp软件有哪些软件/科学新概念seo外链
最近在确定项目架构模型时, 出现了一些问题, 参考了许多文章和设计, 以下记录着这些文章和设计, 以便以后总结: 1. 企业应用架构模式 - Patterns of Enterprise Application Architecture 试读 (企业应用架构模式 Patterns of Enterprise Application Architecture (RicCC)…...