1.Block的应用场景
代理-协议(一对一) 通知(一对多) Block(一对一)
三种通信方式都实现了对象之间的解耦合
但是三者的耦合性不一样,通知的代码可读性不高
2.block的用法
//
声明一个
Block
int (^myBlock)( int , int );
// 定义
myBlock = ^( int a, int b){
return a + b; // 不会马上执行这行代码,而是等到下边调用后,才会去执行这行代码,所以称之为回调
};
// 调用
int (^myBlock)( int , int );
// 定义
myBlock = ^( int a, int b){
return a + b; // 不会马上执行这行代码,而是等到下边调用后,才会去执行这行代码,所以称之为回调
};
// 调用
result = myBlock(10, 20);
//
用
typedef
简化在方法中写
block
typedef int (^MyBlock)( int , int );
@interface Person : NSObject
// 声明一个返回值是 int 型的方法,带有 int 型的 block 参数, block 带有两个整型的参数
- ( int )personMedon:( int (^)( int , int ))block;
// 声明一个返回值是 void 的方法,带有 void 的 block 参数, block 的参数也为 void
- ( void )personMedon1:( void (^)())block;
// 用 typedef
typedef int (^MyBlock)( int , int );
@interface Person : NSObject
// 声明一个返回值是 int 型的方法,带有 int 型的 block 参数, block 带有两个整型的参数
- ( int )personMedon:( int (^)( int , int ))block;
// 声明一个返回值是 void 的方法,带有 void 的 block 参数, block 的参数也为 void
- ( void )personMedon1:( void (^)())block;
// 用 typedef
- (int)personMedon2:(MyBlock)block;
一般把block的调用和声明放在一个类中,实现放在外部,这样能更好的实现回调功能
//
可以直接拿到
typedef
的
block(在类中声明过的block)
MyBlock
myblock = ^(
int
a,
int
b){
return a + b;
return a + b;
};
3.block代码块可以引用全局变量和局部变量,但有不同的管理机制
//
局部的基本数据类型变量,进入
block
中,
num
会变成常量
,
如果需要在
block
中对
num
进行修改,需要加上关健字
__block
(全局的不用)
__block
int
num =
10
;
MyBlock myblock= ^{
NSLog ( @"%d" , num);
num++; // 如果不加上 __block ,是错误的,不能对常量进行自加操作
};
num += 10 ;
myblock();
NSLog ( @"%d" , num);
// 局部的 OC 对象进入 block 时,该对象会被 retain 一次 ---block 持有它 ( 注意: block 在堆区上时会起到 retain 的作用 )
// 加上 __block 会避免 block 对对象的持有 , 不会 retain
__block NSObject *object = [[ NSObject alloc ] init ];
MyBlock block = ^{
NSLog ( @"%ld" , object. retainCount );
};
// block 创建后的内存是分配在栈区上的,调用 copy, 会将 block 从栈移到堆上
[block copy ]; //copy 到堆区
[object release ];
MyBlock myblock= ^{
NSLog ( @"%d" , num);
num++; // 如果不加上 __block ,是错误的,不能对常量进行自加操作
};
num += 10 ;
myblock();
NSLog ( @"%d" , num);
// 局部的 OC 对象进入 block 时,该对象会被 retain 一次 ---block 持有它 ( 注意: block 在堆区上时会起到 retain 的作用 )
// 加上 __block 会避免 block 对对象的持有 , 不会 retain
__block NSObject *object = [[ NSObject alloc ] init ];
MyBlock block = ^{
NSLog ( @"%ld" , object. retainCount );
};
// block 创建后的内存是分配在栈区上的,调用 copy, 会将 block 从栈移到堆上
[block copy ]; //copy 到堆区
[object release ];
block();
block在栈区上是不安全的
4.Block造成的循环引用
①第一种情况
_myObject
= [[
NSObject
alloc
]
init
];
// 中间变量 1
// NSObject *obj = _myObject;
// 中间变量 2
__block Person *ps = self ;
// 这里使用的 self 点语法,对 MyBlock 自动 copy
//self->_myblock->self 循环引用
self . myblock = ^{
// 1. 这里不会对 _myObject 进行 retain 而是对 Person 对象 self 进行 retain
// NSLog(@"_myObject.retainCount is %ld", _myObject.retainCount);
//2. 所以需要引用中间变量不让 myblock 持有 self
// NSLog(@"_myObject.retainCount is %ld", obj.retainCount);
//3. 更加优化中间变量
NSLog ( @"_myObject.retainCount is %ld" , ps-> _myObject . retainCount );
// 中间变量 1
// NSObject *obj = _myObject;
// 中间变量 2
__block Person *ps = self ;
// 这里使用的 self 点语法,对 MyBlock 自动 copy
//self->_myblock->self 循环引用
self . myblock = ^{
// 1. 这里不会对 _myObject 进行 retain 而是对 Person 对象 self 进行 retain
// NSLog(@"_myObject.retainCount is %ld", _myObject.retainCount);
//2. 所以需要引用中间变量不让 myblock 持有 self
// NSLog(@"_myObject.retainCount is %ld", obj.retainCount);
//3. 更加优化中间变量
NSLog ( @"_myObject.retainCount is %ld" , ps-> _myObject . retainCount );
};
②第二种情况
//
第二种循环引用
__block Person *person = [[Person alloc] init];
//
这里持有了
person,
所以要在
Person
之前加上
__block
person. myblock = ^{
[person testFun1 ];
};
person. myblock ();
person. myblock = ^{
[person testFun1 ];
};
person. myblock ();
[person release];
5.ARC下的循环引用
//
引入中间变量来解决
block
加上
__block
的循环引用问题
//
在
ARC
下,使用
__block
关健字不能解决循环引用的问题,因为即使使用了
__block
关健字,它仍旧是一个
strong
类型的对象,进入到
block
块时,仍旧被
block
持有,这个时候
__block
关健字起的作用仅仅只是表示该指针变量进入
block
块变成一个可修改的变量
// 所以要使用 __weak 修饰
// 所以要使用 __weak 修饰
__weak RootViewController *
weakVC
= self;
//
创建
button
BlockButton *btn = [[ BlockButton alloc ] initWithFrame : CGRectMake ( 100 , 100 , 200 , 50 ) WithBlock :^( UIButton *btn) {
// 通常情况下,在 block 块中,我们再将 __weak 对象转换成一个 strong 对象,为了更方便拿到自身的成员变量 (_weak 情况下也可以使用 @property 方法 )
__strong RootViewController *strongSelf = weakSelf;
BlockButton *btn = [[ BlockButton alloc ] initWithFrame : CGRectMake ( 100 , 100 , 200 , 50 ) WithBlock :^( UIButton *btn) {
// 通常情况下,在 block 块中,我们再将 __weak 对象转换成一个 strong 对象,为了更方便拿到自身的成员变量 (_weak 情况下也可以使用 @property 方法 )
__strong RootViewController *strongSelf = weakSelf;
SecondViewController *
weakVC
= [[SecondViewController alloc] init];
// weakVC.age = 10;
//
这里必须还要用
->
指向,否则还会持有
strongVC
strongVC -> _age = 10;
[strongSelf.navigationController pushViewController:secondVC animated:YES];
}
6.
复写父类的带多参数的初始化方法,得使用到系统
C
语言里边的库函数
#import
<stdarg.h>
- (
instancetype
)initWithTitle:(
NSString
*)title message:(
NSString
*)message delegate:(
id
)delegate cancelButtonTitle:(
NSString
*)cancelButtonTitle otherButtonTitles:(
NSString
*)otherButtonTitles, ...
{
self = [ super initWithTitle :title message :message delegate : self cancelButtonTitle :cancelButtonTitle otherButtonTitles :otherButtonTitles, nil ];
if ( self ) {
//..
va_list ap; // 定义一个 va_list 指针访问参数表
if (otherButtonTitles) {
va_start (ap, otherButtonTitles); // 初始化 ap, 让它指向第一个参数
id test;
// 定义一个可变数组用来装 test
NSMutableArray *mArr = [ NSMutableArray array ];
while ((test = va_arg (ap, id ))) { // 取出参数列表中指针所指向的下一个参数,只要一直有参数就一直循环
//button 的名字就是参数的名字
[ self addButtonWithTitle :test];
[mArr addObject :test];
}
NSLog ( @"mArr %@" , mArr);
}
// 停止
va_end (ap);
}
return self ;
}
{
self = [ super initWithTitle :title message :message delegate : self cancelButtonTitle :cancelButtonTitle otherButtonTitles :otherButtonTitles, nil ];
if ( self ) {
//..
va_list ap; // 定义一个 va_list 指针访问参数表
if (otherButtonTitles) {
va_start (ap, otherButtonTitles); // 初始化 ap, 让它指向第一个参数
id test;
// 定义一个可变数组用来装 test
NSMutableArray *mArr = [ NSMutableArray array ];
while ((test = va_arg (ap, id ))) { // 取出参数列表中指针所指向的下一个参数,只要一直有参数就一直循环
//button 的名字就是参数的名字
[ self addButtonWithTitle :test];
[mArr addObject :test];
}
NSLog ( @"mArr %@" , mArr);
}
// 停止
va_end (ap);
}
return self ;
}