ios_iostream - CSDN
ios 订阅
iOS是由苹果公司开发的移动操作系统 [1]  。苹果公司最早于2007年1月9日的Macworld大会上公布这个系统,最初是设计给iPhone使用的,后来陆续套用到iPod touch、iPad上。iOS与苹果的macOS操作系统一样,属于类Unix的商业操作系统。原本这个系统名为iPhone OS,因为iPad,iPhone,iPod touch都使用iPhone OS,所以2010年WWDC大会上宣布改名为iOS(iOS为美国Cisco公司网络设备操作系统注册商标,苹果改名已获得Cisco公司授权)。2016年1月,随着iOS 9.2.1版本的发布,苹果修复了一个存在了3年的漏洞 [2]  。2018年9月22日,美国苹果公司在最新的操作系统中秘密加入了基于iPhone用户和该公司其他设备使用者的“信任评级”功能。 展开全文
iOS是由苹果公司开发的移动操作系统 [1]  。苹果公司最早于2007年1月9日的Macworld大会上公布这个系统,最初是设计给iPhone使用的,后来陆续套用到iPod touch、iPad上。iOS与苹果的macOS操作系统一样,属于类Unix的商业操作系统。原本这个系统名为iPhone OS,因为iPad,iPhone,iPod touch都使用iPhone OS,所以2010年WWDC大会上宣布改名为iOS(iOS为美国Cisco公司网络设备操作系统注册商标,苹果改名已获得Cisco公司授权)。2016年1月,随着iOS 9.2.1版本的发布,苹果修复了一个存在了3年的漏洞 [2]  。2018年9月22日,美国苹果公司在最新的操作系统中秘密加入了基于iPhone用户和该公司其他设备使用者的“信任评级”功能。
信息
软件语言
Swift,Objective-C、C、C++
软件大小
1.2 GB
开发商
Apple - 苹果
软件授权
仅苹果移动设备使用
更新时间
2020年7月16日
中文名
苹果移动设备操作系统
最新正式版
iOS 13.6(17G68)
全    称
iPhone Operation System
最新测试版
iOS 14.0 Beta3(18A5332f)
外文名
iOS
iOS发展历程
2007年1月9日苹果公司在Macworld展览会上公布,随后于同年的6月发布第一版iOS操作系统,最初的名称为“iPhone Runs OS X”。2007年10月17日,苹果公司发布了第一个本地化iPhone应用程序开发包(SDK),并且计划在2月发送到每个开发者以及开发商手中。2008年3月6日,苹果发布了第一个测试版开发包,并且将“iPhone runs OS X”改名为“iPhone OS”。2008年9月,苹果公司将iPod touch的系统也换成了”iPhone OS“。2010年2月27日,苹果公司发布iPad,iPad同样搭载了”iPhone OS”。这年,苹果公司重新设计了“iPhone OS”的系统结构和自带程序。2010年6月,苹果公司将“iPhone OS”改名为“iOS”,同时还获得了思科iOS的名称授权。2010年第四季度,苹果公司的iOS占据了全球智能手机操作系统26%的市场份额。2011年10月4日,苹果公司宣布iOS平台的应用程序已经突破50万个。2012年2月,应用总量达到552,247个,其中游戏应用最多,达到95,324个,比重为17.26%;书籍类以60,604个排在第二,比重为10.97%;娱乐应用排在第三,总量为56,998个,比重为10.32%。2012年6月,苹果公司在WWDC 2012上发布了iOS 6,提供了超过200项新功能。2012年9月20日,苹果发布iOS 6正式版,本次更新拥有超过200项新功能。这其中尤其加强了针对中国用户的定制功能,包括Siri开始支持中文语言,系统整合新浪微博、163邮箱等方面。 [3]  2013年6月10日,苹果公司在WWDC 2013上发布了iOS 7,几乎重绘了所有的系统App,去掉了所有的仿实物化,整体设计风格转为扁平化设计。将于2013年秋正式开放下载更新。 [4]  2013年9月19日,苹果发布iOS 7正式版,带来超过200项全新功能。 [5]  2014年6月3日(西八区时间2014年6月2日),苹果公司在WWDC 2014上发布了iOS 8,并提供了开发者预览版更新。 [6]  2014年9月17日,苹果发布iOS 8正式版。 [7]  2015年9月17日,苹果发布iOS 9正式版。 [8]  2016年9月14日,苹果发布iOS 10正式版,这是苹果推出移动操作系统以来最大的一次更新,尤其增加了很多特别适应中国国情的功能,比如骚扰电话识别、苹果地图进一步本地化等。 [9-10]  2017年9月20日,苹果发布iOS 11正式版,iOS 11为iPad带来了更强大的生产力,具体是通过新的Dock栏、文件App、多任务处理、拖拽等功能来实现的。另外,iOS 11重要的一项新功能便是AR功能,这使得该平台成为世界最大的AR平台,用户只需用手机便可享受AR的无穷乐趣。除此之外,iOS 11还带来了更生动更有趣的Live Photo、相机扫码、App Store、控制中心、锁屏、勿扰模式等。 [11-12]  2018年9月18日,苹果发布iOS 12正式版,主要为旧iPhone设备带来性能提升,以及带来了部分全新功能。 [13-14]  2018年9月13日,2018苹果秋季新品发布会上,苹果CEO库克介绍了苹果生态的一些数据。他表示,搭载苹果iOS系统设备已达20亿部。 [15]  2019年9月11日,苹果宣布于2019年9月19日推送iOS 13正式版,支持iPhone 6S及后续机型。 [16]  2019年6月4日,苹果公司在WWDC 2019上发布了iOS 13。2019年9月20日,苹果发布iOS 13正式版,iOS 13推出的"深色"模式为iPhone带来了全然不同的新风格,提供了浏览和编辑照片的全新方式,并新增了保护隐私的登录方式,轻点一下即可登录App和网站。iOS 13速度更快、响应更迅速。系统经整体优化后,App启动速度提升、App下载大小缩减,同时面容ID也变得更快。 [17-18]  2019年9月25日,苹果发布iOS 13.1正式版,实现了诸多问题修复和功能改进,包括iPhone 11、iPhone 11 Pro和iPhone 11 Pro Max上采用超宽频技术的隔空投送、快捷指令App中建议的自动化操作,以及地图App中的共享到达时间。 [19]  美国西部时间2019年9月27日上午10点(北京时间28日凌晨一点),苹果发布了iOS 13.1.1 / iPadOS 13.1.1 正式版更新。 [20]  2019年10月29日,苹果发布iOS 13.2正式版,在iPhone 11、iPhone 11 Pro和iPhone 11 Pro Max上推出了先进的图像处理系统Deep Fusion,它使用A13仿生神经网络引擎拍摄纹理及细节更出众、低光环境下噪点更少的照片。其他功能包括更新和新增的表情符号、AirPods信息播报,并支持AirPods Pro、HomeKit安全视频、具有HomeKit功能的路由器以及新的Siri隐私设置。本更新还包括错误修复和改进。 [21]  2019年12月11日,苹果发布iOS 13.3正式版,包括了改进和错误修复,并在“屏幕使用时间”中新增了更多家长控制。根据苹果发布的最新支持文档,iOS 13.3正在进行改进以最大程度地减少信息App中的垃圾短信等内容。 [22]  2020年3月18日,苹果宣布将于北京时间3月25日为iPhone、iPad和iPod touch用户推送iOS 13.4和iPadOS 13.4正式版。除了重新设计的邮件工具栏和iCloud文件夹共享功能之外,iPadOS 13.4还为iPad平台带来触控板和鼠标支持。 [23]  2020年3月19日,苹果发布iOS 13.4 Beta6(GM版),此前iOS 13.4/iPadOS 13.4已经历经了5次Beta预览测试。Golden Master版本(最终测试版)的出现意味着iOS 13.4正式版到来,优先面向开发者/公开测试版渠道用户推送。而最广泛的iOS 13.4/iPadOS 13.4正式版将在3月25日面向普通用户推送。 [24]  2020年3月25日,苹果发布iOS 13.4正式版,在此次更新中苹果主要推出了新的拟我表情贴纸和从“文件”App共享iCloud云盘文件夹的功能,同时iPad更新后支持搭配使用鼠标和触控板了。 [25]  2020年5月21日,苹果发布iOS 13.5正式版,iOS 13.5加快了配备面容ID的设备在用户佩戴口罩时显示密码栏的速度,并加入了“暴露通知”API以支持来自公共卫生管理机构的COVID-19接触追踪App。本更新还增加了控制FaceTime群聊期间视频拼贴自动突显的选项并包括错误修复和其他改进。 [26]  2020年7月16日,苹果发布iOS 13.6正式版,iOS 13.6支持数字车钥匙,并在“健康”App中包含新的症状类别。本更新还包括错误修复和改进。 [27]  2020年6月23日,苹果公司在WWDC 2020上发布了iOS 14,它为iOS主屏幕带来了多年来最大的变化:小插件。 [28]  Java [6]  苹果仍没有宣布任何让iPhone运行Java的计划。但太阳微系统已宣布其将会发布能在iPhone上运行的Java虚拟机(JVM)的计划,它是基于Java的Micro Edition版本。这将让用Java应用程序得以在iPhone和iPod Touch上运行。在这个计划发表之后,熟悉iOS软件开发协议的程序员们相信虽然iOS软件开发协议不允许应用程序后台运行(比如说在接电话的时候仍然运行程序), 但却允许自带的应用程序从其他的来源下载代码,而且它们还能与第三方应用程序相互作用(比如说Safari和Java虚拟机), 这可能会阻碍不与苹果合作的Java虚拟机的发展。 很明显,在iPhone运行的Java在iOS软件开发协议所规定的范畴以外。FlashiOS不支持Flash。Adobe计划在iPhone发行第三方Flash Lite软件程序,但现时仍未有计划。以现时来说,只支持标准Flash。现时可以使用iPhone第三方程序jailbreak观看flash动画。但不支持在线播放。SwiftSwift,苹果于2014年WWDC(苹果开发者大会)发布的新开发语言,可与Objective-C共同运行于Mac OS和iOS平台,用于搭建基于苹果平台的应用程序。Swift是苹果公司在WWDC2014上发布的全新开发语言。从演示视频及随后在App Store上线的标准文档看来,语法内容混合了OC,JS,Python,语法简单,使用方便,并可与OC混合使用。作为一项苹果独立发布的支持型开发语言,已经有了数个应用演示及合作开发公司的测试,相信将在未来得到更广泛的应用。某种意义上Swift作为苹果的新商业战略,将吸引更多的开发者入门,从而增强App Store和Mac Store本来就已经有的应用数量基础。
收起全文
精华内容
参与话题
  • 本课程讲述如何使用OC 实现1V1 iOS端实时音视频通信 包括如何使用nodejs开发WebSocket信令服务器 理解WebRTC媒体的交互流程 coturn服务器搭建 iOS音视频客户端开发
  • 高质量 iOS 博客推荐(iOS界技术大牛)

    万次阅读 2019-02-12 15:39:20
    推荐一些我个人认为非常经典,值得关注的博客: OneV's Den 大家尊称为喵神 @onevcat 的博客。对 Swift 技术在国内的推广做了很大的贡献。...Casa 对架构有很深的理解,《iOS 应用架构谈》系列文章影响深...

    推荐一些我个人认为非常经典,值得关注的博客:

    OneV's Den

    大家尊称为喵神 @onevcat 的博客。对 Swift 技术在国内的推广做了很大的贡献。

    Limboy’s HQ

    李忠关于 RAC 、组件化的文章流传很广。博客中对架构、编程的思考都很精髓。听说现在转管理后不太更新技术文了。

    Casa Taloyum

    Casa 对架构有很深的理解,《iOS 应用架构谈》系列文章影响深远。

    bang’s blog

    JSPatch 作者 bang 的博客。稳定更新,每篇文章质量都很高。

    Garan no dou | 一只魔法师的工坊

    YYKit 作者的博客,很多文章如《iOS 保持界面流畅的技巧》《深入理解RunLoop》等都有极高的实用价值。

    Draveness’s Blog

    被人称为灯塔的 Draveness 的博客。Draveness 涉猎很广,文章也很有深度,一半以上的文章我连标题都看不懂。

    Halfrost-Field

    人称“霜神”冰霜的博客,冰霜的文章针对某个问题总是能有深入、全面的剖析。

    星光社

    滴滴技术专家戴铭的博客,每篇都是精品。

    玉令天下

    腾讯杨萧玉的博客,底层细节能深入到汇编的实现,令人惊叹。

    MrPeak杂货铺

    Peak 以一系列 TCP/IP 相关的文章让人折服,之前博客更新频率很高,多有涉猎。17 年底去 facebook 赚奶粉钱后更新频率有所下降,望早日回归。

    Bestswifter

    @bestswifter 张星宇的博客,《让 BAT 的 offer 不再难拿》介绍了他在大学时准备面试最后进入百度的经历,这篇文章也引起了不小的积极反响。博客除了如 《深入理解 iOS 开发中的锁》 iOS 开发也有不少工程化话题的文章。

    故胤道长

    顶级 iOS 开发者,曾就职于 Uber,现就职于美帝亚马逊。文风流畅,博客也会谈到不少中美编程文化的差异。

    iOS程序犭袁

    CYLTabBarController 作者,博客也是篇篇经典。

    sunnyxx

    滴滴技术专家孙源的博客,对于 OC 语言特性有深入研究。不过整个 17 年没有更新博客。

    雷纯锋的技术博客

    有多篇经典的博客,如《谈谈 iOS 中图片的解压缩》《MVVM With ReactiveCocoa》。不过 17 年只更新了一篇文章。

    瓜地

    冬瓜的博客,作为曾经 acm‘er 的素质与修养,博客总是能够深入底层。

    Joy_xx

    被人称为架构 joy 的博客,有不少关于 APM 的好文。

    J_Knight_

    J_Knight的文章总是清晰易懂,更新的也很勤快。

     

    更新:

    1、专栏整理,内容相当丰富,主要涉及iOS中架构、性能优化、APM、底层原理等高级进阶知识:来自-乐少的Github

    2、大厂公开演讲、技术分享资料,包括PPT和视频等资料:大厂公开演讲、技术分享-Github

    3、众多经典三方库源码解析,来自Draveness:源码解析-Github

     

    参考:https://www.jianshu.com/p/ea9fabdc12ed
     

    展开全文
  • IOS开发从入门到精通-第一章

    千人学习 2019-06-27 11:03:31
    本课程详细介绍了IOS开发环境搭建及开发工具的使用,讲解了IOS开发常用视图控件的使用 ,还讲解了一个实用的功能实例:APP启动引导页的开发。
  • 本视频教程拥有180节课程,包含iOS开发的方方面面:iOS开发基础理论知识、 视图、视图控制器、多媒体、数据处理、常用插件、信用卡卡号识别、自动化测试、网络访问、多线程、ShareSDK社会化分享、CoreImage、...
  • 18个 ios 项目源代码

    万次下载 热门讨论 2020-07-30 23:32:25
    18个 ios 项目源代码 各种布局 控件使用 及扩展视图 自定义UITableViewCell。实现各种样式的表格输入界面、自定义Tab Bar的文字、颜色和图片加箭头、列表个性化列表项、IOS欢迎界面等。
  • 事件是由硬件捕捉并产生一个表示用户操作设备的对象发送给IOS。 UIEvent事件类,事件包括点击事件、晃动事件、远程控制事件。 响应者链:  手势、事件、响应者链(笔记)" TITLE="IOS之UI 手势、事件、响应者...

    事件:
    事件是由硬件捕捉并产生一个表示用户操作设备的对象发送给IOS。
    UIEvent事件类,事件包括点击事件、晃动事件、远程控制事件。

    响应者链:
    IOS之UI <wbr> <wbr>手势、事件、响应者链(笔记)



    UISegmentedControl:
    IOS中的单选控件

    UIImageView:
    UIImageView相当于一个相框,专门用作显示图片,可以存放一个图片或一组图片。
    UIImage表示图片对象。

    手势:
    手势识别:
    1、轻拍,单击,双击,多指点击
    2、长按
    3、轻扫,分上下左右四个方向
    4、旋转
    5、捏合,放大缩小
    6、拖拽,移动位置

    UISlider:
    UISlider滑块控制器;通过滑动上面的按钮改变其数值。

    矩阵变化:旋转,缩放,位移

    1、UIView触摸需要实现哪些方法?
    - (void)touchesBegan:(NSSet *)touches
    withEvent:(UIEvent *)event;   检测触摸事件

       Moved; Ended;Cancelled

    2、如何在自定义UIView里绘图?

    UIGraphicsGetCurrentContext() 获得绘图上下文

    CGContextSetStrokeColorWithColor()   设置画笔颜色

    CGContextSetLineWidth()   设置画笔粗细 

    CGContextMoveToPoint()   移动画笔到指定位置

    CGContextAddLineToPoint()   画直线

    3、什么是CGContextRef?

    在代码中,用它来表示一个上下文,一个上下文表示一个绘制目标,定义了基本的绘制属性,如颜色,裁剪区域、线条宽度和样式信息,字体信息和混合模式等。





    展开全文
  • iOS 使用 WKWebView 无法截获上、下手势的问题解决

    iOS 与 Java 服务器之间 SSL 握手失败的解决:Cipher Suites

    太阳火神的美丽人生 (http://blog.csdn.net/opengl_es)

    本文遵循“署名-非商业用途-保持一致”创作公用协议

    转载请保留此句:太阳火神的美丽人生 -  本博客专注于 敏捷开发及移动和物联设备研究:iOS、Android、Html5、Arduino、pcDuino否则,出自本博客的文章拒绝转载或再转载,谢谢合作。


    关于手势,有空会在此处扩展,本文重点是说明一个关键点,那就是一个事件被多个手势同时响应时,如何各自都被处理,在 WKWebView 上,有这样一个代理方法:


    - gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:
    

    该协议方法中返回 YES,就表示这前一个手势会在后一个手势被处理后,继续被处理,这样我们就可以在 上、下轻扫手势的 target 方法中接到事件信息了。


    时下苹果的很多组件,由原来的开放,开始变得严格管控起来了,手势可能会多次用到,这个方法能以一种优雅的方式,让你能切入到组件事件中。

    展开全文
  • 手势识别器(Gesture Recognizer)用于识别触摸序列并触发响应事件。当手势识别器识别到一个手势或手势发生变化时,会触发响应事件。UIGestureRecognizer类作为抽象类,不能直接使用。只能使用UIGestureRecognizer的...
  • 主要介绍了IOS 单击手势的添加实现代码的相关资料,需要的朋友可以参考下
  • iOS 多线程GCD

    2018-10-12 10:51:58
    为了了解、记忆更深刻,记录下~~ 一:什么是GCD GCD本身是苹果公司为多核的并行运算提出的解决方案。GCD在工作时会自动利用更多的处理器核心,以充分利用更强大的机器。GCD是Grand Central Dispatch的简称,它是...

    为了了解、记忆更深刻,记录下,后面也会附上自己git 上demo,欢迎互相学习!!
    一:什么是GCD
    GCD本身是苹果公司为多核的并行运算提出的解决方案。GCD在工作时会自动利用更多的处理器核心,以充分利用更强大的机器。GCD是Grand Central Dispatch的简称,它是基于C语言的。如果使用GCD,完全由系统管理线程,我们不需要编写线程代码。只需定义想要执行的任务,然后添加到适当的调度队列(dispatch queue)。GCD会负责创建线程和调度你的任务,系统直接提供线程管理

    GCD优势:
    GCD是苹果公司为多核的并行计算提出的解决方案,GCD会自动利用更多的CPU内核(比如双核、四核);GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码

    通过GCD,开发者不用再直接跟线程打交道了,只需要向队列中添加代码块即可,GCD在后端管理着一个线程池。GCD不仅决定着你的代码块将在哪个线程被执行,它还根据可用的系统资源对这些线程进行管理,来缓解大量线程被创建的问题。

    GCD带来的另一个重要改变的是,做为开发者可以将工作考虑为一个队列,而不是一堆线程(GCD把线程问题抽象成队列问题了),这种并行的抽象模型更容易掌握和使用。
    GCD公开有5个不同的队列:
    运行在主线程中的main queue(主队列),3个不同优先级的后台队列(优先级队列),以及一个优先级更低的后台队列(用于I/O)。

    这里写图片描述

    二:GCD中两个核心概念任务和队列:
    任务:执行什么操作(block)
    就是执行操作的意思,换句话说就是你在线程中执行的那段代码。在 GCD 中是放在 block 中的。执行任务有两种方式:同步执行(sync)和异步执行(async)。两者的主要区别是:是否等待队列的任务执行结束,以及是否具备开启新线程的能力。

    队列:用来存放任务(queue)
    这里的队列指执行任务的等待队列,即用来存放任务的队列。队列是一种特殊的线性表,采用 FIFO(先进先出)的原则,即新任务总是被插入到队列的末尾,而读取任务的时候总是从队列的头部开始读取。每读取一个任务,则从队列中释放一个任务。队列的结构可参考下图:
    这里写图片描述

    GCD的使用就两个步骤
    1)定制任务确定想做的事情
    2)将任务添加到队列中
    GCD会自动将队列中的任务取出,放到对应的线程中执行
    任务的取出(任务的执行)遵循队列的FIFO原则:先进先出,后进后出

    三:执行任务
    有两个重要概念:
    执行任务时的函数类型(同步,异步);执行任务时的队列类型(串行,并行)

    1.同步或异步函数:
    函数作用:
    将任务添加到队列中

    函数类型:
    决定是否有开启新线程的能力,是否立即执行

    函数针对对象:
    线程

    函数区别:
    同步:在当前线程中执行,任务会立即执行(没有开辟新线程的能力),会阻塞当前线程
    异步:在开辟新线程中执行,任务不会立即执行(有开辟新线程的能力)
    注意:异步具备开启线程的能力,但不一定开启新线程,比如:当前队列为主队列,异步函数也不会开启新的线程

    1.1.同步:
    同步任务优先级高,在线程中有执行顺序,不会开启新的线程。

    用同步的方式执行任务:
    dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
    queue:队列
    block:任务

    • 会把一个block加入到指定的队列中,而且会一直等到执行完blcok,这个函数才返回。因此在block执行完之前,会一直等待,直到队列里面的任务完成之后再继续执行。在block执行完之前,调用dispatch_sync方法的线程是阻塞的。
    • 是同步操作,任务会依次顺序执行,能够决定任务的执行顺序
    • 只能在当前线程中执行任务,不具备开启新线程的能力。

    1.2.异步:
    异步任务优先级低,在线程中执行没有顺序,看cpu闲不闲。在主队列中不会开启新的线程,其他队列会开启新的线程。

    用异步的方式执行任务;
    dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
    queue:队列
    block:任务

    • 会把一个block加入到指定的队列中,函数把block加入队列后不等block的执行就立刻返回了,也就是它不会做任何等待,可以继续执行任务
    • 异步操作,会并发执行,无法确定任务的执行顺序
    • “可以”在新的线程中执行任务,具备开启新线程的能力
    • 注意: 异步执行(async) 虽然具有开启新线程的能力,但是并不一定开启新线程。这跟任务所指定的队列类型有关。

    2.队列类型(串行,并行)

    队列概念:

    2.1:串行队列(Serial Dispatch Queue)

    • 让任务一个接一个的执行(一个任务执行完毕后,再执行下一个任务)
    • 这个队列中所有任务,一定按照先来后到的顺序执行。不仅如此,还可以保证在执行某个任务时,在它前面进入队列的所有任务肯定执行完了。对于每一个不同的串行队列,系统会为这个队列建立唯一的线程来执行代码。

    2.2:并行队列(Concurrent Dispatch Queue )

    • 可以让多个任务并行(同时)执行(自动开启多个线程同时执行任务)
      并行功能只能在异步(dispatch_async)函数下才有效
    • 这个队列中的任务也是按照先来后到的顺序开始执行,注意是开始,但是它们的执行结束时间是不确定的,取决于每个任务的耗时。对于n个并发队列,GCD不会创建对应的n个线程而是进行适当的优化

    两者具体区别如下两图所示:
    这里写图片描述

    这里写图片描述

    这里写图片描述

    这里写图片描述

    区别示意图:
    这里写图片描述

    如何创建/获取队列:
    1.使用dispatch_queue_create函数创建队列
    dispatch_queue_create(const char * _Nullable label, dispatch_queue_attr_t _Nullable attr)
    label:队列名称
    arr:队列属性
    DISPATCH_QUEUE_SERIAL或NULL:串行队列
    DISPATCH_QUEUE_CONCURRENT:并发队列

    2.使用dispatch_get_global_queue(long identifier, unsigned long flags)获取全局队列(优先级队列)
    identifier:在ios7中代表队列优先级(priority),在ios8及以后代表服务质量(A quality of service),一般设置为DISPATCH_QUEUE_PRIORITY_DEFAULT即可。
    两者的对应关系如下:

    • DISPATCH_QUEUE_PRIORITY_HIGH: QOS_CLASS_USER_INITIATED (2:高)
    • DISPATCH_QUEUE_PRIORITY_DEFAULT: QOS_CLASS_DEFAULT (0:默认)
    • DISPATCH_QUEUE_PRIORITY_LOW: QOS_CLASS_UTILITY (-2:低)
    • DISPATCH_QUEUE_PRIORITY_BACKGROUND: QOS_CLASS_BACKGROUND
      flags:保留参数,以便以后使用,一般传0即可。

    3.使用dispatch_get_main_queue()获取主队列
    主队列:在主线程中运行,因为主线程只有一个,所以这是一个串行队列

    特点:
    a.主队列是与主线程相关联的队列
    b.主队列是GCD自带的一种特殊的串行队列

    四:具体使用
    队列类型(串行队列/并行队列),任务执行方式(同步执行/异步执行),那么我们就有了四种不同的组合方式。这四种不同的组合方式是:
    1.同步执行 + 并发队列
    2.异步执行 + 并发队列
    3.同步执行 + 串行队列
    4.异步执行 + 串行队列

    实际上,刚才还说了两种特殊队列:全局并发队列、主队列。全局并发队列可以作为普通并发队列来使用。但是主队列因为有点特殊,所以我们就又多了两种组合方式。这样就有六种不同的组合方式了。
    5.同步执行 + 主队列
    6.异步执行 + 主队列

    几种组合方式执行区别如下图:
    这里写图片描述

    基本使用情况
    4.1.同步执行 + 并发队列
    特点:在当前线程中执行任务,不会开启新线程,执行完一个任务,再执行下一个任务。

    - (IBAction)SynAndConcurrent:(id)sender {
        // 创建一个并发队列
        dispatch_queue_t queue = dispatch_queue_create("com.zoe3", DISPATCH_QUEUE_CONCURRENT);
        
        ZWWLog(@"同步任务开始,当前线程==%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
       
        for (int i = 0; i<5; i++) {
            //同步函数
            dispatch_sync(queue, ^{
                 if (i==1) {
                    [NSThread sleepForTimeInterval:10];
                }
                ZWWLog(@"当前打印值==%d,线程==%@",i,[NSThread currentThread]);
                
            });
        }
        ZWWLog(@"同步任务结束,当前线程==%@",[NSThread currentThread]);
    }
    

    打印结果:

    2018-10-10 18:57:28.108512+0800 ThreadTest[7169:829829] syncConcurrent begin,当前线程==<NSThread: 0x6000017c6940>{number = 1, name = main}
    2018-10-10 18:57:30.109866+0800 ThreadTest[7169:829829] 当前打印值==0,线程==<NSThread: 0x6000017c6940>{number = 1, name = main}
    2018-10-10 18:57:40.110826+0800 ThreadTest[7169:829829] 当前打印值==1,线程==<NSThread: 0x6000017c6940>{number = 1, name = main}
    2018-10-10 18:57:40.110939+0800 ThreadTest[7169:829829] 当前打印值==2,线程==<NSThread: 0x6000017c6940>{number = 1, name = main}
    2018-10-10 18:57:40.111026+0800 ThreadTest[7169:829829] 当前打印值==3,线程==<NSThread: 0x6000017c6940>{number = 1, name = main}
    2018-10-10 18:57:40.111094+0800 ThreadTest[7169:829829] 当前打印值==4,线程==<NSThread: 0x6000017c6940>{number = 1, name = main}
    2018-10-10 18:57:40.111180+0800 ThreadTest[7169:829829] syncConcurrent end,当前线程==<NSThread: 0x6000017c6940>{number = 1, name = main}
    

    可看到:

    • 所有任务都是在当前线程(主线程)中执行的,没有开启新的线程(同步执行不具备开启新线程的能力)
    • 所有任务都在打印的 同步任务开始和syncConcurrent begin—syncConcurrent end之间执行的(同步任务需要等待队列的任务执行结束)
    • 任务按顺序执行的。按顺序执行的原因:虽然并发队列可以开启多个线程,并且同时执行多个任务。但是因为本身不能创建新线程,只有当前线程这一个线程(同步任务不具备开启新线程的能力),所以也就不存在并发。而且当前线程只有等待当前队列中正在执行的任务执行完毕之后,才能继续接着执行下面的操作(同步任务需要等待队列的任务执行结束)。所以任务只能一个接一个按顺序执行,不能同时被执行。

    效果图:
    这里写图片描述

    4.2 异步+并行
    特点:可以开启多个线程(一般会开启多个线程),任务交替并发(同时)执行。

    - (IBAction)AsyncAndConcurrent:(id)sender {
        
        // 创建一个并发队列
        dispatch_queue_t queue = dispatch_queue_create("com.zoe4", DISPATCH_QUEUE_CONCURRENT);
        
        ZWWLog(@"AsyncConcurrent begin,当前线程==%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
        
        for (int i = 0; i<5; i++) {
            //异步函数
            dispatch_async(queue, ^{
               if (i==1) {
                    [NSThread sleepForTimeInterval:10];
                }
                ZWWLog(@"当前打印值==%d,线程==%@",i,[NSThread currentThread]);
            });
        }
        
        ZWWLog(@"AsyncConcurrent end,当前线程==%@",[NSThread currentThread]);
    }
    

    打印结果:

    2018-10-10 19:00:25.033221+0800 ThreadTest[7169:829829] AsyncConcurrent begin,当前线程==<NSThread: 0x6000017c6940>{number = 1, name = main}
    2018-10-10 19:00:27.034291+0800 ThreadTest[7169:829829] AsyncConcurrent end,当前线程==<NSThread: 0x6000017c6940>{number = 1, name = main}
    2018-10-10 19:00:27.034322+0800 ThreadTest[7169:829918] 当前打印值==0,线程==<NSThread: 0x6000017a31c0>{number = 3, name = (null)}
    2018-10-10 19:00:27.034378+0800 ThreadTest[7169:843673] 当前打印值==2,线程==<NSThread: 0x600001740400>{number = 4, name = (null)}
    2018-10-10 19:00:27.034396+0800 ThreadTest[7169:843674] 当前打印值==3,线程==<NSThread: 0x6000017a35c0>{number = 5, name = (null)}
    2018-10-10 19:00:27.034430+0800 ThreadTest[7169:843675] 当前打印值==4,线程==<NSThread: 0x600001740300>{number = 6, name = (null)}
    2018-10-10 19:00:37.034737+0800 ThreadTest[7169:843672] 当前打印值==1,线程==<NSThread: 0x60000174eb80>{number = 7, name = (null)}
    

    可以看出:

    • 除了当前线程(主线程),系统又开启了5个线程,并且任务是交替/同时执行的。(异步执行具备开启新线程的能力。且并发队列可开启多个线程,同时执行多个任务)。
    • 所有任务是在打印的AsyncConcurrent begin和AsyncConcurrent end之后才执行的。说明当前线程没有等待,而是直接开启了新线程,在新线程中执行任务(异步执行不做等待,可以继续执行任务)。

    效果图:
    这里写图片描述

    4.3 同步+串行
    特点:不会开启新线程,在当前线程执行任务。任务是串行的,执行完一个任务,再执行下一个任务

     - (IBAction)SyncAndSerial:(id)sender {
        // 创建一个串行队列:DISPATCH_QUEUE_SERIAL串行队列的优先级没有主队列优先级高,所以在这个串行队列中开启同步任务不会堵塞主线程
        dispatch_queue_t queue = dispatch_queue_create("com.zoe1", DISPATCH_QUEUE_SERIAL);
        
        ZWWLog(@"syncSerial begin,当前线程==%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
        for (int i = 0; i<5; i++) {
            //同步函数
            dispatch_sync(queue, ^{
                 if (i==1) {
                    [NSThread sleepForTimeInterval:10];
                }
                ZWWLog(@"当前打印值==%d,线程==%@",i,[NSThread currentThread]);
               
            });
        }
       
        ZWWLog(@"syncSerial end,当前线程==%@",[NSThread currentThread]);
    }
    

    打印结果:

    2018-10-10 19:02:53.609622+0800 ThreadTest[7257:848994] syncSerial begin,当前线程==<NSThread: 0x600001177f80>{number = 1, name = main}
    2018-10-10 19:02:55.610760+0800 ThreadTest[7257:848994] 当前打印值==0,线程==<NSThread: 0x600001177f80>{number = 1, name = main}
    2018-10-10 19:03:05.612065+0800 ThreadTest[7257:848994] 当前打印值==1,线程==<NSThread: 0x600001177f80>{number = 1, name = main}
    2018-10-10 19:03:05.612214+0800 ThreadTest[7257:848994] 当前打印值==2,线程==<NSThread: 0x600001177f80>{number = 1, name = main}
    2018-10-10 19:03:05.612304+0800 ThreadTest[7257:848994] 当前打印值==3,线程==<NSThread: 0x600001177f80>{number = 1, name = main}
    2018-10-10 19:03:05.612391+0800 ThreadTest[7257:848994] 当前打印值==4,线程==<NSThread: 0x600001177f80>{number = 1, name = main}
    2018-10-10 19:03:05.612476+0800 ThreadTest[7257:848994] syncSerial end,当前线程==<NSThread: 0x600001177f80>{number = 1, name = main}
    

    可以看到

    • 所有任务都是在当前线程(主线程)中执行的,并没有开启新的线程(同步执行不具备开启新线程的能力)
    • 所有任务都在打印的syncSerial begin和syncSerial end之间执行(同步任务需要等待队列的任务执行结束)
    • 任务是按顺序执行的(串行队列每次只有一个任务被执行,任务一个接一个按顺序执行)

    4.4.异步+串行
    特点:会开启新线程(一般只开启1条线程),但是因为任务是放到串行队列中的,所有一个任务执行完毕后再执行下一个任务

    - (IBAction)AsyncAndSerial:(id)sender {
        
        // 创建一个串行队列
        dispatch_queue_t queue = dispatch_queue_create("com.zoe2", DISPATCH_QUEUE_SERIAL);
        
       
        ZWWLog(@"AsyncSerial begin,当前线程==%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
        for (int i = 0; i<5; i++) {
            //异步函数
            dispatch_async(queue, ^{
                 if (i==1) {
                    [NSThread sleepForTimeInterval:10];
                }
                ZWWLog(@"当前打印值==%d,线程==%@",i,[NSThread currentThread]);
    
            });
        }
        
        ZWWLog(@"AsyncSerial end,当前线程==%@",[NSThread currentThread]);
    }
    
    

    打印结果:

    2018-10-10 19:02:02.480701+0800 ThreadTest[7257:848994] AsyncSerial begin,当前线程==<NSThread: 0x600001177f80>{number = 1, name = main}
    2018-10-10 19:02:04.482203+0800 ThreadTest[7257:848994] AsyncSerial end,当前线程==<NSThread: 0x600001177f80>{number = 1, name = main}
    2018-10-10 19:02:04.482215+0800 ThreadTest[7257:849051] 当前打印值==0,线程==<NSThread: 0x6000011f41c0>{number = 3, name = (null)}
    2018-10-10 19:02:14.484280+0800 ThreadTest[7257:849051] 当前打印值==1,线程==<NSThread: 0x6000011f41c0>{number = 3, name = (null)}
    2018-10-10 19:02:14.484433+0800 ThreadTest[7257:849051] 当前打印值==2,线程==<NSThread: 0x6000011f41c0>{number = 3, name = (null)}
    2018-10-10 19:02:14.484523+0800 ThreadTest[7257:849051] 当前打印值==3,线程==<NSThread: 0x6000011f41c0>{number = 3, name = (null)}
    2018-10-10 19:02:14.484609+0800 ThreadTest[7257:849051] 当前打印值==4,线程==<NSThread: 0x6000011f41c0>{number = 3, name = (null)}
    

    可以看到:

    • 开启了一条新线程(异步执行具备开启新线程的能力,串行队列只开启一个线程)。
    • 所有任务是在打印的AsynSerial begin和AsynSerial end之后才开始执行的(异步执行不会做任何等待,可以继续执行任务)。
    • 放入队列中的任务是按顺序执行的(串行队列每次只有一个任务被执行,任务一个接一个按顺序执行)。

    效果图:
    这里写图片描述
    4.5.同步+主队列

    4.5.1 同步+主队列(主线程)
    特点:同步执行 + 主队列在不同线程中调用结果也是不一样,在主线程中调用会出现死锁,而在其他线程中则不会。

    • 特点(主线程调用):互等卡住不执行。
    • 特点(其他线程调用):不会开启新线程,执行完一个任务,再执行下一个任务。
    - (IBAction)SyncAndMainQueue:(id)sender {
        //主队列+同步请求=死锁=线程阻塞(viewDidLoad方法未执行完,同步又要求立即执行循环打印任务,造成死锁)
        dispatch_queue_t mainQueue = dispatch_get_main_queue();
       
        ZWWLog(@"SyncMainQueue begin,当前线程begin==%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
        for (int i = 0; i<5; i++) {
            //同步任务:要求立即执行
            dispatch_sync(mainQueue, ^{
                ZWWLog(@"当前线程==%@,打印值==%d",[NSThread currentThread],i);
            });
        }
        
        ZWWLog(@"SyncMainQueue end,当前线程end==%@",[NSThread currentThread]);
    }
    

    打印结果:
    这里写图片描述

    可以看出
    打印完 SyncMainQueue begin,在执行同步函数内部代码时,由于死锁而crash

    主队列在执行完SyncMainQueue begin,去执行函数dispatch_sync,函数会把一个block加入到指定的队列,此函数要求执行完block才返回,函数要求此时去执行block内容,但是主队列此时还卡在函数,函数线程还在,不能去执行block,也就是说函数和block是两个操作,在队列中有前后关系。若是异步,函数添加完block就返回,顺序执行block内容,不存在死锁问题

    • 同步函数和主队列的特点
    • 同步函数 dispatch_sync :这个函数会把一个block加入到指定的队列中,而且要求立即执行,而且会一直等到执行完block,这个函数才返回。因此在block执行完之前,调用dispatch_sync方法的线程是阻塞的
    • 主队列特点:凡是放到主队列中的任务,都会放到主线程中执行…如果主队列发现当前主线程有任务在执行,那么主队列会暂停调度队列中的任务,直到主线程空闲为止
      效果图:
      这里写图片描述

    4.5.1 同步+主队列(其它线程)
    特点:不会开启新线程,执行完一个任务,再执行下一个任务

     - (IBAction)AsyncAndMainQueueOtherThread:(id)sender {
        [NSThread detachNewThreadSelector:@selector(SyncAndMainQueue:) toTarget:self withObject:nil];
    }
    

    打印结果:
    这里写图片描述
    可以看到:

    • 所有任务都是在主线程(非当前线程)中执行的,没有开启新的线程(所有放在主队列中的任务,都会放到主线程中执行)。
    • 所有任务都在打印的SyncMainQueue begin和SyncMainQueue end之间执行(同步任务需要等待队列的任务执行结束)。
    • “放到队列”中的任务是按顺序执行的(主队列是串行队列,每次只有一个任务被执行,任务一个接一个按顺序执行)。

    为什么现在就不会卡住了呢?
    因为SyncAndMainQueue 任务放到了其他线程里,而任务1、2,3,4都在追加到主队列中,这三个任务都会在主线程中执行。SyncAndMainQueue 任务在其他线程中执行到追加任务1到主队列中,因为主队列现在没有正在执行的任务,所以,会直接执行主队列的任务1,等任务1执行完毕,再接着执行任务2、3、4。所以这里不会卡住线程。

    4.6 异步+主队列
    特点:只在主线程中执行任务,执行完一个任务,再执行下一个任务

    - (IBAction)AsyncAndMainQueue:(id)sender {
        
        dispatch_queue_t mainQueue = dispatch_get_main_queue();
        ZWWLog(@"AsyncMainQueue begin,当前线程begin==%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
        for (int i = 0; i<5; i++) {
            //异步任务:不要求立即执行
            dispatch_async(mainQueue, ^{
                ZWWLog(@"当前线程==%@,打印值==%d",[NSThread currentThread],i);
            });
        }
    
        ZWWLog(@"AsyncMainQueue end,当前线程end==%@",[NSThread currentThread]);
    }
    

    打印结果:
    这里写图片描述

    可以看到:

    • 所有任务都是在当前线程(主线程)中执行的,并没有开启新的线程(虽然异步执行具备开启线程的能力,但因为是主队列,所以所有任务都在主线程中)。
    • 所有任务是在打印的AsyncMainQueue begin和AsyncMainQueue end之后才开始执行的(异步执行不会做任何等待,可以继续执行任务)。
    • “放入队列中”的任务是按顺序执行的(因为主队列是串行队列,每次只有一个任务被执行,任务一个接一个按顺序执行)。

    效果图:
    这里写图片描述

    五:线程间通信
    在 iOS 开发过程中,我们一般在主线程里边进行 UI 刷新,例如:点击、滚动、拖拽等事件。我们通常把一些耗时的操作放在其他线程,比如说图片下载、文件上传等耗时操作。而当我们有时候在其他线程完成了耗时操作时,需要回到主线程,那么就用到了线程之间的通讯

    - (void)testDownImg{
        ZWWLog(@"当前线程==%@",[NSThread currentThread]);
        dispatch_async(dispatch_get_global_queue(0, 0), ^{//在一个新的并发队列中异步下载图片
            ZWWLog(@"开始下载图片,所在线程==%@",[NSThread currentThread]);
            NSURL *imagURL1 = [NSURL URLWithString:@"https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=2753165990,2892529492&fm=200&gp=0.jpg"];
            NSData *imgData1 = [NSData dataWithContentsOfURL:imagURL1];
            UIImage *image1 = [UIImage imageWithData:imgData1];
    
    
            NSURL *imagURL2 = [NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1523511772126&di=10a69a6a130ddc5e9265b72510aa16bb&imgtype=0&src=http%3A%2F%2Fimgsrc.baidu.com%2Fimage%2Fc0%253Dpixel_huitu%252C0%252C0%252C294%252C40%2Fsign%3D1c7d31d2b73eb13550cabffbcf66cdbf%2Ffd039245d688d43f1be38cc8761ed21b0ef43b45.jpg"];
            NSData *imgData2 = [NSData dataWithContentsOfURL:imagURL2];
            UIImage *image2 = [UIImage imageWithData:imgData2];
            
            [NSThread sleepForTimeInterval:10];
    
            dispatch_async(dispatch_get_main_queue(), ^{//
                ZWWLog(@"回到主线程,所在线程==%@",[NSThread currentThread]);
                [self.showIV setImage:image1];
                [self.showIV2 setImage:image2];
            });
        });
    }
    

    打印结果:
    这里写图片描述
    可以看到:
    在其他线程中先执行任务(耗时的下载图片任务),执行完了之后回到主线程执行主线程的相应操作。

    六:GCD的其它方法
    6.1 GCD一次性代码(只执行一次):dispatch_once
    我们在创建单例、或者有整个程序运行过程中只执行一次的代码时,我们就用到了 GCD 的 dispatch_once 函数。使用dispatch_once 函数能保证某段代码在程序运行过程中只被执行1次,并且即使在多线程的环境下,dispatch_once也可以保证线程安全。

    - (void)useGCD1{
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
        // 只执行1次的代码(这里面默认是线程安全的)
            ZWWLog(@"使用GCD创建单例");
        });
    }
    

    6.2 GCD 延时执行方法:dispatch_after
    我们经常会遇到这样的需求:在指定时间(例如3秒)之后执行某个任务。可以用 GCD 的dispatch_after函数来实现。
    需要注意的是:dispatch_after函数并不是在指定时间之后才开始执行处理,而是在指定时间之后将任务追加到主队列中。严格来说,这个时间并不是绝对准确的,但想要大致延迟执行任务,dispatch_after函数是很有效的。

    - (void)useGCD2{
        ZWWLog(@"任务开始,当前线程==%@",[NSThread currentThread]);
    //    [self performSelector:@selector(handleThread) withObject:nil afterDelay:2];
        
        //DISPATCH_TIME_NOW  立即开始
        //DISPATCH_TIME_FOREVER 永远
        //NSEC_PER_SEC 秒
        //NSEC_PER_MSEC 毫秒
        dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2* NSEC_PER_SEC));
        dispatch_after(time, dispatch_get_main_queue(), ^{
            ZWWLog(@"延迟后打印任务");
        });
        ZWWLog(@"任务结束,当前线程==%@",[NSThread currentThread]);
    }
    
    

    打印结果:
    这里写图片描述

    6.3 GCD调度组 :dispatch_group_t
    有时候我们会有这样的需求:分别异步执行几个耗时任务,然后当这几个耗时任务都执行完毕后再回到主线程执行任务。这时候我们可以用到 GCD 的调度组。
    使用情况1:调用队列组的 dispatch_group_async 先把任务放到队列中,然后将队列放入队列组中
    使用情况2:使用队列组的 dispatch_group_enter、dispatch_group_leave 组合 来实dispatch_group_async。
    dispatch_group_enter 标志着一个任务追加到 group,执行一次,相当于 group 中未执行完毕任务数+1
    dispatch_group_leave 标志着一个任务离开了 group,执行一次,相当于 group 中未执行完毕任务数-1。
    当 group 中未执行完毕任务数为0的时候,才会使dispatch_group_wait解除阻塞,以及执行追加到dispatch_group_notify中的任务。

    回到主线程最后执行代码时:

    1. 调用队列组的 dispatch_group_notify 回到指定线程执行任务
    2. 使用 dispatch_group_wait 回到当前线程继续向下执行(会阻塞当前线程)。
    - (void)group{
        
        //创建一个调度组
        dispatch_group_t group = dispatch_group_create();
        
        //获取全局队列
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        
        NSLog(@"任务开始,当前线程==%@",[NSThread currentThread]);
        //调度组使用情况1
        dispatch_group_async(group, queue, ^{
            [NSThread sleepForTimeInterval:2.0];
            NSLog(@"下载图片1,当前线程==%@",[NSThread currentThread]);
        });
        
        dispatch_group_async(group, queue, ^{
            [NSThread sleepForTimeInterval:1.0];
            NSLog(@"下载图片2,当前线程==%@",[NSThread currentThread]);
        });
        
        dispatch_group_async(group, queue, ^{
            [NSThread sleepForTimeInterval:3.0];
            NSLog(@"下载图片3,当前线程==%@",[NSThread currentThread]);
        });
        
        //回主线程刷新UI方法1:notify通知,所有异步请求完毕后会通知
        dispatch_group_notify(group, dispatch_get_main_queue(), ^{
            NSLog(@"下载完毕更新UI,当前线程==%@",[NSThread currentThread]);
        });
        
        //调度组使用情况2
        //进入队列
        //    dispatch_group_enter(group);
        //    dispatch_group_async(group, queue, ^{
        //        [NSThread sleepForTimeInterval:2.0];
        //        NSLog(@"下载图片1,当前线程==%@",[NSThread currentThread]);
        //        //离开队列
        //        dispatch_group_leave(group);
        //    });
        //
        //    //进入队列
        //    dispatch_group_enter(group);
        //    dispatch_group_async(group, queue, ^{
        //        [NSThread sleepForTimeInterval:1.0];
        //        NSLog(@"下载图片2,当前线程==%@",[NSThread currentThread]);
        //        //离开队列
        //        dispatch_group_leave(group);
        //    });
        //
        //    //进入队列
        //    dispatch_group_enter(group);
        //    dispatch_group_async(group, queue, ^{
        //        [NSThread sleepForTimeInterval:3.0];
        //        NSLog(@"下载图片3,当前线程==%@",[NSThread currentThread]);
        //        //离开队列
        //        dispatch_group_leave(group);
        //    });
        //
        
        
            //回主线程刷新UI方法2:wait 相当于阻塞
    //        dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    //        NSLog(@"下载完毕更新UI,当前线程==%@",[NSThread currentThread]);
        
        NSLog(@"任务结束,当前线程==%@",[NSThread currentThread]);
    }
    
    

    使用(dispatch_group_notify通知)打印结果:
    这里写图片描述
    可以看到:
    下载任务会在“任务结束”后再执行,不会阻塞当前线程

    使用(dispatch_group_wait)打印结果:
    这里写图片描述
    可以看到:
    “任务结束”在最后再打印,证明会阻塞当前线程

    但无论使用情况1,还是情况2,无论使用dispatch_group_notify还是dispatch_group_wait都可以保证执行耗时操作后最后在主线程刷新UI

    6.4 栅栏dispatch_barrier_async

    我们有时需要异步执行“两组”操作,而且第一组操作执行完之后,才能开始执行第二组操作。这样我们就需要一个相当于 栅栏 一样的一个方法将两组异步执行的操作组给分割起来,当然这里的操作组里可以包含一个或多个任务。这就需要用到dispatch_barrier_async方法在两个操作组间形成栅栏。
    dispatch_barrier_async函数会等待前边追加到并发队列中的任务全部执行完毕之后,再将指定的任务追加到该异步队列中。然后在dispatch_barrier_async函数追加的任务执行完毕之后,异步队列才恢复为一般动作,接着追加任务到该异步队列并开始执行。具体如下图所示:
    这里写图片描述

    - (void)barrierGCD{
        dispatch_queue_t queue = dispatch_queue_create("zoe", DISPATCH_QUEUE_CONCURRENT);
        dispatch_async(queue, ^{
            
            [NSThread sleepForTimeInterval:2.0];
            ZWWLog(@"任务1");
        });
        
        dispatch_async(queue, ^{
            [NSThread sleepForTimeInterval:2.0];
            for (int i = 0; i < 2; ++i){
            ZWWLog(@"任务2,i==%d",i);
            }
        });
        
        dispatch_barrier_async(queue, ^{
            ZWWLog(@"中断");
        });
        
        dispatch_async(queue, ^{
            [NSThread sleepForTimeInterval:2.0];
            for (int i = 0; i < 3; ++i){
                ZWWLog(@"任务3,i==%d",i);
            }
        });
        dispatch_async(queue, ^{
            ZWWLog(@"任务4");
        });
    }
    

    打印结果:
    这里写图片描述

    可以看到:
    执行顺序无论耗时多少,任务多少,肯定先打印任务1,2(或者任务2,1)再打印任务4,3(或3,4)

    6.5 GCD 快速迭代方法:dispatch_apply
    通常我们会用 for 循环遍历,但是 GCD 给我们提供了快速迭代的函数dispatch_apply。dispatch_apply按照指定的次数将指定的任务追加到指定的队列中,并等待全部队列执行结束。
    我们可以利用异步队列同时遍历。比如说遍历 0~5 这6个数字,for 循环的做法是每次取出一个元素,逐个遍历。dispatch_apply可以同时遍历多个数字。

    - (void)useGCD4 {
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        ZWWLog(@"apply---begin");
        dispatch_apply(6, queue, ^(size_t index) {
            NSLog(@"%zd---%@",index, [NSThread currentThread]);
        });
        ZWWLog(@"apply---end");
    }
    

    打印结果:
    这里写图片描述
    可以看出:
    0~5 打印顺序不定,最后打印了 apply—end。
    因为是在并发队列中异步队执行任务,所以各个任务的执行时间长短不定,最后结束顺序也不定。但是apply—end一定在最后执行。这是因为dispatch_apply函数会等待全部任务执行完毕。

    6.6 GCD 信号量:dispatch_semaphore
    GCD 中的信号量是指 Dispatch Semaphore,是持有计数的信号。类似于过高速路收费站的栏杆。可以通过时,打开栏杆,不可以通过时,关闭栏杆。在 Dispatch Semaphore 中,使用计数来完成这个功能,计数为0时等待,不可通过。计数为1或大于1时,计数减1且不等待,可通过。Dispatch Semaphore 提供了三个函数。
    dispatch_semaphore_create:创建一个Semaphore并初始化信号的总量
    dispatch_semaphore_signal:发送一个信号,让信号总量加1
    dispatch_semaphore_wait:可以使总信号量减1,当信号总量为0时就会一直等待(阻塞所在线程),否则就可以正常执行。
    注意:信号量的使用前提是:想清楚你需要处理哪个线程等待(阻塞),又要哪个线程继续执行,然后使用信号量。
    Dispatch Semaphore 在实际开发中主要用于:
    保持线程同步,将异步执行任务转换为同步执行任务
    保证线程安全,为线程加锁

    6.6.1 Dispatch Semaphore 线程同步
    我们在开发中,会遇到这样的需求:异步执行耗时任务,并使用异步执行的结果进行一些额外的操作。换句话说,相当于,将将异步执行任务转换为同步执行任务。比如说:AFNetworking 中 AFURLSessionManager.m 里面的 tasksForKeyPath: 方法。通过引入信号量的方式,等待异步执行任务结果,获取到 tasks,然后再返回该 tasks。

    AFN使用Semaphore代码:

    - (NSArray *)tasksForKeyPath:(NSString *)keyPath {
        __block NSArray *tasks = nil;
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
        [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
            if ([keyPath isEqualToString:NSStringFromSelector(@selector(dataTasks))]) {
                tasks = dataTasks;
            } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(uploadTasks))]) {
                tasks = uploadTasks;
            } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(downloadTasks))]) {
                tasks = downloadTasks;
            } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(tasks))]) {
                tasks = [@[dataTasks, uploadTasks, downloadTasks] valueForKeyPath:@"@unionOfArrays.self"];
            }
            dispatch_semaphore_signal(semaphore);
        }];
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        return tasks;
    }
    

    下面,我们来利用 Dispatch Semaphore 实现线程同步,将异步执行任务转换为同步执行任务

    - (void)semaphoreSync{
        NSLog(@"semaphore begin,当前线程==%@",[NSThread currentThread]);
        
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
        __block int number = 0;
        dispatch_async(queue, ^{
            // 追加任务1
            [NSThread sleepForTimeInterval:2];
            NSLog(@"当前线程==%@",[NSThread currentThread]);
            number = 100;
            dispatch_semaphore_signal(semaphore);
        });
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"semaphore---end,number = %d",number);
        
    }
    

    打印结果:
    这里写图片描述

    可以看到:

    • semaphore—end 是在执行完 number = 100; 之后才打印的。而且输出结果 number 为 100。这是因为异步执行不会做任何等待,可以继续执行任务。异步执行将任务1追加到队列之后,不做等待,接着执行dispatch_semaphore_wait方法。此时 semaphore == 0,当前线程进入等待状态。然后,异步任务1开始执行。任务1执行到dispatch_semaphore_signal之后,总信号量+1,此时 semaphore == 1,dispatch_semaphore_wait方法使总信号量-1,正在被阻塞的线程(主线程)恢复继续执行。最后打印semaphore—end,number = 100。这样就实现了线程同步,将异步执行任务转换为同步执行任务。

    6.6.2 Dispatch Semaphore 线程安全和线程同步(为线程加锁)

    线程安全:如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
    若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作(更改变量),一般都需要考虑线程同步,否则的话就可能影响线程安全。

    线程同步:可理解为线程 A 和 线程 B 一块配合,A 执行到一定程度时要依靠线程 B 的某个结果,于是停下来,示意 B 运行;B 依言执行,再将结果给 A;A 再继续操作。
    举个简单例子就是:两个人在一起聊天。两个人不能同时说话,避免听不清(操作冲突)。等一个人说完(一个线程结束操作),另一个再说(另一个线程再开始操作)。

    下面,我们模拟火车票售卖的方式,实现 NSThread 线程安全和解决线程同步问题。
    场景:总共有50张火车票,有两个售卖火车票的窗口,一个是北京火车票售卖窗口,另一个是上海火车票售卖窗口。两个窗口同时售卖火车票,卖完为止。

    6.6.2.1 非线程安全(不使用 semaphore)

    static int ticketSurplusCountNotSafe = 50;
    
    - (void)initTicketStatusNotSafe {
        NSLog(@"semaphore begin,当前线程==%@",[NSThread currentThread]);
       
        // queue1 代表北京火车票售卖窗口
        dispatch_queue_t queue1 = dispatch_queue_create("net.bujige.testQueue1", DISPATCH_QUEUE_SERIAL);
        // queue2 代表上海火车票售卖窗口
        dispatch_queue_t queue2 = dispatch_queue_create("net.bujige.testQueue2", DISPATCH_QUEUE_SERIAL);
        kWeakSelf(self);
        dispatch_async(queue1, ^{
            [weakself saleTicketNotSafe];
        });
        dispatch_async(queue2, ^{
            [weakself saleTicketNotSafe];
        });
        NSLog(@"semaphore begin,当前线程==%@",[NSThread currentThread]);
    }
    /**
     * 售卖火车票(非线程安全)
     */
    - (void)saleTicketNotSafe {
        while (1) {
            if (ticketSurplusCount > 0) { //如果还有票,继续售卖
                ticketSurplusCount--;
                NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%d 窗口:%@", ticketSurplusCount, [NSThread currentThread]]);
                [NSThread sleepForTimeInterval:0.5];
            } else { //如果已卖完,关闭售票窗口
                NSLog(@"所有火车票均已售完");
                break;
            }
        }
    }
    
    

    打印结果:

    2018-07-30 14:45:22.664003+0800 ThreadTest[34929:343950] semaphore begin,当前线程==<NSThread: 0x60400007b240>{number = 1, name = main}
    2018-07-30 14:45:22.664216+0800 ThreadTest[34929:343950] semaphore begin,当前线程==<NSThread: 0x60400007b240>{number = 1, name = main}
    2018-07-30 14:45:22.664246+0800 ThreadTest[34929:344111] 剩余票数:49 窗口:<NSThread: 0x608000475a80>{number = 5, name = (null)}
    2018-07-30 14:45:22.664250+0800 ThreadTest[34929:347341] 剩余票数:48 窗口:<NSThread: 0x600000273b40>{number = 6, name = (null)}
    2018-07-30 14:45:23.164530+0800 ThreadTest[34929:344111] 剩余票数:47 窗口:<NSThread: 0x608000475a80>{number = 5, name = (null)}
    2018-07-30 14:45:23.164530+0800 ThreadTest[34929:347341] 剩余票数:46 窗口:<NSThread: 0x600000273b40>{number = 6, name = (null)}
    2018-07-30 14:45:23.668978+0800 ThreadTest[34929:347341] 剩余票数:44 窗口:<NSThread: 0x600000273b40>{number = 6, name = (null)}
    2018-07-30 14:45:23.668983+0800 ThreadTest[34929:344111] 剩余票数:45 窗口:<NSThread: 0x608000475a80>{number = 5, name = (null)}
    
    ...
    

    可以看到:
    在不考虑线程安全,不使用 semaphore 的情况下,得到票数是错乱的,这样显然不符合我们的需求,所以我们需要考虑线程安全问题。

    6.6.2.2 线程安全(使用 semaphore 加锁)

    顶部定义静态变量
    
    static int ticketSurplusCountSafe = 50;
    static dispatch_semaphore_t semaphoreLock;
    - (void)initTicketStatusSafe {
        NSLog(@"semaphore begin,当前线程==%@",[NSThread currentThread]);
        semaphoreLock = dispatch_semaphore_create(1);
        // queue1 代表北京火车票售卖窗口
        dispatch_queue_t queue1 = dispatch_queue_create("net.bujige.testQueue1", DISPATCH_QUEUE_SERIAL);
        // queue2 代表上海火车票售卖窗口
        dispatch_queue_t queue2 = dispatch_queue_create("net.bujige.testQueue2", DISPATCH_QUEUE_SERIAL);
        kWeakSelf(self);
        dispatch_async(queue1, ^{
            [weakself saleTicketSafe];
        });
        dispatch_async(queue2, ^{
            [weakself saleTicketSafe];
        });
         NSLog(@"semaphore end,当前线程==%@",[NSThread currentThread]);
    }
    /**
     * 售卖火车票(线程安全)
     */
    - (void)saleTicketSafe {
        while (1) {
            // 相当于加锁
            dispatch_semaphore_wait(semaphoreLock, DISPATCH_TIME_FOREVER);
            if (ticketSurplusCountSafe > 0) { //如果还有票,继续售卖
               ticketSurplusCountSafe--;
                NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%d 窗口:%@", ticketSurplusCountSafe, [NSThread currentThread]]);
                [NSThread sleepForTimeInterval:0.5];
            } else { //如果已卖完,关闭售票窗口
                NSLog(@"所有火车票均已售完");
                // 相当于解锁
                dispatch_semaphore_signal(semaphoreLock);
                break;
            }
            // 相当于解锁
            dispatch_semaphore_signal(semaphoreLock);
        }
    }
    
    
    或者使用互斥锁保证线程安全:
    - (void)saleTicketSafe1{
        while (1) {
            
            //互斥锁
            @synchronized(self){//没有这句同步锁,票数将会重复出现相同余票
                if (ticketSurplusCountSafe > 0) {
                    ticketSurplusCountSafe--;
                    NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%d 窗口:%@", ticketSurplusCountSafe, [NSThread currentThread]]);
                    [NSThread sleepForTimeInterval:0.5];
                } else {
                    NSLog(@"所有火车票均已售完");
                    break;
                }
            }
        }
    }
    

    打印结果:

    2018-07-30 14:42:17.374356+0800 ThreadTest[34929:343950] semaphore begin,当前线程==<NSThread: 0x60400007b240>{number = 1, name = main}
    2018-07-30 14:42:17.374591+0800 ThreadTest[34929:343950] semaphore end,当前线程==<NSThread: 0x60400007b240>{number = 1, name = main}
    2018-07-30 14:42:17.374626+0800 ThreadTest[34929:344061] 剩余票数:49 窗口:<NSThread: 0x608000475440>{number = 3, name = (null)}
    2018-07-30 14:42:22.109815+0800 ThreadTest[34929:343988] 剩余票数:48 窗口:<NSThread: 0x600000273780>{number = 4, name = (null)}
    2018-07-30 14:42:22.615040+0800 ThreadTest[34929:344061] 剩余票数:47 窗口:<NSThread: 0x608000475440>{number = 3, name = (null)}
    2018-07-30 14:42:23.118436+0800 ThreadTest[34929:343988] 剩余票数:46 窗口:<NSThread: 0x600000273780>{number = 4, name = (null)}
    2018-07-30 14:42:23.619037+0800 ThreadTest[34929:344061] 剩余票数:45 窗口:<NSThread: 0x608000475440>{number = 3, name = (null)}
    2018-07-30 14:42:24.119736+0800 ThreadTest[34929:343988] 剩余票数:44 窗口:<NSThread: 0x600000273780>{number = 4, name = (null)}
    2018-07-30 14:42:24.624486+0800 ThreadTest[34929:344061] 剩余票数:43 窗口:<NSThread: 0x608000475440>{number = 3, name = (null)}
    
    ...
    

    可以看到到:
    在考虑了线程安全的情况下,使用 dispatch_semaphore 机制之后,得到的票数是正确的,没有出现混乱的情况。我们也就解决了多个线程同步的问题。

    demo链接

    展开全文
  • IOS账号分享】《隐形守护者》:是时候做出选择了!哪个妹子你最爱?...iPhone / iPad | 2.6 GB| 中文|iOS 8.0 + 简介: 《旁观者2》是一款由Warm Lamp Games制作Alawar Premium发行的策略RPG类游戏...
  • &lt;video id="video_2" class="video-js " style="width:100%;height:auto;" autoplay loop muted playsinline -weblit-playsinline x5-playsinline preload="...
  • iOS特殊符号集合

    千次阅读 2019-09-18 23:28:50
    ☮☯☹☺☻✓✗✪✩❄❀⌘⌥⇧♪♫♬☹☺☻✓✔✗✘☚☛☜☝☞☟✌✉✍✎✏✐✑✒✁✂✃✄✆✇✈♩♪♫♬♭♮♯⌘⌥⇧^⌛©®™π♃♄♅♆♇♈♉♊♋♌♍♎♏♐♑♒♓☥✝✡☠☢☣☤☨✠☩☮☯☪☭☫☬☸☀☁☂☃☼...
  • IOS磁力下载软件,老司机必备品

    万次阅读 2019-08-05 18:20:35
    由于iOS玩机应用的极度匮乏其下载工具类应用更是稀有,先前也为大家推荐过黑科下载器和迅雷安装途径,但是体验上都不完美,很多功能还得付费才能使用,普通用户限速限制次数已经见怪不怪了,稍微敏感点的资源还直接...
  • 本课程主要介绍了开发iOS app的一些基本情况。例如,需要的硬件和软件条件,如何申请开发者账号,XCode的常用功能介绍。后给出一个iOS App的案例来演示开发App的过程。
  • ios::ate和ios::app在C++文件中的区别

    万次阅读 多人点赞 2016-06-01 21:57:15
    ios::app与ios::ate的区别     文件流 ios::app ios::ate 打开方式 结果 打开方式 结果 ofstream (默认是ios::in | ios::trunc) ios::app或ios::...
  • IOS破解软件,比较全的网站。

    万次阅读 2019-06-20 14:42:05
    转载于:https://blog.51cto.com/14259888/2369621
  • ofstream之ios::ate,ios::app,ios::in,ios::out

    万次阅读 多人点赞 2016-11-23 16:47:35
    ofstream流,以ios::app打开(或者“ios::app|ios::out”),如果没有文件,那么生成空文件;如果有文件,那么在文件尾追加。 以ios::app|ios::in打开,不管有没有文件,都是失败。 以ios::ate打开(或者”ios::ate...
  • ios12.1.3 越狱方法

    万次阅读 2019-01-25 06:14:23
    ios12.0越狱工具下载 ios11.3越狱工具下载 在线越狱
  • 从零练就iOS高手实战班

    万人学习 2018-10-22 21:38:03
    iOS课程教学从入门到实战,系统讲解Swift编程,精讲基础语法,详解iOS基础框架,知识点涵盖新潮实用的swift,AppleWatch App开发。 咨询QQ:2528047463 咨询群:462917576 付费学员答疑群:446896569
1 2 3 4 5 ... 20
收藏数 1,002,444
精华内容 400,977
关键字:

ios