C语言代码块
1、 代码块
代码块对象是对C语言中函数的扩展。除了函数中的代码,代码块还包含变量绑定。它包含两种类型的绑定:自动型与托管型。自动绑定使用的是栈中的内存,而托管绑定是通过堆创建的。代码块有时也称为闭包。
因为代码块是C语言实现的,所以他们在各种以C为基础的语言内都是有效的,如objective-c、c++以及objective-c++等。
2、代码块和函数指针
代码块与函数指针类似,它由如下特征:
1) 返回类型可以手动声明也可以由编译器推导。
2) 具有指定类型的参数列表。
3) 拥有名称。
代码块的定义方式:
(^blockname)(list of arguments) = ^(arguments) {body;};
3、 代码块使用的相关特点
1)
2) 代码块整体可以看成是一个特殊的变量,因此在代码块中可以使用与它同作用域的有效变量。
3) 代码块如同一个变量,可以像函数一样使用它。
4) 代码块可以像一个简单的变量一样作为方法的参数。
5) 使用typedef关键字可以将声明的代码块名称作为此代码块的类型使用。
4、 代码块与变量的使用
1) 本地变量可以在代码块中直接使用。本地变量就是与代码块在同一范围内声明的变量,它们有着相同的作用域。
注意,代码块在定义时会复制并保存本地变量当前的状态,也就是说本地变量会被代码块作为常量获取到。因此,在定义代码块后重新给前面定义的本地变量赋予新值后再调用代码块并不会改变代码块之前的状态。
2) 代码块还可以调用包含了它的作用域更大范围的变量,如全局变量。
3) 代码块中的参数变量与函数中的参数变量具有同样的作用。
4) 如果想修改被代码块使用的本地变量或全局变量,你必须将它们声明为可修改的,也就是给声明的变量加上__block属性。
5、 代码块的内存管理
代码块在objective-c经常使用,你可以像使用对象一样使用它。使用时最大的问题就是内存管理,处理内存问题有以下规则:
1) 在代码块中,如果引用了一个objective-c对象,必须保留它。
2) 在代码块中,如果通过引用访问了一个实例变量,要保留一次self(即执行方法的对象)。
3) 在代码块中,如果通过数值访问一个实例变量,变量需要保留。
在 objective-c中我们将代码块视为对象看待,所以可以向它发送任何与内存管理有关的消息。在c语言级别中,必须使用Block_copy()和Block_release()函数来适当管理内存。
6、同步
我对同步的理解是:各事件在运行时相互之间的一种顺序或制约。比如:当一个对象发送了一条消息,接收者必须接收到消息后才能对消息进行处理。当接收者接收到上一条消息后,发送者才能发送下一条消息。
为了在多个线程间实现同步,我们使用了标记(flag)或互斥(mutex)。它们以确保进程间不会在同一时间进入临界区。临界区指多个线程所共有的那部分资源。
Objective-c中提供了语言级别的关键字@synchronized。它拥有一个参数:
@synchronized(theObject)
{
//Critical section
}
这个关键字可以确保不同的进程会连续地访问临界区的代码。
对于多进程的应用补充一点,属性中nonatomic表示非原子性,atomic表示原子性。编译器会将原子性的属性生成强制彼此互斥的getter和setter方法。atomic特质用于多进程之间,nonatomic用于非多进程之间。
7、后台运行
如果你想让一些代码在后台执行,NSObject提供了一些方法。这些方法的名字中都有performSelector:。举个例子performSelectInBackground:withObject:方法,它能在后台执行一个方法。
定义在后台执行的方法时必须遵从一下限制:
1) 这些方法运行在各自的线程中,因此你必须为这些方法创建一个自动释放池。
2) 这些方法不能有返回值,并且要么没有参数,要么只有一个参数对象。
如:
- (void) myMethod;
- (void) myMethod:(id)myObject;
该方法如何创建一个线程在后台执行:
[self performSelectorInBackground:@selector(myMethod) withObject:nil];
或 [self performSelectorInBackground:@selector(myMethod) withObject:argumentObject];
所创建的方法在后台自动运行并结束,objective-c会清除并弃掉线程所占内存。
8、调度队列
GCD可以使用调度队列(dispatch queue)。你只需写下代码,把它指派为一个队列,系统就会执行它了。可以同步或异步地执行你写的任意代码。
有以下三种类型的队列:
1) 连续队列:指会根据指派的顺序执行任务的队列。可以创建任意数量的连续队列,队列间可以并行执行。
2) 并发队列:指能并发执行一个或多个任务的队列。并发队列中的任务可以在前一个任务还没完成就开始执行下一个任务,也就是同时执行队列中的多个任务。任务开始的顺序根据指派到队列中的顺序。
3) 主队列:它是应用程序主线程任务。
注意,三种队列只能选择一个来使用。
9、连续队列
一连串任务需要按照一定的顺序执行,这时便可以创建连续队列。任务按照先进先出(FIFO)的顺序执行。连续队列都是不会发生死锁的。
使用连续队列:
dispatch_queue_t my_serial_queue = dispatch_queue_create(“com.apress.MySe rialQueue1”,NULL);
第一个参数是队列的名称,第二个参数负责提供队列的特性(不需要使用时用NULL取代)。
10、并发队列
当任务之间可以并行运行时可以采用并发队列。并发队列也遵从先进先出(FIFO),且任务可以在前一个任务还没有结束时就可以开始运行。并发队列调度时,一次运行的任务数量是不确定的。
每个应用程序都有三种并发队列可以使用:高优先级(high)、默认优先级(default)和低优先级(low)。如果想引用它们,我们可以通过调用dispatch_get_global_queue方法。
11、主队列
主队列是应用程序中一直存在的队列,使用dispatch_get_main_queue可以访问与应用程序主线程相关的连续队列。
因为这个队列与主线程有关,一般要小心安排队列中的任务顺序,通常以同步方式使用这个队列,提交多个任务并在它们操作完成之后执行其它操作。
dispatch_queue_t myQueue = dispatch_get_current_queue(void);
12、队列的内存管理
调度队列是引用计数对象。可以使用dispatch_retain()和dispatch_release()来修改队列的保留计数器的值。
队列内存管理函数只能对你自己创建的队列使用,而无法使用在全局调度队列上。
如果你编写的是一个使用垃圾回收的OS X应用程序,那么你必须手动管理这些队列的内存。
队列的上下文中,你可以为调度对象指派任意类型的全局数据上下文,比如objective-c对象或指针。必须在需要它的时候为上下文数据分配内存并在队列销毁之前进行内存清理。可以使用dispatch_set_context()和dispatch_get_context()函数为设置或获取上下文,使用如下
通过函数体可以清楚的看到我们先将上下文对象转换成之前的数据类型,再通过调用removeAllObjects方法来释放掉对象中的所有成员。
13、添加任务
有两种方式可以向队列中添加任务。
同步:队列会一直等待前面任务结束。
异步:添加任务后,不必等待任务,函数会立刻返回。
前面我们已经说过通过代码块或函数我们可以增加一个线程。向队列中增加任务,我们需要将该代码块或函数提交给队列。有四个调度函数,分别是关于代码块和函数各自的同步与异步方式。
添加任务的最简单方式是代码块。代码块必须是dispatch_block_t这样的类型,而且要定义为没有参数和没有返回值:
typedef void (^dispatch_block_t)(void);
1) 使用dispatch_async函数异步代码块添加任务。这个函数有两个参数,分别是队列和代码块。
该函数中代码块使用的是内联方式创建的。
2) 使用dispatch_sync函数同步代码块添加任务
3) 使用dispatch_async_f()调度函数异步添加到队列:
4) 使用dispatch_sync_f()调度函数同步添加到队列: