iOS Block 实现类似通知、一键换肤的功能


需求:

  1. 比如我们一键换肤功能,
  2. 一键切换字体的时候,
  3. 这一类都属于执行一个动作,发出通知一样。

我们先自己定义一个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,是我比较喜欢用的一个回调函数,因为他已经足够强大

  1. 我们慢慢来分析:
    typedef void (^CallBlock)(NSString * string); 自定义block
    typedef -> 自定义内容
    void -> block块返回值
    (^CallBclok) -> block 名字,相当于我们的方法名,其实真实的调用也就是根据isa 指针调用的 。
    ^ -> 相当于我们的一个block方法标示
    (NSString * string) : 相当于 形参,这里我们就写了一个参数,可以写多个参数。

  2. 我们将block定义了一个属性
    @property (nonatomic,copy) CallBlock block; block通过属性访问。
    一般在ARC机制中,block我们用的一般是copy

  3. 我们在BlockManager 中定义个数组
    @property (nonatomic,strong) NSMutableArray * blockArray;
    为什么定义数组,这里有什么作用?
    我们知道copy 的作用复制一个新的对象进入新的内存地址,引用计数会增加,我们copy其实就是代码块,我们给每个代码块一个独自的标准空间,我们用数组存储每一个block块,以便我们能够在调用的时候全部调用到。

讨论:这个是不是和代理很类似?
[self respondsToSelector:@selector(<#selector#>)] 代理一般都会使用响应函数,其实和底层原理一样,都是找到对应的函数指针,我们调用的是函数指针。
只要我们调用了对应的指针,那么对应的方法或者block一定会调用,有些同学可能会遇到block收不到回调,其实就是对应的isa 指针并没有收到调用,大家无论怎么学习,要知道原理,那么解决问题的思路就很快。

  1. 自定义一个成员方法
    - (void)mutiBlockWith:(CallBlock)call;
    我们定义了一个带有block的成员方法,我们多次调用这个方法,
    其内部实现如下:
- (void)mutiBlockWith:(CallBlock)call{
   self.block = [call copy];
   [self.blockArray addObject:self.block];  
}

这里我们通过数组将我们调用的每一个block都加入到数组中了,想必上面的已经明白了

  1. 那么我们的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
也可以一起讨论工作中的问题

你可能感兴趣的:(iOS Block 实现类似通知、一键换肤的功能)