iOS开发者必备并发编程技巧

在iOS并发编程中经常会遇到一些问题,我们在这里并不探究 NSThread 、 GCD 、 NSOperation 、 NSOperationQueue 的具体用法,只探讨一些容易被遗忘的小点。希望对广大iOS开发者能够起到一定的帮助。

线程成本

维基百科上对线程的解释是:

A thread of execution is the smallest sequence of programmed instructions that can be managed independently by a scheduler.

通俗来说,线程(thread),指的是一个独立的代码执行路径,也就是说线程是代码执行路径的最小分支。在 iOS 中,线程的底层实现是基于 POSIX threads API 的,也就是我们常说的 pthreads。

在iOS中,进程启动之后,一个最主要的线程我们称为主线程。主线程会创建和管理所有的UI元素。一般来说,与用户交互相关的中断性操作都会派发到主线程上进行处理,包括你的 IBAction 的方法。

线程的创建是需要成本的,每个线程不仅仅在创建的过程中需要耗费时间,同时,它也会占用一定的内核的内存空间和app的内存空间。

有关iOS多线程开发更详细的内容推荐观看:iOS多线程及异步任务处理(http://www.maiziedu.com/course/ios/23-2525/)

内核数据结构(Kernel data structures)

据官方文档,每个线程在内核空间上大概要消耗 1KB 大小的内存。而这块内存是用于存储线程的数据结构和属性的。这是一个连系内存(wired memory),不能在磁盘上分页。

This memory is used to store the thread data structures and attributes, much of which is allocated as wired memory and therefore cannot be paged to disk.

线程栈空间大小(Stack space)

在iOS中,主线程的栈空间大小为 1MB , 在OS X中,主线程的栈空间大小为 8MB ,并且,这都是不可修改的。子线程默认栈空间为 512KB 。

栈空间不是立即被创建分配的,它会随着使用而增长。所以说,即使主线程有 1MB 的栈空间,那么,在很大的一段时间之内,你都只会用到很少的一部分。

子线程允许分配的最小栈空间是 16KB ,并且,必须为 4KB 的倍数。我们可以通过 stackSize 属性来修改一个子线程的栈空间:

NSThread *t = [[NSThread alloc] initWithTarget:target

selector:selector object:object];

t.stackSize = size;

线程创建时间(Creation time)

The figures were determined by analyzing the mean and median values generated during thread creation on an Intel-based iMac with a 2 GHz Core Duo processor and 1 GB of RAM running OS X v10.5.

据官方文档,在一个2GHz的双核Intel处理器、1GB内存、OS X 10.5系统的iMac上,需要花费 90微秒 的时间(有些人会写90ms或者是90毫秒,其实,这里的ms是microsecond,而不是millisecond)。

原子属性

在声明属性的时候,我们两种选择,一种是 atomic ,一种是 nonatomic ,前者是原子的,后者是非原子的。基本上,他们的区别就在于, atomic 会在属性的 setter 方法上加上一个互斥锁(也 有一种说法 是使用自旋锁spin locks,不过,由于 自旋锁的bug ,可能苹果并不会使用自旋锁,转而使用 pthread_mutex 或者 dispatch_semaphore 等):

atomic

- (void)setCurrentImage:(UIImage *)currentImage

{

@synchronized(self) {

if (_currentImage != currentImage) {

_currentImage = currentImage;

}

}

}

- (UIImage *)currentImage

{

@synchronized(self) {

return _currentImage;

}

}

nonatomic

- (void)setCurrentImage:(UIImage *)currentImage

{

if (_currentImage != currentImage) {

_currentImage = currentImage;

}

}

- (UIImage *)currentImage

{

return _currentImage;

}

属性默认是 atomic 修饰的,明确写 nonatomic 才会是非原子操作。

比如:

@property(nonatomic, strong) UITextField *userName;

@property(atomic, strong) UITextField *userName;

@property(strong) UITextField *userName;

后两者其实是一样的,只有第一种才是非原子操作。

是否线程安全?

从上面的代码来看, atomic 最多也就只能保证属性的 setter 和 getter 方法是线程安全的。

我们举个例子,如果现在同时发生:

线程A在调用 getter 方法。

线程B、线程C在调用 setter 方法,并且它们设置的值是不一致的。

那么,线程A可能会获得原来的值,也可能会获得线程B或者线程C的值,这是不一定的。而且,属性最终的值可能是线程B,也可能是线程C设置的值。

用《 Effective Objective-C 2.0 》上面的话说,就是:

这么做虽然能提供某种程度的“线程安全”,但却无法保证访问该对象时绝对是线程安全的。当然,访问属性的操作确实是“原子”的。使用属性时,必定能从中获取到有效值,然而在同一个线程上多次调用获取方法,每次获取到的结果却未必相同。在两次访问操作之间,其他线程可能会写入新的值。

所以,要说到真正的线程安全, atomic 的差距还是有点大的。

是否应该使用?

在没有资源竞争的情况下(比如,单线程的时候), atomic 可能还是很快的,但是 在比较普遍的情况下, atomic 想比起 nonatomic 可能会有靠近20倍的性能差异, stack overflow中有人对此进行了测试 。

那么,究竟是否该使用 atomic 呢,这个要看你是否需要。对我来说,我一般很少使用 atomic ,如果实在有需要的话,我一般会使用 dispatch_barrier 代替(具体例子可以参考下面的 dispatch_barrier 的 setter 和 getter 的写法)。

有关并发同步的问题,下期文章将会举实际的例子来讲。

时间: 2024-10-16 22:12:26

iOS开发者必备并发编程技巧的相关文章

iOS开发者必备的10款工具

当前iOS和Android两大移动操作系统"二足鼎立",几乎覆盖了市面上大部分的智能手机.相比Android,iOS开发适配更简单,且随着各种实用工具和Swift语言的出现,iOS开发门槛地降低,开发前景巨大.而对于开发者和设计师来说,是否拥有一款功能强大且实用的开发工具将直接影响其工作效率和成果.为此,我们结合现有资讯.整合最新资源,从开发.设计.分析.内测分发等方面整理出"iOS开发者必备的十款工具"供大家参考. 开发环境: 1)Xcode 作为苹果公司的当家I

iOS开发者必备:自己总结的iOS、mac开源项目及库

UI 下拉刷新 EGOTableViewPullRefresh - 最早的下拉刷新控件. SVPullToRefresh - 下拉刷新控件. MJRefresh - 仅需一行代码就可以为UITableView或者CollectionView加上下拉刷新或者上拉刷新功能.可以自定义上下拉刷新的文字说明.具体使用看“使用方法”. (国人写) XHRefreshControl - XHRefreshControl 是一款高扩展性.低耦合度的下拉刷新.上提加载更多的组件.(国人写) CBStoreHou

12个ios开发者必备效率工具

这里推荐12个可以帮你简化iOS app开发流程的工具.很多开发者都使用过这些工具,涉及原型和设计.编程.测试.上架以及最后的营销,基本上涵盖了整个开发过程. 原型和设计 有了一个很好的创意后,你要做的不是立刻编程,而是设计UI和创建原型,这样你才能知道app如何运行,根据用户体验需要做哪些调整. App Cooker AppCooker 不仅是一个创建原型的优秀工具,它提供的许多功能还可以帮助你将程序发布到App store中.它集成了Dropbox,Box.net和photo roll,你可

适用于iOS开发者的Android开发技巧

我曾经从事过五年的iOS应用开发工作,那段时间我一直在尽量避免同Android打交道--不过现在情况不同了.不管大家是否相信,Android开发其实乐趣满满.而且与iOS开发相比也不像大家想象的那样差异巨大. 我在Android平台上开发出这款"七分钟锻炼"应用,并借此学到了很多宝贵的知识.我希望这篇文章分享的一些小技巧也能帮助大家解决实际问题.请注意,我接下来进行比较的内容并不一定完全匹配,而且本文的重点也不在于完整地叙述Android开发;当然,我一定会提到自己在开发这款简单应用的

iOS开发者必备:四款后端服务工具

本文转载至 http://mobile.51cto.com/iphone-411917.htm 对于开发者来说,连接后端数据或许是一件特别痛苦的事情.但后端服务却能够帮助开发人员以更快的速度构建移动应用程序而不牺牲后端功能. AD:2014WOT全球软件技术峰会北京站 课程视频发布 后端服务加速了移动应用的开发,改进了可扩展性并减少了开发者对服务器端编码的需求.对于开发者来说,连接后端数据到前端或许是一件特别痛苦的事情.但后端服务却能够帮助开发者以更快的速度构建移动应用,不仅如此,它还能减少着移

OC中并发编程的相关API和面临的挑战

OC中并发编程的相关API和面临的挑战(1) 小引 http://www.objc.io/站点主要以杂志的形式,深入挖掘在OC中的最佳编程实践和高级技术,每个月探讨一个主题,每个主题都会有几篇相关的文章出炉,2013年7月份的主题是并发编程,今天挑选其中的第2篇文章(Concurrent Programming: APIs and Challenges)进行翻译,与大家分享一下主要内容.由于内容比较多,我将分两部分翻译(API和难点)完成,翻译中,如有错误,还请指正. 目录 1.介绍 2.OS

iOS并发编程对比总结,NSThread,NSOperation,GCD - iOS

1. 多线程概念 进程 正在进行中的程序被称为进程,负责程序运行的内存分配 每一个进程都有自己独立的虚拟内存空间 线程 线程是进程中一个独立的执行路径(控制单元) 一个进程中至少包含一条线程,即主线程 可以将耗时的执行路径(如:网络请求)放在其他线程中执行 创建线程的目的就是为了开启一条新的执行路径,运行指定的代码,与主线程中的代码实现同时运行 1.1 多任务系统调度示意图 说明:每个应用程序由操作系统分配的短暂的时间片(Timeslice)轮流使用CPU,由于CPU对每个时间片的处理速度非常快

高并发编程必备基础 -- 转载自 并发编程网

文章转载自 并发编程网  本文链接地址:高并发编程必备基础 一. 前言 借用Java并发编程实践中的话"编写正确的程序并不容易,而编写正常的并发程序就更难了",相比于顺序执行的情况,多线程的线程安全问题是微妙而且出乎意料的,因为在没有进行适当同步的情况下多线程中各个操作的顺序是不可预期的,本文算是对多线程情况下同步策略的一个简单介绍. 二. 什么是线程安全问题 线程安全问题是指当多个线程同时读写一个状态变量,并且没有任何同步措施时候,导致脏数据或者其他不可预见的结果的问题.Java中首

iOS并发编程笔记【转】

线程 使用Instruments的CPU strategy view查看代码如何在多核CPU中执行.创建线程可以使用POSIX 线程API,或者NSThread(封装POSIX 线程API).下面是并发4个线程在一百万个数字中找最小值和最大值的pthread例子: #import <pthread.h> struct threadInfo { uint32_t * inputValues; size_t count; }; struct threadResult { uint32_t min;