iOS 笔记七:block
2015-09-09 08:46
411 查看
块(block)
1.什么是块?
块是一块代码,如一系列在{}中的表达式。
通常以"in-line"方式被包含在调用块的方法中。
以下是调用一个需要一个block作为参数的方法的例子:
block以字符^开始,然后是返回类型(可选)、参数(可选)、{, code }.
可以在block中使用在block前定义的本地变量(local variable).
要让block正常执行,这是很明显的要求。
2.创建一个可以持有block的变量类型。
声明持有block的变量时,block有点像有特殊语法的对象。
一般情况下,我们使用typedef声明一个类型来存储一个block到变量中,如:
然后我们可以定义一个类型为unary_operation_t、名为square的变量,并给该变量赋值:
例如你可以使用一个block数组属性:
然后,我们可以使用一个已经被添加到字典中的操作:
经常不需要typedef,以下是字典枚举方法的声明:
参数名"block"是参数的关键字。
如果使用typedef,以上参数block看起来应该像这样:
block还有一些缩略的定义方法,如果block没有参数,你可以不使用圆括号:
如以下block作为参数时
3.为什么blocks可以表现得像对象(objects).
通过以上学习表面blocks可以存储在objects中(in property,arrays,dictionarys,etc.).
其实blocks表现像objects的唯一目的就是为了存储它们(block的唯一”方法“是copy).
如下:
另外,你可以用以下方式触发(调用)这个数组中的block:
4.内存循环引用(交差引用).
我们之前说过所有在block中引用的对象都会保存在堆中,直到block被释放。
换句话说,block持有其中引用到的所有对象的strong指针。
在上面的例子中,self是block中引用的对象
因为这个block在myBlocks数组中。
这是个严重的问题!!!
现在self和blcok都无法从堆中释放了,因为它们相互持有对方的强指针。
这个称为内存循环引用。
内存循环引用的解决方案:
我们回忆下,本地变量总是strong类型的,这是可以的,
因为当程序调用栈离开这个范围时,它们会消失,strong指针也会消失。
可以声明一个本地变量是weak类型的,以下是方法:
现在如果我们在block中引用weakSelf,block将不会以strong方式引用到self:
block的指针就是安全的;而只要self被释放了,block也会被释放掉。
5.在iOS中,我们什么时候会用block?
A.枚举,如上面提到的NSDictionary.
B.视图动画;
C.排序,使用block作为比较方法;
D.通知Notification,当某些事件发生时,执行block;
E.错误处理Error handlers,当错误发生时,执行block;
F.结束处理Completion handlers,完成某项任务后,执行block;
还有一个超级重要的使用方式:多线程
使用Grand Central Dispatch(GCD)API.
1.什么是块?
块是一块代码,如一系列在{}中的表达式。
通常以"in-line"方式被包含在调用块的方法中。
以下是调用一个需要一个block作为参数的方法的例子:
[aDictionary enumerateKeysAndObjectsUsingblock:^(id key, id value, BOOL *stop) { NSLog(@"value for key %@ is %@", key, value); if ([@"ENOUGH" isEqualToString:key]) { *stop = YES; } }];这跟Java中的匿名类有点类似。
block以字符^开始,然后是返回类型(可选)、参数(可选)、{, code }.
可以在block中使用在block前定义的本地变量(local variable).
double stopValue = 53.5; [aDictionary enumerateKeysAndObjectsUsingblock:^(id key, id value, BOOL *stop) { NSLog(@"value for key %@ is %@", key, value); if ([@"ENOUGH" isEqualToString:key] || ([value doubleValue] == stopValue)) { *stop = YES; } }];但是它们是只读的!
BOOL stopedEarly = NO; double stopValue = 53.5; [aDictionary enumerateKeysAndObjectsUsingblock:^(id key, id value, BOOL *stop) { NSLog(@"value for key %@ is %@", key, value); if ([@"ENOUGH" isEqualToString:key] || ([value doubleValue] == stopValue)) { *stop = YES; stoppedEarly = YES; // ILLEGAL! 非法 } }];除非你以__block标识本地变量:
__block BOOL stopedEarly = NO; double stopValue = 53.5; [aDictionary enumerateKeysAndObjectsUsingblock:^(id key, id value, BOOL *stop) { NSLog(@"value for key %@ is %@", key, value); if ([@"ENOUGH" isEqualToString:key] || ([value doubleValue] == stopValue)) { *stop = YES; stoppedEarly = YES; // this is legal now } }]; if (stoppedEarly) NSLog(@"I stopped logging dictionary value early!");本地变量也可以是对象实例,但是我们只能以setter、getter方式来访问。
NSString *stopKey = [@"Enough" uppercaseString]; __block BOOL stopedEarly = NO; double stopValue = 53.5; [aDictionary enumerateKeysAndObjectsUsingblock:^(id key, id value, BOOL *stop) { NSLog(@"value for key %@ is %@", key, value); if ([stopKey isEqualToString:key] || ([value doubleValue] == stopValue)) { *stop = YES; stoppedEarly = YES; // this is legal now } }]; if (stoppedEarly) NSLog(@"I stopped logging dictionary value early!");block中将自动持有stopKey的强引用(strong)指针,直到block被释放。
要让block正常执行,这是很明显的要求。
2.创建一个可以持有block的变量类型。
声明持有block的变量时,block有点像有特殊语法的对象。
一般情况下,我们使用typedef声明一个类型来存储一个block到变量中,如:
typedef double (^unary_operation_t)(double op);以上代码声明了一个"unary_operation_t"的类型,该类型对应的变量可以存储block。
然后我们可以定义一个类型为unary_operation_t、名为square的变量,并给该变量赋值:
unary_operation_t square; square = ^(double operand) { // square变量的值是一个block return operand * operand; }然后用以下方式来使用square变量:
double squareOfFive = square(5.0); // 这句代码执行后squareOfFile的值是25.0并不需要强制使用typedef,比如以下代码也是创建square的合法方式:
double (^square)(double op) = ^(double op) {return op * op;};我们可以用以下方式来使用unary_operation_t:
例如你可以使用一个block数组属性:
@property (nonatomic, strong) NSMutableDictionary *unaryOperations;然后像这样实现一个方法:
typedef double (^unary_operation_t)(double op); -(void)addUnaryOperation:(NSString *)op whichExecutesBlock:(unary_operation_t)opBlock { self.unaryOperations[op] = opBlock; }注意block某种程度上可以被当作对象(e.g. adding it to a dictionary).
然后,我们可以使用一个已经被添加到字典中的操作:
-(double)performOperation:(NSString *)operation onOperand:(double)operand { unary_operation_t unaryOp = self.unaryOperations[operation]; if(unaryOp) { double result = unaryOp(operand); } ... }我们并不总是需要typedef,当一个block是一个方法的参数并且需要马上使用的时候,
经常不需要typedef,以下是字典枚举方法的声明:
-(void)enumerateKeyAndObjectUsingBlock:(void (^)(id key, id obj, BOOL *stop))block;以上方法定义的block没有名字,除了没有名字之外,语法其实跟使用typedef是一样的.
参数名"block"是参数的关键字。
如果使用typedef,以上参数block看起来应该像这样:
typedef void (^enumeratingBlock)(id key, id obj, BOOL *stop);其中enumeratingBlock这部分在方法参数中并没有使用。
block还有一些缩略的定义方法,如果block没有参数,你可以不使用圆括号:
如以下block作为参数时
[UIView animateWithDuration:5.0 animations:^() { view.opacity = 0.5; }];可以写成这样:
[UIView animateWithDuration:5.0 animations:^ { view.opacity = 0.5; }];与此类似的还有返回类型,返回类型经常可以被推断出来,此时返回类型是可以省略的。
NSSet * mySet = ...; NSSet * matches = [mySet objectsPassingTest:^(id obj, ...) { return [obj isKindOfClass:[UIView class]]; }];return语句表面此block返回的是BOOL类型,所以在定义以上匿名block时没有使用返回类型。
3.为什么blocks可以表现得像对象(objects).
通过以上学习表面blocks可以存储在objects中(in property,arrays,dictionarys,etc.).
其实blocks表现像objects的唯一目的就是为了存储它们(block的唯一”方法“是copy).
如下:
@property (nonatomic, strong) NSMutableArray * myBlocks; // array for blocks你可以做以下事情
[self.myBlocks addObject:^{ [self doSomething]; }];非常简洁有木有!
另外,你可以用以下方式触发(调用)这个数组中的block:
void (^doit)(void) = self.myBlocks[0]; doit();但是这样是有危险的。
4.内存循环引用(交差引用).
我们之前说过所有在block中引用的对象都会保存在堆中,直到block被释放。
换句话说,block持有其中引用到的所有对象的strong指针。
在上面的例子中,self是block中引用的对象
[self .myBlocks addObject:^ { [self doSomething]; }];所以这个block持有self的strong指针,但是请注意self同时也持有block的strong指针,
因为这个block在myBlocks数组中。
这是个严重的问题!!!
现在self和blcok都无法从堆中释放了,因为它们相互持有对方的强指针。
这个称为内存循环引用。
内存循环引用的解决方案:
我们回忆下,本地变量总是strong类型的,这是可以的,
因为当程序调用栈离开这个范围时,它们会消失,strong指针也会消失。
可以声明一个本地变量是weak类型的,以下是方法:
__weak MyClass *weakSelf = self; //虽然even是strong类型的,weakSelf是weak类型的。
现在如果我们在block中引用weakSelf,block将不会以strong方式引用到self:
[self.myBlocks addObject:^ { [weakSelf doSomething]; }现在我们不再有内存循环引用了,只要外部什么地方持有了self的strong指针,
block的指针就是安全的;而只要self被释放了,block也会被释放掉。
5.在iOS中,我们什么时候会用block?
A.枚举,如上面提到的NSDictionary.
B.视图动画;
C.排序,使用block作为比较方法;
D.通知Notification,当某些事件发生时,执行block;
E.错误处理Error handlers,当错误发生时,执行block;
F.结束处理Completion handlers,完成某项任务后,执行block;
还有一个超级重要的使用方式:多线程
使用Grand Central Dispatch(GCD)API.
相关文章推荐
- iOS SDK: Working with URL Schemes
- The Complete Tutorial on iOS/iPhone Custom URL Schemes
- iOS开发一款小巧简洁的日历控件
- iOS开发利器-CocoaPods安装和使用教程
- ios-音频播放-短视频加载-封装工具类
- Runtime运行时机制
- iOS textView 高度自适应
- 给学习iOS开发同学的建议
- 零基础学iOS开发
- iOS 开发工程师
- iOS判断是否含有非法字符代码
- iOS-runtime-根据类名推送到任意控制器,且实现属性传值
- iOS分页控制器
- iOS-QQ好友列表实现
- IOS 入门学习记录
- IOS 绘制背景色渐变的矩形
- IOS真机推送
- ios 内存管理个人一些见解
- IOS开发之深拷贝与浅拷贝(mutableCopy与Copy)详解
- iOS开发:通讯录之个人中心页面的实现