GCD API 理解 (一)

资料先行

GCD 深入理解:第一部分
GCD 深入理解:第二部分
以上两篇文章是关于GCD讲的比较好的文章,翻译自raywenderlich,该网站有很多关于iOS 开发的优秀文章。

引子

iOS 开发中有三大进阶性的技术点,分别是GCD、runtime 和runloop。其中GCD用的最多,runtime也有不少使用场景,runloop在系统的API里体现的比较多,项目里实际使用比较少。
一直都想就这三个技术点做一些总结,没事的时候可以回来复习巩固一下,可是记录了很多要写的点,但是文章却是一拖再拖。本文就记录GCD的一些API自己的理解和用法等,遇到新的API也会补充进来。

扩展

pthread 也是C 语言API(pthread现在已经基本看不到有使用的了),而NSThread 是Objective-C对pthread的封装;虽然GCD也是C语言API,但是非常容易使用,而NSOpretion是Objective-C对GCD的进一步封装。这是iOS 中几种多线程技术的关系。

介绍

GCD(Grand Central Dispatch)是OS X与iOS 开发中一种多核开发的解决方案。GCD是在libdispatch框架内,而该框架是iOS 应用默认的已经包含进去了。
苹果是在 OS X 10.6 和 iOS 4 中引入了 GCD,它是低层级的C语言 API。使用GCD,它能够让开发者更加方便、更加容易得使用多核CPU。而且我们开发者不需要再直接跟线程打交道了,只需要向队列中添加代码块即可,而GCD 在后端其实是管理着一个线程池。GCD 不仅决定着我们的代码块将在哪个线程被执行,它还根据可用的系统资源对这些线程进行管理。通过集中的管理线程,来缓解大量线程被创建的问题。
妈妈再也不用担心我们自己来处理线程的创建和销毁了

基本知识

1.串行队列和并发队列

串行队列就是每次只有一个任务执行,当一个任务执行完毕后,才会执行下一个任务。
而并发队列就是同时有多个任务在执行,同时执行的任务哪个先执行完,我们根本不知道。
iOS 中的串行队列有两种:主队列(dispatch_get_main_queue())、通过dispatch_queue_create创建的串行队列

1.1创建串行队列

主队列是系统为我们的应用创建的,我们是没办法创建一个新的主队列的。
以下是一个创建串行队列的例子。

dispatch_queue_t serial_queue = dispatch_queue_create("com.haley.com", DISPATCH_QUEUE_SERIAL);

其中第一个参数队列的label标记,第二个可以控制创建出来的队列是串行队列还是并行对列。
DISPATCH_QUEUE_SERIAL其实就是一个宏,实际上是NULL。
这也就是为什么,我们经常会看到别人是这样创建串行队列的:

dispatch_queue_t serial_queue = dispatch_queue_create("com.haley.com", NULL);
1.2创建并发队列

第一种创建并发队列的方式就是将上面方法的第二个参数修改为并发参数:

dispatch_queue_t concurrent_queue = dispatch_queue_create("com.haley.com", DISPATCH_QUEUE_CONCURRENT);

第二种方式创建全局并发队列:

dispatch_queue_t concurrent_queue =  dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

该方法第一个参数是创建的queue的级别。有四个宏,分别对应不同的级别:

#define DISPATCH_QUEUE_PRIORITY_HIGH 2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN

第二个参数,是苹果留作以后备用的,一般默认就填0就OK了。
因为DISPATCH_QUEUE_PRIORITY_DEFAULT其实就是等于0,所以我们常常会看到:

dispatch_queue_t concurrent_queue =  dispatch_get_global_queue(0, 0);
2 同步和异步

同步函数就是等待任务的执行,等任务执行完成后再返回。所以同步就意味着在当前线程中执行任务,并不会开启新的线程,不管是串行队列还是并发队列。

dispatch_sync(concurrent_queue, ^{ NSLog(@"打印%d----线程:%@", i,[NSThread currentThread]); //在当前线程中执行
});

而异步函数就不会等待任务的执行完成,它会立即返回。所以异步也就意味着会开启一个新的线程,所以并不会阻塞当前的线程。

dispatch_async(concurrent_queue, ^{ NSLog(@"打印%d----线程:%@", i,[NSThread currentThread]);  //在新的线程中执行
});
3 总结

GCD的多线程使用都是同步/异步 与串行队列/并发队列组合使用的。

同步异步
串行队列不创建新线程,顺序执行
并发队列不创建新线程,顺序执行

串行队列无论是同步的执行任务,还是异步的执行任务,任务都是顺序执行的。

串行队列同步任务:

    dispatch_queue_t serial_queue = dispatch_queue_create("com.haley.com", DISPATCH_QUEUE_SERIAL);for (int i = 0 ; i < 5; i++) {dispatch_sync(serial_queue, ^{NSLog(@"串行%d----线程:%@", i,[NSThread currentThread]);});}dispatch_sync(serial_queue, ^{NSLog(@"串行最后----线程:%@",[NSThread currentThread]);});
// 打印结果:
2016-07-08 16:15:13.421 PractiseProject[9528:187413] 串行0----线程:<NSThread: 0x7f93da408fe0>{number = 1, name = main}
2016-07-08 16:15:13.421 PractiseProject[9528:187413] 串行1----线程:<NSThread: 0x7f93da408fe0>{number = 1, name = main}
2016-07-08 16:15:13.422 PractiseProject[9528:187413] 串行2----线程:<NSThread: 0x7f93da408fe0>{number = 1, name = main}
2016-07-08 16:15:13.422 PractiseProject[9528:187413] 串行3----线程:<NSThread: 0x7f93da408fe0>{number = 1, name = main}
2016-07-08 16:15:13.422 PractiseProject[9528:187413] 串行4----线程:<NSThread: 0x7f93da408fe0>{number = 1, name = main}
2016-07-08 16:15:13.422 PractiseProject[9528:187413] 串行最后----线程:<NSThread: 0x7f93da408fe0>{number = 1, name = main}

* 串行队列异步任务:*

    dispatch_queue_t serial_queue = dispatch_queue_create("com.haley.com", DISPATCH_QUEUE_SERIAL);for (int i = 0 ; i < 5; i++) {dispatch_async(serial_queue, ^{NSLog(@"串行%d----线程:%@", i,[NSThread currentThread]);});}dispatch_async(serial_queue, ^{NSLog(@"串行最后----线程:%@",[NSThread currentThread]);});
// 打印结果:
2016-07-08 16:16:34.852 PractiseProject[9545:188432] 串行0----线程:<NSThread: 0x7fc499c09870>{number = 2, name = (null)}
2016-07-08 16:16:34.852 PractiseProject[9545:188432] 串行1----线程:<NSThread: 0x7fc499c09870>{number = 2, name = (null)}
2016-07-08 16:16:34.853 PractiseProject[9545:188432] 串行2----线程:<NSThread: 0x7fc499c09870>{number = 2, name = (null)}
2016-07-08 16:16:34.853 PractiseProject[9545:188432] 串行3----线程:<NSThread: 0x7fc499c09870>{number = 2, name = (null)}
2016-07-08 16:16:34.853 PractiseProject[9545:188432] 串行4----线程:<NSThread: 0x7fc499c09870>{number = 2, name = (null)}
2016-07-08 16:16:34.853 PractiseProject[9545:188432] 串行最后----线程:<NSThread: 0x7fc499c09870>{number = 2, name = (null)}

* 串行队列混合任务: *
即使串行队列中有同步任务和异步任务,也还是按照添加任务的顺序执行,只不过同步任务在当前线程执行,异步任务在新线程中执行:

    dispatch_queue_t serial_queue = dispatch_queue_create("com.haley.com", DISPATCH_QUEUE_SERIAL);for (int i = 0 ; i < 5; i++) {dispatch_sync(serial_queue, ^{NSLog(@"串行%d----线程:%@", i,[NSThread currentThread]);});}dispatch_async(serial_queue, ^{NSLog(@"串行最后----线程:%@",[NSThread currentThread]);});
// 打印结果:
2016-07-08 16:21:34.545 PractiseProject[9588:190084] 串行0----线程:<NSThread: 0x7fb283609b30>{number = 1, name = main}
2016-07-08 16:21:34.545 PractiseProject[9588:190084] 串行1----线程:<NSThread: 0x7fb283609b30>{number = 1, name = main}
2016-07-08 16:21:34.545 PractiseProject[9588:190084] 串行2----线程:<NSThread: 0x7fb283609b30>{number = 1, name = main}
2016-07-08 16:21:34.545 PractiseProject[9588:190084] 串行3----线程:<NSThread: 0x7fb283609b30>{number = 1, name = main}
2016-07-08 16:21:34.546 PractiseProject[9588:190084] 串行4----线程:<NSThread: 0x7fb283609b30>{number = 1, name = main}
2016-07-08 16:21:34.546 PractiseProject[9588:190120] 串行最后----线程:<NSThread: 0x7fb28362ee30>{number = 2, name = (null)}

* 并发队列同步任务:*
因为不会开启新的线程,所以就在当前线程中执行,只有一条线程,因此只能顺序执行了。

    dispatch_queue_t concurrent_queue =  dispatch_get_global_queue(0, 0);for (int i = 0 ; i < 5; i++) {dispatch_sync(concurrent_queue, ^{NSLog(@"并行%d----线程:%@", i,[NSThread currentThread]);});}dispatch_sync(concurrent_queue, ^{NSLog(@"并行最后----线程:%@",[NSThread currentThread]);});
//打印结果:
2016-07-08 16:26:44.264 PractiseProject[9671:193541] 并发0----线程:<NSThread: 0x7f905a404a00>{number = 1, name = main}
2016-07-08 16:26:44.264 PractiseProject[9671:193541] 并发1----线程:<NSThread: 0x7f905a404a00>{number = 1, name = main}
2016-07-08 16:26:44.265 PractiseProject[9671:193541] 并发2----线程:<NSThread: 0x7f905a404a00>{number = 1, name = main}
2016-07-08 16:26:44.265 PractiseProject[9671:193541] 并发3----线程:<NSThread: 0x7f905a404a00>{number = 1, name = main}
2016-07-08 16:26:44.265 PractiseProject[9671:193541] 并发4----线程:<NSThread: 0x7f905a404a00>{number = 1, name = main}
2016-07-08 16:26:44.265 PractiseProject[9671:193541] 并发最后----线程:<NSThread: 0x7f905a404a00>{number = 1, name = main}

* 并发队列异步任务:*
因为异步任务会开启新的线程,所以哪个任务先执行完毕,是不知道的。相同的示例代码多次运行,任务执行完成的顺序是不一样的。这里也可以看出一个GCD的优点,它会复用之前使用过的闲置的线程。

    dispatch_queue_t concurrent_queue =  dispatch_get_global_queue(0, 0);for (int i = 0 ; i < 5; i++) {dispatch_async(concurrent_queue, ^{NSLog(@"并行%d----线程:%@", i,[NSThread currentThread]);});}dispatch_async(concurrent_queue, ^{NSLog(@"并行最后----线程:%@",[NSThread currentThread]);});
// 打印结果:
2016-07-08 16:29:03.522 PractiseProject[9696:194688] 并发1----线程:<NSThread: 0x7f8020c2bee0>{number = 4, name = (null)}
2016-07-08 16:29:03.522 PractiseProject[9696:194694] 并发0----线程:<NSThread: 0x7f8020d1ec90>{number = 3, name = (null)}
2016-07-08 16:29:03.522 PractiseProject[9696:194703] 并发3----线程:<NSThread: 0x7f8020e153e0>{number = 6, name = (null)}
2016-07-08 16:29:03.522 PractiseProject[9696:194693] 并发2----线程:<NSThread: 0x7f8020c2bf20>{number = 5, name = (null)}
2016-07-08 16:29:03.523 PractiseProject[9696:194688] 并发4----线程:<NSThread: 0x7f8020c2bee0>{number = 4, name = (null)}
2016-07-08 16:29:03.523 PractiseProject[9696:194694] 并发最后----线程:<NSThread: 0x7f8020d1ec90>{number = 3, name = (null)}

* 并发队列混合任务:*
可以确定的是如果异步任务添加在同步任务后面,则一定会等同步任务执行完毕后,才会执行异步任务;如果异步任务在同步任务前添加,则异步任务什么时候执行完毕也是未知的,但是多个同步任务一定是顺序执行的。

    dispatch_queue_t concurrent_queue =  dispatch_get_global_queue(0, 0);for (int i = 0 ; i < 5; i++) {dispatch_sync(concurrent_queue, ^{NSLog(@"并行%d----线程:%@", i,[NSThread currentThread]);});}dispatch_async(concurrent_queue, ^{NSLog(@"并行最后----线程:%@",[NSThread currentThread]);});for (int i = 0 ; i < 5; i++) {dispatch_sync(concurrent_queue, ^{NSLog(@"并行%d----线程:%@", i,[NSThread currentThread]);});}
// 打印结果(多次运行打印结果并不相同)
2016-07-08 16:38:56.273 PractiseProject[9805:199354] 并发0----线程:<NSThread: 0x7f9433408d40>{number = 1, name = main}
2016-07-08 16:38:56.273 PractiseProject[9805:199354] 并发1----线程:<NSThread: 0x7f9433408d40>{number = 1, name = main}
2016-07-08 16:38:56.273 PractiseProject[9805:199354] 并发2----线程:<NSThread: 0x7f9433408d40>{number = 1, name = main}
2016-07-08 16:38:56.273 PractiseProject[9805:199354] 并发3----线程:<NSThread: 0x7f9433408d40>{number = 1, name = main}
2016-07-08 16:38:56.273 PractiseProject[9805:199354] 并发4----线程:<NSThread: 0x7f9433408d40>{number = 1, name = main}
2016-07-08 16:38:56.274 PractiseProject[9805:199354] 并发0----线程:<NSThread: 0x7f9433408d40>{number = 1, name = main}
2016-07-08 16:38:56.274 PractiseProject[9805:199354] 并发1----线程:<NSThread: 0x7f9433408d40>{number = 1, name = main}
2016-07-08 16:38:56.274 PractiseProject[9805:199354] 并发2----线程:<NSThread: 0x7f9433408d40>{number = 1, name = main}
2016-07-08 16:38:56.274 PractiseProject[9805:199470] 并发最后----线程:<NSThread: 0x7f943362ffc0>{number = 3, name = (null)}
2016-07-08 16:38:56.274 PractiseProject[9805:199354] 并发3----线程:<NSThread: 0x7f9433408d40>{number = 1, name = main}
2016-07-08 16:38:56.274 PractiseProject[9805:199354] 并发4----线程:<NSThread: 0x7f9433408d40>{number = 1, name = main}

注意事项

利用同步/异步任务 和串行队列/并发队列时,需要注意一些情况防止发生死锁。
* 情形一*
在主线程中调度主队列完成一个同步任务,会发生死锁。

- (void)viewDidLoad {[super viewDidLoad];// Do any additional setup after loading the view, typically from a nib.self.view.backgroundColor = [UIColor orangeColor];dispatch_sync(dispatch_get_main_queue(), ^{NSLog(@"串行----线程:%@",[NSThread currentThread]);});
}

如上代码,界面永远不会加载出来,里面的NSLog永远也不会执行。原因是ViewDidLoad是在主队列的主线程中执行,执行到dispatch_sync 时会阻塞住,等待dispatch_sync中的打印任务执行完毕。而dispatch_sync又会等viewDidLoad执行完毕,再开始执行,因此就互相等待发生死锁。
* 情形二 *
在串行队列的同步任务中再执行同步任务,会发生死锁。

    dispatch_queue_t serial_queue = dispatch_queue_create("com.haley.com", DISPATCH_QUEUE_SERIAL);dispatch_sync(serial_queue, ^{NSLog(@"串行1----线程:%@",[NSThread currentThread]);dispatch_sync(serial_queue, ^{NSLog(@"串行2----线程:%@",[NSThread currentThread]);});});

上面示例中的NSLog(@"串行1----线程:%@",[NSThread currentThread]);会打印,
但是NSLog(@"串行2----线程:%@",[NSThread currentThread]);永远也不会执行。
因为串行队列一次只能执行一个任务,执行完毕返回后,才会执行下一个任务,而外层任务的完成需要等待内层任务的结束,而内层任务的开始需要等外层任务结束。
其实情形一是情形二的一种特殊情况。

* 情形三 *
在串行队列的异步任务中再嵌套执行同步任务,也会发生死锁。

    dispatch_queue_t serial_queue = dispatch_queue_create("com.haley.com", DISPATCH_QUEUE_SERIAL);dispatch_async(serial_queue, ^{NSLog(@"串行异步----线程:%@",[NSThread currentThread]);dispatch_sync(serial_queue, ^{NSLog(@"串行2----线程:%@",[NSThread currentThread]);});[NSThread sleepForTimeInterval:2.0];});

同样的,由于串行队列一次只能执行一个任务,任务结束后,才能执行下一个任务。
所以异步任务的结束需要等里面同步任务结束,而里面同步任务的开始需要等外面异步任务结束,所以就相互等待,发生死锁了。
第一篇就到这里了,下一篇记录GCD的其他API。Have Fun!

转载于:https://www.cnblogs.com/wanghang/p/6298855.html

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

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

相关文章

java怎么生成字母_在Java中生成字母序列

我的版本实现了Iterator并且保持一个int计数器.计数器值转换为相应的字符串&#xff1a;import com.google.common.collect.AbstractIterator;class Sequence extends AbstractIterator {private int now;private static char[] vs;static {vs new char[Z - A 1];for(char iA…

linux 的终端字体色和背景色的修改方法(二)

Linux终端下的颜色设置 2013-08-31 22:57:15分类&#xff1a; LINUX 在 ANSI 兼容终端&#xff08;例如 xterm、rxvt、konsole 等&#xff09;里&#xff0c; 可以用彩色显示文本而不仅仅是黑白。 本文示范了如何使用粗体和彩色的文字。 相信每一位Linux使用者都要面对 Bash。 …

java中的STL库_C++11 STL线程库实现一个简单的线程池

使用C11 STL线程库实现一个线程池。处理机制是抢占式的&#xff0c;即所有线程从一个队列(std::queue)中获取任务执行(计算字符串简单HASH值)&#xff0c;使用std::mutex和std::conditional_variable实现队列访问并发协调。#include #include #include #include #include #incl…

广搜 广搜 poj 3984

***求最短路径&#xff0c;然后再输出路径&#xff0c; BFS路径输出*** #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <stack> #include <queue> #include <cmath>using namespace std…

pycharm新建python file没有默认头_Pycharm 设置默认头的图文教程

Pycharm 设置默认头的图文教程1. 设置的路径是File->settings->Editor->File and Code Templates->Python Script内容见图&#xff1a;这样新建文件的时候就会默认头如下&#xff1a;这里说明下#!/usr/bin/evn python 和 #!/usr/bin/python的区别&#xff1a;第一种…

python redis订阅_Python 学习笔记 - Redis

Redis 和Memcached类似 也是一个开源的内存存储系统&#xff0c;存储格式也是通过键值对的方式。不过和memcached比起来&#xff0c;memcached的value只支持字符串&#xff0c;而redis支持字符串&#xff0c;列表&#xff0c;集合&#xff0c;hash等等&#xff1b;这些数据类型…

egret中loadingUI的自定义

loadingUI中是加载资源&#xff0c;此时如果想自定义loadingUI界面&#xff0c;那么此刻正在加载的图片肯定不能用了。 解决方法就是使用服务器资源 直接加载服务器资源就好了 class LoadingUI extends egret.Sprite { public constructor(){ super(); this.c…

python不支持的数据类型有achar bint cfloat dlist_第1篇:Cython的数据类型(第二部分)

Cython的C指针与C一样&#xff0c;尽管指针性与变量而不是类型相关联&#xff0c;但可以在类型或变量附近声*号。%%cythoncdef int *acdef int *b但这样在变量a,b写在一行,cython编译器会发出警告的信息,因此建议每个变量单独声明%%cythoncdef int *a,*bCython中的指针的解引操…

java 调用dll 方法 postmessage实现后台模拟按键_VB实现模拟键盘及模拟鼠标(后台操作)...

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼接下来来研究下模拟鼠标模拟鼠标的常数整理&#xff0c;如下&#xff1a;WM_MOUSEFIRST 0x0200 //移动鼠标时发生WM_MOUSEMOVE 0x0200 //移动鼠标时发生&#xff0c;同WM_MOUSEFIRSTWM_LBUTTONDOWN 0x0201 //按下鼠标左键WM_LBUTTO…

【micropython】用python来进行BadUSB的USB-HID测试(含无线控制)

转载请注明&#xff1a;小五义http://www.cnblogs.com/xiaowuyi QQ群&#xff1a;64770604 本文以TPYBoardv101开发板为例讲解了利用micropython进行BadUSB的usb-HID设备测试的主要方法&#xff0c;使用mt7681模块进行了一个简单的实验&#xff0c;实现了手机摇控键盘输入的测…

php返回类中方法,php如何获取类中所有的方法名

php获取类中所有的方法名的方法&#xff1a;可以利用【get_class_methods()】函数来获取&#xff0c;【get_class_methods()】函数可以返回指定类中所有的方法名&#xff0c;并且会将方法名保存到数组中。【相关学习推荐&#xff1a;php编程(视频)】php获取类所有方法名的方法&…

C#学习之向量运算符重载

//未重载运算符&#xff0c;但编译通过&#xff1b;C#不允许重载运算符‘’&#xff0c;但如果重载‘’运算符&#xff0c;编译器会自动使用‘’运算符的重载来执行‘’运算符的操作&#xff1b; //‘-’、‘*’、‘&’和‘/’等所有赋值运算都遵循此规则&#xff1b; 1、若…

如何安装php5.5,源码安装php5.5

centos6.6首先上传php-5.5.10至服务器安装依赖环境yum -y install gcc gcc-c autoconf libjpeg libjpeg-devel libpng libpng-devel freetype freetype-devel libxml2 libxml2-devel zlib zlib-devel glibc glibc-devel glib2 glib2-devel bzip2 bzip2-devel ncurses ncurses-d…

PHP 表单文件上传的原理,php多文件上传功能实现原理及代码

今天对多图片上传功能小小的研究了一下&#xff0c;把下面的代码整理出来&#xff0c;方便以后自己使用以及供大家交流1、upload.html页面&#xff0c;即先是input typefile的文件&#xff1a;图片上传第一张图片第二张图片第三张图片第四张图片第五张图片2、do_upload.php页面…

php的c方法,thinkphp的c方法的使用

这篇文章主要介绍了关于thinkphp的c方法的使用&#xff0c;有着一定的参考价值&#xff0c;现在分享给大家&#xff0c;有需要的朋友可以参考一下用过thinkphp的朋友都知道&#xff0c;C()方法在整个框架中用的非常普遍&#xff0c;C方法的实现非常简单&#xff0c;但是功能非常…

Fiddler中session的请求/响应类型与图标对照表

转载于:https://www.cnblogs.com/chengchengla1990/p/5681978.html

php打印矩阵,PHP实现顺时针打印矩阵(螺旋矩阵)的方法示例

这篇文章主要介绍了PHP实现顺时针打印矩阵(螺旋矩阵)的方法,涉及PHP基于数组遍历、运算模拟打印实现螺旋矩阵功能的相关操作技巧,对PHP感兴趣的朋友可以参考下本篇文章。本文实例讲述了PHP实现顺时针打印矩阵的方法。分享给大家供大家参考&#xff0c;具体如下&#xff1a;问题…

NOIP2014 uoj20解方程 数论(同余)

又是数论题 Q&A Q&#xff1a;你TM做数论上瘾了吗 A&#xff1a;没办法我数论太差了&#xff0c;得多练&#xff08;shui&#xff09;啊 题意 题目描述 已知多项式方程&#xff1a; a0a1xa2x^2..anx^n0 求这个方程在[1, m ] 内的整数解&#xff08;n 和m 均为正整数&#x…

php transfer-encoding: chunked,php – 使用chunked transfer encoding和gzip

我最近开始在我的网站上使用gzip,它在除Opera之外的所有浏览器上都起到了一种魅力作用,它给出了一个错误,即由于数据损坏而无法解压缩内容.从我可以从测试和谷歌搜索中收集到的,使用gzip和chunked传输编码可能是一个问题.请求像css文件这样的小文件时没有错误这一事实也指向了这…

@override

目录 用处 作用 注意 用处&#xff1a; 继承抽象类&#xff0c;必须实现抽象方法&#xff0c;方法上要加override 实现接口时&#xff0c;必须实现接口里定义的方法&#xff0c;方法上要加override 作用&#xff1a; 可以检查方法签名的拼写错误 改变父类中的一些行为 Override…