很多童鞋可能对Apple开发中的异步调用和多线程的区别不是太清楚,这里本猫将用一些简单的示例来展示一下它们到底直观上有神马不同.

首先异步调用可以在同一个线程中,也可以在多个不同的线程中.每个线程都有一个run loop,主线程的运行环称为main run loop,所有和UI界面有关的操作必须在主运行环中完成.在run loop中线程将会轮询消息源发出的消息.比如在MacOS中鼠标就是一个消息源,鼠标按下就会给系统中相关线程的消息环发送消息;而在iOS中手势操作也是一个消息源,当对应的手势出现时,也会给有关线程的run loop发送消息.

run loop接收到消息源发送的消息后会发生什么呢?它会调用之前设置好的消息回调方法,比如手势操作对象会在创建时设置一个回调闭包.在手势消息送达运行环时,就会调用这个闭包.这就是异步调用的一种典型的使用方法.

异步调用不立即返回值,而是等到某个特定的时机再完成特定的调用.值得注意的是,正如前面强调过的那样,如果你在多线程中使用异步调用视图更新用户界面,你必须将实际updateUI的代码放到main run loop中执行,否则没有效果.

下面先看一下异步调用的示例代码,必须在Xcode8.0beta的playground中测试,因为是Swift 3.0的语法:

import UIKit

@objc class Time:NSObject{
    @objc(one:)
    func one(timer:Timer!){
        print(#function)
    }

    @objc(two:)
    func two(timer:Timer!){
        print(#function)
    }
}

let t = Time()
let timer1 = Timer(timeInterval: 1.0, target: t, selector: #selector(Time.one(timer:)), userInfo: nil, repeats: true)
let timer2 = Timer(timeInterval: 1.0, target: t, selector: #selector(Time.two(timer:)), userInfo: nil, repeats: true)
RunLoop.current.add(timer1, forMode: .defaultRunLoopMode)
RunLoop.current.add(timer2, forMode: .defaultRunLoopMode)
RunLoop.current.run()

大家注意最后一行,实际在graphic类型的App中这一行是不需要加的,因为UI App自身会驱动消息环使其中的任何Timer自动触发,但是在console类型的App中(比如MacOS)必须要手动run一次.

以上代码运行结果会反复打印出以下内容:

one(timer:)
two(timer:)
one(timer:)
two(timer:)
one(timer:)
two(timer:)

下面我们马上来验证异步调用默认都是在同一线程这一特性,因为在线程的run loop中所有触发和处理都是同步的,如果某一个事件的处理耗时很久或挂起了线程,则该线程中其他的操作都不会响应,即便有新的事件源被触发.

我们只需要简单的将代码中one方法修改为如下内容即可观察这一有趣的现象:

func one(timer:Timer!){
        print(#function)
        for _ in 0...100000000{
            //Do Nothing...
        }
    }

运行代码片段,你可以很清楚的看到,在one方法执行完成之前two方法不可能被执行,即使前面two方法被设置为1.0秒执行一次.这是因为one方法挂起了run loop,所有其他消息源都不会被处理.

最后让本猫带大家看一下异步调用在多线程中带来的变化,我们在playground中新建一个helper方法:

func invokeTimerInNewThread(methodName:String){
    let thread = Thread() {
        let timer = Timer(timeInterval: 1.0, target: t, selector: NSSelectorFromString(methodName), userInfo: nil, repeats: true)
        RunLoop.current.add(timer, forMode: .defaultRunLoopMode)
        RunLoop.current.run()
        while true{
            Thread.sleep(forTimeInterval: 1.0)
        }
    }
    thread.start()
}

以上代码有2点要注意:

  1. 如果不做任何操作线程在执行完闭包方法中的代码后会很快终止,所以你必须添加一个无限循环,尽管你可以将死循环可以写的更优雅一些 ;]
  2. 线程对象必须用start方法启动后才会运行

哦鸟!就是这么简单,下面把原来playground中的Timer创建代码统统删除,修改为如下代码:

invokeTimerInNewThread(methodName: "one:")
invokeTimerInNewThread(methodName: "two:")
while true{
    Thread.sleep(forTimeInterval: 1.0)
}

没错,playground中的主线程也必须最后添加一个无限循环,否则你看不到任何东西.运行playground,你将看到如下输出:

two(timer:)
one(timer:)
two(timer:)
two(timer:)
two(timer:)
two(timer:)
two(timer:)

这正是异步调用和多线程的一本质区别!!!one方法在循环结束前会一直挂起,之后的one方法回调将不会得到任何机会执行,原因前面说过了,one方法所在的线程被挂起了!但是这丝毫不会影响two方法的执行,因为two方法在另一个线程中啊!

MacOS和iOS开发中异步调用与多线程的区别的更多相关文章

  1. ios开发中如何调用苹果自带地图导航

    前段时间一直在赶项目,在外包公司工作就是命苦,天天加班不说,工作都是和工期合同挂钩的,稍微逾期就有可能被扣奖金,不谈这些伤脑筋的事情了,让我们说说iOS开发中如何调用苹果手机自带的地图. 学习如逆水行 ...

  2. 在ios开发中nil和NUll和Nilde区别————和如何判断连个对象的关系和UISlider不能拖动的问题

    nil表示一个对象指针为空,针对对象 >示例代码: NSString *someString = nil; NSURL *someURL = nil; id someObject = nil; ...

  3. iOS开发中常用方法调用顺序

  4. IOS开发中AVFoundation中AVAudioPlayer的使用

    IOS开发中如何调用音频播放组件 1.与音频相关的头文件等都在AVFoundation.h中,所以第一步是添加音频库文件: #import <AVFoundation/AVFoundation. ...

  5. ios开发中全局变量设置和调用方法

    ios开发中,全局变量设置和调用方法如下:在AppDelegate.h文件中设置全局变量:@interface ***AppDelegate{NSString *myName;}@property ( ...

  6. 总结iOS开发中的断点续传那些事儿

    前言 断点续传概述 断点续传就是从文件赏赐中断的地方重新开始下载或者上传数据,而不是从头文件开始.当下载大文件的时候,如果没有实现断点续传功能,那么每次出现异常或者用户主动的暂停,都会从头下载,这样很 ...

  7. 多线程在iOS开发中的应用

    多线程基本概念 01 进程 进程是指在系统中正在运行的一个应用程序.每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内. 02 线程 2-1 基本概念 1个进程要想执行任务,必须得有线程 ...

  8. iOS开发中文件的上传和下载功能的基本实现-备用

    感谢大神分享 这篇文章主要介绍了iOS开发中文件的上传和下载功能的基本实现,并且下载方面讲到了大文件的多线程断点下载,需要的朋友可以参考下 文件的上传 说明:文件上传使用的时POST请求,通常把要上传 ...

  9. iOS开发中遇到的一些问题及解决方案【转载】

    iOS开发中遇到的一些问题及解决方案[转载] 2015-12-29 [385][scrollView不接受点击事件,是因为事件传递失败] // //  MyScrollView.m //  Creat ...

随机推荐

  1. 妙用 scale 与 transfrom-origin,精准控制动画方向

    上次发完 不可思议的纯 CSS 导航栏下划线跟随效果 这篇文章之后,很多朋友找我讨论,感叹 CSS 的奇妙. 然后昨天,群里一位朋友问到了一个和这个效果比较类似的效果,问如何 将下面这个动画的下划线效 ...

  2. 不用第三方解码库取得图片宽高 附完整C++算法实现代码

    在特定的应用场景下,有时候我们只是想获取图片的宽高, 但不想通过解码图片才取得这个信息. 预先知道图片的宽高信息,进而提速图片加载,预处理等相关操作以提升体验. 在stackoverflow有一篇相关 ...

  3. [HNOI 2011]数矩形

    Description 题库链接 给出平面上 \(n\) 个点,选出四个点作为矩形顶点.求出矩形最大面积. \(1\leq n\leq 1500\) Solution 转载自 Z-Y-Y-S dark ...

  4. hdu 5427(水)

    题意:按照年龄从小到大排序 名字中可能有空格什么的,处理下即可 #include<iostream> #include<cstdio> #include<cstring& ...

  5. Orz

    OR: 说实话,感觉Virtual Judge挺好使的,至少到现在,Uva都没注册成功过QAQ,估计是校园网的问题 不得不说现在课越来越多,而且对于我们这种学校ACM才开展两年的来说,时间真的好有限, ...

  6. SpringBoot学习之SpringBoot执行器

    在以往的分布式开发当中,各个服务节点的监控必不可少.监控包含有很多方面,比如说:内存占用情况,节点是否健康等.在spring-boot会给我们提供相关资源监控叫做spring-boot-actuato ...

  7. java开发笔记——表映射实体类代码示例

    package com.special.ipmsdm; import java.io.Serializable; import javax.persistence.Column; import jav ...

  8. Python【第四课】 装饰器

    本篇内容 什么是装饰器 装饰器需要遵循的原则 实现装饰器的知识储备 高阶函数 函数嵌套 闭包函数 无参函数 装饰器示例 1.什么是装饰器 器即函数 装饰即修饰,意指为其他函数添加新功能 装饰器定义:本 ...

  9. C++ 智能指针 auto_ptr 和 shared_ptr

    首先,如果你不知道什么是智能指针,请先移步:C++智能指针简单剖析 1.auto_ptr #ifndef AUTO_PTR_H #define AUTO_PTR_H template<typen ...

  10. Linux编程之内存池的设计与实现(C++98)

    假设服务器的硬件资源"充裕",那么提高服务器性能的一个很直接的方法就是空间换时间,即"浪费"服务器的硬件资源,以换取其运行效率.提升服务器性能的一个重要方法就是 ...