@(iOS Study)[实战技术]
- 作者: Liwx
- 邮箱: 1032282633@qq.com
目录
- 04.实战技术 block深入研究,UICollectionView的使用
- 1.block的深入研究
- block基本使用
- block开发使用场景(保存代码)
- block开发使用场景(传值)
- block内存管理(MRC)
- block内存管理(ARC)
- block循环引用
- block循环引用(复杂)
- block变量传递
- block开发使用场景(参数使用)
- block开发中使用场景(返回值)
- 2.UICollectionView的使用
- UICollectionView注意点
1.block的深入研究
block的作用: 可以用来
保存一段代码段
,也可以用来传递参数
.block如果存放代码段,该代码段并不会马上执行,需用手动调用.
- 快速生成block代码
- 输入'inlineBlock'快速生成block代码
-block类型是对象
,不是普通数据类型
- 输入'inlineBlock'快速生成block代码
block基本使用
- block的格式
// block的完整格式
block返回类型(^block变量名)(block参数) = ^(block返回类型,一般情况都省略)(block参数>) {
// block代码段
}
- block的声明方式
// block声明:返回值(^Block变量名)(block参数类型),参数变量名可以省略
void(^block)();
void(^block1)(int);
- block的定义方式
// block定义: 等号右边 ^(参数类型 参数变量名){};
void(^block2)(int a) = ^(int a){
};
// block定义二: 等号右边 ^返回值(参数类型 参数变量名){};,返回值(=号后面第一个int)可以省略,但是也有不省略
int(^block3)(int a) = ^int(int a){
return 2;
};
// block定义三: 当没有返回值,没有参数,可以省略
void(^block4)() = ^{
};
block开发使用场景(保存代码)
- 模型中block类型属性的使用
- 给模型添加一个block类型的成员属性,用来存放代码段.
- 参考代码
// 声明block类型的成员属性
@interface CellItem : NSObject
// 声明一个block类型的成员属性
@property (nonatomic, strong) void(^block)();
@property (nonatomic, strong) NSString *title;
+ (instancetype)itemWithTitle:(NSString *)title;
@end
// 给block添加代码段
CellItem *item = [CellItem itemWithTitle:@"打电话"];
item.block = ^{
NSLog(@"打电话");
};
// 执行block,需判断block是否为空,如果为空,调用时会导致程序奔溃
if (item.block) {
item.block();
}
block开发使用场景(传值)
顺传:定义属性
逆传:代理,block
,block可以用来替代代理
-
block逆传值步骤(控制器B由控制器A Modal出来的, 控制器B逆传值给控制器A)
- 1.在控制器B添加一个block属性
// 控制器B @property (nonatomic, strong) void(^blockValue)(NSString *value);
- 2.在控制器A中给控制器B的block添加代码段
// 控制器A // REMARKS: block传值(逆传) - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { ModalViewController *modalVc = [[ModalViewController alloc] init]; // 给modalVc的block属性添加代码段 modalVc.blockValue = ^(NSString *value){ NSLog(@"%@", value); }; [self presentViewController:modalVc animated:YES completion:nil]; }
- 3.在控制器B点击时调用block
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { // block逆传 if (self.blockValue) { self.blockValue(@"123"); } [self dismissViewControllerAnimated:YES completion:nil]; }
- 4.控制器B调用block时,会将@"123"传递给控制器A中的
// 控制器B调用block会调用以下代码段,并将value传递过来 modalVc.blockValue = ^(NSString *value){ NSLog(@"%@", value); };
block内存管理(MRC)
-
内存5个区:堆,栈,方法区,常量区,全局区
- 堆:手动管理内存
- 栈:自动管理,代码块一过,就会自动释放.
-
在MRC中的存储
- 如果block
没有访问外部的局部变量
或者访问被static修饰局部变量
,block默认存放在全局区
. - 如果block访问外部的局部变量,block存放在"栈"里面
// REMARKS: 测试block访问外部局部变量,block的存储 - (void)test2 { // 1.验证block的存储区域 int a = 10; void(^block)() = ^{ NSLog(@"%d", a); }; self.block = block; // ARC打印结果: <__NSMallocBlock__: 0x7fc433d0fab0> (block存储在堆区) // MRC打印结果: <__NSStackBlock__: 0x7fff51e24a18> (block存储在栈区) NSLog(@"%@", block); }
// REMARKS: 验证block的方法static修饰的局部变量,block的存储 - (void)test1 { // 1.验证block的存储区域 static int a = 10; void(^block)() = ^{ NSLog(@"%d", a); }; // ARC和MRC打印结果一样: <__NSGlobalBlock__: 0x10e1d9090> NSLog(@"%@", block); }
- 如果block
-
MRC使用block的注意点
- MRC:
不能使用retain声明block,block依然放在栈里面,会自动销毁
.如果用retain声明的属性引用block,则程序奔溃. - MRC:
使用copy声明block,才会放在堆里面
- MRC:
-
MRC开发习惯:访问属性或者设置属性,
MRC必须使用点语法
,不要使用下划线
.因为点语法会调用get方法,get方法会做引用计数器+1操作.而用下划线没有对引用计数器+1
. -
MRC:没有strong,和weak
, 只有assign, retain, copy -
区分MRC代码:
- 1.看能否调用release retain retainCount
- 2.ARC不能调用[super dealloc]
block内存管理(ARC)
-
ARC环境block的存储区
如果block访问外部的局部变量,block存放在"堆"里面 -
block原则
- 如果block没有访问外部的局部变量或者局部变量被static修饰,block默认存放在"全局区"
- ARC:使用
strong声明block
,不要使用weak.
-
ARC管理原则:默认一个局部变量对象,都是强指针,存放堆里面
block循环引用
block会对外部所有
强指针对象给强引用
,block不会对外部弱指针对象给强引用
.
-
block代码段内部使用self
- 正确用法: 使用__weak typeof(self) weakSelf = self;将self转换成弱指针.对象能正常被销毁
// 使用__weak typeof(self) weakSelf = self;将self转换成弱指针. __weak typeof(self) weakSelf = self; _block = ^{ NSLog(@"%@", weakSelf); };
- 错误用法: 直接在block内部使用self,造成循环引用,对象不会被销毁.
_block = ^{ NSLog(@"%@", self); };
block循环引用(复杂)
-
block在多线程中的应用
- block内部执行延迟操作或者异步任务需要在block内部对weakSelf做一次__strong的引用,让对象延迟释放,以确保在执行延迟操作或异步任务时,对象还没被释放.
- 以下block内部执行延迟操作,对象会延迟2秒后才释放.
- (void)blockTest { // SINGLE: block内部执行延迟操作或者异步任务需要在block内部对weakSelf做一次__strong的引用 __weak typeof(self) weakSelf = self; _block = ^{ // 需要在block内部对weakSelf做一次__strong的引用,必须用__strong强引用weakSelf,这样才能延迟释放self对象,否则会出现延迟打印结果为null,在执行延迟任务前,对象已经被释放了. __strong typeof(weakSelf) strongSelf = weakSelf; // 执行延迟操作或异步任务, dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"%@", strongSelf); }); }; // 执行block _block(); }
block变量传递
- block访问
外部局部变量没有被任何关键字修饰,都是值传递
.值传递表示block中的value不会随外部的改变而改变
.
- (void)test
{
// block访问外部局部变量没有被任何关键字修饰,都是值传递.值传递表示外部的value值改变不会影响block内部的值.
int value = 10;
void(^block)() = ^{
NSLog(@"%d", value);
};
value = 20;
block();
// 打印结果: 10
}
- block访问
外部变量是全局变量
或者被__block,static修饰
,都是指针传递
,block中的value会随外部的改变而变
.
- (void)test1
{
// block访问外部变量是全局变量或者被__block,static修饰,都是指针传递,block中的value会随外部的改变而改变.
static int value = 10;
void(^block)() = ^{
NSLog(@"%d", value);
};
value = 20;
block();
// 打印结果: 20
}
block开发使用场景(参数使用)
- block作为方法参数使用
// block当参数使用 caculatorBlock带有一个参数result,并且返回值为int类型
- (void)caculator:(int(^)(int result))caculatorBlock;
block开发中使用场景(返回值)
-
链式编程思想:把方法调用通过点语法链接,可读性非常好 (Masonry框架使用)
-
链式编程思想的简单使用,
block当返回值
- (void)test1
{
// self.add()相当于调用 add的get方法,get方法返回值是block类型的,再用返回值调用
// void(^block)() = self.add;
// block();
self.add();
}
- (void(^)())add
{
return ^{
NSLog(@"add");
};
}
-
使用block当做返回值, 实现manager.add(10).add(5)链式编程
- 1.先分析
manager.minus(3)
操作,相当于先调用get方法
,返回block类型的值,再通过返回值调用block
.
// manager.minus(3)相当于执行以下两个操作 // void(^block)(int) = manager.minus; // block(3);
- 2.由以上可推出manager的方法声明
- (void(^block)(int))minus; // 实现- (void(^block)(int))minus方法 - (void(^block)(int))minus { return ^(int value){ _result -= value; }; }
- 3.分析
manager.minus(3).minus(10)
,如果想再调用.minus(10)执行操作,manager.minus(3)返回值必须是CalculatorManager类型对象
,由此可以分析出,block的返回值不是void,而是CalculatorManager类型
.
- (CalculatorManager *(^)(int)) minus; // 实现- (CalculatorManager *(^)(int)) minus方法 - (CalculatorManager *(^)(int)) minus { return ^(int value){ _result -= value; // 下面的return表示block的返回值,返回当前对象 return self; }; }
- 4.以上操作完成,外部就能用manager.minus(3).minus(10)链式编程.
- 1.先分析
2.UICollectionView的使用
UICollectionView注意点
- UICollectionView注意点:
- 1.
初始化必须要传入布局
,(流水布局:九宫格布局) - 2.
UICollectionViewCell必须要注册
- 3.
必须自定义cell
- 1.
网友评论