【iOS】—— 浅看block源码
block
文章目录
- block
- 如何通过终端clang生成源码cpp文件
- block实质
- 截获自动变量
- 全局变量和静态变量的截获
- __block说明符
- iOS开发“强弱共舞”——weak和strong配套使用解决block循环引用问题
如何通过终端clang生成源码cpp文件
之前在学习block中学习的比较浅,只看了oc高级编程书上有的源码,学习的比较浅,这节来对之前的作以补充。
首先来看看怎么将oc文件生成cpp的源码文件:
将项目大文件里的二级文件,打开终端 cd 并将此文件拖入:
然后再输入:
clang -rewrite-objc main.m
回车打开之前的项目文件,即可看到生成了一个cpp文件,打开就是oc的c++源码。
打开后发现有60000多行,很抽象,关键部分看最前面和最后面即可。
block实质
我们现在时文件中写一个最简单的block,看看源码是什么:
int main(int argc, const char * argv[]) {@autoreleasepool {// insert code here...void (^blk)(void) = ^{printf("Block\n");};blk();}return 0;
}
源码:
//经过clang转换后的C++代码
struct __block_impl {void *isa;//指向所属类的指针int Flags;//标志性参数,暂时没用到所以默认为0int Reserved;//今后版本升级所需的区域大小。void *FuncPtr;//函数指针,指向实际执行的函数,也就是block中花括号里面的代码内容。
};struct __main_block_impl_0 {struct __block_impl impl;//上面点1中的结构体的变量struct __main_block_desc_0* Desc;//上面点2中的结构体的指针 __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("Block\n");
}static struct __main_block_desc_0 {size_t reserved; //今后版本升级所需区域的大小(一般填0)size_t Block_size; //Block的大小
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};int main(int argc, const char * argv[]) {void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);return 0;
}
我们来分开说一下每部分的意义:
1.第一个结构体
struct __block_impl {void *isa;//指向所属类的指针int Flags;//标志性参数,暂时没用到所以默认为0int Reserved;//今后版本升级所需的区域大小。void *FuncPtr;//函数指针,指向实际执行的函数,也就是block中花括号里面的代码内容。
};
- 结构体的名称:impl即implementation的缩写,换句话说这一部分是block的实现部分结构体。
- void *isa:声明一个不确定类型的指针,用于保存Block结构体实例。
- int Flags:标识符。
- int Reserved:今后版本升级所需的区域大小。
- void *FuncPtr:函数指针,指向实际执行的函数,也就是block中花括号里面的代码内容。
2.在介绍struct __main_block_impl_0结构体之前,先介绍一下这个结构体里的static struct __main_block_desc_0结构体
这个结构体在上面一坨源码的最底下:
static struct __main_block_desc_0 {size_t reserved; //今后版本升级所需区域的大小(一般填0)size_t Block_size; //Block的大小
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
- 第一个成员变量指的是今后版本升级所需区域的大小(一般填0)。
- 第二个成员变量是Block的大小。
__main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};:
1⃣️.这就是和我们平时用结构体一样,在定义完最后写一个结构体实例变量,变量名就是__main_block_desc_0_DATA。
2⃣️.其中reserved为0,Block_size是sizeof(struct __main_block_impl_0)。
3.接下来看struct __main_block_impl_0结构体
struct __main_block_impl_0 {struct __block_impl impl;//上面点1中的结构体的变量struct __main_block_desc_0* Desc;//上面点2中的结构体的指针__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;}
};
- 第一个成员变量是impl,也就是上面点1中的结构体的变量。
- 第二个成员变量是Desc指针,就是上面点2中的结构体的指针。
- 剩下的代码是:初始化含有这些结构体的__main_block_impl_0结构体的构造函数,给结构体里的元素赋值。
4.然后是static void __main_block_func_0
static void __main_block_func_0(struct __main_block_impl_0 *__cself {printf("Block\n");
}
这个就比较简单了,Blcok执行的实际代码块。也是点3中fp指针指向的函数。括号中的参数__cself是相当于OC语言版的self,代表的是Block本身。
5.最后来看main函数
int main(int argc, const char * argv[]) {void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);return 0;
}
这两句代码不长但是比较恶心,我们先来看第一句,把强制类型转换去一下试试:
//加了个临时变量temp,看着更方便点
struct __main_block_impl_0 temp = __main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA);
struct __main_block_impl_0 *blk = &temp;
该源代码将__main_block_impl_0结构体类型的自动变量,也就是栈上生成的__main_block_impl_0结构体实例的指针,赋值给__main_block_impl_0结构体指针类型的变量blk。
第二行代码就是相当于源代码中的blk(),即使用该Block部分。去掉转换部分就是:
(*blk->impl.FuncPtr)(blk);
这是使用函数指针调用函数。由Block语法转换的__main_block_impl_0函数的指针被赋值成员变量FunPtr中。
截获自动变量
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);};blk();return 0;
}
转化后的代码:
//经过clang转换后的C++代码
struct __block_impl {void *isa;int Flags;int Reserved;void *FuncPtr;
};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;int val = __cself->val; printf(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 argc, const char * argv[]) {int dmy = 256;int val = 10;const char *fmt = "val = %d\n";void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, fmt, val));return 0;
}
与上次不同的是在Block语法表达式中使用的自动变量被当作成员变量追加到了__main_block_impl_0结构体中:
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)
初始化时自动变量fmt和val进行了赋值操作:
impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;fmt = "val = %d\n";val = 10;
在转换后的代码中,截获到__main_block_impl_0结构体实例的成员变量上的自动变量,这些变量在Block语法表达式之前被声明定义,所以之后即使改变自动变量的值也不会对Block语法中的内容有所变化。
总的来说,所谓“截获自动变量值”意味着在执行Block语法时,Block语法表达式所使用的自动变量值被保存到Block的结构体实例(即Block自身中)。
总结一下就是说:
调用__main_block_impl_0的构造函数,构造函数正常有两个参数,func_0和desc_0,在截获自动变量时,会把需要截获的自动变量也放入参数列表中,同时__main_block_impl_0中也会增加两个成员变量a,b,构造函数带参数就是自动给这两个成员变量赋值。在调用func_0时,直接通过__cself(相当于self,其传递的参数也是其本身)的a、b(此时的a,b就是成员变量中的,因为上面的构造函数已经赋值了)
全局变量和静态变量的截获
我们以以下代码为例:
int global_val = 10; // 全局变量
static int static_global_val = 20; // 静态全局变量int main(int argc, const char * argv[]) {@autoreleasepool {static int static_val = 30; // 静态局部变量void (^myLocalBlock)(void) = ^{global_val *= 1;static_global_val *= 2;static_val *= 3;printf("static_val = %d, static_global_val = %d, global_val = %d\n",static_val, static_global_val, global_val);};myLocalBlock();}return 0;
}
源码:
struct __block_impl {void *isa;int Flags;int Reserved;void *FuncPtr;
};int global_val = 10;
static int static_global_val = 20;struct __main_block_impl_0 {struct __block_impl impl;struct __main_block_desc_0* Desc;int *static_val;__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_static_val, int flags=0) : static_val(_static_val) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {int *static_val = __cself->static_val; // bound by copyglobal_val *= 1;static_global_val *= 2;(*static_val) *= 3;printf("static_val = %d, static_global_val = %d, global_val = %d\n",(*static_val), static_global_val, global_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 argc, const char * argv[]) {/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; static int static_val = 30;void (*myLocalBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &static_val));((void (*)(__block_impl *))((__block_impl *)myLocalBlock)->FuncPtr)((__block_impl *)myLocalBlock);}return 0;
}
我们主要来看下这段代码:
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {int *static_val = __cself->static_val; // bound by copyglobal_val *= 1;static_global_val *= 2;(*static_val) *= 3;printf("static_val = %d, static_global_val = %d, global_val = %d\n",(*static_val), static_global_val, global_val);}
我们发现只有static_val是从__cself中获取的值
再来看看上面的一段代码:
struct __main_block_impl_0 {struct __block_impl impl;struct __main_block_desc_0* Desc;int *static_val;__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_static_val, int flags=0) : static_val(_static_val) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;}
};
- 我们发现全局变量,静态全局变量,我们的Block都没有用结构体去特地保存它
- 只有对于我们的静态局部变量会来保存,但这里要注意,我们使用的不是int static_val,而是int *static_val
- 也就是说我们使用一个指针来保存的静态局部变量
- 它会直接保存该变量的地址,之后的操作也是直接对该值本身进行操作,而不是向之前截获的那些变量,等于是重新开辟空间进行保存
产生这个问题的原因:
原因在于,我们的静态变量是存在数据区的,在程序结束前它其实一直都会存在,之所以会被称为局部,只是说出了作用域无法调用到它了,并不是说这块数据不存在了。因此我们只要自己准备好一个指针,保证出了作用域依然能调用到他就行;而对于自动变量,它们真正的问题在于一但出了作用域,直接被释放了,所以要在结构体里开辟空间重新存放,进行值传递
__block说明符
来看书上的例子:
int main(int argc, const char * argv[]) {__block int val = 10;void (^blk)(void) = ^{val = 1;printf("val = %d\n", val);};blk();return 0;
}
转换后的源码:
struct __Block_byref_val_0 {void *__isa;__Block_byref_val_0 *__forwarding;int __flags;int __size;int val;
};struct __main_block_impl_0 {struct __block_impl impl;struct __main_block_desc_0* Desc;__Block_byref_val_0 *val; // by ref__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_val_0 *_val, int flags=0) : val(_val->__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_val_0 *val = __cself->val; (val->__forwarding->val) = 1;printf("val = %d\n", (val->__forwarding->val));
}static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->val, (void*)src->val, BLOCK_FIELD_IS_BYREF);
}static void __main_block_dispose_0(struct __main_block_impl_0*src {_Block_object_dispose((void*)src->val, BLOCK_FIELD_IS_BYREF);
}static struct __main_block_desc_0 {size_t reserved;size_t Block_size;void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0
};int main(int argc, const char * argv[]) {__Block_byref_val_0 val = {0,(__Block_byref_val_0 *)&val, 0, sizeof(__Block_byref_val_0), 10};blk = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, &val, 0x22000000);return 0;
}
和没加修饰符的比较来说,主要有两处不同:
struct __Block_byref_val_0 {void *__isa;__Block_byref_val_0 *__forwarding;int __flags;int __size;int val;
};
在第一个结构体中,我们发现多了一个__Block_byref_val_0 *__forwarding,这个相当于指向该结构体本身的一个指针。
第二个地方:
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {__Block_byref_val_0 *val = __cself->val; (val->__forwarding->val) = 1;printf("val = %d\n", (val->__forwarding->val));
}
在这里我们发现打印的并不是__Block_byref_val_0 *val而是(val->__forwarding->val)。
最后来看一下主函数:
int main(int argc, const char * argv[]) {__Block_byref_val_0 val = {0,(__Block_byref_val_0 *)&val, 0, sizeof(__Block_byref_val_0), 10};blk = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, &val, 0x22000000);return 0;
}
看下其中赋值部分:
__Block_byref_val_0 val = {0,(__Block_byref_val_0 *)&val, 0, sizeof(__Block_byref_val_0), 10};
这个__block变量val变为了__Block_byref_val_0结构体变量。通过调用 static void __main_block_func_0函数(通过__Block_byref_val_0结构体成员变量__forwarding访问成员变量val),将10赋给val。
iOS开发“强弱共舞”——weak和strong配套使用解决block循环引用问题
__weak是为了解决循环引用
如果一个对象A持有了一个block,同时block内又持有了对象A,为了解决循环引用我们要在用__weak修饰完对象A后再去持有它,这样就解决了循环引用。
__strong可以防止block持有的对象提前释放
我们用GCD延迟方法打印self的信息:
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{[self dismissViewControllerAnimated:YES completion:nil];__weak typeof(self) weakSelf = self;void (^Block) (void) = ^{dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{NSLog(@"%@", weakSelf);});};Block();
}
输出结果:
点击屏幕,当前控制器消失,同时被销毁掉,5秒后打印的weakSelf就是一个(null),而我们如果在block内使用__strong后就能保证再打印完strongSelf之后再释放当前控制器。
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{[self dismissViewControllerAnimated:YES completion:nil];__weak typeof(self) weakSelf = self;void (^Block) (void) = ^{__strong typeof(self) strongSelf = weakSelf;dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{NSLog(@"%@", strongSelf);});};Block();
}
相关文章:
【iOS】—— 浅看block源码
block 文章目录block如何通过终端clang生成源码cpp文件block实质截获自动变量全局变量和静态变量的截获__block说明符iOS开发“强弱共舞”——weak和strong配套使用解决block循环引用问题如何通过终端clang生成源码cpp文件 之前在学习block中学习的比较浅,只看了oc…...
I.MX6ULL_Linux_系统篇(23) busybox文件系统构建
Linux“三巨头”已经完成了 2 个了,就剩最后一个 rootfs(根文件系统)了,本章我们就来学习一下根文件系统的组成以及如何构建根文件系统。这是 Linux 移植的最后一步,根文件系统构建好以后就意味着我们已经拥有了一个完整的、可以运行的最小系…...
shpjs将.zip文件转成geoJson
一、npm install shpjs二、import shp from shpjs三、async setLayerSource() {const geoJsonData await shp(dataUrl)}一直报错:是因为Buffer这个插件一直没找到Uncaught Error: nodebuffer is not supported by this browser解决办法npm install node-polyfill-w…...
eBay是不是一定要养号?是的
相信每个运营过eBay的用户遇到过这个棘手的问题,eBay个人账户的刊登数量是有限的,尤其是新账户只有5个sku,所以一开始的运营会比较艰难。想要快点走上正轨的话,就一定要去注重这个“养号”。eBay运营模式 1.拍卖 eBay最开始是一个…...
宝塔(二):升级JDK版本
目录 背景 一、下载JDK17 二、配置环境变量 三、配置新的JDK路径 背景 宝塔的软件商店只有JDK8,不满足我当前项目所需的JDK版本,因此想对JDK版本进行升级,升级为JDK17。 一、下载JDK17 先进入 /usr/lib/jvm 目录 点击终端,进…...
【LeetCode】螺旋矩阵 [M](数组)
54. 螺旋矩阵 - 力扣(LeetCode) 一、题目 给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。 示例 1: 输入:matrix [[1,2,3],[4,5,6],[7,8,9]] 输出:[1,2,3,…...
实验二:动态规划
1.双11的红包雨 问题描述 双11到了,据说这2天会下红包雨,每个红包有不同的价值,小k好开心,但有个规则,就只能接掉落在他身旁的10米范围内的红包(0-10这11个位置)。小k想尽可能的多抢红包&…...
华为机试 HJ27 查找兄弟单词
题目链接:https://www.nowcoder.com/practice/03ba8aeeef73400ca7a37a5f3370fe68?tpId37&tqId21250&rp1&ru/exam/oj/ta&qru/exam/oj/ta&sourceUrl%2Fexam%2Foj%2Fta%3Fdifficulty%3D3%26page%3D1%26pageSize%3D50%26search%3D%26tpId%3D37%26t…...
佩戴舒适的蓝牙耳机有哪些?佩戴舒适的蓝牙耳机推荐
音乐对许多人而言,都是一种抚慰生命的力量,特别是在上下班的时候,在熙熙攘攘的人流中,戴着耳机听一首动听的曲子,无疑会让人心情变得更加舒畅,要想获得出色的音乐体验,没有一副出色的耳机可不行…...
ESXI主机安装Zabbix 6.2
1:首先下载Zabbix Appliance 2:还需要下载VMware converter ,这个需要VMware的账号,或者从其他地方下载也可以。 3:vmdk格式的 image 可直接在 VMware Player、 Server 和 Workstation 产品中使用。要在 ESX,、ESXi 和 vSphere 中…...
【Linux 网络编程1】使用UDP/TCP编写套接字,多进程/多线程版本的TCP编写的套接字,将套接字封装
目录 1.学习网络编程前的一些基础知识 2.UDP(user datagram protocol)协议的特点 3.使用有UPD编写套接字 4.使用TCP编写套接字 4.2.TCP客服端 4.3.TCP服务器端 4.4.单进程版本(没有人会使用) 4.5.多进程版本 4.6.多线程版本 5.把套接字封装 1…...
滑动窗口求最大和最小
滑动窗口 要区分最小和最大滑窗,内层while循环的条件和更新结果的地方 核心: 关键的区别在于,最大滑窗是在迭代右移右边界的过程中更新结果,而最小滑窗是在迭代右移左边界的过程中更新结果。 最小滑窗 给定数组 nums࿰…...
跟ChatGPT学量化,菜鸟炒股也不怕
你是一个python资深量化开发工程师,请介绍量化交易的基本框架 量化交易的基本框架包括以下几个步骤: 数据获取:获取市场数据和公司财务数据等,可以使用API接口、爬虫等方式获取。数据清洗和预处理:对获取到的数据进行清…...
扬帆优配|2600亿新能源巨头狂飙!外资唱多中国:再涨15%
全国停摆的危机,正在迫临法国。 大停工正在将法国推向风险境地。法国政府估计,当地时间3月7日,将迸发全国大型停工游行。法国总工会宣告,到时将让全法国停摆。法国担任交通业务的部长级代表克莱蒙博讷正告称,7日将成为…...
ChatGPT技术与商业模式及产业发展布局方案
文章目录模块一:概念模块二:架构模块三:技术模块四:算力模块五:体验模块六:应用模块七:商业模块八:产业模块九:建议结语主要内容: 采用模块化教学方法&#x…...
CIMCAI port ai shipping ai artificial intelligence smart port
上海人工智能独角兽中集集团高科技中集飞瞳,是全球应用落地最广,规模最大,最先进的的港航人工智能高科技企业,工业级成熟港航人工智能产品全球规模化落地应用,全球前三大船公司及港口码头应用落地。上海人工智能独角兽…...
《数据解构》HashMap源码解读
👑作者主页:Java冰激凌 📖专栏链接:数据结构 目录 了解HashMap HashMap的构造 两个参数的构造方法 一个参数的构造方法 不带参数的构造方法 哈希表初始化的长度 HashMap源码中的成员 Pt Get 了解HashMap 首先我们要明…...
Databend 开源周报 第 83 期
Databend 是一款现代云数仓。专为弹性和高效设计,为您的大规模分析需求保驾护航。自由且开源。即刻体验云服务:https://app.databend.com 。Whats New探索 Databend 本周新进展,遇到更贴近你心意的 Databend 。Support for WebHDFSHDFS 是大数…...
Spring | 基础
1. IOC和DI IOC:控制反转,其思想是反转资源获取的方向,传统的资源查找方式要求组件向容器发起请求查找资源,作为回应,容器适时的返回资源。而应用了 IOC 之后,则是**容器主动地将资源推送给它所管理的组件…...
windows7安装sql server 2000安装步骤 及安装过程中遇到的问题和解决方式
提示:文章写完后windows7安装sql server 2000安装步骤 及安装过程中遇到的问题和解决方式, 文章目录一、ms sql server 2000是什么?版本简介:**特点:****优点:**二、步骤1.下载安装包及Sq4补丁包2.安装 ms …...
Python 开发-批量 FofaSRC 提取POC 验证
数据来源 学习内容和目的: ---Request 爬虫技术,lxml 数据提取,异常护理,Fofa 等使用说明---掌握利用公开或 0day 漏洞进行批量化的收集及验证脚本开发Python 开发-某漏洞 POC 验证批量脚本---glassfish存在任意文件读取在默认4…...
Linux系统中部署软件
目录 1.Mysql 2.Redis 3.ZooKeeper 声明 致谢 1.Mysql 参考:CentOS7安装MySQL 补充: ① 执行:rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2022 再执行:yum -y install mysql-community-server ② mysql…...
PHP常用框架介绍与比较
HP是一种广泛应用于Web开发的编程语言。随着互联网的快速发展,PHP的应用场景变得越来越广泛,从简单的网站到复杂的Web应用程序都可以使用PHP来开发。为了更好地组织和管理PHP代码,开发人员经常会使用框架来提高开发效率和代码质量。 本文将介绍一些常用的PHP框架,并进行简…...
Umi + React + Ant Design Pro 项目实践(一)—— 项目搭建
学习一下 Umi、 Ant Design 和 Ant Design Pro 从 0 开始创建一个简单应用。 首先,新建项目目录: 在项目目录 D:\react\demo 中,安装 Umi 脚手架: yarn create umi # npm create umi安装成功: 接下来,…...
MySQL知识点总结(1)
目录 1、sql、DB、DBMS分别是什么,他们之间的关系? 2、什么是表? 3、SQL语句怎么分类呢? 4、导入数据 5、什么是sql脚本呢? 6、删除数据库 7、查看表结构 8、表中的数据 10、查看创建表的语句 11、简单的查询…...
day45第九章动态规划(二刷)
今日任务 70.爬楼梯(进阶)322.零钱兑换279.完全平方数 70.爬楼梯(进阶) 题目链接: https://leetcode.cn/problems/climbing-stairs/description/ 题目描述: 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不…...
第十四届蓝桥杯第三期模拟赛原题与详解
文章目录 一、填空题 1、1 找最小全字母十六进制数 1、1、1 题目描述 1、1、2 题解关键思路与解答 1、2 给列命名 1、2、1 题目描述 1、2、2 题解关键思路与解答 1、3 日期相等 1、3、1 题目描述 1、3、2 题解关键思路与解答 1、4 乘积方案数 1、4、1 题目描…...
client打包升级
目录 前言 一、client如何打包升级? 二、使用步骤 1.先进行改版本 2.执行打包升级命令 总结 前言 本文章主要记录一下,日常开发中,常需要进行打包升级的步骤。 一、client如何打包升级? # 升级发布版本 ## 修改版本 * 父p…...
Blazor_WASM之3:项目结构
Blazor_WASM之3:项目结构 Blazor WebAssembly项目模板可选两种,Blazor WebAssemblyAPP及Blazor WebAssemblyAPP-Empty 如果使用Blazor WebAssemblyAPP模板,则应用将填充以下内容: 一个 FetchData 组件的演示代码,该…...
OperWrt 包管理系统02
文章目录 OperWrt 包管理系统OPKG简介OPKG的工作原理OPKG命令介绍软件包的更新、安装、卸载和升级等功能软件包的信息查询OPKG配置文件说明OPKG包结构(.ipk)OPKG演示案例OperWrt 包管理系统 OPKG简介 OPKG(Open/OpenWrt Package)是一个轻量快速的软件包管理系统,是 IPKG…...
国外网站推荐/常州seo外包
哈工大威海数据结构实验一 线性结构 一元多项式的求导 直接放代码 #include <iostream> using namespace std; typedef struct Function{int coef ;int index ;struct Function * next;}Data ; class List{ public:Data* CreatData(int coef,int index){Data * head;h…...
解决wordpress主题缺少style.css无法安装的方法/谷歌seo推广培训班
一、docker是什么 1、为什么会有docker 一款产品从开发到上线,从操作系统,到运行环境,再到应用配置。作为开发运维之间的协作我们需要关心很多东西,这也是很多互联网公司都不得不面对的问题,特别是各种版本的迭代之后…...
网站优化建设绵阳/长春网站优化页面
442.数组中重复的数据442.数组中重复的数据题解代码442.数组中重复的数据 442.数组中重复的数据 题解 题目:给一个数组,返回数组中出现两次的数,数是1-n的,并且只出现一次或两次,要求常数的时间复杂度 思路&#x…...
政府门户网站等保建设方案/2021小学生新闻摘抄
题解思路: 将所有条件存起来 枚举每个点是否为裁判,枚举时对涉及到此人的回合不进行操作,看是否出现矛盾,记录出现矛盾的回合。 如果仅有一点未出现矛盾,则此点为裁判,判断回合为max(出现矛盾的回合) 如果都…...
做网站公司汉狮价格/西安网站外包
在发布APP后,发现等了很久都访问不出来页面,这个可能是因为我们没有将本地的文件公开出去。 在【Assets】中按照以下步骤将数据公开即可...
网站开发所需基础知识/深圳网站优化公司
故障现象:逻辑DG数据库日志能够应用,但是确不会将应用后的日志删除,查看日志发现已经自动设置了TURNING OFF LOG AUTO DELETECompleted: alter database register logfile /archivelog/archive_2_25797_614088933.arcTue Apr 24 11:06:10 201…...