需求:
- 比如我们一键换肤功能,
- 一键切换字体的时候,
- 这一类都属于执行一个动作,发出通知一样。
我们先自己定义一个model类:我们先来熟悉一下整体代码
#import "BlockManager.h"
头文件定义部分
#import
typedef void (^CallBlock)(NSString * string);
@interface BlockManager : NSObject
@property (nonatomic,strong) NSString * userName;
@property (nonatomic,copy) CallBlock block;
@property (nonatomic,strong) NSMutableArray * blockArray;
- (void)mutiBlockWith:(CallBlock)call;
@end
#import "BlockManager.m"
类中的实现代码
#import "BlockManager.h"
@implementation BlockManager
- (void)mutiBlockWith:(CallBlock)call{
self.block = [call copy];
[self.blockArray addObject:self.block];
}
- (void)setUserName:(NSString *)userName{
for (CallBlock block in self.blockArray) {
if (block) {
block(userName);
}
}
}
- (instancetype)init
{
self = [super init];
if (self) {
self.blockArray = [NSMutableArray array];
}
return self;
}
@end
block: 我们称之为闭包函数、匿名函数,代码块。
block,是我比较喜欢用的一个回调函数,因为他已经足够强大
我们慢慢来分析:
typedef void (^CallBlock)(NSString * string);
自定义block
typedef -> 自定义内容
void -> block块返回值
(^CallBclok) -> block 名字,相当于我们的方法名,其实真实的调用也就是根据isa 指针调用的 。
^ -> 相当于我们的一个block方法标示
(NSString * string) : 相当于 形参,这里我们就写了一个参数,可以写多个参数。我们将block定义了一个属性
@property (nonatomic,copy) CallBlock block;
block通过属性访问。
一般在ARC机制中,block我们用的一般是copy我们在BlockManager 中定义个数组
@property (nonatomic,strong) NSMutableArray * blockArray;
为什么定义数组,这里有什么作用?
我们知道copy 的作用复制一个新的对象进入新的内存地址,引用计数会增加,我们copy其实就是代码块,我们给每个代码块一个独自的标准空间,我们用数组存储每一个block块,以便我们能够在调用的时候全部调用到。
讨论:这个是不是和代理很类似?
[self respondsToSelector:@selector(<#selector#>)]
代理一般都会使用响应函数,其实和底层原理一样,都是找到对应的函数指针,我们调用的是函数指针。
只要我们调用了对应的指针,那么对应的方法或者block一定会调用,有些同学可能会遇到block收不到回调,其实就是对应的isa 指针并没有收到调用,大家无论怎么学习,要知道原理,那么解决问题的思路就很快。
- 自定义一个成员方法
- (void)mutiBlockWith:(CallBlock)call;
我们定义了一个带有block的成员方法,我们多次调用这个方法,
其内部实现如下:
- (void)mutiBlockWith:(CallBlock)call{
self.block = [call copy];
[self.blockArray addObject:self.block];
}
这里我们通过数组将我们调用的每一个block都加入到数组中了,想必上面的已经明白了
- 那么我们的block函数什么时候调用呢
- (void)setUserName:(NSString *)userName{
for (CallBlock block in self.blockArray) {
if (block) {
block(userName);
}
}
}
我们假设通过一个属性的setter 方法进行调用block,下面我们看下使用:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
BlockManager * block = [[BlockManager alloc]init];
[block mutiBlockWith:^(NSString *string) {
NSLog(@"111");
}];
[block mutiBlockWith:^(NSString *string) {
NSLog(@"22");
}];
[block mutiBlockWith:^(NSString *string) {
NSLog(@"33");
}];
block.userName = @"lala";
}
其实使用很简单,只是告诉一下,block其实可以类似我们通知一样在全局工程中调用。控制台打印结果是
2016-03-07 22:23:34.299 BlockTest[57023:2003321] 111
2016-03-07 22:23:34.302 BlockTest[57023:2003321] 22
2016-03-07 22:23:34.304 BlockTest[57023:2003321] 33
如果我们修改一下代码:
BlockManager * block = [[BlockManager alloc]init];
block.userName = @"lala";
[block mutiBlockWith:^(NSString *string) {
NSLog(@"111");
}];
[block mutiBlockWith:^(NSString *string) {
NSLog(@"22");
}];
[block mutiBlockWith:^(NSString *string) {
NSLog(@"33");
}];
我们看到的是将属性放到上面了,我们再次运行,控制台无任何输出
问题:为什么会收不到block内部的输出呢?
细心的程序猿会发现你的调用block是在设置属性的setter 方法内部调用的,代码执行时按照编译顺序执行的,所以当时的block还没有被拷贝内存中,所以此时并没有调用block块,相信这个问题都知道自己的block为什么不执行了吧。
今天这个操作我们就说到这里,我们下次说一下多图上传的问题:
怎么自定义线程,实现我们的多图上传思路
注:文章如果过错之处,还望指正,以便更好的理解。
在这里临时建立了一个小小的讨论群QQ:375940898
也可以一起讨论工作中的问题