blocks 学习

来源:互联网 发布:js 多时区夏令时切换 编辑:程序博客网 时间:2024/06/05 08:45

 

Ios4已经直接支持blocks,很有必要学习一下。

在ios,blocks是对象,它封装了一段代码,这段代码可以在任何时候执行。Blocks可以作为函数参数或者函数的返回值,而其本身又可以带输入参数或返回值。它和传统的函数指针很类似,但是有区别:blocks是inline的,并且它对局部变量是只读的。

Blocks的定义:

         int (^Multiply)(intint) = ^(int num1, int num2) {return num1 * num2;};

定义了一个Multiply的blocks对象,它带有两个int参数,返回int。等式右边就是blocks的具体实现,注意{}blocks体里的;。

Blocks可以访问局部变量,但是不能修改。

              int multiplier = 7;

     int (^myBlock)(int) = ^(int num) {

         multiplier ++;//编译报错

         return num * multiplier;

     };

如果要修改就要加关键字:__block

         __block int multiplier = 7;

     int (^myBlock)(int) = ^(int num) {

         multiplier ++;//这样就可以了

         return num * multiplier;

     };

 

作为函数的参数,blocks某种意义上替代了回调函数或者delegate。当函数调用了,假设某个事件触发,这时blocks里的内容就会运行。这样有利于代码的整合和阅读,你不需要到处去实现委托方法了。

系统API中已经有很多支持blocks参数了

·       Completion handlers

·       Notification handlers

·       Error handlers

·       Enumeration

·       View animation and transitions

·       Sorting

例如:

[UIView animateWithDuration:(NSTimeInterval)duration animations:(void (^)())animations]

集合体中也可以运用blocks。枚举一个数组时我们通常:

         for (id obj in Array);

现在,

NSString *area = @"Europe";

    NSArray *timeZoneNames = [NSTimeZone knownTimeZoneNames];

    NSMutableArray *areaArray = [NSMutableArray arrayWithCapacity:1];

    NSIndexSet *areaIndexes = [timeZoneNames indexesOfObjectsWithOptions:NSEnumerationConcurrent

                                                             passingTest:^(id obj, NSUInteger idx, BOOL *stop) {

                                                                 NSString  *tmpStr = (NSString *)obj;

                                                                 return [tmpStr hasPrefix:area];

                                                             }];

   

    NSArray *tmpArray = [timeZoneNames objectsAtIndexes:areaIndexes];

    [tmpArray enumerateObjectsWithOptions:NSEnumerationConcurrent|NSEnumerationReverse

                               usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {

                                   [areaArray addObject:[obj substringFromIndex:[area length]+1]];

                               }];

    NSLog(@"Cities in %@ time zone:%@", area, areaArray);

在blocks中obj就是数组中的每个成员,我们就可以在blocks内对每个对象进行处理。再比如:

NSMutableArray *mArray = [NSMutableArray arrayWithObjects:@"a",@"b",@"abc",nil];

    NSMutableArray *mArrayCount = [NSMutableArray arrayWithCapacity:1];

    [mArray enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock: ^(id obj,NSUInteger idx, BOOL *stop){

        [mArrayCount addObject:[NSNumber numberWithInt:[obj length]]];

    }];

   

    NSLog(@"%@",mArrayCount);

你会发现,这样写代码更容易读懂。

最后,看一个排序的例子:

    NSArray *stringsArray = [NSArray arrayWithObjects:

                             @"string 1",

                             @"String 21",

                             @"string 12",

                             @"String 11",

                             @"String 02"nil];

    static NSStringCompareOptions comparisonOptions = NSCaseInsensitiveSearch | NSNumericSearch |

    NSWidthInsensitiveSearch | NSForcedOrderingSearch;

    NSLocale *currentLocale = [NSLocale currentLocale];

    NSComparator finderSort = ^(id string1, id string2) {

        NSRange string1Range = NSMakeRange(0, [string1 length]);

        return [string1 compare:string2 options:comparisonOptions range:string1Range locale:currentLocale];

    };

    NSLog(@"finderSort: %@", [stringsArray sortedArrayUsingComparator:finderSort]);

结果:finderSort: (

    "string 1",

    "String 02",

    "String 11",

    "string 12",

    "String 21"

)


iOS中多线程编程工具主要有:

  •  NSThread
  • NSOperation
  • GCD

这三种方法都简单易用,各有千秋.但无疑GCD是最有诱惑力的,因为其本身是apple为多核的并行运算提出的解决方案.虽然当前移动平台用双核的不多,但不影响GCD作为多线程编程的利器(ipad2已经是双核了,这无疑是一个趋势).

http://www.cnblogs.com/scorpiozj/archive/2011/07/25/2116459.html

GCD是和block紧密相连的,所以最好先了解下block.GCD是C level的函数,这意味着它也提供了C的函数指针作为参数,方便了C程序员.

下面首先来看GCD的使用:

dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

async表明异步运行,block代表的是你要做的事情,queue则是你把任务交给谁来处理了.(除了async,还有sync,delay,本文以async为例).

之所以程序中会用到多线程是因为程序往往会需要读取数据,然后更新UI.为了良好的用户体验,读取数据的操作会倾向于在后台运行,这样以避免阻塞主线程.GCD里就有三种queue来处理.

1. Main queue:

  顾名思义,运行在主线程,由dispatch_get_main_queue获得.和ui相关的就要使用Main Queue.

2.Serial quque(private dispatch queue)

  每次运行一个任务,可以添加多个,执行次序FIFO. 通常是指程序员生成的,比如:

NSDate *da = [NSDate date];NSString *daStr = [da description];const char *queueName = [daStr UTF8String];dispatch_queue_t myQueue = dispatch_queue_create(queueName, NULL);

3. Concurrent queue(global dispatch queue):

可以同时运行多个任务,每个任务的启动时间是按照加入queue的顺序,结束的顺序依赖各自的任务.使用dispatch_get_global_queue获得.

所以我们可以大致了解使用GCD的框架:

复制代码
dispatch_async(getDataQueue,^{    //获取数据,获得一组后,刷新UI.    dispatch_aysnc (mainQueue, ^{    //UI的更新需在主线程中进行};})
复制代码

由此可见,GCD的使用非常简单,以我的使用经验来看,以后会逐步淘汰使用NSOperation而改用GCD.

最后感慨下,苹果为吸引开发者而将开发门槛降的非常低.以多线程编程为例,似乎还没有比iOS更容易的平台.这无疑会吸引更多的人来淘金,但无疑竞争也会异常激烈.要脱颖而出app在创意上无疑得有独到之处. 这真的是把双刃剑,吐槽下~~


Delegate & Block 

Delegate (协议也就是protocol):

1. 声明Delegate: (通常写在一个.h文件中,这样容易import)
@protocol xxxDelegate <ParentClass>
@optional - (return_type) methodName:(certain_type) args; // optional delegate methods
- (return_type) methodName2:(certain_type) args; // the class who implements this delegate must implement this method.
@end

2.采用一个协议:
#import "xxxDelegate.h"
@interface xxxClassA<xxxDelegate, xxxxxxDelegate>
@end

@implementation xxxClassA

- (return_type) delegateMethod:(certain_type) args
{
// do something with the args
}

3. delegate 作为类变量:
@interface xxxClassB
@property(nonatomic, assign) id<xxxDelegate> delegate; //用assign , 否则会有引用循环。
...
@end

@implementation xxxClassB
- (void)dealloc
{
self.delegate = nil; // be a responsible delegate. not necessary .
[super dealloc];
}
@end

4. xxxClassA and xxxClassB 有什么关系:
xxxClassA *a = [[xxxClassA alloc] init];
xxxClassB *b = [[xxxClassB alloc] init];
b.delegate = a;
所以, b中可以通过调用[delegate  delegateMethod]; 而影响到 a.

Block (块) 是GCD(Grand Central Dispatch) 要执行的工作单元。 GCD是操作系统管理的一个线程池。
Block可以用做代替delegate机制的回调,也应用于并行开发(Concurrency programing)等技术中。
如何定义一个块:
1. 块的声明,(块的直接量,没名字,所以有时候也叫匿名函数):
^(argument_list){
body
};
2. 如何调用块: 
方法一:int j = ^(int n){return n*2;} (9); // j equals 18 now, (可以在直接量后面圆括号括起来要传入的参数, 块就被调用了)
方法二: 定义块指针。 return_type (^blockPointerName)(list of arguments);
如: int (^doubler)(int);
doubler = ^(int n){return n*2;};
int j = doubler(9);
以块指针作为函数的参数:
void someFunction(int (^blockArg) (int));
int (^doubler)(int ) = ^(int n){return n *2};
someFunction(doubler);
以块指针作为Objective-C方法(声明)的参数:
- (void) doSomethingWithBlockPointer: (float (^)(float)) blockPointer;
3.一个块如何访问其包围环境中的变量:
3.1 当执行流程通过块直接量时,块用于其环境中的一个本地变量的值,就是该本地变量的值。
int j  = 10;
int (^blockPtr)(int) = ^(int n) {return j + n;};
j = 20;
int k = blockPtr(5); // k is now 15, not 25.  
原因:实际上blockPtr在被赋值的时候,是用的当时j 的一个私有副本,所以这个副本j' 值为10.
3.2 块可以通过指针来访问静态和外部变量。 块当执行时会查找这些值,所以这些值不一定是定义该块时候的值。
static int j = 10;
int (^blockPtr)(int) = ^(int n){ return j +n;};
j = 20;
int k = blockPtr(5); // k is 25.
3.3 块对于本地变量的访问是只读的。
int j = 10;
void(^blockPtr)(void) = ^(void){j = 20;} ; // 编译错误。
3.4 如果本地有个变量保存了指向某个对象的指针,那么块不能修改这个变量指向别的对象,但是可以修改当前指向对象的值。
如: NSMutableArray *localArray = .......
void (^shortArray)(void) = ^(void){[localArray removeLastObject];}; // correct.
shortArray();
3.5 定义块变量: __block int integerBlockVariable;
对于块变量作用域中定义的任何块,它都是可见的,可共享的,可以修改的。
__block int j = 10;
void (^blockPtr_1) (void) = ^(void) {j += 15;};
void (^blockPtr_2) (void) = ^ (void) {j += 25;};
blockPtr_1(); // j  is now 25;
blockPtr_2(); // j is now 50;
4.块的内存管理:
4.1块其实是一个Objective-C类。块可以调用 copy, release, autorelease, 或者Block_copy(); Block_release(),来管理引用计数, 块是基于栈的,所以retain操作不起作用,必须用copy复制块(这有时候也是陷阱,不过我都没用过这么高级的东东,所以,初学者也太担心这个)。
4.2一个块在被复制的时候,它所引用的变量如果保存一个对象,那么这个对象会被保留(retain), 这个块释放时,也会释放这个对象(release).
4.3 块内调用的对于self的直接引用会导致self被保留(retain), 直接引用实例对象的变量,也会导致self 被保留(retain),方法中的本地变量,也会被保留(retain)。

5.块与函数指针的对比:
声明,定义都很像,就是* 和 ^符号的区别。
在应用上,主要的区别(我认为的):
5.1:用函数指针指向函数,你需要把函数内所用到的外部的变量,都作为参数传递给函数才可以用。
5.2: 函数指针的话,你必须事先定义这个函数,然后再声明函数指针,然后再赋值。Block你无需声明,可以用块直接量。


原创粉丝点击