Objective-C类族和工厂模式

相信大家都了解GoF的《Design Patterns》中提到的23种设计模式,其中将常见的设计模式分为三大类:创建型模式、行为型模式、结构型模式。而在《Clean Code》中也提到建造酒店的例子,系统中对象的构建和使用应当分离开,那么应该怎么构建对象更加整洁和符合使用场景就很重要。

在iOS的系统类库中也有一种方式使得开发者不必关注类中具体的存储实现,但可以根据不同需求场景创建出合适的对象来。比如Foudation中的NSArray、UIkit中的UIButton。

本文结合几种工厂设计模式的原理,对Objective-C类族的概念做一下简要的整理。

0. 三种工厂

其实除了《Design Patterns》中提到的Factory Method和Abstract Factory,常提到的还有另一种工厂模式Simple Factory。

在简单工厂中,产品有一个统一的interface,而可以有不同implementation,同时有一个(通常是仅有一个)工厂对象。需要产品的时候,工厂会根据已有条件做switch,选择一种产品实现,构建一个实例出来。这种设计将产品的抽象和实现分离开来,可以针对统一的产品接口进行通信,而不必特意关注具体实现的不同。对于产品类别的扩充也是可以的,但每增加一个产品,都需要修改工厂的逻辑,有一定维护成本。

工厂方法更进一步,将工厂也抽象出来,进行接口、实现分离。这样具体工厂和具体产品可以对应着同时扩充,而不需要修改现有逻辑。当然,使用者也许在不同场景要在一定程度上自己对应的工厂选择(这个总要有人知道,不可避免)。

抽象工厂相对于工厂方法主要是对整个产品类的体系进行了横向扩充,构成一个更为完整的系统。

1. Objective-C的类族(Class Cluster)

做iOS开发的朋友们一定用过NSNumber的numberWith…方法。但大家有可能都不知道NSNumber这样的方法调用返回的不是NSNumber类本身的对象,这正是Objective-C类族的微妙之处。

如上图所示,Number的概念很大。而实际上NSNumber实际上是有很多隐藏的子类的,而我们通过NSNumber的numberWith…方法得到的对象正是其子类的对象,但对于使用者几乎可以不必知道这一点,只要知道它是一个NSNumber对象就OK了。

“Simple Concept and Simple Interface”,这正是苹果设计类族的初衷,也是类族的优点所在。可以想象我们要用整数作为参数拿到一个NSNumber对象和一个布尔值参数拿到的NSNumber对象是不同的,这略微有些类似于switch逻辑(虽然是通过不同的方法),根据不同的条件提供不同的子类对象,而这一切都集中声明在公共接口类NSNumber中。我们很容易联想到上面提到的Simple Factory(简单工厂)设计模式。

没错,与简单工厂类似,类族的一个缺点也显现出来,那就是已有的类族不好扩展。比如你想NSNumber再多支持一种情况,这个恐怕很难。好在这些系统的类库已经将大部分可能都做进去了,考虑得比较完善,通常你只是去用就可以了。

2. 类族的子类扩展

了解了类族的概念,我们在实际开发当中也可以采用其方式,利用其优点。上面提到对已有类族进行子类扩展是很难的,但这不代表NSNumber、NSArray等类就没法继承了。他们还是可以有自定义的子类的。

既然要做类族的子类,就要做到:

  • 以公共“抽象”类为父类,比如NSNumber、NSArray等,而非其子类
  • 提供自定义存储
  • 重写(覆盖)父类所有初始化方法
  • 重写父类中“原始”方法

其中第二点最重要,系统的类族通常在父类中只是提供了各种方法声明,而自身并不提供存储,所以要自定义子类一定要提供自己的存储,一般情况下这也是自定义子类的意义所在。

重写初始化方法,要遵从Objective-C初始化链的规范。

而重写“原始”方法,这个要说一下。按照苹果的文档,和指定初始化方法形式类似,这些类里面的众多方法可以分为两类,“原始”方法和“衍生”方法。“原始”方法定义了这个类及对象的最基本行为,而“衍生”方法则基于这些“原始”方法进行更复杂逻辑的包装。所以,重写了“原始”方法,“衍生”方法也自然效果就不同了。

除了自定义子类外,苹果官方更建议开发者用组合的方式对类族类进行包装。

3. 对象所属类的判断

有人会问,如果我没有特殊需求,不需要写NSArray、NSNumber的子类,是不是了解类族就没有多大意义了。这里记一下,通过了解类族概念,我们至少知道了,通过NSNumber得到的对象,不一定是(基本上就不会是)NSNumber类本身的对象。

可以试验下,通过[NSNumber numberWithInt:2]和[NSNumber numberWithBool:YES]得到的对象对应的类,一个是__NSCFNumber,另一个是__NSCFBoolean。
那么,如下这样的判断就不行了:

id maybeAnArray = /* ... */;
if ([maybeAnArray class] == [NSArray class]) {
   // Will never be hit
}

需要在适当的情况下选择使用isMemberOfClass和isKindOfClass。

本文到这就整理这么多,更多内容可参看:

发表在 iOS, Objective-C, 开发, 计算机技术 | 标签为 , , , , | 4 条评论

Java服务端开发与Servlet

由于最近相当长一段时间都是忙于iOS相关的开发,很久没有整理Java的文章了。iOS接触得久了,有时候发现有些东西和之前了解到的Java很相似,或关系密切,可以互相借鉴。这很有意思,正所谓“他山之石,可以攻玉”,无论哪门语言,很多技术都是相通的。接下来一段时间,会将过去所做过的东西和零散的技术笔记整理出来,晒在Blog上和大家分享交流。如果能帮到新从事这个领域开发的朋友,那自然是更好不过了。

之前Java的文章已经把Java语言本身特点的基本东西整理过了,接下来的文章主要则是与Java具体应用相关的技术方面。

根据本人参与开发的经历,我认为Java当前应用最广泛的领域应当算Web网站应用和后端应用服务相关的开发。而在这些开发重一个很基本的技术点,那就就是Java的Servlet。说它是最基本的技术点的原因就在于,它在Socket等Java网络通信的基础上,把IO、并发等技术内容整合并包装起来,对业务逻辑提供了上层抽象的API,满足了很多复杂的应用场景,是Java EE的重要组成部分。现在的Spring MVC、Struts也都是在Servlet之上封装起来。这里,我就从对Java Servlet的理解开始整理起来。这也为后面整理其它Java技术文章做一个铺垫。

0. 起源

通过本人小站早期的一些写Java的文章,我们知道,大约在1995年前后,Java正式向这个世界say hello。1995年那个时候,互联网也已经逐步流行起来,面向对象语言的概念也很时髦,Java应运而生。那个时候,Java有个东西叫applet,就是在浏览器端运行的应用。

Applet这个想法是好的,但是想通过它来填平各个平台的差异是太难了。比如有一次,微软在IE中用了一套未遵循规范的浏览器支持,使得很多开发出来的applet只能在微软平台上正常运行。虽然Sun当时起诉了微软也赢得了诉讼,但我们知道,由于各种各样的原因,applet这种浏览器端形式的应用并没有真正成功起来。

“东方不亮西方亮”,Java的applet在浏览器开发领域没有流行起来,但是Java在服务端的应用越来越广泛,并成为主流。而相对浏览器端的applet,服务端也有类似的概念,那正是servlet。最终在1997年,Servlet API1.0发布。

1. 什么是Java Servlet

互联网刚刚兴起时,一个很主要的应用就是使用浏览器,通过HTTP协议,访问互联网上的超文本内容资源。提供超文本内容资源的计算机可以认为是服务器,而在这个服务器上还有一个专门负责提供服务的软件,也叫“服务器”(Server)。当前比较主流的服务器,基本上就是Apache http server和Nginx了。微软有过自己的IIS,现在的一些大公司也都有自己定制化过的服务器,比如淘宝的Tengien、百度的BWS。

最初,这些Http server不过是把一些固定的(静态的)的文档放在上面,供用户访问和查阅。可是,随着需求的变化、技术的发展,静态资源已经远远不够了。于是服务器端需要根据不同的访问需求返回对应的不同的结果。这就是程序需要做的事儿了!我们可以用各种开发工具各种编程语言来满足这个需求,只要能输出符合html格式的字符串,最后通过CGI脚本返回给Http server,再由Http server返回给浏览器UA。

刚刚提到了生成超文本Web内容可以使用各种语言,Java当然也包括在内。而Java在服务端处理服务器请求的部分,就是Java servlet。

可以认为Java是在Http Server后面的支持CGI的动态内容生成程序中的一种语言。但Java提供的技术方案不仅如此,相比于Perl等,Java提供的方案不必为每一个请求创建一个重量级的进程,自然就更高效。Java语言本身的语法特性,也降低了开发成本。

这里需要注意的是,Servlet只是服务器端的应用中处理具体业务逻辑的部分,而Servlet本身是需要生存在一个大的环境当中的,那就是Servlet容器。

Java的Servlet API约定了一个统一的接口层,其下是Servlet容器需要提供的公共服务,其上则是对应具体业务逻辑的Servlet实现。Java Servlet 和其它技术一起,构成了Java EE,Servlet API是Java EE API重要的一部分。下面就说说这个Servlet容器。

2. Servlet容器

Servlet容器,就是Servlet的生存和运行环境。对于一个Java之外的CGI程序来说,通常是在一个请求来临时开启一个进程来利用系统资源处理请求,给出响应,但Java的Servlet则不是,它会一直“保持”对请求的高度关注,必要时用一个线程来解决请求。

简单来想,我们一个简单的基于Java SE API的程序,只需要把逻辑写在main方法里,直接执行,到任务完成为止,main方法退出。这是一个线性的执行过程。而如果时时保持对请求的及时响应,显然这个程序是没有退出执行的,是一个非线性的,类似于环形的执行过程。而这个非线性的运行着的环境,就是Servlet Container提供的,普通的Servlet则不需要关注这些,只需要提供具体的业务内容处理逻辑即可。

Servlet容器

Servlet容器

对于一个典型的Servlet容器实现,比如Tomcat,就是用了一个while(running)循环保持着持续运行,请求来了的时候呼唤Servlet。但这只是Servlet容器提供Servlet运行环境的一个方面而已。

刚刚说容器在Servlet API之下提供了基础服务,那我们就详细看它提供了什么样的服务:

  • 通信支持。不管是作为Http server的一个后台支持也好,还是直接面对浏览器请求,网络通信都是基于socket进行的,而这些通信的具体细节,都由Servlet容器来负责的,Servlet无需关注。
  • 生命周期管理。上面提到的例子就是生命周期管理的一个方面体现,除此之外Servlet的加载、实例化也都有容器统一管理。
  • 多线程并发。作为一个比较权威的网站,可想其访问量会是相当大的,如果没有良好的并发支持,用户使用浏览器进行访问必然不能得到很好的响应。而无论是BIO,还是NIO,抑或AIO,都是与服务器操作系统相关的,与具体逻辑相关度不大,Servlet容器也将这个公共部分抽取出来进行实现。
  • 安全。
  • 除此之外,有些容器也要对JSP技术进行支持。

Tomcat是一个典型的Servlet容器实现,目前已经到了8.0版本,包罗了各项功能,可谓成熟、完善。其它的实现也有Jetty、Jboss等。

(接下来如果有时间有机会,我会以Tomcat源码为例整理一篇文章出来,分析下Servlet容器是如何实现的)

3. Servlet API

Servlet API是Java EE的重要组成部分,规定了Servlet和其容器间的接口。Java是开放的,JCP只是规定Servlet API的规范,无论是Servlet还是容器,只要符合API,就可以有各自不同的实现。

Servlet API在1997年发布1.0,到目前已有最新的3.1版本,本人当时做服务端开发接触较多的还是2.5。

Oracle最新的Servlet API文档是和整个Java EE 7 放在一起的,可参看:

https://docs.oracle.com/javaee/7/api/

此外,Tomcat也单独提供了Servlet API部分的文档,版本3.1:

http://tomcat.apache.org/tomcat-8.0-doc/servletapi/

这里面有4个Java package,而实际上我们主要关注的是其中两个包:

  • javax.servlet
  • javax.servlet.http

其中前者定义了Servlet相关的很多通用类,后者则是基于http应用层面的实现。这两个包里面定义了这样几个主要概念:

  • Servlet,一个为请求服务,并返回响应的处理具体业务逻辑单元。
  • Filter,顾名思义,在Servlet前后进行过滤处理操作的单元,多个Filter依次序形成一个过滤器链,请求资源完成前依次执行其doFilter()前的逻辑,请求后反序执行过滤器链上doFilter()之后的逻辑。
  • 各类Event及Listener。不考虑版本3之后增加的异步支持外,则围绕着Request、Servlet Context以及HttpSession提供了6类Event和8类Listener,这些API对于基于Servlet进行的开发设计和实现都有着很重要的意义。

这几项通常需要在web.xml中配置好,一起在Servlet容器中生效。

在Servlet API3.0以后,增加了servlet处理过程中的异步支持、Servlet配置注解化等新特性,更详细的可参考IBM developer上的这篇技术日志:

Servlet 3.0 新特性详解

4. JSP

最后再介绍下JSP。Servlet虽然很好的替代了传统的CGI,解决了服务端的需求,但由于Web超文本内容大多都是遵循HTML的标签格式。这样,Servlet中大部分都是字符串输出处理,而且在代码中大部分出现HTML标签常量,开发起来并不是很方便。同时,微软也在自家的IIS里加入了ASP技术。

在这样一种情况下,Java也考虑将HTML标签常量等“静态”地放置于页面里,而将需要动态处理的代码部分也以特殊的形式置入其中。并在页面中,内置一些常用对象,方便页面开发中使用。

这就是JSP。Sun在1999年发布了JSP技术标准第一版。

然而,现如今JSP由于将页面内容和业务逻辑缠绕在一起,在很多场景下并不是最优选择。一些页面的合成的工作有被Freemarker和Velocity等取代的趋势。

关于Java Servlet和JSP相关的内容,就先整理到这。

发表在 Java, Java的应用, 开发, 计算机技术 | 标签为 , , | 留下评论

iOS7之后的各种bar和布局

在iOS从版本6向iOS7升级的时候,UI上发生了颠覆性的变化。而且iOS7的UI设计风格大部分都在iOS8中保留了下来,相信也会持续下去。在本Blog此前的一些文章中也整理过(如《iOS7和状态条适配的问题》《再说说iOS的状态栏(显示与隐藏) 》),但仍然不够全面,也不彻底。最近看到一篇来自于外国朋友Jared Sinclair对iOS7之后状态栏、导航栏等变化和特点的整理,觉得写得很详细。本文将结合自己的实践,对这些要点再重新整理一下。

0. 状态条

状态条几乎是iOS6到iOS7之后讨论得比较多的一个UI关注点,以下对常见的问题点整理了一下:

  • iOS7下无法通过修改属性直接做到iOS7以前的效果,wantsFullScreenLayout的作用也彻底无效了;如果非要做成和iOS6相同相似的样子,可以用比较麻烦的布局手段来实现,但这是苹果所极力不支持的
  • 样式和布局等是不同的方面,互相没有直接影响
  • iOS7中只有2个样式可用:UIStatusBarStyleDefault(模糊透明背景+黑色前景文字)、UIStatusBarStyleLightContent(模糊透明背景+白色前景文字),其它样式均不生效,其中前者适用于浅色背景view,后者适用于神色背景view,这里所谓的“背景view”就是会显示在状态栏下面(后面)那层view
  • 容器Controller,比如UINavigationController可能会对基于ViewController的状态栏样式设置产生影响
  • iOS7之后,状态栏的属性可以通过“基于ViewController”和使用UIApplication对象动态设置两种方式来决定,前者需要在ViewController中实现prefer…方法,后者则可以调用application对象的setStatusBar…方法进行动态控制;这两种方式是互斥的,不能一起用
  • 如果想控制启动页面的状态栏样式或者显示/隐藏,可通过Info.plist中的key-value设置

1. NavigationBar、TabBar和edgesForExtendedLayout属性

在iOS7中,有个不成文的布局规则,当导航条(navigation bar)遇到了状态条(status bar),系统会自动连接成一个高度为64(状态条的20+导航条的44)的半透明结合体顶部Bar。而如果导航条没有挨到状态条(我只能说这种状况很少见),则高度还按自己单独的44计算。

默认情况下,用UINavigationController包装过的controller的view会自动延伸到屏幕顶端,在如上所说的64高度连接体顶部Bar出现的情况下,content view的上部64高度部分会被半透明地遮盖住。

默认情况下,使用UITabBarController情况类似,底部的44部分会被半透明形式遮盖住。

而这正是edgesForExtendedLayout属性发挥作用的地方,可以通过对content view controller对象的此属性设置UIRectEdgeNone的值,保证content view的任何部分都不被导航条或者TabBar遮盖。ToolBar应该也是类似的。

2. UIScrollView的contentInset自动匹配

我们知道iPhone的屏幕尺寸在不断变大,可是就算变得再大,也总有更多的内容,让一屏不能展示完全。这也正是UIScrollView的价值所在。

在iOS7中,在存在status bar、navigation bar,或者tab bar、tool bar的情况下,我们可能要担心,content view中的scroll view的上边、下边部分会不会被以半透明的形式覆盖掉。

实际上,这是不必担心的。首先,上面提到的edgesForExtendedLayout属性可以帮我们把content view只控制在可视范围内。另外,在iOS7中,UIViewController还提供了一个automaticallyAdjustsScrollViewInsets属性。它可以使得scroll view的上下部分仍然在各种Bar的后面以半透明的形式显示出来,同时scroll的主题内容不被bar遮盖。

在实际使用的时候,本人发现并不是任何时候都是这样的,能够生效的前提条件是UIScrollView对象是作为content view controller的root view或者是view0。

UITableView和UICollectionView是UIScrollView的子类,因此同样适用。

3. 其它

由于苹果官方文档中,对本文涉及到的内容只是略提,并未详述。本文内容主要是对Jared Sinclair老兄blog内容的翻译,不过其中也结合了很多其它实践、试验过程中的要点,将其丰富化详细化。

感兴趣的可以参看原文:http://blog.jaredsinclair.com/post/61507315630/wrestling-with-status-bars-and-navigation-bars-on-ios-7

此外,破船之家的blog也有一片文章,里面涉及到了各种bar style和layout之外的内容,如tintColor的使用等,供参考:http://beyondvincent.com/blog/2013/11/03/120-customize-navigation-status-bar-ios-7/

发表在 iOS, iOS开发基础, 开发, 计算机技术 | 标签为 , , , , , , | 留下评论

iOS中社交分享相关的API

现如今,Twitter、Facebook、微博等都陪伴我们度过每一天,社交网络工具已成为人与人之间主流的沟通工具,同时也是媒体平台和产品推广渠道。一个产品能否为更多人所知,一定程度上也为能否很好的利用社交网络所影响。比如我们做一款App,希望通过用户在微博分享,口口相传,那么就需要一些API上的支持。

现在无论是新浪微博,还是腾讯的微信等主流的社交媒体,都提供了开放平台和客户端SDK,在其中有着丰富的API支持。但实际上iOS本身也集成了很多社交的API,对于比较简单的需求场景,使用起来是非常方便的,这篇文章简要整理一下

0. 概览

iOS和社交工具的合作发展,是一个漫长的过程。

从iOS5.0开始,iOS系统开始集成Twitter,开发者可以使用Twitter.framework。

iOS6又开辟了新的API支持,添加了Social.framework,对社交媒体的支持不仅仅是Twitter,新增了Facebook和新浪微博。同时,配合着Social framework,还在UIKit中添加了UIActivityViewController和相关的类,社交分享已经成为iOS App很自然的一个组成部分。

iOS7中,进一步扩大了支持范围,增加分享到Flickr、Vimeo以及国内的腾讯微博。

iOS8则直接在新的App Extension机制中增加了Share一类。

1. UIActivityViewController

UIActivityViewController展现界面

UIActivityViewController会根据App中特定的内容和当前系统的支持的情况,“智能”判断可以做哪些操作,如果是文字文本、图片、URL,并且系统已经登录过社交网络账号,则可以分享内容到微博或者Facebook等。

最简单的方式就是通过

- (instancetype)initWithActivityItems:(NSArray *)activityItems applicationActivities:(NSArray *)applicationActivities

这个方法初始化UIActivityViewController这个类的对象,然后使用当前ViewCotroller对象将其present出来。

其中,activityItems中包含想要分享的内容,而applicationActivities则是一个UIActivity对象数组,代表要进行分享的的服务,通常传nil就可以了,系统会根据前一个参数来决定。

2. Social framework

假如在上面的UIActivityViewController使用的例子中,初始化的参数内容是“[NSURL URLWithString:@"http://www.molotang.com"]”,而且最终选择的是微博,则会弹出一个确认框,然后点击“发布”就分享到系统记录的微博账号上去了。

iOS中分享到微博

而这个过程中最终使用到的实际上就是Social Framework中的内容。

如果想直接使用新浪微博,可以通过Social framework中的SLComposeViewController。最简单的例子代码如下:

SLComposeViewController *socialController = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeSinaWeibo];
[socialController addURL:  [NSURL URLWithString:@"http://www.molotang.com"]];
[self presentViewController:socialController animated:YES completion:nil];

如上代码中的self是一个UIViewController对象。

直接这样使用的一个好处是,如果系统当前没有记录登陆过的新浪微博账号,则会提示用户登陆,而这样的情况在使用UIActivityViewController的时候发生时,则直接没有微博的选项了。

3. 其它

  • UIActivityViewController展现的页面其实是很常见的,而且有时我们在某个应用里想要通过另外一个App的方式打开某个文档的时候,也见过这样的界面:

UIDocumentInteractionController

而这个实际上是UIDocumentInteractionController这个类支持的功能,这个早在iOS3的时候就支持了,也是App间共享或者说传递文档的一种方式。

  • 在iOS8中,一个新增的特性是App Extension,一个App发布的时候可以带有一个扩展插件,供系统其它App使用。而其中一个类型就是Share,通过这个功能,我们可以定制自己的分享服务供系统使用。详细可参考苹果文档:App Extension – Share

以上,是iOS系统自身集成的一些社交相关API。如果需要针对某个社交媒体,比如“来往”,那就要到其开放平台注册和接入专门的SDK了,如果使用很多社交网络分享,一个比较方便的方案是使用ShareSDK,除了集成各家SDK外,还做了其它一些功能,比如统计等。

发表在 iOS, iOS开发基础 | 标签为 , , , | 留下评论

iOS并发开发简要整理(下)

上篇文章中简要介绍了iOS开发当中几种主要的并发实现方式,也重点整理了GCD和OperationQueue相关的内容。这篇就更进一步对比下GCD和Operation两种方式,同时整理下其它几个和并发相关的技术点,如NSThread、Runloop。

0. GCD & NSOperation

在“iOS并发开发简要整理(上)”里,已经详细地介绍了GCD和NSOperation(Queue)这两种实现并发的方式,这两者都摆脱了传统并发开发中“线程”概念的束缚,使用任务和队列的方式,在很多场景下规避了锁的使用,同时对线程的管理自动化了。

GCD和Operation都是在iOS平台上开发的推荐技术,从苹果的《Concurrency Programming Guide》中也可以看得出,这是苹果主推的并发开发方案。那么这两者又有什么关系,如何区别使用呢。

首先,NSOperation是上层技术,GCD则略偏底层,通过各种技术文档渠道我们可以了解到,NSOperation不过是面向对象化的任务-队列形式,是建立在GCD之上的。所以说,归根结底,这两者是一回事。

但是,既然GCD和Operation都是并发开发的主流方案,他们俩还是有所区别,各有长短的。

从GCD来看:

  • GCD是使用类C的方式实现的,相对偏底层,执行效率略高
  • GCD是用C语法形式的函数来做的,同时和Block紧密集合,对于熟悉、善于使用Block的开发者来说是非常不错的选择
  • 代码短小精悍、简单方便,轻量级,无需声明较复杂的类和对象

关于GCD的其实可以就说这么多,因为Operation技术方案是建立在GCD之上的,我们更多的还是要考量下NSOperation和NSOperationQueue:

  • 和GCD比,做了更上层的封装,虽然略牺牲了效率,但是他们是真正的Objective-C类,更适合在Objective-C的语境中出现
  • 支持面向对象,简单这么几个字可能说得太抽象,不过确实就是面向对象的一些优势,任务以对象的形式封装,可“重用”可复用,各Operation之间可以形成继承关系,同时具备封装、多态特性,有利于架构设计
  • 实现了任务间依赖关系管理
  • 支持取消
  • 具备KVO特性,随着任务状态变化,对应观察者可以收到通知
  • 很多人,包括苹果介绍Operation的Guide中,也还提到了优先级,但个人觉得这不是最吸引人的地方; Operation有2个优先级概念,一个是在队列中的优先级,一个是设置其潜在线程的优先级,前者不但与队列相关,也受依赖关系制约

基本上,对于Operation的优势所在,能想到的就是如上这些。这几个原因都是Operation相对GCD而言的,由于Operation也是基于GCD,所以用GCD肯定也都能做到,不过用Operation自然会更方便些,更合适些。

大家选用并发方案时,可按如上情况进行考量。

1. NSThread

说完了GCD和Operation这两个主流的方案,让我们回归源头,看一看NSThread。

我个人的感觉,这个和Java的java.lang.Thread很相似。让我们回想下Java的Thread怎么用? 对,用Runable对象传参构造或者继承Thread,然后start。

Objective-C的NSThread也是一样,通过target对象、实现方法的selector以及方法参数来初始化,原方法为:

- (id)initWithTarget:(id)target selector:(SEL)selector object:(id)argument;

然后调用Thread对象的start。

还有比较简单的,通过类方法直接开启新线程去执行:

+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument;

如是而已。

2. NSObject与线程

接触Objective-C时间比较长的朋友们都应该知道,我们Cocoa下的根类(之一),NSObject也有一些支持多线程的内容。

看NSObject.h的生命和NSThread.h里对NSObject的category扩展,我们会发现NSObject有一组performSelector方法。对于没有线程参数的performSelector来说,就只和当前线程有关系了。

这些performSelector和NSThread的初始化方法类似,通过selector来设定任务逻辑,有一点比较别扭的就是,参数个数有限,这个在实际开发当中用起来是非常不方便的,难怪这两种方案不是苹果主推的(当然还有其他原因)。

这里需要注意的一点是,在某个线程上想要performSelector的代码被执行,需要run loop开启。既然提到run loop,我们就来稍微详细地理一下,这也是本文的一个小重点。

3. RunLoop

Run loop是什么,直译为“运行循环”。没错,其实就是这个意思,我们在iPhone上的app,不是打开以下,过一段时间执行完代码就自动关闭,而是持久地运行着。这说明我们的代码不是一条直线,不是main运行一下就结束了,而有一个“循环”让它坚持地跑着,那就是Runloop。

这么说可能还是不知道Runloop是干嘛的,而且苹果的文档说得也不多。我这里多解释一句就是Runloop帮我们调度和处理程序运行中发生的各种“事件”。

先来看下NSRunLoop的几个运行的方法,然后再进一步说明。

- (void)run;
- (void)runUntilDate:(NSDate *)limitDate;
- (BOOL)runMode:(NSString *)mode beforeDate:(NSDate *)limitDate;

第一个方法的特点是,要么直接返回,要么很可能持久的运行下去(线程卡住了)。正如文档所说,它实际上是重复循环地调用第三个方法。

类似,第二个方法是如果没有任务就返回,要么也是死等执行直到limitDate,实际上也是重复调用第三个方法。

第三个方法……我们最后说。

刚刚提到了,run loop对象在run起来之后就去处理事件,执行任务。我们可以想象生活中一个死循环(比如while(YES){…}),里面的代码逻辑就是检查日历时间表,看看今天有没有安排,安排事情的事件有没有到,如果有或者时间到了,我们就马上去做某事。就这样,day after day,日历/时间表上上的安排也在随时在变化更新,有新的安排就记录到日程表上。直到某一天,自由日来临,不用继续做日程表上安排的任务了,或者明确知道日程表上不会再有事情了,到此为止。

这些任务安排,实际上就是iOS系统中的事件,苹果的文档解释为Timer & Input Source。而日复一日的检查日历表,就是Runloop的工作。

回到技术角度,我在上篇文章开头处就指出,多线程并发和IO的关系是很密切的,而run loop正是对包括IO事件做相应任务的一个异步化的处理机制。上一篇文章中也提到,对于一个线程来说,为了处理一个IO事件而阻塞,即使这个线程不是主线程,不会使得用户的交互行为得不到响应,但也是占用或者说浪费了系统资源的,不如释放这个线程,等待后续IO响应时重新开辟新线程处理任务。

相信很多人都知道select(我在Java的NIO相关文章里也提到过),而Run loop非常类似于循环地调用select,进而对感兴趣的事件进行处理。有所不同的是,我们在查看“日程表”的时候,如果发现当前没有安排的时候,不是一直死盯着日程表直到下一个安排时间点到来,我们可以趁这段事件睡个懒觉。这正是iOS在系统上对AIO这一理念的体现,也是run loop的优点所在,很好地节省系统资源。

此外,上述的第三个方法,是参数最全的一个方法,从参数中我们可以看到,run loop是分不同运行模式(mode)的,在一个模式下运行的run loop只会关注这个模式下的事件。

ASIHttpRequest这个框架中在网络请求的调度上,就很好地使用了run loop,感兴趣的朋友可以去参看下那段源代码。另外,如下这个网页也对run loop做了很好的讲解:

https://mikeash.com/pyblog/friday-qa-2010-01-01-nsrunloop-internals.html    

http://blog.shinetech.com/2009/06/02/run-loops-vs-threads-in-cocoa/

4. pthread

pthread即为POSIX thread,为支持POSIX标准的操作系统所支持。从实现来看,pthread是一套C语言的API和函数库,Objective-C是建立在C语言的基础之上的,支持C层面的开发,支持pthread。

对pthread感兴趣的,可以参看这里:

https://computing.llnl.gov/tutorials/pthreads/

5. 其它

关于iOS相关的并发技术,本文就先整理这些,下面是临时想到的几个需要注意的点,随手补在这里:

  • UI相关的一定要放在主线程处理,否则无效
  • dispatch_get_current_queue已经被deprecated,不建议再使用
发表在 iOS, 开发, 计算机技术 | 标签为 , , , , , , | 6 条评论
第 4 页,共 23 页« 最新...23456...91215...最旧 »