iOS 开发小记 (九)

1,GCD的Concurrent、Serial queues

Concurrent queues (also known as a type of global dispatch queue) execute one or more tasks concurrently, but tasks are still started in the order in which they were added to the queue. The currently executing tasks run on distinct threads that are managed by the dispatch queue. The exact number of tasks executing at any given point is variable and depends on system conditions.

并发队列用来并发执行一个或者多个任务,但是任务还是按照添加进队列的顺序开始。由并发队列来管理在不同线程执行的任务。并发执行的任务数是动态的,取决于系统的条件。 

Serial queues (also known as private dispatch queues) execute one task at a time in the order in which they are added to the queue. The currently executing task runs on a distinct thread (which can vary from task to task) that is managed by the dispatch queue.
串行队列按照添加顺序执行任务。
 
The kernel creates additional threads when workunits on existing GCD worker threads for a global concurrent queue are blocked in the kernel for a significant amount of time (as long as there is further work pending on the global queue).
当并发队列里面的任务阻塞时间过长的时候,系统会新建新的线程来执行后面的任务,因为后面还有很多任务在等待。
 
dispatch_barrier_async 屏障,等待前面的完成。
dispatch_semaphore_wait
简单来说就是GCD的异步队列是为了尽快执行,那么如果有block阻塞了,那么会新建一个新的线程。
如果不希望有太多的线程,那么可以用同步队列,

concurrent queues和main queue 都是由系统生成而且 dispatch_suspend, dispatch_resume, dispatch_set_context,这些函数对他们无效 

 

2,线程间通讯

Mach Port 
在苹果的Thread Programming Guide的Run Pool一节的Configuring a Port-Based Input Source 这一段中就有使用Mach Port进行线程间通信的例子。 其实质就是父线程创建一个NSMachPort对象,在创建子线程的时候以参数的方式将其传递给子线程,这样子线程中就可以向这个传过来的 NSMachPort对象发送消息,如果想让父线程也可以向子线程发消息的话,那么子线程可以先向父线程发个特殊的消息,传过来的是自己创建的另一个 NSMachPort对象,这样父线程便持有了子线程创建的port对象了,可以向这个子线程的port对象发送消息了。

当然各自的port对象需要设置delegate以及schdule到自己所在线程的RunLoop中,这样来了消息之后,处理port消息的delegate方法会被调用,你就可以自己处理消息了。 
 
 
3,RUN LOOP

如果你使用过select系统调用写过程序你便可以快速的理解runloop事件源的概念,本质上讲事件源的机制和select一样是一种多路复用IO的 实现,在一个线程中我们需要做的事情并不单一,如需要处理定时钟事件,需要处理用户的触控事件,需要接受网络远端发过来的数据,将这些需要做的事情统统注 册到事件源中,每一次循环的开始便去检查这些事件源是否有需要处理的数据,有的话则去处理。 拿具体的应用举个例子,NSURLConnection网络数据请求,默认是异步的方式,其实现原理就是创建之后将其作为事件源加入到当前的 RunLoop,而等待网络响应以及网络数据接受的过程则在一个新创建的独立的线程中完成,当这个线程处理到某个阶段的时候比如得到对方的响应或者接受完 了网络数据之后便通知之前的线程去执行其相关的delegate方法。所以在Cocoa中经常看到scheduleInRunLoop:forMode: 这样的方法,这个便是将其加入到事件源中,当检测到某个事件发生的时候,相关的delegate方法便被调用。对于CoreFoundation这一层而 言,通常的模式是创建输入源,然后将输入源通过CFRunLoopAddSource函数加入到RunLoop中,相关事件发生后,相关的回调函数会被调 用。如CFSocket的使用。 另外RunLoop中还有一个运行模式的概念,每一个运行循环必然运行在某个模式下,而模式的存在是为了过滤事件源和观察者的,只有那些和当前 RunLoop运行模式一致的事件源和观察者才会被激活。

 
每一个线程都有其对应的RunLoop,但是默认非主线程的RunLoop是没有运行的,需要为RunLoop添加至少一个事件源,然后去run它。一般情况下我们是没有必要去启用线程的RunLoop的,除非你在一个单独的线程中需要长久的检测某个事件。 
 
4,view hierarchy
viewDidLoad 此方法在view被  addsubWiew后调用viewDidLoad用于初始化,加载时用到的。 (不论是从xib中加载视图,还是从loadview生成视图,都会被调用。)
loadView 此方法在控制器的view为nil的时候被调用。 此方法用于以编程的方式创建view的时候用到。loadView是使用代码生成视图的时候,当视图第一次载入的时候调用的方法。用于使用(写)代码来实现控件。用于使用代码生成控件的函数。
果设备内存不足的时候, view 控制器会收到didReceiveMemoryWarning的消息。 默认的实现是检查当前控制器的view是否在使用。如果它的view不在当前正在使用的view hierarchy里面,且你的控制器实现了loadView方法,那么这个view将被release, loadView方法将被再次调用来创建一个新的view。 
 
view hierarchy同时也是responder chain的重要部分,当我们需要渲染window中的内容的时候,应用程序的框架会用它来检测views的layer的层次,来决定需要渲染的部分,从而来避免做一些无用功,这点至关重要。
 

在开发的时候,有时候会遇到

1.ios attempt to present whose view is not in the window hierarchy

2.Warning: Attempt to present on whose view is not in the window hierarchy!

等等这样类似的提示,只要里面提示有 window hierarchy,都是view hierarchy的理解不到位导致的。

上面的问题都是在一个controller的view还没加到window上的时候又取present另外一个controller,这就相当于在盖楼,2楼还没盖完,直接去盖3楼了,这样肯定是不行。

 
遇到上面的问题 最直接的解决方法就是在controller的viewDidAppear里面去调用present。这样可以确保view hierarchy的层次结构不乱。 
 
 
 

posted on 2016-02-19 12:06  loying  阅读(163)  评论(0编辑  收藏  举报

导航