关于Block的定义,和作为参数的写法
转载 : http://www.dahuangphone.com/dispbbs.asp?boardid=8&id=85&page=3&star=1
Block的定义
Block的格式: ^ + 返回值类型(可以省略) + (参数)(如果没有参数,可以省略) + {表达式}
Block变量格式: 返回值类型(不可省略, 最少void) + (^变量名称) + (参数) (不可省略, 至少()). 格式和函数指针很相似,只是把*改成了^.
int (^blockName)(int, NSString*)=^(int para1, NSString *para2){
return 1;
};
//int (^blockName)(int, NSString*)的意思是要定义一个名字为"blockName"的block,它据有两个参数,具有一个int类型返回值.
void (^blockName)()=^{
};//如果没有参数,可省略如上面写法, 如果没有返回值, 必须要写void
使用typedef: (Block声明比较复杂, 建议使用这种方式生命Block)
typedef int(^blockName1)(int,NSString*);
blockName1 bn=^(int para1, NSString *para2){
return 1;
};
无参数情况:
typedef int(^blockName1)();
blockName1 bn=^{
return 1;
};
.......................................................................
作为函数参数写法:
c函数:
参数
typedef int(^ABlock)();
void cFunc(void(^blockName)(), ABlock block){//两种写法都可以
}
返回值写法
int (^fun())(int){
return ^(int cout){ return cout;};
}
ABlock fun(){
return ^(int cout){ return cout;};
}
OC函数
参数
-(void)OCFunc:(void(^)())blockName andOtherBlock:(ABlock)block{ //注意第一种写法的特别之处, OC函数要求变量类型和形参名分开, 所以写法和C不同
}
@property 写法
@property(nonatomic,copy) int (^block)(int a); //使用c的方式, 不能使用OC函数形参的写法.
@property (nonatomic,copy) Block blockName;
以Block作为方法返回值的写法:
-(int (^)(int))blockBack{ //和c的写法是不同的, 需要注意
return ^(int cout){ return cout;};
}
block不同其它变量的原因在于它不是一个单一变量, 而是一个方法,
我们要传递的是一个代码块,并且这个代码块可以存在参数,
这个参数并不是在定义block的时候就赋予值, 而是我们在实际运行block的时候才赋予值.
因此对于有参数的block,当我们传递过去的时候, 它的需要接收方提供相应的参数才能运行,
这么做我们就可以在A类为B类将来会发生的事件提前做好处理的方法,即使我们还没有这些事件的具体参数.
某种意义上将这样就不需要两者之间的委托关系.
委托关系就是B类发生一个事件后,通知A类,让A类再针对这个事件进行一些处理
而使用block,则是A已经提前将这个事件的处理方法告诉了B类, 等时间发生的时候, B类无需通知A类, 直接运行实现设置好的处理方法(block)即可.
如果你在运行一个方法的时候又想告诉这个方法在某一特定情况你还要怎么做的话, 就可以使用Block.
GCD:
GCD主要使用block来代替委托模式,使程序变得简洁,同时运行效率也得到提高.
dispatch_async(dispatch_get_global_queue(0, 0), ^{
static int i=0;
while (i<20) {
dispatch_async(dispatch_get_main_queue(), ^{
_label.text=[NSString stringWithFormat:@"%d",i++]; //UI的更新必须要在主线程完成
});
[NSThread sleepForTimeInterval:1];
}
});
NSLog(@"s");
这个函数意思是更新label的显示.每秒钟更新一次, 如果我不实用异步更新直接使用一个方法如下:
-(void)runLabel{
static int i=0;
while (i<20) {
_label.text=[NSString stringWithFormat:@"%d",i++];
[NSThread sleepForTimeInterval:1];
}
}
这回导致整个程序无法响应其它事件.
如果使用NSThread 完成则需要委托, 非常繁琐.
[此贴子已经被作者于2013/6/27 12:18:57编辑过]
2楼 dahuangphone 发表于:2013/4/30 1
block的基础知识
block是一个特殊的OC对象, 它建立在栈上, 而不是堆上, 这么做一个是为性能考虑,还有就是方便访问局部变量.
默认情况下block使用到的局部变量都会被复制,而不是保留.
所以它无法改变局部变量的值.
如果在变量面前加上__block, 那么编译器回去不会复制变量, 而是去找变量的地址, 通过地址来访问变量, 实际上就是直接操作变量.
另外块是在栈上分配的, 所以一旦离开作用域, 就会释放, 因此如果你要把快用在别的地方, 必须要复制一份.
所以在属性定义一个快的时候需要使用copy: @property (nonatomic, copy) void (^onTextEntered)(NSString *enteredText);
块是不能保留的, retain对块没有意义.
用块遍历字典:
[dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
NSLog(@"%@,%@",key,obj);
}
ARC下Block何时会从栈自动被复制到推, 以及__block和__weak的使用问题
由于Block是默认建立在栈上, 所以如果离开方法作用域, Block就会被丢弃, 在非ARC情况下, 我们要返回一个Block ,需要 [Block copy];
在ARC下, 以下几种情况, Block会自动被从栈复制到堆:
1.被执行copy方法
2.作为方法返回值
3.将Block赋值给附有__strong修饰符的id类型的类或者Blcok类型成员变量时
4.在方法名中含有usingBlock的Cocoa框架方法或者GDC的API中传递的时候.
对于非ARC下, 为了防止循环引用, 我们使用__block来修饰在Block中实用的对象:
__block id blockSelf=self;
self.blk=^{
NSLog(@"%@",blockSelf); //在非ARC下对于栈上的_block对象, Block不会对其复制, 仅仅使用, 不会增加引用计数.
};
对于ARC下, 为了防止循环引用, 我们使用__weak来修饰在Block中实用的对象:
__weak id weakSelf=self;
self.blk=^{
NSLog(@"%@",weakSelf);
};
如果要在ARC下, 为了防止循环引用, 使用__block来修饰在Block中实用的对象,仍然会被retain, 所以需要多做一些设置
__block id blockSelf=self;
self.blk=^{
NSLog(@"%@",blockSelf);
self.blk=nil; //blk被释放, blk只有的blockSelf也就被释放了
};
blk(); //并且一定要运行一次, 否则不能被释放
这样就使blk断开了与blockSelf的持有关系.
这么多好处是可以自己控制对self的持有时间.
不过在最新的ios版本中, 这些会始终被已叹号形式提示存在循环引用问题.
这种书写方式不被推荐. 除非你要在block中修改__block的指针指向.
其实我们用使用__weak修饰符, 只是不能修改对象本身, 但是可以修改对象的属性.
文章评论