关于块对象

About the Blocks

关于块对象(block),以前大都用来代替Delegate来完成一些事情。所以,一直没怎么注意。 但是最近发现确实很有用,所以写个Demo,发上来,大家共鉴。

使用方法分好多种,常用的示例大体如下:

  • 定义block

 
NSComparisonResult (^theBlock)(NSString *value) = ^NSComparisonResult (NSString *value) {
    if([value isEqualToString:@"Nori"]) {
        return NSOrderedSame;
    }
    return NSOrderedAscending;
};
  • 定义以block为参数的函数

 
void useCodeBlock(NSComparisonResult (^theBlock)(NSString *value), NSString *value) {
    if(theBlock(value) == NSOrderedSame) {
        NSLog(@"Equal: What a clever boy %@ is.", value);
    }
    else {
        NSLog(@"unEqual: %@ is a Fool.", value);
    }
}
  • 定义以block为参数得方法

 
- (NSMutableArray *)filterArray:(NSArray *)inArray withBlock:(NSComparisonResult (^)(NSString *value))block {
    NSMutableArray *result = [NSMutableArray array];
    for(NSString *value in inArray) {
        if(block(value) != NSOrderedSame) {
            [result addObject:value];
        }
    }
    return result;
}
  • 测试

 
- (void)viewDidLoad {
    [super viewDidLoad];

    // test 1
    useCodeBlock(theBlock, @"Leo");
    useCodeBlock(theBlock, @"Nori");

    // test 2
    NSArray *array = [[NSArray alloc] initWithObjects:@"Leo", @"Amber", @"Nori", @"Wesly", @"Steven", @"Rain", @"Alice", @"Tracy", @"Nori", nil];
    NSArray *filtedArray = [[self filterArray:array withBlock:theBlock] retain];
    [array release];
    NSLog(@"%@", filtedArray);
    [filtedArray release];

}

测试结果输出:

 
2011-09-07 11:45:55.534 BlocksDemo[3234:207] unEqual: Leo is a Fool.
2011-09-07 11:45:55.535 BlocksDemo[3234:207] Equal: What a clever boy Nori is.
2011-09-07 11:45:55.535 BlocksDemo[3234:207] (
    Leo,
    Amber,
    Wesly,
    Steven,
    Rain,
    Alice,
    Tracy
)

此外,稍做说明。block本质是代码块(对象)的指针,所以需要进行内存管理的,这个demo没有处理。其管理方式和普通的对象内存管理基本相同。例如,做属性得时候,注意dealloc中得release。

附件1

Block做筛选器


关于Block做筛选器,文档上说是并行的,速度会快。写了个demo,确实速度有所提高。 一下是一个Block做筛选器的Demo,求2-20000间素数。相同得算法,block会快很多。

 
@interface PrimeFinder : NSObject {
    NSInteger maxNumber;
    NSDate *startedDate;
    NSDate *endedDate;
    NSMutableArray *primes;

    NSMutableArray *candidates;
}
@property (retain, nonatomic) NSMutableArray * candidates;

@property (retain, nonatomic) NSMutableArray * primes;
@property (retain, nonatomic) NSDate * startedDate;
@property (retain, nonatomic) NSDate * endedDate;

- (id)initWithMaxNumber:(NSInteger)inMaxNumber;
- (void)startWithNormal;
-(void)startWithBlock;
- (NSTimeInterval)elapsedTime;

@end

@interface PrimeFinder ()

-(BOOL)isPrime:(NSInteger)number;
-(NSMutableArray *)filterArray:(NSArray *)inArray withBlock:(BOOL (^)(id))block;

@end

@implementation PrimeFinder

@synthesize primes, startedDate, endedDate, candidates;

- (void)dealloc {
    self.candidates = nil;
    self.primes = nil;
    self.startedDate = nil;
    self.endedDate = nil;
    [super dealloc];
}

- (id)initWithMaxNumber:(NSInteger)inMaxNumber {
    if ((self = [super init])) {
        maxNumber = inMaxNumber;
        primes = [[NSMutableArray alloc] init];

        candidates = [NSMutableArray new];
        for (int n = 2; n <= inMaxNumber; ++ n) {
            [candidates addObject:[NSNumber numberWithInteger:n]];
        }
    }
    return self;
}
- (void)startWithNormal {
    [self setStartedDate:[NSDate date]];
    for(NSInteger n = 2; n <= maxNumber; ++n) {
        if([self isPrime:n]) {
            [primes addObject:[NSNumber numberWithInteger:n]];
        }
    }
    [self setEndedDate:[NSDate date]];
}

- (NSMutableArray *)filterArray:(NSArray *)inArray withBlock:(BOOL (^)(id))block {
    NSMutableArray *result = [NSMutableArray array]; for(id item in inArray) {
        if(block(item)) [result addObject:item];
    } return result;
}
-(void)startWithBlock {
    [self setStartedDate:[NSDate date]];
    NSMutableArray *result = [NSMutableArray array];
    typedef void (^dispatch_block_t)(void);//此语句是系统的,本不改有,为了方便大家理解,所以写上了。
    dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
    dispatch_group_t group = dispatch_group_create();

    for(NSInteger number = 2; number <= maxNumber; ++number) {
        dispatch_block_t isPrime = ^{
            for (NSInteger n = 2; n < number; ++n) {
                if ((number % n) == 0) {
                    return;
                }
            }
            @synchronized(result) {
                [result addObject:[NSNumber numberWithInt:number]];
            }
        };
        dispatch_group_async(group, globalQueue, isPrime);
    }
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    [self setEndedDate:[NSDate date]];
    [self setPrimes:result];
}

-(BOOL)isPrime:(NSInteger)number {
    for(NSInteger n = 2; n < number; ++n) {
        if((number % n) == 0) {
        return NO;
        }
    }
    return YES;
}

- (NSTimeInterval)elapsedTime {
    return [endedDate timeIntervalSinceDate:startedDate];
}

@end

测试代码:

 
PrimeFinder *finder = [[PrimeFinder alloc] initWithMaxNumber:200000];
[finder startWithNormal];
NSLog(@"Found all the primes in %fs with Normal", [finder elapsedTime]);
[finder startWithBlock];
NSLog(@"Found all the primes in %fs with Block", [finder elapsedTime]);
[finder release];

结果如下:

 
Found all the primes in 7.195403s with Normal
Found all the primes in 6.285822s with Block

附件2

注,因为for中各block是并行的,所以就要考虑到互斥锁的问题。所以,应当将对result得操作放在block内,加锁@synchronized(result){xxoo}。

还有,内存管理没做,如果要使用block得时候,请注意这些细节。


##(续)今天补上系统一点的使用方法:

  • 声明和定义分离

    a. 声明类型(typedef),那么可以有多种实现

// 声明类型:
typedef <return type> (^<block name>) (type arg1, ...);

// 完成定义:
<block name> <instance name> = ^<return type> (type arg1, ...) {
    ...
    return ...;
};
// 使用:
<instance name> (a, ...);

// 例:
typedef void (^PrintBlock) (NSObject *obj);
typedef int (^PlusBlocks) (int arg1, int arg2);

- (void)test {
 PrintBlock print = ^(NSObject *obj) {
        NSLog(@"%@", obj);
    };
    PrintBlock printPtr = ^(NSObject *obj) {
        NSLog(@"ptr: %@", obj);
    };
    printPtr (@"Hello Blocks.");
    print(@"Hello Blocks.");
    
    PlusBlocks add = ^int (int a, int b) {
        return a + b;
    };
    int sum = add(10,2);
    NSLog(@"%i", sum);
}

b. 声明Block(不声名类型),那么只能有一个定义

// 声明:
<return type> (^<block name>) (type arg1, ...);
// 定义:
<block name> = ^<return type> (type arg1, ...) {
 ...
 return ...;
};
// 使用:
<block name> (a, ...);
// 例:
int (^PlusBlocks) (int arg1, int arg2);
- (void)test {
    PlusBlocks = ^int (int a, int b) {
        return a + b;
    };
    int sum = PlusBlocks(10,2);
    NSLog(@"%i", sum);
}
  • 声明并定义

// 声明并定义:
<return type> (^<block name>) (type arg1, ...) = ^<return type>(type arg1, ...) { do something... return ... };
// 使用:
<block name>(a, ...);

// 例:
- (void)test {
    int (^CusBlocks)(int a, int b) = ^(int a, int b){
        return a + b;
    };
    NSLog(@"%i", CusBlocks(12,12));
}
  • 与方法混用(使用方法设置并生成block)

    注意内存的使用,方法返回的block要copy; 外部使用完毕要release

例:
- ( int(^)(int) )blockRaisedToPower:(int)y {
   int (^block) (int) = ^ int (int x) {
       return pow(x, y); // Close the value of "y"
   };
   return Block_copy(block); 
   // 注:如果你想在其他地方使用该block,那么在return时记得copy
}
// 测试代码
- (void)test {
   int (^square) (int) = [self blockRaisedToPower:2];// 平方
   int (^cube) (int) = [self blockRaisedToPower:3];// 3次方
   int (^zenzizenzizenzic) (int) = [self blockRaisedToPower:8];// 8次方
 
   NSLog(@"%d", square(3));// 9
   NSLog(@"%d", cube(3)); // 27
   NSLog(@"%d", zenzizenzizenzic(3));// 6561
 
   // 用完记得release掉,copy的block
   Block_release(square);
   Block_release(cube);
   Block_release(zenzizenzizenzic);
}
  • 与方法混用(做Delegate)

- (void)xxx... usingBlock:(<block name>)<arg name>;

方法执行的时候传入,并保持(直接等于); 在合适的时候调用,用完记得release(例,在dealloc里)。

  • 注意事项

    • void可省
    • 声明时,形参可省
    • 内存管理: blcok默认是创建在中而不是堆中,所以要通过copy移动到堆中以保持,用完之后记得release。 block会默认保持引用的外部变量(比如self),为了防止循环引用而死锁,引用而不保持的变量应该加__block标识,例如:
// MyClass.h 的成员变量
WhateverObject *anObject;
BOOL aBoolean;
 
// MyClass.m
anObject = [[WhateverObject alloc] initWithBlock: ^ {
   NSLog(@"%d", aBoolean); // Possible retain cycle
}];