如果您对【荐】GrandCentralDispatchTutorialforSwift:Part1/2感兴趣,那么本文将是一篇不错的选择,我们将为您详在本文中,您将会了解到关于【荐】GrandCent
如果您对【荐】Grand Central Dispatch Tutorial for Swift: Part 1/2感兴趣,那么本文将是一篇不错的选择,我们将为您详在本文中,您将会了解到关于【荐】Grand Central Dispatch Tutorial for Swift: Part 1/2的详细内容,我们还将为您解答for swift parrot and many rare native flowers的相关问题,并且为您提供关于A Guide to Blocks & Grand Central Dispatch (and the Cocoa API's making use of them)、Beginning Auto Layout Tutorial in iOS 7: Part 2、c# – Xamarin iOS中的Grand Central Dispatch vs ThreadPool性能、dispatch_semaphore_create的grand-central-dispatch – 左值参数?的有价值信息。
本文目录一览:- 【荐】Grand Central Dispatch Tutorial for Swift: Part 1/2(for swift parrot and many rare native flowers)
- A Guide to Blocks & Grand Central Dispatch (and the Cocoa API's making use of them)
- Beginning Auto Layout Tutorial in iOS 7: Part 2
- c# – Xamarin iOS中的Grand Central Dispatch vs ThreadPool性能
- dispatch_semaphore_create的grand-central-dispatch – 左值参数?
【荐】Grand Central Dispatch Tutorial for Swift: Part 1/2(for swift parrot and many rare native flowers)
这篇文章非常详细的介绍了iOS中的GCD相关知识,并且结合具体实例进行了分析。
原文地址(建议评论也看一下)
感兴趣的朋友可以看看原文,在这里我总结和翻译了我个人认为比较精华的部分。
预备知识
顺序与并发(serial vs concurrent)
顺序执行指在同一时间只有一个任务被执行;并发则指任务可能会同时被执行。
任务(tasks)
在本文中,一个任务可以被认为就是一个闭包。实际上,你同样可以用函数指针进行GCD操作,但是这种用法会非常复杂。闭包则会非常简单。
不知道什么是闭包(closure)的自己去百度。
同步与异步
这两个术语描述了一个方法何时会把控制权返回给调用者,还有在那之前会干些什么事情。
同步方法只有在任务完成后才会把控制权返回给调用者。
异步方法则会立即返回,不会等待它完成。也就是说异步方法不会阻塞当前线程执行下一个方法。
还有一些概念比较常见如,race condition,死锁,线程安全,上下文切换等,可自行百度。
并发与平行(Concurrency vs Parallelism)
这两个概念通常会被一同提到。
不同的并发代码可以“同时”被执行。不过,这取决与系统如何实现--或者压根就不实现。
多核的设备可以真正意义上的同时执行多个线程,不过对于单核设备要实现并发,就必须要首先实现上下文切换。上下文切换通常是非常迅速的,因此给用户了一个并发的假象。
队列
GCD提供了dispatch queues来处理提交的任务,这些队列都是FIFO的。所有的dispatch queues自身都是线程安全的。当你知道了如何选择正确的dispatch queue和dispatch function,你就可以很好的理解GCD的带来的好处。
顺序队列
在顺序队列中的任务同一时间只执行一个,后面的任务只有在前一任务完成后才能被启动。同样的,你不能确定下一个任务的启动时间和上一个任务的结束时间之间的间隔。如下图所示:
由此可知,顺序队列中的任务都是线程安全的,因为他们不可能同时访问临界区。
并发队列
并发队列中的任务只能保证他们的启动时间和他们入队的时间一致。如下图:
队列和线程
队列和线程是不同的东西。队列只是用来管理任务的执行顺序,是否并发和优先级。任务都是运行在线程上的,GCD是用线程池实现的,你不用自己来维护线程池。不过,你需要意识到一些系统队列比如main queue和global queue。他们都有一些特殊的属性并且被iOS系统使用。比如main queue是一个和主线程绑定的顺序队列。
队列类型
1.主队列(main queue)
主队列是一个顺序队列,他可以保证所有的任务都在主线程中执行。主线程是唯一一个可以更新UI的线程。
2.全局队列
全局队列是一个并发队列和下面的各种QoS一起使用,注意全局队列中可能已经有其他的任务,所以除了你添加的任务,有可能还有其他任务在队列中。
3.并发队列
GCD还提供了一系列的同步队列。这些队列都和各自的QoS类进行绑定。这些不同的QoS类表明了该队列的用途,以此来帮助GCD进行优化:
- QOS_CLASS_USER_INteraCTIVE:该队列中的任务需要立即执行以保证良好的用户体验。在进行UI更新,事件处理和低负荷小延迟的任务时使用这个队列。
- QOS_CLASS_USER_INITIATED:这个类代表那些由UI发起的任务,同时这些任务又可以被异步处理。当用户需要任务立即返回以进行后续互动的时候,用这个队列。
- QOS_CLASS_USER_UTILITY:这个类代表长时间运行的任务,通常会伴有用户可见的进度条。在复杂运算,IO,网络,连续数据传输时使用这个队列。
- QOS_CLASS_USER_BACKGROUND:后台运行非时间敏感的任务所使用
dispatch_async
把任务添加到对应队列并立即返回。适用于基于网络或者高负荷cpu任务。
- 自定义顺序队列(custom serial queue):适用于后台顺序执行工作。排除了资源竞争问题,如需要获得结果需要内嵌闭包来获取或者使用dispatch_sync。
- 主队列(main queue):主要用于更新UI,需要内嵌于其他闭包中。如果你在main queue中调用dispatch_async把任务添加到main queue中,那么你可以确保新的任务会在当前方法结束后被执行。
- 并发队列(concurrent queue):通常用于后台处理。
dispatch_sync
把任务添加到对应队列并等待其完成后再继续执行当前任务,容易造成死锁,或阻塞当前任务。
- 自定义顺序队列(custom serial queue):谨慎使用。如果你在一个队列中使用dispatch_sync把任务提交到同一个队列,那么会造成死锁
- 主队列(main queue):同理,谨慎使用。
- 并发队列(concurrent queue):最好的候选者。
dispatch_barrier_async
一般需要首先使用dispatch_queue_create来创建自定义队列,然后使用。在并发队列中以顺序执行的方式完成任务。
- 自定义顺序队列(custom serial queue):不好的选择,因为不会带来任何好处。
- 全局并发队列(global concurrent queue):谨慎使用,因为barrier会阻塞队列,该队列可能还有其他任务。
- 自定义并发队列(custom concurrent queue):最好的候选者。
dispatch_after
指定时间后把任务添加到队列中。效果就像是延时后的dispatch_async。
单例模式中的线程安全
在swift中,所有全局变量的初始化都是线程安全的。在swift中,全局变量在首次被访问的时候进行初始化,并且初始化是一个原子过程。因为swift在初始化全局变量的时候使用了dispatch_once。这个方法确保了任务以线程安全的方式被执行一次。
初始化解决了,但是读写同步的问题还没有解决。
看如下代码:
func addPhoto(photo: Photo) { _photos.append(photo) dispatch_async(dispatch_get_main_queue()) { self.postContentAddednotification() } }这是一个写操作
private var _photos: [Photo] = [] var photos: [Photo] { return _photos }这是一个读操作
当一个线程在调用写操作的同时,如果另一个线程调用读操作,就会出现问题。
注:在swift中class的传递是引用传递(pass-by-reference),enum和struct是值传递(pass-by-value)。而array和dictionary在swift中是以struct的形式实现的,所以以上的读操作返回的是一个副本。
其实,这就是经典的读者-作者问题。在GCD中使用dispatch barrier来解决这个问题。
dispatch barrier是一组方法,它们都已顺序化的方式来结合同步队列进行工作。使用barrier的API确保了在特定时间同一队列中仅有被barrier提交的任务被执行。如下图所示:
使用dispatch barrier,首先需要手动建立一个队列:
private let concurrentPhotoQueue = dispatch_queue_create( "com.raywenderlich.GooglyPuff.photoQueue",disPATCH_QUEUE_CONCURRENT)第一个参数是队列的标示,第二个参数表明该队列是并发还是顺序执行。
然后修改写操作如下:
func addPhoto(photo: Photo) { dispatch_barrier_async(concurrentPhotoQueue) { // 1 self._photos.append(photo) // 2 dispatch_async(GlobalMainQueue) { // 3 self.postContentAddednotification() } } }写操作搞定了,接下来要搞定读操作.
为了保证线程安全,你需要在concurrentPhotoQueue上进行读操作。并且必须要等待任务返回后,读操作才能返回,所以不能使用dispatch_async。
在这种情况下应该使用dispatch_sync。dispatch_sync把任务提交到队列中人后等待它完成后才返回。一般情况下,dispatch_sync都是与dispatch_barrier一起使用的。
使用这个函数要相当小心。设想一下如果你用dispatch_sync在当前队列中添加任务的话,那么就会造成死锁。这就迫使你明确你调用的队列是什么和你提交的队列是什么。
修改后的读操作如下:
var photos: [Photo] { var photoscopy: [Photo]! dispatch_sync(concurrentPhotoQueue) { // 1 photoscopy = self._photos // 2 } return photoscopy }
A Guide to Blocks & Grand Central Dispatch (and the Cocoa API's making use of them)
Intro As you may or may not kNow I recently did a talk at the Des Moines Cocoaheads in which I reviewed Blocks and Grand Central dispatch. I have tried to capture the content of that talk and a lot more here in this article. The talk encompassed
- Blocks
- Grand Central dispatch
- GCD Design Patterns
- Cocoa API''s using GCD and Blocks
All of the content of this article applies only to Mac OS X 10.6 SNow Leopard as blocks support and Grand Central dispatch are only available there. There are alternate methods to get blocks onto Mac OS X 10.5 and the iPhone OS via projects likePlausible Blocks which have blocks support,though do not have Grand Central dispatch (libdispatch.)
Grand Central dispatch is Open Source I should mention that Apple has in fact Open Sourced libdispatch (Grand Central Dispatch) on Mac OS Forge and the other components like Kernel Support for GCD (although if implemented on other OS''s this is not necessary) and the blocks runtime support are all freely available and if you want you can even checkout the libdispatch repository using Git with the command git clone git://git.macosforge.org/libdispatch.git
Blocks
Blocks are part of a new C Language Extension,and are available in C,Objective-C,C++ and Objective-C++. Right off the bat,I should say that while we will use blocks with Grand Central dispatch a lot later on,they are not required when using Grand Central dispatch. Blocks are very useful onto themselves even if you never use them with anything else. However we gain a lot of benefit when we use blocks with Grand Central dispatch,so pretty much all my examples here will use blocks.
What are blocks? Well let me show you a very basic example of one
^{ NSLog(@"Inside a block"); }
This is a very basic example of a block,but it is basically a block that accepts no arguments and contains a NSLog()
statement. Think of blocks as either a method or snippet of code that accepts arguments and captures lexical scope. Other languages have already had something like this concept implemented for a while (since the 70''s at least if I remember correctly.) Here''s a couple examples of this concept in one of my favorite languages Python
>>>f = lambda x,y,z: x + y + z ... >>> f(2,3,4) 9
Here we are defining a lambda in Python which is basically a function that we can execute later on. In Python after the lambda keyword you define the arguments that you are passing in to the left of the colon and the right is the actual expression that will get executed. So in the first line of code we''ve defined a lambda that accepts 3 arguments and when it''s invoked all it will do is accept the arguments and add them together,hence when we invoke f like f(2,4)
we get 9 back. We can do more with Python lambda''s. Python has functions that actually do more with lambdas like in this example...
>>>reduce((lambda x,y: x+y),[1,2,4]) 10 >>>reduce((lambda x,y: x*y),4]) 24
This reduce function uses a lambda that accepts 2 arguments to iterate over an array. The lambda in this case accepts 2 arguments (as the reduce function requires) and in the first example just iterates over the array with it. Python begins by calling the lambda using the first 2 elements of the array then gets a resulting value and again calls the lambda with that resulting value and the next element in the array and keeps on calling the lambda until it has fully iterated over the data set. So in other words the function is executed like so (((1 + 2) + 3) + 4)
Blocks bring this concept to C and do a lot more. You might ask yourself "But haven''t we already had this in C? I mean there are C Function Pointers." Well yeah,but while blocks are similar in concept,they do a lot more than C Function Pointers,and even better if you already kNow how to use C function pointers,Blocks should be fairly easy to pick up.
Here is a C Function Pointer Declaration...
void (*func)(void);
...and here is a Block Declaration...
void (^block)(void);
Both define a function that returns nothing (void) and takes no arguments. The only difference is that we''ve changed the name and swapped out a "*" for a "^". So lets create a basic block
int (^MyBlock)(int) = ^(int num) { return num * 3; };
The block is laid out like so. The first part signifies that it''s a block returning an int. The (^MyBlock)(int)
is defining a block of the MyBlock type that accepts an int as an argument. Then the ^(int num)
to the right of the assignment operator is the beginning of the block,it means this is a block that accepts an int as an argument (matching the declaration earlier.) Then the { return num * 3; };
is the actual body of the block that will be executed.
When we''ve defined the block as shown earlier we can then assign it to variables and pass it in as arguments like so...
int aNum = MyBlock(3); printf(“Num %i”,aNum); //9
Blocks Capturing Scope: When I said earlier that blocks capture lexical scope this is what I mean,blocks are not only useful to use as a replacement for c function pointers,but they also capture the state of any references you use within the block itself. Let me show you...
int spec = 4; int (^MyBlock)(int) = ^(int aNum){ return aNum * spec; }; spec = 0; printf("Block value is %d",MyBlock(4));
Here we''ve done a few things. First I declared an integer and assigned it a value of 4. Then we created the block and assigned it to an actual block implementation and finally called the block in a printf statement. And finally it prints out "Block value is 16"? Wait we changed the spec number to 0 just before we called it didn''t we? Well yes actually we did. But what blocks do actually is create a const copy of anything you reference in the block itself that is not passed in as an argument. So in other words we can change the variable spec to anything we want after assigning the block,but unless we are passing in the variable as an argument the block will always return 16 assuming we are calling it as MyBlock(4). I should also note that we can also use C''s typedef utility to make referencing this type of block easier. So in other words...
int spec = 4; typedef int (^MyBlock)(int); MyBlock InBlock = ^(int aNum){ return aNum * spec; }; spec = 0; printf("InBlock value is %d",InBlock(4));
is exactly equivalent to the prevIoUs code example. The difference being is that the latter is more readable.
__block Blocks do have a new storage attribute that you can affix onto variables. Lets say that in the prevIoUs example we want the block to read in our spec variable by reference so that when we do change the variable spec that our call toInBlock(4)
actually returns what we expect it to return which is 0. To do so all we need to change is adding __block to spec like so...
__block int spec = 4; typedef int (^MyBlock)(int); MyBlock InBlock = ^(int aNum){ return aNum * spec; }; spec = 0; printf("InBlock value is %d",245)"> and Now the printf statement finally spits out "InBlock value is 0",because Now it''s reading in the variable spec by reference instead of using the const copy it would otherwise use.Blocks as Objective-C objects and more! Naturally going through this you''d almost be thinking right Now that blocks are great,but they Could potentially have some problems with Objective-C,not so Blocks are Objective-C objects! They do have a isa pointer and do respond to basic commands like -copy and -release which means we can use them in Objective-C dot Syntax like so...
@property(copy) void(^myCallback)(id obj); @property(readwrite,copy) MyBlock inBlock;and in your Objective-C code you can call your blocks just like so
self.inBlock();
.Finally I should note that while debugging your code there is a new GDB command specifically for calling blocks like so
$gdb invoke-block MyBlock 12 //like MyBlock(12) $gdb invoke-block StringBlock “\” String \””These give you the ability to call your blocks and pass in arguments to them during your debug sessions.
Grand Central dispatch
Now onto Grand Central dispatch (which I may just reference as GCD from here on out.) Unlike past additions to Mac OS X like say NSOperation/NSThread Subclasses,Grand Central dispatch is not just a new abstraction around what we''ve already been using,it''s an entire new underlying mechanism that makes multithreading easier and makes it easy to be as concurrent as your code can be without worrying about the variables like how much work your cpu cores are doing,how many cpu cores you have and how much threads you should spawn in response. You just use the Grand Central dispatch API''s and it handles the work of doing the appropriate amount of work. This is also not just in Cocoa,anything running on Mac OS X 10.6 SNow Leopard can take advantage of Grand Central dispatch ( libdispatch ) because it''s included in libSystem.dylib and all you need to do is include
#import <dispatch/dispatch.h>
in your app and you''ll be able to take advantage of Grand Central dispatch.Grand Central dispatch also has some other nice benefits. I''ve mentioned this before in other talks,but in OS design there are 2 main memory spaces (kernel space and user land.) When code you call executes a syscall and digs down into the kernel you pay a time penalty for doing so. Grand Central dispatch will try and do it''s best with some of it''s API''s and by pass the kernel to return to your application without digging into the kernel which means this is very fast. However if GCD needs to it can go down into the kernel and execute the equivalent system call and return back to your application.
Lastly GCD does some things that threading solutions in Leopard and earlier did not do. For example NSOperationQueue in Leopard took in NSOperation objects and created a thread,ran the NSOperation
-(void)main
on the thread and then killed the thread and repeated the process for each NSOperation object it ran,pretty much all we did on Leopard and earlier was creating threads,running them and then killing the threads. Grand Central dispatch however has a pool of threads. When you call into GCD it will give you a thread that you run your code on and then when it''s done it will give the thread back to GCD. Additionally queues in GCD will (when they have multiple blocks to run) just keep the same thread(s) running and run multiple blocks on the thread,which gives you a nice speed boost,and only then when it has no more work to do hand the thread back to GCD. So with GCD on SNow Leopard we are getting a nice speed boost just by using it because we are reusing resources over and over again and then we we aren''t using them we just give them back to the system.This makes GCD very nice to work with,it''s very fast,efficient and light on your system. Even though GCD is fast and light however you should make sure that when you give blocks to GCD that there is enough work to do such that it''s worth it to use a thread and concurrency. You can also create as many queues as you want to match however many tasks you are doing,the only constraint is the memory available on the users system.
GCD API So if we have a basic block again like this
^{ NSLog(@"Doing something"); }then to get this running on another thread all we need to do is use
dispatch_async()
like so...dispatch_async(queue,^{ NSLog(@"Doing something"); });so where did that
queue
reference come from? Well we just need to create or get a reference to a Grand Central dispatch Queue ( dispatch_queue_t ) like thisdispatch_queue_t queue = dispatch_get_global_queue(0,0);which just in case you''ve seen this code is equivalent to
dispatch_queue_t queue = dispatch_get_global_queue(disPATCH_QUEUE_PRIORITY_DEFAULT,245)"> In Grand Central dispatch the two most basic things you''ll deal with are queues (dispatch_queue_t
) and the API''s to submit blocks to a queue such asdispatch_async()
ordispatch_sync()
and I''ll explain the difference between the two later on. For Now let''s look at the GCD Queues.The Main Queue The Main Queue in GCD is analogous to the main app thread (aka the AppKit thread.) The Main Queue cooperates with
NSApplicationMain()
to schedule blocks you submit to it to run on the main thread. This will be very handy to use later on,for Now this is how you get a handle to the main queuedispatch_queue_t main = dispatch_get_main_queue();or you Could just call get main queue inside of a dispatch call like so
dispatch_async(dispatch_get_main_queue(),^ {....The Global Queues The next type of queue in GCD are the global queues. You have 3 of them of which you can submit blocks to. The only difference to them are the priority in which blocks are dequeued. GCD defines the following priorities which help you get a reference to each of the queues...
enum { disPATCH_QUEUE_PRIORITY_HIGH = 2,disPATCH_QUEUE_PRIORITY_DEFAULT = 0,disPATCH_QUEUE_PRIORITY_LOW = -2,};When you call
dispatch_get_global_queue()
withdisPATCH_QUEUE_PRIORITY_HIGH
as the first argument you''ve got a reference to the high global queue and so on for the default and low. As I said earlier the only difference is the order in which GCD will empty the queues. By default it will go and dequeue the high priority queue''s blocks,then dequeue the default queues blocks and then the low. This priority doesn''t really have anything to do with cpu time.Private Queues Finally there are the private queues,these are your own queues that dequeue blocks serially. You can create them like so
dispatch_queue_t queue = dispatch_queue_create("com.MyApp.AppTask",NULL);The first argument to
dispatch_queue_create()
is essentially a C string which represents the label for the queue. This label is important for several reasons
- You can see it when running Debug tools on your app such as Instruments
- If your app crashes in a private queue the label will show up on the crash report
- As there are going to be lots of queues on 10.6 it''s a good idea to differentiate them
By default when you create your private queues they actually all point to the default global queue. Yes you can point these queues to other queues to make a queue hierarchy using dispatch_set_target_queue()
. The only thing Apple discourages is making an loop graph where you make a queue that points to another and another eventually winding back to pointing at the first one because that behavior is undefined. So you can create a queue and set it to the high priority queue or even any other queue like so
dispatch_queue_t queue = dispatch_queue_create("com.App.AppTask,0); dispatch_queue_t high = dispatch_get_global_queue(disPATCH_QUEUE_PRIORITY_HIGH,NULL); dispatch_set_target_queue(queue,high);
If you wanted to you Could do the exact same with your own queues to create the queue hierarchies that I described earlier on.
Suspending Queues Additionally you may need to suspend queue''s which you can do with dispatch_suspend(queue)
. This runs exactly like NSOperationQueue in that it won''t suspend execution of the current block,but it will stop the queue dequeueing any more blocks. You should be aware of how you do this though,for example in the next example it''s not clear at all what''s actually run
In the above example it''s not clear at all what has run,because it''s entirely possible that any combination of blocks may have run.
Memory Management It may seem a bit odd,but even in fully Garbage Collected code you still have to call dispatch_retain()
and dispatch_release()
on your grand central dispatch objects,because as of right Now they don''t participate in garbage collection.
Recursive Decomposition Now calling dispatch_async()
is okay to run code in a background thread,but we need to update that work back in the main thread,how how would one go about this? Well we can use that main queue and justdispatch_async()
back to the main thread from within the first dispatch_async()
call and update the UI there. Apple has referred to this as recursive decomposition,and it works like this
dispatch_queue_t queue = dispatch_queue_create(“com.app.task”,NULL) dispatch_queue_t main = dispatch_get_main_queue(); dispatch_async(queue,^{ CGFLoat num = [self doSomeMassivecomputation]; dispatch_async(main,^{ [self updateUIWithNumber:num]; }); });
In this bit of code the computation is offloaded onto a background thread with dispatch_async()
and then all we need to do is dispatch_async()
back into the main queue which will schedule our block to run with the updated data that we computed in the background thread. This is generally the most preferable approach to using grand central dispatch is that it works best with this asynchronous design pattern. If you really need to use dispatch_sync()
and absolutely make sure a block has run before going on for some reason,you Could accomplish the same thing with this bit of code
dispatch_sync()
works just like dispatch_async()
in that it takes a queue as an argument and a block to submit to the queue,but dispatch_sync()
does not return until the block you''ve submitted to the queue has finished executing. So in other words the [self updateUIWithNumber:num];
code is guaranteed to not execute before the code in the block has finished running on another thread. dispatch_sync()
will work just fine,but remember that Grand Central dispatch works best with asynchronous design patterns like the first bit of code where we simply dispatch_async()
back to the main queue to update the user interface as appropriate.
dispatch_apply() dispatch_async()
and dispatch_sync()
are all okay for dispatching bits of code one at a time,but if you need to dispatch many blocks at once this is inefficient. You Could use a for loop to dispatch many blocks,but luckly GCD has a built in function for doing this and automatically waiting till the blocks all have executed. dispatch_apply()
is really aimed at going through an array of items and then continuing execution of code after all the blocks have executed,like so
This is GCD''s way of going through arrays,you''ll see later on that Apple has added Cocoa API''s for accomplishing this with NSArrays''s,NSSets,etc. dispatch_apply()
will take your block and iterate over the array as concurrently as it can. I''ve run it sometimes where it takes the indexes 0,4,6,8 on Core 1 and 1,5,7,9 on Core 2 and sometimes it''s done odd patterns where it does most of the items on 1 and some on core 2,the point being that you don''t kNow how concurrent it will be,but you do kNow GCD will iterate over your array or dispatch all the blocks within the max count you give it as concurrently as it can and then once it''s done you just go on and work with your updated data.
dispatch Groups dispatch Groups were created to group several blocks together and then dispatch another block upon all the blocks in the group completing their execution. Groups are setup very easily and the Syntax isn''t very dissimilar fromdispatch_async()
. The API dispatch_group_notify()
is what sets the final block to be executed upon all the other blocks finishing their execution.
Other GCD API you may be Interested in
//Make sure GCD dispatches a block only 1 time dispatch_once() //dispatch a Block after a period of time dispatch_after() //Print Debugging information dispatch_debug() //Create a new dispatch source to monitor low-level System objects //and automatically submit a handler block to a dispatch queue in response to events. dispatch_source_create()
Cocoa & Grand Central dispatch/Blocks
The GCD API''s for being low level API''s are very easy to write and quite frankly I love them and have no problem using them,but they are not appropriate for all situations. Apple has implemented many new API''s in Mac OS X 10.6 SNow Leopard that take advantage of Blocks and Grand Central dispatch such that you can work with existing classes easier & faster and when possible concurrently.
NSOperation and NSBlockOperation NSOperation has been entirely rewritten on top of GCD to take advantage of it and provide some new functionality. In Leopard when you used NSOperation(Queue) it created and killed a thread for every NSOperation object,in Mac OS X 10.6 Now it uses GCD and will reuse threads to give you a nice performance boost. Additionally Apple has added a new NSOperation subclass called NSBlockOperation to which you can add a block and add multiple blocks. Apple has additionally added a completion block method to NSOperation where you can specify a block to be executed upon a NSOperation object completing (goodbye KVO for many NSOperation Objects.)
NSBlockOperation can be a nice easy way to use everything that NSOperation offers and still use blocks with NSOperation.
NSOperationQueue *queue = [[NSOperationQueue alloc] init]; NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"Doing something..."); }]; //you can add more blocks [operation addExecutionBlock:^{ NSLog(@"Another block"); }]; [operation setCompletionBlock:^{ NSLog(@"Doing something once the operation has finished..."); }]; [queue addOperation:operation];
in this way it starts to make the NSBlockOperation look exactly like high level dispatch groups in that you can add multiple blocks and set a completion block to be executed.
Concurrent Enumeration Methods
One of the biggest implications of Blocks and Grand Central dispatch is adding support for them throughout the Cocoa API''s to make working with Cocoa/Objective-C easier and faster. Here are a couple of examples of enumerating over a NSDictionary using just a block and enumerating over a block concurrently.
//non concurrent dictionary enumeration with a block [dict enumerateKeysAndobjectsUsingBlock:^(id key,id obj,BOOL *stop) { NSLog(@"Enumerating Key %@ and Value %@",key,obj); }]; //concurrent dictionary enumeration with a block [dict enumerateKeysAndobjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id key,BOOL *stop) { NSLog(@"Enumerating Key %@ and Value %@",obj); }];
The Documentation is a little dry on what happens here saying just "Applies a given block object to the entries of the receiver." What it doesn''t make mention of is that because it has a block reference it can do this concurrently and GCD will take care of all the details of how it accomplishes this concurrency for you. You Could also use the BOOL *stop
pointer and search for objects inside NSDictionary and just set *stop = YES;
to stop any further enumeration inside the block once you''ve found the key you are looking for.
High Level Cocoa API''s vs Low Level GCD API''s
Chris Hanson earlier wrote about why you should use NSOperation vs GCD API''s. He does make some good points,however I will say that I haven''t actually used NSOperation yet on Mac OS X 10.6 (although I definitely will be using it later on in the development of my app) because the Grand Central dispatch API''s are very easy to use and read and I really enjoy using them. Although he wants you to use NSOperation,I would say use what you like and is appropriate to the situation. I would say one reason I really haven''t used NSOperation is because when GCD was introduced at WWDC,I heard over and over about the GCD API,and I saw how great it was and I can''t really remember NSOperation or NSBlockOperation being talked about much.
To Chris''s credit he does make good points about NSOperation handling dependencies better and you can use KVO if you need to use it with NSOperation Objects. Just about all the things you can do with the basic GCD API''s you can accomplish with NSOperation(Queue) with the same or a minimal couple lines of more code to get the same effect. There are also several Cocoa API that are specifically meant to be used with NSOperationQueue''s,so in those cases you really have no choice but to use NSOperationQueue anyway.
Overall I''d say think what you''ll need to do and why you would need GCD or NSOperation(Queue) and pick appropriately. If you need to you can always write NSBlockOperation objects and then at some point later on convert those blocks to using the GCD API with a minimal amount of effort.
Further Reading on Grand Central dispatch/Blocks
Because I only have so much time to write here,and have to split my time between work and multiple projects,I am linking to people I like who have written some great information about Grand Central dispatch and/or Blocks. Although this article will most definitely not be my last on Grand Central dispatch and/or Blocks.
http://www.friday.com/bbum/2009/08/29/basic-blocks/ http://www.friday.com/bbum/2009/08/29/blocks-tips-tricks/ http://www.mikeash.com/?page=pyblog/friday-qa-2009-08-28-intro-to-grand-central-dispatch-part-i-basics-and-dispatch-queues.html http://www.mikeash.com/?page=pyblog/friday-qa-2009-09-04-intro-to-grand-central-dispatch-part-ii-multi-core-performance.html http://www.mikeash.com/?page=pyblog/friday-qa-2009-09-11-intro-to-grand-central-dispatch-part-iii-dispatch-sources.html
A couple projects making nice use of Blocks
http://github.com/amazingsyco/sscore Andy Matauschak''s KVO with Blocks http://gist.github.com/153676
Interesting Cocoa API''s Making Use of Blocks
A list of some of the Cocoa API''s that make use of blocks (thanks to a certain someone for doing this,really appreciate it.) I should note that Apple has tried not to use the word block everywhere in it''s API for a very good reason. When you come to a new API and you saw something like -[NSArray block]
you would probably think it had something to do with blocking using the NSArray or something where you are blocking execution. Although many API do have block in their name,it is by no means the only keyword you should use when looking for API''s dealing with blocks,for these links to work you must have the Documentation installed on your HD.
NSEvent
addGlobalMonitorForEventsMatchingMask:handler:
addLocalMonitorForEventsMatchingMask:handler:
NSSavePanel
beginSheetModalForWindow:completionHandler:
NSWorkspace
duplicateURLs:completionHandler:
recycleURLs:completionHandler:
NSUserInterfaceItemSearching Protocol
searchForItemsWithSearchString:resultLimit: matchedItemHandler:
NSArray
enumerateObjectsAtIndexes:options:usingBlock:
enumerateObjectsUsingBlock:
enumerateObjectsWithOptions:usingBlock:
indexesOfObjectsAtIndexes:options:passingTest:
indexesOfObjectsPassingTest:
indexesOfObjectsWithOptions:passingTest:
indexOfObjectAtIndexes:options:passingTest:
indexOfObjectPassingTest:
indexOfObjectWithOptions:passingTest:
NSAttributedString
enumerateAttribute:inRange:options:usingBlock:
enumerateAttributesInRange:options:usingBlock:
NSBlockOperation
blockOperationWithBlock:
addExecutionBlock:
executionBlocks
NSDictionary
enumerateKeysAndObjectsUsingBlock:
enumerateKeysAndObjectsWithOptions:usingBlock:
keysOfEntriesPassingTest:
keysOfEntriesWithOptions:passingTest:
NSExpression
expressionForBlock:arguments:
expressionBlock
NSFileManager
enumeratorAtURL:includingPropertiesForKeys: options:errorHandler:
NSIndexSet
enumerateIndexesInRange:options:usingBlock:
enumerateIndexesUsingBlock:
enumerateIndexesWithOptions:usingBlock:
indexesInRange:options:passingTest:
indexesPassingTest:
indexesWithOptions:passingTest:
indexInRange:options:passingTest:
indexPassingTest:
indexWithOptions:passingTest:
NSNotificationCenter
addObserverForName:object:queue:usingBlock:
NSOperation
completionBlock
setCompletionBlock:
NSOperationQueue
addOperationWithBlock:
NSPredicate
predicateWithBlock:
NSSet
objectsPassingTest:
objectsWithOptions:passingTest:
NSString
enumerateLinesUsingBlock:
enumerateSubstringsInRange:options:usingBlock:
CATransaction
completionBlock
Beginning Auto Layout Tutorial in iOS 7: Part 2
本篇是学习iOS Auto Layout难得的好资料,值得一读!资料来自www.raywenderlich.com
--------------------------------------------------------------------------------
Note from Ray: Tutorial Team member Matthijs Hollemans (the iOS Apprentice Series author) has ported this tutorial to iOS 7 as part of the iOS 7 feast. We hope you enjoy!
In part 1 of this Auto Layout tutorial you saw that the old “struts-and-springs” model for making user interfaces cannot easily solve all layout problems. Auto Layout is the solution,but because this technology is so powerful it is also a bit more tricky to use.
Thankfully,Xcode 5 makes Auto Layout a lot easier. If you tried Auto Layout in Xcode 4 and gave up,then we invite you to give it another try with Xcode 5.
In this second part and final part of the Auto Layout tutorial series,you’ll continue learning all about constraints and how to apply them!
A little runtime excursion
This Auto Layout tutorial begins with a very simple app that looks like this:
It has two buttons that have their background color set just so it’s clearer to see their boundaries. The buttons have a number of constraints between them. If you’ve been following along with the previous part you can continue using your existing app. Simply remove the other two buttons from the canvas.
If you’re starting from scratch,create a new iPhone application using the Single View Application template. Drag two buttons into the scene and give them a background color. Use the Editor\Pin menu to make a Vertical Spacing constraint between the two buttons (40 points),and a Bottom Space to Superview constraint on the lower button (20 points). Use the Editor\Align menu to center the yellow button horizontally in the container,and again to align the left edges of both buttons.
Playing with this in Interface Builder is all well and good,but let’s see how this works at runtime. Add the following method to ViewController.m:
- (IBAction)buttonTapped:(UIButton *)sender { if ([[sender titleForState:UIControlStatenormal] isEqualToString:@"X"]) { [sender setTitle:@"A very long title for this button" forState:UIControlStatenormal]; } else { [sender setTitle:@"X" forState:UIControlStatenormal]; } }
This toggles between a long title and a short title for the button that triggered the event. Connect this action method to both of the buttons in Interface Builder. Ctrl-drag from each button to the view controller and select buttonTapped: in the popup.
Run the app and tap the buttons to see how it behaves. Perform the test in both portrait and landscape orientations.
Regardless of which button has the long title and which has the short title,the layout always satisfies the constraints you have given it:
- The lower button is always center-aligned in the window,horizontally.
- The lower button always sits 20 points from the bottom of the window.
- The top button is always left-aligned with the lower button and 40 points above it.
That is the entire specification for your user interface.
For fun,remove the Leading Alignment constraint (select it in the outline pane and press Delete on your keyboard),then select both buttons in Interface Builder and from the Align menu pick Right Edges. Now run the app again and notice the differences.
Repeat,but Now choose Align\Horizontal Centers. That will always center the top button with respect to the bottom button. Run the app and see how the buttons act when you tap them. (Remember,if you get a dashed orange Box when you change the constraints,you can use the Editor\Resolve Auto Layout Issues menu to update the button frames accordingly.)
Fixing the width
The Pin menu has an option for Widths Equally. If you set this constraint on two views,then Auto Layout will always make both views equally wide,based on which one is the largest. Let’s play with that for a minute.
Select both buttons and choose Editor\Pin\Widths Equally. This adds a new constraint to both buttons:
You have seen this type of constraint before,in the first part of this tutorial. It looks like the usual T-bar but in the middle it has a circle with an equals sign.
Even though there are two T-bars,in the Document Outline this shows up as a single Equal Widths constraint:
Changing the label text on one button will Now change the size of the other one as well. Change the bottom button’s label to “X”,just to make it really small. You will notice that the top button no longer fits its text:
So how does Auto Layout kNow which button’s size to use for both of them? If you pay close attention,you’ll see that the top button’s frame is no longer correct:
ObvIoUsly this is not what you want,so select the top button and choose Size to Fit Content from the Editormenu (or press ⌘ =). Now the text fits inside the button again – or rather,the button fits around the text – and due to the Equal Widths constraint the yellow button also resizes.
Run the app and tap the buttons. The buttons always have the same width,regardless of which one has the largest label:
Of course,when both labels are very short,both buttons will shrink equally. After all,unless there is a constraint that prevents it,buttons will size themselves to fit their content exactly,no more,no less. What was that called again? Right,the intrinsic content size.
Intrinsic Content Size
Before Auto Layout,you always had to tell buttons and other controls how big they should be,either by setting their frame or bounds properties or by resizing them in Interface Builder. But it turns out that most controls are perfectly capable of determining how much space they need,based on their content.
A label kNows how wide and tall it is because it kNows the length of the text that has been set on it,as well as the font size for that text. Likewise for a button,which might combine the text with a background image and some padding.
The same is true for segmented controls,progress bars,and most other controls,although some may only have a predetermined height but an unkNown width.
This is kNown as the intrinsic content size,and it is an important concept in Auto Layout. You have already seen it in action with the buttons. Auto Layout asks your controls how big they need to be and lays out the screen based on that information.
Usually you want to use the intrinsic content size,but there are some cases where you may not want to do that. You can prevent this by setting an explicit Width or Height constraint on a control.
Imagine what happens when you set an image on a UIImageView if that image is much larger than the screen. You usually want to give image views a fixed width and height and scale the content,unless you want the view to resize to the dimensions of the image.
So what happens when one of the buttons has a fixed Width constraint on it? Buttons calculate their own size,but you can override this by giving them a fixed width. Select the top button and choose Pin\Width from the menu. This adds a solid T-bar below the button:
Because this sort of constraint only applies to the button itself,not to its superview,it is listed in the Document Outline below the button object. In this case,you have fixed the button to a width of 46 points.
You cannot simply drag the button’s resize handles to make the button wider. If you do,you’ll end up with a whole bunch of orange Boxes. Remember that Xcode 5 does not automatically update the constraints for you (unlike Xcode 4). So if you make a change to the button’s frame,it’s up to you to make the constraints match again. The alternative approach is to simply change the constraint instead.
Select the Width constraint and go to the Attributes inspector. Change Constant to 80 to make the button wider:
Run the app and tap the buttons. What happens? The button text does change,but it gets truncated because there is not enough room:
Because the top button has a fixed-width constraint and both buttons are required to be the same size,they will never shrink or grow.
Note: You probably wouldn’t set a Width constraint on a button by design – it is best to let the button use its intrinsic size – but if you ever run into a layout problem where you expect your controls to change size and they don’t,then double check to make sure a fixed Width constraint didn’t sneak in there.
Play around with this stuff for a bit to get the hang of pinning and aligning views. Get a feel for it,because not everything is immediately obvIoUs. Just remember that there must always be enough constraints so that Auto Layout can determine the position and size for all views.
gallery example
You should Now have an idea of what constraints are and how you can build up your layouts by forging relationships between the different views. In the following sections,you will see how to use Auto Layout and constraints to create layouts that meet real-world scenarios.
Let’s pretend you want to make an app that has a gallery of your favorite programmers. It looks like this in portrait and landscape:
The screen is divided into four equal quarters. Each quarter has an image view and a label. How would you approach this?
Let’s start by setting up the basic app. Create a new iPhone project using the Single View Application template and name it “gallery”.
Open Main.storyboard. From the Object Library,drag a plain View object onto the canvas. Resize the view so that it is 160 by 284 points,and change its background color to be something other than white (for example,green):
Let’s give this view some constraints. You’ve already seen two ways to make constraints: the Editor\Pin andAlign menus,and Ctrl-dragging between views. There is a third method that you’ll use here. At the bottom of the Interface Builder window is a row of buttons:
The four circled buttons are for Auto Layout. From left to right they are: Align,Pin,Resolve Auto Layout Issues,and Resizing Behavior. The first three perform the same functions as the corresponding items from the Editormenu. The Resizing Behavior button allows you to change what happens to the constraints when you resize views.
Select the green view and click the Pin button. A popup appears that lets you add a variety of constraints:
The Spacing to nearest neighbor section at the top is what you’ll use most often. Click the four T-bar thingies so they become solid red:
This will create four new constraints between the green view and its superview,one for each side of the view. The actual spacing values may be different for you,depending on where you placed the view. (You don’t have the change these values to match mine). Click Add 4 Constraints to finish.
Your storyboard should Now look something like this:
This view needs four constraints to keep it in place. Unlike a button or label,a plain UIView does not have an intrinsic content size. There must always be enough constraints to determine the position and size of each view,so this view also needs constraints to tell it what size it needs to be.
You may wonder,where are these size constraints? In this case,the size of the view is implied by the size of the superview. The constraints in this layout are two Horizontal Spaces and two Vertical Spaces,and these all have fixed lengths. You can see this in the Document Outline:
The width of the green view is calculated by the formula “width of superview minus (98 + 62)” and its height by the formula “height of superview minus (65 + 199)”. The space constraints are fixed,so the view has no choice but to resize. (Again,your values may be different depending on where you put the view.)
When you rotate the app,the dimensions of the superview change from 320×568 to 568×320. Plug this new width and height into these formulas,and you’ll get the new size of the green view (408×56).
You can see this for yourself when you run the app and flip to landscape,but you can also simulate it directly in Interface Builder. Open the Assistant editor (press the button in Xcode’s toolbar that looks like a butler/alien) and select Preview in the jump bar:
Click the arrow button at the bottom to change the orientation to landscape. This gives you an instant preview of what the storyboard’s layout will look like in landscape orientation. The green view has resized in order to satisfy its Horizontal and Vertical Space constraints.
You can leave this preview pane open as you design your UI and it will update automatically. You can also use it to toggle between the 3.5 and 4-inch form factors.
Instead it stops at the status bar. But in iOS 7 the status bar is always drawn on top of the view controller — it is no longer a separate bar — so what gives? When you created the constraint it didn’t actually attach to the top of the screen but to an invisible line called the Top Layout Guide.
On a regular view controller this guide sits at 20 points from the top of the screen,at least when the status bar is not hidden. In a navigation controller it sits below the navigation bar. Because the navigation bar has a different height in landscape,the Top Layout Guide moves with the bar when the device is rotated. That makes it easy to place views relative to the navigation bar. There is also a Bottom Layout Guide that is used for the tab bar and toolbars.
You may not always want your UIView to resize when the device rotates,so you can use constraints to give the view a fixed width and/or height. Let’s do that Now. Select the green view and click the Pin button; in the popup put checkmarks in front of Width and Height.
Click Add 2 Constraints to finish. You have Now added two new constraints to the view,a 160 point Width constraint and a 284 point Height constraint:
Because Width and Height apply to just this view,they are located in the Document Outline under the View itself. Usually,constraints express a relationship between two different views – for example,the Horizontal and Vertical Space constraints are between the green view and its superview – but you can consider the Width and Height constraints to be a relationship between the green view and itself.
Run the app. Yup,looks good in portrait. Now flip over to landscape. Whoops! Not only does it not look like you wanted – the view has changed size again – but the Xcode debug pane has dumped a nasty error message:
Remember when I said that there must be enough constraints so that Auto Layout can calculate the positions and sizes of all the views? Well,this is an example where there are too many constraints. Whenever you get the error “Unable to simultaneously satisfy constraints”,it means that your constraints are conflicting somewhere.
Let’s look at those constraints again:
There are six constraints set on the green view,the four Spacing constraints you saw earlier (1-4) and the new Width and Height constraints that you have just set on it (5 and 6). So where is the conflict?
In portrait mode there shouldn’t be a problem because the math adds up. The width of the superview is 320 points. If you add the lengths of the Horizontal Space and Width constraints,then you should also end up at 320. The way I have positioned the view,that is: 98 + 160 + 62 = 320 indeed. Likewise,the vertical constraints should add up to 568.
But when you rotate the device to landscape,the window (and therefore the superview) is 568 points wide. That means 98 + 160 + 62 + ? = 568. There are 248 extra points that need to go somewhere in that equation and Auto Layout doesn’t kNow where to get them from. Likewise for the vertical axis.
The conflict here is that either the width of the view is fixed and one of the margins must be flexible,or the margins are fixed and the width must be flexible. You can’t have both. So one of these constraints has to go. In the above example,you want the view to have the same width in both portrait and landscape,so the trailing Horizontal Space has got to go.
Remove the Horizontal Space at the right and the Vertical Space at the bottom. The storyboard should look like this:
Now the view has just the right number of constraints to determine its size and position — no more,no less. Run the app and verify that the error message is gone and that the view stays the same size after rotating.
Painting the portraits
Drag a Label onto the green view. Notice that Now the guides appear within that green view,because it will be the superview for the label.
Position the label against the bottom margin,horizontally centered against the guides. Add a space constraint to anchor the label against the bottom of the green view,at 20 points distance. The quickest way is to use thePin button and just select the T-bar at the bottom:
Now add a constraint to center the label horizontally. You’ve seen how to do this with the Editor\Align menu but you can also use the Align button from the floating Auto Layout menu. Select the label and click the Align button to bring up the popup:
Put a checkBox in front of Horizontal Center in Container and then click Add 1 Constraint. The storyboard should Now look like this:
Notice that these two new Horizontal and Vertical Space constraints are listed under the green view’s own Constraints section,not in the main view.
Drag a new Image View object onto the storyboard,and make the layout look like this:
The image view is pinned to the top,left,and right edges of its superview,but its bottom is connected to the top of the label with a standard spacing of 8 points. If you’re unsure of how to do this,then follow these steps.
1. Drag the image view into the green view but don’t worry too much about its size or position:
2. With the image view selected,press the Pin button and choose the following options:
The top,and right T-bars are set to 20 points but the bottom one is set to 8 points. Important: For Update Frames you should choose Items of New Constraints. If you had left this to the default of None,the storyboard would look something like this:
The constraints you chose result in a different frame than the image view’s current position and size. But if you choose Items of New Constraints,Interface Builder will automatically adjust the frame as it adds the constraints and everything looks dandy:
Resolve Auto Layout Issues button to fix it:
Download the resources for this tutorial and unzip the file. You will find an Images folder – add this folder into your project. Set Ray.png as the image for the image view,change the image view’s mode to Aspect Fit and set its background color to white. Change the label’s text to say “Ray”.
Your layout should Now look like this:
@L_301_39@
Notice that the constraints inside the green view turned to orange. This happened the moment you set the image on the image view. How come your layout is suddenly invalid? Fortunately you can take the guesswork out of it and let Xcode tell you exactly what’s wrong.
Click the small red arrow next to View Controller Scene in the Document Outline to view the issues:
You have a Content Priority Ambiguity error. That’s quite the mouthful. This is what it means: If neither the image view nor the label has a fixed height,then Auto Layout doesn’t kNow by how much to scale each if the height of the green view should change. (Interface Builder seems to ignore for Now that the green view actually has a fixed Height constraint set on it.)
Let’s say at some point in your app the green view becomes 100 points taller. How should Auto Layout distribute these new 100 points among the label and the image view? Does the image view become 100 points taller while the label stays the same size? Or does the label become taller while the image view stays the same? Do they both get 50 points extra,or is it split 25/75,40/60,or in some other possible combination?
If you don’t solve this problem somehow then Auto Layout is going to have to guess and the results may be unpredictable.
The proper solution is to change the “Content Compression Resistance Priority” of the label. You will learn more about that later on. For Now,go into the Size inspector for the label and set the vertical Content Compression Resistance Priority to 751. That makes it one higher than the priority of the image view. While you’re at it,set Content Hugging Priority to 252.
The T-bars should turn blue again and the Auto Layout warnings are gone.
Adding the other heads
Drag the green view into the main view’s top-left corner. Recall that the green view had Horizontal Space and Vertical Space constraints that determined its position in the parent view. It still has those and they cause the frame of the view to be misaligned.
To fix this,use the Resolve Auto Layout Issues button and choose Update Constraints. PrevIoUsly you usedUpdate Frames,which moved and resized the view the match the constraints. Here you want to do the opposite: you want the constraints to update to match the frame.
Note that the Vertical Space at the top is Now negative. That happens because this constraint is connected to the Top Layout Guide. But there’s no reason why constraints cannot have negative values,so you can leave this as is. (If it bothers you,delete that “Vertical Space (-20)” constraint and pin the view to the top of the window.)
The Horizontal Space Now has size 0 and is represented by a thick blue line at the left edge of the window. So even though the view sits completely in the corner,it still needs constraints to anchor it there:
Select the green view and tap ⌘D to duplicate it. Move the duplicate into the top-right corner:
Notice that the T-bars are orange. When you made the duplicate,it apparently lost its constraints for the X and Y position. To fix that,pin the view to the top and the right edges of the window.
Duplicate two more times and put these copies in the bottom-left and bottom-right corners,respectively. Again,pin these views to their corners.
Change the screen design to the following:
Those are some good-looking programmers! :-)
Run the app. It looks good in portrait,but not so much in landscape:
It should be pretty obvIoUs what went wrong: you’ve set a fixed width and height on the four brightly-colored container views,so they will always have those sizes,regardless of the size of their superview.
Select the Width (160) and Height (284) constraints from all four views and delete them (this is easiest in the Document Outline). If you run the app Now,you’ll get something like this:
This looks very much like the problem you solved in the introduction in part 1,so if you think back to how you solved that,you’ll recall that you gave the views equal widths and heights.
Select all four colored views. This is easiest in the Document Outline; hold ⌘ and click on the four views. You can add the constraints in one go. In the Pin popup put checkmarks in front of Equal Widths and Equal Heights and then press Add 6 Constraints.
Run the app again and rotate the device. Hmm… still no good:
All the views do have the same height,and they also appear to have the same width,so your constraints are being met. It’s just not the width and height that you want them to have.
Just saying that all four views must have equal sizes is not enough to determine what those sizes should actually be,because Auto Layout does not kNow how these four views are connected to each other. They appear side-by-side in the design,but there are no actual constraints between them. Auto Layout does not kNow that it needs to split the window width between the “Ray” and “Matthijs” Boxes.
If Auto Layout can’t figure this out by itself,you have to tell it.
Select the Ray and Matthijs Boxes and choose Pin\Horizontal Spacing from the Editor menu. Because the Boxes are side-by-side,this adds a Horizontal Space constraint with size 0 between them,and that is enough to let Auto Layout kNow how these two views are related. Also put a Vertical Space between the Ray and Dennis Ritchie Boxes using Editor\Pin\Vertical Spacing.
Run the app again,and this time it looks all right:
A quick note on the image views: they stretch out because you have not given them a fixed size. You may not kNow it,but that’s intentional on your part. ☺ The image views wouldn’t fit in landscape mode otherwise. However,if you want an image view to keep its original aspect ratio,then you’re out of luck. You cannot achieve the following effect using Interface Builder:
Unfortunately,Interface Builder does not currently provide a way to make constraints that keep the aspect ratio of a view intact. To do that,you need to create and set the constraints programmatically. You will learn how to do that in “Intermediate Auto Layout” in iOS 6 by Tutorials.
Where To Go From Here?
If you’ve made it this far,congratulations – you Now kNow what Auto Layout is all about,and have experimented with the basics! But there’s a lot left to learn…
The tutorial you have just read is only the first half of the Beginning Auto Layout chapter from the book iOS 6 by Tutorials. The second half teaches how to use Auto Layout to create more “real-world” screen layouts,and everything else you need to kNow about using Auto Layout from Interface Builder.
But like any visual design tool,Interface Builder has its limitations and sometimes it just makes more sense to work with the NSLayoutConstraint objects directly from code.
From: http://www.raywenderlich.com/50319/beginning-auto-layout-tutorial-in-ios-7-part-2
c# – Xamarin iOS中的Grand Central Dispatch vs ThreadPool性能
http://docs.xamarin.com/guides/ios/application_fundamentals/threading
但是,已经完成了一项基准测试,表明Grand Central dispatch比ThreadPool更具性能
http://joeengalan.wordpress.com/2012/02/26/execution-differences-between-grand-central-dispatch-and-threadpool-queueuserworkitem-in-monotouch/
因此我的问题是,为什么Xamarin推荐ThreadPool而不是Grand Central dispatch? Xamarin最终是否会将ThreadPool绑定到Grand Central dispatch?什么时候选择一个而不是另一个?因为如果ThreadPool将由Xamarin优化,并最终胜过Grand Central dispatch,那么我不想使用Grand Central dispatch.
解决方法
就像我对你所链接的那篇文章的评论(从2012年2月开始)以及你链接的文章的第一段解释了原因.
GCD和ThreadPool之间的区别在于Mono中的ThreadPool具有“慢启动”设置,因此在出现工作峰值时它不会创建超过必要的线程.您可以通过启动太多线程来轻松地使cpu匮乏,因此线程池在创建初始线程之后限制自身,然后尝试每秒仅创建一个新线程(给予或接受,我不记得实际细节).
如果要强制ThreadPool实际启动大量线程,可以使用ThreadPool.SetMinThreads控制它.
使用ThreadPool的原因是相同的代码可以在所有平台上运行.
请注意,该文档讨论了如何使用ThreadPool而不是其他标准.NET线程API,并且没有说明是否使用GCD.仅仅是线程池比使用线程滚动自己的管理更好.
也就是说API,这些天我建议人们使用任务并行库(TPL),这是一种比线程更高层次的思考后台操作的方式.此外,通过切换一行代码,您可以灵活地使用内置线程池或调度到GCD,从而跨平台获得相同的API.
dispatch_semaphore_create的grand-central-dispatch – 左值参数?
dispatch_semaphore_create(long value)
我没有在文档中看到这一点,只有它与零参数一起使用的例子.
解决方法
dispatch_semaphore_wait()递减信号量计数并等待结果值是否小于0(即,您可以调用dispatch_semaphore_wait四次而无需等待使用值4创建的信号量).
dispatch_semaphore_signal()递增信号量计数,并在结果值小于或等于0时唤醒服务员.
有关典型用法示例(管理有限资源池),请参阅dispatch_semaphore_create(3)联机帮助页.
关于【荐】Grand Central Dispatch Tutorial for Swift: Part 1/2和for swift parrot and many rare native flowers的介绍已经告一段落,感谢您的耐心阅读,如果想了解更多关于A Guide to Blocks & Grand Central Dispatch (and the Cocoa API's making use of them)、Beginning Auto Layout Tutorial in iOS 7: Part 2、c# – Xamarin iOS中的Grand Central Dispatch vs ThreadPool性能、dispatch_semaphore_create的grand-central-dispatch – 左值参数?的相关信息,请在本站寻找。
本文标签: