文章目录
理解和推倒
C 的声明符
先复习一下 C 的声明符:
1、基本申明
int a
int
指定了类型,a
是变量的名称。
声明符可以使用修饰符,C 有三种修饰符:*
、[]
、()
。
1.1 指针
int *a
a
是指向int
的一个指针。
1.2 数组
int a[]
a
是一个由int
组成的数组。
1.3 函数
int f()
f
是一个返回int
的函数。
2、混合申明
这些修饰符可以混合使用。混合时,从变量名称开始往右读,读到尽头或闭括号之后再从变量名称开始往左读。
2.1 int *a[]
先往右读,[]
是有一个数组,再往左读int *
是指向int
的指针。
所以int *a[]
就是由 指向int
的指针 组成的数组,等价于int *(a[])
。数组a 中的元素,都是指向 int 的指针。
2.2 int (*a)[]
先由a往右读,碰到),因此再往左读,读到*,因此是一个指针。然后再往右读[],再往左读int。所以最后int (*a)[]是指向由int组成的数组的指针。
2.3 int* f()
//声明一个函数f,返回类型为int* ;
比如
int* f(){ // 返回一个new 的 int,要记得delete哦~~
return new int();
}
2.4 int (*f)()
f 是个指向函数的指针,所指向的函数没有参数,返回int。
这是一个函数指针,指向一个 int f() 这样的函数。比如有函数:
int get1() { return 1; }
int get2() { return 2; }
Block 的申明符
block 修饰符和指向函数的指针非常相似。上面我们提到int (*f)()是一个指向返回int函数的指针,类似地,int (^b)() 是一个指向返回int函数的 block 指针,简称返回int的 block。
因为 block 总是指向函数的,所以int ^a和int ^b()都是非法的,因为不存在指向int的 block。
block 和指向函数的指针的区别在于,block 创建了一个闭包。
代码块(也称闭包)修饰符(^)
该修饰符只能用于函数,所以int ^a;是错误的
“block是一个OC对象,这意味着它能被添加到集合,比如NSArray、NSDictionary”
申明一个block变量
1、无入参、无出参的 blcok
void (^myBlock)();
其中第一个void是返回值,可以是任意类型;
中间括号中^后面的是这个block变量的名字,我把它命名为myBlock;
最后一个括号中是参数;
//返回值类型(^block的名字)(参数类型) = ^(参数类型和参数名) {};
void(^block)() = ^(){
NSLog(@"调用了block");
};
//当然,没有参数的时候可以把括号省去
void(^block)() = ^{
NSLog(@"调用了block");
};
2、多参数有返回值的 block,可以写成如下样式:
int (^myBlock)(int,int);
同样,你也可以给参数起名字:
int (^myBlock)(int a,int b);
使用
int(^block)(int) = ^(int a){
return a + 1;
};
NSLog(@"block5 : %d",block(5));
inline:系统提供的定义block的宏
block快捷方式 输入:inline
returnType(^blockName)(parameterTypes) = ^(parameters) {
statements
};
设置block类型私有变量
1、申明
@property (nonatomic,copy) void (^progressHandle)(double progress);
2、初始化
self.progressHandle = ^(double progress){
NSLog(@"progress:%f",progress);
};
3、调用
- (IBAction)btnOnClick:(id)sender {
self.progressHandle(1.2333);
}
函数入参为block
##1、 没有入参、没有出参的block
//创建传入block的方法
//方法名中,block 是变量名称,修饰block 的void,代表这个block返回为空,(^)代表这是一个block,()代表传入参数为空
-(void)btnClickwithClock:(void (^)())block{
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0];
block();
[UIView commitAnimations];
}
//调用
[self btnClickwithClock:^{
NSLog(@"btnClickwithClock ---- ");
}];
##2、 有入参、没有出参的block
//入参为两个字符串的block
-(void)btnClickwithClock2:(void (^)(NSString *str0,NSString *str1))block{
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0];
block(@"1",@"2");
[UIView commitAnimations];
}
[self btnClickwithClock2:^(NSString *str0, NSString *str1) {
NSLog(@"str0 : %@ , str1 : %@",str0,str1);
}];
##3、 有入参、有出参的block
1、定义
//入参为两个字符串、出参为一个整数的block,
-(void)btnClickwithClock2:(NSInteger (^)(NSString *str0,NSString *str1))block{
NSInteger a = block(@"1",@"2");
NSLog(@"a : %ld",(long)a);
}
//入参为两个字符串、出参为一个字符串的block,
-(void)btnClickwithClock3:(NSString *(^)(NSString *str0,NSString *str1))block{
NSString *b = block(@"1",@"2");
NSLog(@"b : %@",b);
}
2、调用
[self btnClickwithClock2:^NSInteger(NSString *str0, NSString *str1) {
return 5;
}];
[self btnClickwithClock3:^NSString *(NSString *str0, NSString *str1) {
return @"123";
}];
打印结果
a : 5
自定义block:使用block保存代码
1、创建名称、设置返回类型
typedef void(^MSBlockButtonOnClick)(void);
typedef void(^MSBlockSelectPeriod)(NSString *startTime,NSString *endTime);
typedef void(^MSBlockDidSelectPhoto)(NSArray <UIImage *>*photoArray);
2、设置变量及初始化方法
@property (nonatomic,copy) MSBlockButtonOnClick blockBtnOnClick;
@property (nonatomic,copy) MSBlockDidSelectPhoto blockDidSelectPhoto;
@property (nonatomic,copy) MSBlockSelectPeriod blockSelectPeriod;
-(void)setBlockCommitBtnOnClick:(MSBlockButtonOnClick)blockCommitBtnOnClick
{
_blockCommitBtnOnClick = blockCommitBtnOnClick;
}
-(void)setBlockDidSelectPhoto:(MSBlockDidSelectPhoto)blockDidSelectPhoto
{
_blockDidSelectPhoto = blockDidSelectPhoto;
}
//选择控制器
- (void)datePicker:(MSTimePicker *)datePicker didSelectDate:(MSTimeModel *)datePickerDate{
if (_blockSelectPeriod) {
NSString *startStr = [MSTimeTools getStrFromDate:self.startDate withStyle:MSTimeStyleDateTime];
NSString *endStr = [MSTimeTools getStrFromDate:self.endDate withStyle:MSTimeStyleDateTime];
_blockSelectPeriod(startStr,endStr);
}
}
3、调用
- (void)ensureBtnOnClick{
if (_blockCommitBtnOnClick) {
_blockCommitBtnOnClick();
}
}
- (void)setImgContent:(UIImage *)image{
if (_blockDidSelectPhoto) {
_blockDidSelectPhoto(@[image]);
}
}
block里面调用变量
在Block中使用附有__strong修饰符的对象类型自动变量,那么Block从栈复制到堆时,该对象为Block所持有。因而容易引起循环引用。
为避免此类事件,可用__weak修饰对象。
常用的宏
//block 中使用Self
#define WS(weakSelf) __weak __typeof(&*self)weakSelf = self
//block 中调用cell
#define WC(weakCell,cell) __weak __typeof(&*cell)weakCell = cell
当在block内部使用,block外部定义的局部变量时,如果变量没有被__block修饰,则在block内部是readonly(只读的),不能对他修改。如果想修改,变量前必须要有__block修饰。
__block的作用告诉编译器,编译时在block内部不要把外部变量当做常量使用,还是要当做变量使用.
如果再block中访问全局变量,就不需要__block修饰.
参考资料
-
weakish:从 C 的声明符到 Objective-C 的 block 语法
https://segmentfault.com/a/1190000000508740 -
珲少:iOS 中block结构的简单用法
https://my.oschina.net/u/2340880/blog/398154 -
DrunkenMouse:
block的深度探究(截取自动变量、__block、截获对象、存储域、copy函数 与 dispose函数、循环引用、ARC无效时Block的copy/release)
http://www.jianshu.com/p/0a7cbcdc0ab5 -
王技术:block入门和简单使用(定义,做参数,做返回值,内存管理,循环引用)
http://www.jianshu.com/p/6b59ca5ad2d0