一、代理
1、代理设计模式
代理也称“委托”,就是一件事情发生后,自己不处理,让别人去处理。其目的为了在程序直接解藕,让程序关系不是很紧密。
代理是一对一的关系
2、代理的应用场景
监听思想:当A对象想监听B对象的变化的时候,就可以让A对象成为B对象的代理
通知思想:当B对象发生了变化的时候想通知A对象,就可以让A对象成为B对象的代理
3、代理的命名规范
协议名称:控件名称+delegate
协议方法名称:控件的名称去掉前缀 + 含义
在协议方法中将自己(触发方法的)控件传出去的目的是方便用于区分那个控件出发了该方法
4、代理的代码实现
#import
@class MyView;
//1、指定代理的协议方法
@protocol MyViewDelegate
@required
//注:代理方法的返回值为视图控制器向控件发送的数据(即通知控件接收数据),控制器看做代理,视图看做委托。
- (void)myViewChangeColor:(MyView *)myView;
@end
@interface MyView : UIView
/** 2、设置代理属性 * 设置代理属性 * weak修饰符:是防止循环引用 */
@property (nonatomic, weak)id 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];
}
二、通知
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 *)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#>
};
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 *)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 *)touches withEvent:(UIEvent *)event{
//如果控制器被dismiss就会被销毁
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)dealloc{
NSLog(@"%s", __func__);
}
@end
@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
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、代理和通知的区别:代理是一对一,通知是一对多;通知没有返回值,一般没有级联关系的用通知,代理有返回值。