总结 总结

总结 总结 总结

命名规范课题

NSString 为什么用copy修饰

Nsstring比较特殊 继承nsobject oc将他归到oc对象。

Oc对象都是动态分配内存 存放到堆区

例如 @“dancer” 只是把数据区的字符串 进行了一个类型转换,oc把他当成字符串对象处理,例如@“dancer”并不是存在堆区,并不是动态分配出来的

我们无法判断这类字符串什么时候释放,如果释放,指针指向他毫无意义

?????

***** objective-c 动态性

编译时/运行时

编译时 词法/语法检查,将代码 编译成计算机执行语言 装到内存中跑起来

运行时 

动态类型:

譬如id类型修饰的对象 为动态类型对象 编译时并不知道是什么类型 运行时才会检查确定出对象的类型

iOS 自省方法 

is Kindleofclass 检查运行时对象类型

is Memberofclass

动态绑定 

基于动态类型  当运行时对象的类型被确定  类的原有的属性 方法被确定,也包括运行时添加的方法 属性,这就是所谓的动态绑定了

核心就是运行时 动态添加属性,方法

动态加载

1 动态资源加载 例如1x 2x 3x 图片 ,程序会根据机型动态加载 不同分辨率的图片

2 可执行代码的动态加载,譬如懒加载

譬如懒加载数组

-(NSArray *)countArray

{

If(!_countArray){

_countArray = [[NSArray alloc]  init];

}

Return  _countArray;

}

[self.countArray  addObject:@“”];

***** 关于kvo: 

1 监听机制,基于NSObject 两个方法willChangeValueForKey 和didChangeValueForKey,实现的。

2 当给一个属性添加观察者时候,运行时,会创建一个继承对象所属类的派生类(子类),并且重写被观察属性的set方法,默认一个对象的isa指针会之前所属类,而这是isa会指向这个新的派生类,所以其实被观察的属性的赋值操作是在这个派生类 里实现的。

3 的当被监听的属性发生改变前,调用willChange方法 记录旧的值,改变结束 调用DidChangeValue新的值,出发kvo监听方法。

Ps 对象isa指针默认所属类 class类里面也有个isa指针 指向元类 元类里包含调用的 1 方法列表 2类的版本信息 3成员变量数组ivars等

*****kvo应用场景

1 推送消息 消息时 放数组里面 我们的birgeValue显示红点提示 需要兼监听数组改变 给提示

2 视频 监听进度条

3 做轮播图监听 scrollview 监听contentOffSet 

***** 深拷贝 浅拷贝

1 最大的区别就是 有否创建新对象。深拷贝内容拷贝,浅拷贝指针拷贝(内存地址拷贝)

2 深拷贝,修改一个对象不影响另一个对象。

3 浅拷贝,修改一个对象影响另外一个对象

简单说:

深拷贝:指针指向一块新创建的内存

浅拷贝:指针指向一块已经存在的内存

大体上 copy mutable copy 策略是深拷贝

retain  strong是浅拷贝

A0E92ED1-AA8F-422D-A070-072CCAFD7F2E.png

***** Super /self 总结

1F05A479-F581-4D07-8690-5BF88ADE6D6A.png

***** SDWebImage细节

1 sd_setImageWithURL

2 SDWebImageManager  单例 downloadImageWithURL

 内部  SDImageCache类(nscache)以以url为key queryDiskCacheForKey方法 Memory Cache 以url为key去内存中查询缓存图片,如果没有  Disk Cache:去磁盘上找。

如果磁盘找到,会将图片设置到memory cache中。

3 如果缓存数据查询成功,则返回,不成功则请求网络self.imageDownloader down…

4 如果下载成功 [self.imageCache storeImage]储存 completedBlock回调前端展示

ps 磁盘缓存清除策略

1 清理过期缓存

2 清理写入时间早的图片

@interface SDImageCache : NSObject

@property (assign, nonatomic) NSInteger maxCacheAge;//最大缓存时间

@property (assign, nonatomic) NSUInteger maxCacheSize;//最大缓存空间

//设置最大缓存空间

[SDImageCache sharedImageCache].maxCacheSize = 1024 * 1024 * 50;    // 50M

SDWebImage Bug

磁盘图片渲染之前,需要得到图片的原始像素数据,然后去做绘制操作,所以图片需要解压缩。

***** 公司刷新 流程

size :取多少

offset:从哪里取

count:总数

刷新:

设定size 20条 10条,offset 为0 加到数组中

加载:

设定size20条 10条,第一次 获取数据数组长度 offset 从这取值 取size条,加到数组。

判断更多 has 数据数组个数< count总数

设置footer view 给个提示 没有更多

***** SVPullrefresh 原理

创建个scrollView的分类  分类添加属性,刷新头部显示的view 高度60 ,添加头部刷新view为scrollview的观察者,监听contentOffSet的改变,刷新头部view 有几种状态,加载状态,初始状态,停止状态,拖拽状态,在观察者监听方法里面,根据几种状态切换执行layoutSubview的显示

*****bug

Bug复现:当下拉刷新 拽的不是太多时候,他会先调上拉加载,在下拉刷新。

原因:1 svpull的头部/尾部试图都是scrollview的观察者,监听contentOffSet,2 当拖拽时候结束,scrollview代理结束 两个都调用,设置刷新状态,操作ui。

方案:加判断,scrollviewdidScroll 拖拽结束,判断当前偏移量 如果y>0  做上拉加载时候,设置加载状态 loding状态

9848E84A-B88B-42C4-9FF3-73B557371571.png

uiscrollview的分类

/**********************************************************************************/

*****tableview 滑动 cell 崩溃 (会议人气榜)

且仅仅4s崩溃,其他的不崩溃 而且会有内存明显的飙升 正常100左右飙升到800

断点调试 崩溃在sdwebimage里面 沙盒取出的图片转码 转位图时候,正常我们设置图片imageview setimage 都是在主线程中设置的 这其就包括解压图片为位图(很耗cpu) 然后calyer在view上做渲染。sdwebimage做法是开子线程去去做这个位图的解压缩,然后缓存 供ui使用,去沙盒中去图片的时候不做任何处理,直接取,这次奔溃的原因就是后台返回的图片太大 3000*5000,方案有两个第一大图片不处理 不解码这样性能差点 但不崩溃

第二种是 沙盒取出的图片 先等比压缩下。

我的方案是综合下 沙盒取出图片先判断如果款大于屏幕宽度 等比压缩下,小的不压缩,然后再去转码

******* 线程同步

防止资源抢夺,让线程同步进行,可以加互斥锁/NSLock,

@synchronized(obj):针对对象 ,保证在多条线程同时操作obj时候,一条线程执行完,在执行另一个

NSLock:针对方法,给一条线程中操作对象的方法加锁,保证一条线程执行完,在执行另一个

场景:三方库

******* 多线程场景应用

qq分享要求的图片1m以内,后台传的图片1.2m发布出去 回调不掉。

方案:下载图片nsdata datawithurl 网速慢直接卡死 要开线程 图片存储沙盒,等比压缩图片

******* 信号量 GCD实现 设置线程并发数 / 设置线程顺序

信号量:资源计数器

信号量介绍:

资源计数器,对于信号量有两个操作达到互斥 v(发送信号)/p(信号等待)

当一个操作执行A 执行p操作,信号量为-1未零,系统规定信号量为0,等待,操作A执行完毕,执行v操作,信号量+1,释放资源,线程B才可以执行.通过信号量来达到互斥操作.

方法介绍:

1 创建信号量 指定资源数量

2两个重要参数 发送信号(V),等待信号(P)

part 1 设置线程顺序 (通过一个执行,其他阻塞的方式实现顺序)

1 设置信号量为1,开启一条线程操作时候信号量-1,当前程序信号量为0,则其他线程操作阻塞状态,当当前线程执行结束,信号量+1,则其他线程操作 恢复执行.

2 通过开启一条线程,来阻塞其他线程,来实现多线程执行顺序.

part 2  设置线程最大并发数 

同样设置信号量 为3,来实现最大线程并发数为3的功能

1 信号量为三,意味着可以开启三条线程操作.

2 同样通过开启一条线程 信号量-1 线程指向完毕 信号量+1,实现程序运行中,有三条线程可以执行多线程操作,从而实现最大线程并发数.

******* xmpp 环境配置

材料:openfire服务器 mysql数据库(默认装到控制面板)

1 openfire mac版 基于java开发的 所以要求电脑有java运行环境 

没有的话需要安装jdk 查看java运行环境 终端执行命令 运行java -version 

2 mysql (服务端的数据库)使用是需要有账户密码默认装到控制面板)

  1》安装完 mysql 默认账户是root 密码为空

  2》终端命令配置命令行配置 开启mysql 并设置密码 

  3》创建的数据库默认四个文件 有个test文件 

  4》管理工具navcat 还有个海豚(mysqlworkbeech)的 登录输入连接名称(自定义)输入IP地址 端口号默认3306

       账户名称 root 密码(你配置过得)配置的数据库名称为你自定义的名称

3 关联openfire 和数据库1 导入openfire数据表文件到mysel(创建字段包括用户等)

 1》openfire建立数据库文件openfire数据库编码选择utf-8的

 2》默认open fire数据库安装到/user/local/openfire/ 数据库脚本保存在/user/local/openfire/ /resources/database/中

支持很多种数据库 保存mysql的数据库到桌面 导入到mysql中,这样mysql就添加好数据库字段了

4 关联openfire 和数据库1  后台管理设置关联数据库mysql

屏幕快照 2017-03-06 下午11.19.21.png

后台配置 选中驱动类型:mysql 指定mysql路径 输入mysql   用户名密码:root 密码:123456

配置openfire 后台账户密码 admin 123456

设置jid:

屏幕快照 2017-03-06 下午11.31.03.png

***** const / #define 宏定义

   const   c/c++ 定义常量的关键字

   #define  预编译指令

>>相同点

      都能定义常量

>>不同点

1 从内存分配角度, 

const    编译器不会为他分配内存空间,而是存储在符号表中,,作为一个常量存在,在编译时不会有存储/读取内存的操作

#define 编译时候,,每次赋值操作,先进行宏替换,然后都开启新的内存

所以 在运行时 const只被拷贝一份,而 #define 可以有若干个拷贝  const效率会高于#define

Pasted Graphic.tiff

2 从使用角度

      #define 可以定义函数,可以定义单例,而const不可以

备注: 苹果推荐使用const 因为const是在编译阶段,会编译检查报错;而宏定义,在预编译阶段 仅仅是替换 编译阶段 不检查类型

7F8D5312-5C01-4E54-97D9-024E2667988A.png

From   http://www.cocoachina.com/ios/20160519/16342.html

***** static / extern  全局变量 局部变量牛逼闪闪 

static:修饰变量(全局,局部)使用 / 全局变量/static变量 存储在静态区 程序运行 只初始化一次

两个作用 

Part1 :局部变量copy 进静态区,延长局部变量声明周期,程序运行期间 一直存在

Part2: 防止全局变量外部引用,外部引用extern

》》修饰局部变量:

作用:延长局部变量生命周期 (类似给局部变量变为全局变量的意思!)

ps:局部变量本身存储堆栈区,方法结束局部变量随之销毁;static修饰后将局部变量copy到静态区,修饰后变量在运行中在静态区给局部变量分配一次内存空间,且仅仅执行一次,程序运行期间变量一直存在不会销毁。

ps:打点断 ,发现下次执行这里断点过到下面去了。

995E3A13-6E44-4A03-BAB5-C9F39A06D01F.png

》》修饰全局变量:

主要是作用域的问题,static修饰后仅在本类中可以使用,无法extain引用,全局变量则可以外部引用。

场景:int做自增运算

extern:外部引用  UIKIT EXTERN(牛逼闪闪的宏定义 吼吼~~)

一个类中声明(即使写在方法里面也无妨),另一个类extern 可以直接引用。

079EED68-C52A-4859-BD7D-2EF9787769EE.png

D542B26E-1019-49DA-83BB-E9FAF521B8A2.png

备注:1 其他类声明NSString  *abcdefg  = @“红咔嚓雷“,

2 当两个类同时声明NSString  *abcdefg 则编译报错,两个文件同事O文件报错 解决:

A187D275-75C4-4DA9-B8B1-D6461728B607.png

ps:无需等到程序会否执行到该方法,程序启动即分配内存空间,所以无需加载到该方法。

ps:个人理解,类似通过extern关键字从静态区 引出一个全局变量的意思,然后重新声明,使用。。。

全局变量:变量存储在静态区,程序运行时分配一次内存空间,然后不会再去执行了。

局部变量:存放在栈空间中,方法执行结束,变量销毁。

????为什么 类方法 要用static 修饰全局变量

***** 堆栈

栈 编译器管理内存  栈的容量是系统预先设定好的 超了就会overflow ,分为动态分配/静态分配,局部变量 静态分配;静态/动态分配 都是由编译器管理内存的

堆:程序员手动管理内存  堆都是动态分配内存的

***** @autoreleasepool

 @autoreleasepool :作用避免内存峰值

 数据比较

 加上 @autoreleasepool 内存恒定在27

 不加上 @autoreleasepool  内存27起 匀速飙升 结束53

 Ps:在自动释放池中 调用autorelease方法才会被放到自动释放池,方法结束,才会对对象发送releas方法

 结论:当出现大的for循环 且循环体内api为 @autorelease时候建议加autoreleasepoor

 且:autoreleasepool 不能优化所有的循环

 ******************* ******************* ******************* ******************* ******************* *******************

 引申:@autoreleasepool应用场景

 1> 大数字for循环时候,调用autorelease API方法时候,加入@autoreleasepool{}避免内存峰值

 例如:拼接字符串操作,stringWithFormat API创建字符串,临时变量被大量创建,持续开辟内存,内存持续飙升....for循环结束,对象才会销毁,此时加入@autoreleasepool 会有帮助

 如果字符串是alloc 出来的,非autorelease API 则 @autoreleasepool  是无效的

***** 项目性能优化

1 懒加载  场景:初始化UI控件 2NSDateFormatter(日期格式化)

2 tableview优化 实现: 高度,数据 提前缓存,tableview只做赋值运算 减少subVies数量,rowHeight sectionHooterHeight 直接设定,少用代理

3 常识注意

***** NSURLCache

缓存原理 NSURLQequest 对应一个NSURLURLCacheResponse

缓存NSURLCache *cache = [NSURLCache shareURLCache];//掌握全局缓存

***** Block常识 

***** Block常识 (一)

方法常用声明:@property (copy) void(^MyBlock)(void); 如果超出当前作用域之后仍然继续使用block,那么最好使用copy关键字,拷贝到堆区,防止栈区变量销毁

定义block 

 typedef NSString  (^WebBaseHandlerBlock) (NSDictionary *retInfo, NSError *error);

属性block

@property (nonatomic ,copy)abcBlock myblock;

@property (nonatomic ,copy)void (^youBlock)(NSString *);

_weak typeof(self) weakSelf = self;

因此,__block和__weak修饰符的区别其实是挺明显的: 

1.__block不管是ARC还是MRC模式下都可以使用,可以修饰对象,还可以修饰基本数据类型。 

2.__weak只能在ARC模式下使用,也只能修饰对象(NSString),不能修饰基本数据类型(int)。 

3.__block对象可以在block中被重新赋值,__weak不可以。 

PS:__unsafe_unretained修饰符可以被视为iOS SDK 4.3以前版本的__weak的替代品,不过不会被自动置空为nil。所以尽可能不要使用这个修饰符。

***** Block和copy (二)

为什么要用copy:出于作用域考虑,默认block 存放在栈中,函数调用,栈分配内存,函数结束释放内存,block 销毁。再次调用block 时候报错野指针错误。当前block 变量指向的被释放的内存地址。所以需要copy到堆中,包住block的命。

“他本身的作于域是属于创建时候的作用域,一旦在创建时候的作用域外面调用block将导致程序崩溃。”

***** Block  copy/strong (三)

使用retain也可以,但是block的retain行为默认是用copy的行为实现的,

因为block变量默认是声明为栈变量的,为了能够在block的声明域外使用,所以要把block拷贝(copy)到堆,所以说为了block属性声明和实际的操作一致,最好声明为copy。

***** Block  copy/strong 扩展

block几种类型NSGlobalBlock NSStackBlock

block内部不引入外部变量:__ NSGlobalBlock

block内部引入外部变量   :__ NSStackBlock

***** weak 实现原理

runtime维护这个一个weak表(哈希表),记录已对象的地址为key,value为 所有指向该对象的weak弱指针地址的数组,当对象销毁时候通过weak表中该对象地址的value,查找到存有所有弱指针的地址的数组,赋值为nil,然后从weak表中删除。

对象销毁 会调用objec_clear_deallocating函数

PS:怎么找 对象地址 对应所有指向给对象的weak指针数组

1C8E2790-4CB4-4D81-B42D-5834BB43CAC1.png

block 使用

===场景1

譬如 协议中封装一个 push控制器的操作

调用方:

 [****  **block^(id obj ,NSInteger type){

if(type ==1){

 [self.navagation pushViewController:obj  animation:YES];

}

}];=

+(void)**. ** :(**block)block

{

Uiviewcontroller *obj = [[UIViewController alloc] init];

if(block){

block(obj,1);

}

}

===场景2

譬如 封装一个 请求url block操作

调用方 

[webbasehander  geturl:@“” dict:dic  block^(id result ,NSError error)

操作result

[self.table reloadData];

];

  • (Void)getUrl:(nssting *)url  dict:(nsdictionary *)dict block:(**block)block

{

配置url

配置请求参数

[self request:url:block];

}

  • (void)equest:(nesting *)url block:(**block)block

{

ASI 请求

Request completionBlock{

 block(dic,error);

}};

}

总结 :当 封装方面 设置好数据 触发调用方的block,调用方获取到值 ,操作后续部分

assign weak 区别

assign:修饰对象,和基本数据类型;当被修饰的变量指向变量释放或置空,会出现野指针错误!

weak:只能修饰对象,不会出现野指针错误,当被修饰的变量指向的变量释放或是置空,被修饰变量指向nil,不会出现野指针错误。

引申

6B1A7280-138A-4C00-8BE6-E2083303707C.png

5B49E380-02E0-4E03-AEF5-ACDC6620401C.png

***** 内存分析

静态内存分析 analyze、分析/ 蓝色

例如:逻辑错误

logic error  /  Dead store/Memory

不是太准 不可变赋值给可变数组 ,调方法时候传入的字典参数未实例化

动态内存分析 profile -allocation /leaks   侧面/分配/泄漏

***** IOS  绘图

Ios图像绘制三种API

1 UIKIT

  UIBezierPath  (画线,椭圆 角度 照相机) UIImage 显示图像

2 CoreGraphics/ Quartz 2D (c语言函数API)

Quartz 2D 是2d绘制呈现引擎

  CoreGraphics  CGContent CGPath  CGLayer CGBitmap

3 Open GL

支持2d/3d

CoreAnimation 算动画引擎

*****手势 响应者链条

分两块,

1 定位接收者

2 响应者链条

1定位接收者 当点击屏幕的ui触发一个事件,流程是事件添加到UIAplication的事件组,传递事件经历 window ,父控件且判断控件是否接受事件,如果是,则父控件遍历子控件,判断当前出发点是否在子控件上,继续遍历直到找到触发该点的最后一个子控件,事件传递不可以断,以为这所有控件务必可以处理事件,获取执行触发事件的控件。 

ps:这就说明了 为什么交互期间中间有个什么uiimageview等不能交互的父控件,导致子控件不接受响应事件

可以接受响应事件的控件需要继承UIResponder 但还不够 需要可以交互 不是隐藏的 alpha值不是太小

Pasted Graphic_1.tiff

2 响应者链条,功能是一个事件的发生,可以被多个控件获取。回传靠的是super执行方法

举例子:view上事件触发 默认事件互传给父控件,继续回传父控件,控制器.view 控制器(一样继承uiresponder);如果控制器还有父控制器那就父控件传递 给控制器 然后self.view 然后父控制器 window uiapplication 

*****runloop 

五中模式

1 UITrackingRunLoopMode

2 NSDefaultRunLoopMode

3 内核

4 开始

5NSRunLoopCommonModes = UITrackingRunLoopMode + NSDefaultRunLoopMode

获取两种方式 mainRunLoop currentRunloop

nstimer 会用到

平时api  [NSTimer schedulerTimerWithTimeInterval:1.0 targer:self selected:@selected(timerMethod)userInfo:nil repeats:YES]; // NSDefaultRunLoopMode 默认用的这个 所以处理滑动ui时候 timer 挂起 不执行了

repeats:YES

所以还是 需要实例化

NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 @selected(timerMethod)userInfo:nil repeats:YES ];

NSRunLoop *runLoop = [NSRunLoop currentRunLoop];

[runLoop addTimer:runLoop withMode:NSRunLoopCommonModes];

*****CALayer 详解  CALayer /UIView

CALayer  : 负责绘图 属于quartzCore框架,是一套跨平台绘图框架(Mac osx,ios)所以 layer.backgroundColor是CGColorRef,(因为Mac osx 没有UIKit,osx不是触摸交互的)

UIView    : 继承UIResponder 可以做交互,属于UIKit框架 负责构建用户界面和管理内容.

关     系    : 简单的说就是 UIView内部封装了一个CALayer的对象,calayer对象负责绘图部分,UIView负责交互和管理内容.

当对UiView进行绘制操作属性时候,例如frame center backgroundColor,实际会调用对应layer的相关方法 

例如调用 view setframe 实际会调用layer的 positon bounse anchorPoint

备注:

CALayer 属性 position anchorPoint(锚点) bounse size transform等

UIView的frame实际上是由center 和bounds来决定的

UIkit 构建界面,相应事件

锚点与中心点重合,默认0.5-0.5,

Layer内含隐士动

C24E8886-BDBE-46A3-B65F-6FC5B97B8C60.png

https://blog.csdn.net/u013282174/article/details/50215605

*****TCP UDP 协议——

TCP (传输控制协议): 

1 建立连接,形成数据通道

2 传输数据大小不受限制

3 三次握手,建立长连接,安全协议

4 必须建立连接,效率稍低

UDP(数据报协议): 

1 无需建立连接,将数据源及目的封装成数据包

2 传输大小有限制 小鱼64kb

3 无需建立连接,非安全协议

4 速度快

*****get post tcp udp  传输协议等 ???

*****注册登录 加密流程 对称非对称加密 ????—

*****你在公司负责什么——

*****上传app 推送  ——

*****数据库 sqlite语句——

*****公司缓存策略 首页等——

*****多线程应用  线程同步等 什么线程池

*****runtime 应用场景 事例介绍  ——

场景1

控制器释放打印( 添加属性 关联)

奇葩:logDealloc对象 管理打印信息

原理:

1 push每次新建控制器,initXib调用分类方法 创建一个logDealloc对象 用个属性记录当前类名

2 防止方法结束对象释放,在分类中需要,用个全局变量保存该对象(属性为类名),因为是分类所以关联创建属性,

3 pop控制器(本类)销毁,分类销毁,logDealloc对象销毁,调用对象logDealloc的dealloc方法 打印类名

 场景2 

  控ios默认字体Heiti SC   

82A45CBE-474D-4D27-BA68-E77DD4AF0F3D.png

*****web跳转———

*****单例模式—

*****镂空的遮罩

nsmarch port???

     0 思路:蒙版.layer.mask = layer对象

     1 创建蒙版

     2 绘制贝塞尔路径 添加反转图形(叠加部分会镂空)

     3 创建layer(CAShapeLayer子类).path = 贝塞尔路径.path

     4 设置 蒙版.layer“.mask = layer对象

创建表

create table if not exist t_student (primary key autoincrement,id integer ,name text not null,age integer default 18,)

创建表 create table if not exist 表名(字段 类型 约束,字段类型 约束,…)

insert into t_student (id,name,age) values (001,“小明”,20);

删除

delete from t_student where age>20 and name “小明”;

update 

公司缓存机制

#define HUIYIONELEVELCACHES           [YYUtil CreatCacheschilderPath:@"HUIYIONELEVELCACHES"]//会议页面研究报告1级分类缓存

/var/mobile/Containers/Data/Application/6AB6ABF0-29D8-415C-A71A-239D802FF9D0/Library/Caches/AppCaches/HUIYIONELEVELCACHES

心得体会

上线 要注意是否有 版本升级问题

播放url 视频注意转码  防止地址中带中文

 [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

NSMachPort ???

递归锁

runloop

内存管理

哈希表

二叉树

自动释放吃

推送

本地推送:

http://www.cnblogs.com/ly1973/p/8848459.html

远程推送

http://www.cnblogs.com/ly1973/p/8848404.html

分享      

三方登录  

推送 

Im即时通讯

视频开发

内存泄露

性能优化

缓存

  

音乐播放

数据统计

数据库加密

打包上线

多线程

决定是否开辟线程 看同步/异步 不是看队列

猜你喜欢

转载自www.cnblogs.com/ly1973/p/9075494.html
今日推荐