iOS中代理、通知、block的使用

一、代理

1、代理设计模式
代理也称“委托”,就是一件事情发生后,自己不处理,让别人去处理。其目的为了在程序直接解藕,让程序关系不是很紧密。
代理是一对一的关系

2、代理的应用场景
监听思想:当A对象想监听B对象的变化的时候,就可以让A对象成为B对象的代理
通知思想:当B对象发生了变化的时候想通知A对象,就可以让A对象成为B对象的代理

3、代理的命名规范
协议名称:控件名称+delegate
协议方法名称:控件的名称去掉前缀 + 含义
在协议方法中将自己(触发方法的)控件传出去的目的是方便用于区分那个控件出发了该方法


4、代理的代码实现

#import <UIKit/UIKit.h>
@class MyView;

//1、指定代理的协议方法
@protocol MyViewDelegate <NSObject>

@required
//注:代理方法的返回值为视图控制器向控件发送的数据(即通知控件接收数据),控制器看做代理,视图看做委托。
- (void)myViewChangeColor:(MyView *)myView;
@end

@interface MyView : UIView
/** 2、设置代理属性 * 设置代理属性 * weak修饰符:是防止循环引用 */
@property (nonatomic, weak)id <MyViewDelegate>delegate;

@end

 
@implementation MyView

- (void)click{
    
    //直接这样调用代理的方法,会导致程序崩溃
//    [self.delegate myViewChangeColor:self];
    //3、当发生了事情,让代理对象去做
    //判断代理是否实现了某个方法
    if([self.delegate respondsToSelector:@selector(myViewChangeColor:)]){
        [self.delegate myViewChangeColor:self];
    }

}

- (void)viewDidLoad {
    [super viewDidLoad];
    MyView *myView = [[MyView alloc]initWithFrame:CGRectMake(0, 60, 375, 500)];
    
    myView.backgroundColor = [UIColor purpleColor];
    //4、设置代理
    myView.delegate = self;
    [self.view addSubview:myView];
    
}

//5、实现代理的协议方法
- (void)myViewChangeColor:(MyView *)myView{
    
    myView.backgroundColor = [UIColor redColor];
    
}

5、代理的weak修饰符,为什么防止循环引用




二、通知

1、通知的简介

每一个应用程序都有一个通知中心(NSNotificationCenter)实例,专门负责协助不同对象之间的消息通信;
任何一个对象都可以向通知中心发布通知(NSNotification),描述自己在做什么。其他感兴趣的对象(Observer)可以申请在某个特定通知发布时(或在某个特定的对象发布通知时)收到这个通知。

2、通知的简单使用

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    //1、监听通知,通知的顺序:先监听,后发送
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reciveNote) name:@"note" object:nil];
}

// 一个对象即将销毁就会调用
- (void)dealloc
{
    //3、移除通知
    [[NSNotificationCenter defaultCenter] removeObserver:_observe];
}
@end

@implementation ZMViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
}


- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    //2、发送通知
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"%@-----", [NSThread currentThread]);
        [[NSNotificationCenter defaultCenter] postNotificationName:@"note" object:nil userInfo:nil];
    });
}

@end

3、通知在多线程的使用

接收通知代码 由 发出通知线程决定

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 监听通知:异步
    // 在异步线程,可以监听通知
    // 2.异步任务,执行顺序不确定
    // 异步:监听通知 主线程:发出通知
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
       // 异步任务
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reciveNote) name:@"note" object:nil];
    });
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    // 主线程发出通知
    [[NSNotificationCenter defaultCenter] postNotificationName:@"note" object:nil];
}

// 一个对象即将销毁就会调用
- (void)dealloc
{
    // 移除通知
    [[NSNotificationCenter defaultCenter] removeObserver:_observe];
}


// 总结:接收通知代码 由 发出通知线程决定
- (void)reciveNote
{
    // 注意:在接收通知代码中 可以加上主队列任务
    // 更新UI
    dispatch_sync(dispatch_get_main_queue(), ^{
       // 更新UI
        
    });
}


三、block

1、block的声明和定义
block也称为代码块,主要用来保存一段代码,让它在合适的时候执行

//1.block的声明:返回值(^block变量名)(参数)
    void(^block)();
    
    //2.block定义:三种定义方式
    //方式一:
    void(^block1)() = ^(){
        NSLog(@"调用了block1");
    };
    //方式二:block如果没有参数,可以省略
    void(^block2)() = ^{
        
    };
    //方式三:block的返回值可以省略,不管有没有返回值,都可以省略
    int (^block3)() = ^ int {
        return 3;
    };
    
    //3.block的类型:int(^)(NSString *)
    int (^block4)(NSString *) = ^(NSString *name) {
        return 2;
    };
    
    //4.block的调用
    block1();
    
    //5.block的快捷方式
    //直接敲:inlineblock
    <#returnType#>(^<#blockName#>)(<#parameterTypes#>) = ^(<#parameters#>) {
        <#statements#>
    };

2、block的内存管理

block是不是一个对象?
是,官方文档
 
如何判断文件是MRC或者ARC?
在dealloc方法中能否用super;能否使用retain,release
 
ARC管理原则:只要一个对象没有被一个强指针修饰,就会被销毁。默认的局部变量对象都是强指针,存放到堆里面。在ARC中基本数据类型存放在栈里面进行管理的。

 MRC开发的常识:
MRC没有strong和weak,局部变量对象相当于基本数据类型;MRC给成员属性赋值,一定要使用set方法,不能直接访问下划线成员属性赋值。


堆、栈、全局区的了解

栈区(stack):由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
     
堆区(heap):一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
     
全局区(静态区)(static):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域,程序结束后有系统释放。
     
文字常量区:常量字符串就是放在这里的,程序结束后由系统释放。
     
程序代码区:存放函数体的二进制代码。


2.1、MRC环境下

如果block没有引用外部的局部变量,block放在全局区
如果block引用了外部的局部变量,block就放在栈里面
在MRC中,block只能使用copy,不能使用retain。使用retain,则block存放在栈里面

2.2、ARC环境下

在ARC中,block用strong,最好不要用copy
在ARC环境下,block没有引用外部局部变量,block存放在全局区
在ARC环境下,block引用了外部的局部变量,block存放在堆里面,但是还是不能要weak修饰来引用block


3、block在开发中的使用

3.1、作为对象的属性,在恰当时后调用

@interface CellItem : NSObject

//设计模型:控件需要展示什么内容,就定义什么属性
@property (nonatomic, strong)NSString *title;

//保存点击每个cell需要做的事情
@property (nonatomic, strong)void (^blockName)();

+ (instancetype)itemWithTitle:(NSString *)title;

@end
- (void)viewDidLoad {
    [super viewDidLoad];
    //创建cell模型
    CellItem *item1 = [CellItem itemWithTitle:@"打电话"];
    item1.blockName = ^{
        NSLog(@"打电话");
    };
    CellItem *item2 = [CellItem itemWithTitle:@"发短信"];
    item2.blockName = ^{
        NSLog(@"发短信");
    };

    CellItem *item3 = [CellItem itemWithTitle:@"吃饭啦"];
    item3.blockName = ^{
        NSLog(@"吃饭啦");
    };

    _dataArr = @[item1, item2, item3];
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    //把要做的事情保存到模型中的block中
    CellItem *item = self.dataArr[indexPath.row];
    
    item.blockName();
}

3.2、作为方法的参数

@interface CaculorManager : NSObject

@property (nonatomic, assign)NSInteger result;

//计算
- (void)caculator: (NSInteger (^)(NSInteger num))caculatorBlock;

@end

@implementation CaculorManager

- (void)caculator: (NSInteger (^)(NSInteger num))caculatorBlock{
    if(caculatorBlock){
        _result = caculatorBlock(6);
    }
}

@end

#import "CaculorManager.h"

//什么时候需要把block当作参数去使用呢?做的事情由外界决定,但什么时候做由内部决定。
// 把block当做参数,并不是马上就调用Block,什么时候调用,由方法内部决定

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    CaculorManager *mag = [[CaculorManager alloc] init];
    [mag caculator:^NSInteger(NSInteger num) {
        num += 5;
        num *= 4;
        return num;
    }];
    NSLog(@"%ld", mag.result);
}
@end

3.3、作为方法的返回值

@interface CaculatueManager : NSObject

@property (nonatomic, assign)int result;

- (CaculatueManager * (^)(int))add;

@end

@implementation CaculatueManager

- (CaculatueManager * (^)(int))add{
    return ^(int value) {
        
        _result += value;
        
        return self;
    };
}
@end

#import "CaculatueManager.h"

/** 写的过程中不断优化代码,就会形成一种思想
 *  链式编程思想:把所有的语句用.号链接起来,好处:可读性非常好,如:Masonry框架
 */

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.test();
    
    //实现链式编程的思想
    CaculatueManager *mag = [[CaculatueManager alloc] init];
    mag.add(5).add(4).add(2);
    
    NSLog(@"%d", mag.result);
}

- (void (^)())test{
    NSLog(@"%s", __func__);
    return ^{
      NSLog(@"调用了block");
    };
}

@end

4、block造成循环引用的问题

循环引用:你引用我,我引用你,就会造成循环引用。双方都不会销毁,导致内存泄漏

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    ModalViewController *modalVc = [[ModalViewController alloc] init];
    modalVc.view.backgroundColor = [UIColor redColor];
    
    //模态出来的控制器会被rootViewController的属性强引用
//    self.presentedViewController
    
    [self presentViewController:modalVc animated:YES completion:nil];
}

@end

#import "ModalViewController.h"

@interface ModalViewController ()

@property (nonatomic, strong)void (^block)();

@end

@implementation ModalViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //block循环引用:block会对它里面的所有外部变量进行强引用一次。
    //把self包装成弱指针
    //typeof用来获取self的类型
    __weak typeof(self) weakSelf = self;
    _block = ^{
        //strongSelf是一个局部变量,在这个_block栈里面,
        //主要的作用是,防止控制器已经dismiss了,但是block里面的事情没有做
        __strong typeof(self) strongSelf = weakSelf;
        
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            
//            NSLog(@"%@", weakSelf);
            NSLog(@"%@", strongSelf);
        });
    };
    
    _block();

}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    
    //如果控制器被dismiss就会被销毁
    [self dismissViewControllerAnimated:YES completion:nil];
}

- (void)dealloc{
    NSLog(@"%s", __func__);
}

@end

5、block值传递

@implementation ViewController

int d = 9;

- (void)viewDidLoad {
    [super viewDidLoad];
    
    int a = 3;
    static int b = 5;
    __block int c = 7;
    
    //如果是给block传递局部变量,block是值传递
    //如果给block传递的是静态、全局的变量、__block修饰的局部,block是指针传递
    void (^block) () = ^{
        NSLog(@"a = %d, b = %d, c = %d, d = %d", a, b, c, d);
    };
    
    a = 5;
    b = 7;
    c = 8;
    d = 10;
    
    block();
}
@end

6、block在开发中使用的场景

block的声明和定义,直接敲"inlineBlock"
* block的作用:用于保存一份代码,等到恰当时机再去调用。
* block类型的属性,在ARC中用strong,在MRC中用copy
 
block开发中的使用场景:
1.block保存到对象中的属性,在恰当的时候使用
2.block作为方法的参数使用,block做的事情由外界觉得,但是外界不调用,方法内部调用block
3.把block作为方法的返回值,目的就是为了代替方法,封装方法内部的实现,在外界调用block.

block使用场景二:
传值:1、只要能拿到对方就能传值
 
顺传:给需要传值的对象,直接定义它属性,然后进行传值
逆传:用代理,block,在开发中就是利用block代替代理。在进行逆传需要把前一个控制器保存起来,然后逆向跳转回去时才能传值。


总结:

1、能用代理的地方都能用block吗?

2、代理和block的最大的区别:在于方法的数量上。

block的代码都在一起,非常直观;代理传递消息是为了在控制器和view之间解藕,让视图能够被多个控制器服用。如果视图只是为了封装代码,而且不被其它控制器使用,可以直接通过addTarget方式添加按钮方法,这样做的冗余度高。

3、代理和通知的区别:代理是一对一,通知是一对多;通知没有返回值,一般没有级联关系的用通知,代理有返回值。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值