iOS笔记 - 为什么block要使用copy修饰


为什么 block 使用 copy 修饰

1 - 代码示例:分别使用 assign、copy 进行验证

// - ViewController .m:创建一个 Btn,进入下一页

 1 #import "ViewController.h"
 2 #define WIDTH  [UIScreen mainScreen].bounds.size.width
 3 #define HEIGHT [UIScreen mainScreen].bounds.size.height
 4 #import "SecondViewController.h"
 5 
 6 @implementation ViewController
 7 
 8 - (void)viewDidLoad {
 9     [super viewDidLoad];
10 
11     // 进入下一页
12     UIButton *nextBT = [UIButton buttonWithType:UIButtonTypeCustom];
13     nextBT.frame = CGRectMake(40, 80, WIDTH - 80, 40);
14     [nextBT setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
15     nextBT.backgroundColor = [UIColor cyanColor];
16     nextBT.layer.masksToBounds = YES;
17     nextBT.layer.cornerRadius = 4.0;
18     [nextBT setTitleColor:[UIColor blackColor] forState:UIControlStateHighlighted];
19     [nextBT setTitle:@"click" forState:UIControlStateNormal];
20     nextBT.layer.cornerRadius = 8;
21     [nextBT addTarget:self action:@selector(clickBtn) forControlEvents:UIControlEventTouchUpInside];
22     [self.view addSubview:nextBT];
23     
24 }
25 
26 
27 // 进入下一页面
28 -(void)clickBtn{
29 
30     SecondViewController *secCtr = [SecondViewController new];
31     // pushViewController 会对对象引用计数 +1,无需管理,导航栏全权负责
32     [self.navigationController pushViewController:secCtr animated:YES];
33     
34     [secCtr release];
35     
36 }
37 
38 @end

// - SecondViewController .h

 1 #import 
 2 typedef void(^BlockColor)(void);
 3 
 4 @interface SecondViewController : UIViewController
 5 
 6 // assign
 7 @property(nonatomic,assign)BlockColor blockColor01;
 8 // copy
 9 @property(nonatomic,copy)BlockColor blockColor02;
10 
11 @property(nonatomic,copy)NSString *test03;
12 
13 @end

// - SecondViewController .m

  1 #import "SecondViewController.h"
  2 
  3 @implementation SecondViewController
  4 
  5 - (void)dealloc {
  6 
  7     NSLog(@"SecondViewController dealloc!!");
  8     [_test03 release];
  9 
 10 //    // 测试 ②:调用出了函数的栈区 block
 11 //    // crash,原因: _blockColor02 在栈区,出了方法后已经被系统收回
 12 //    self.blockColor02();
 13 
 14 
 15 //    // 测试 ③
 16 //    // 分测 a:崩溃,_blockColor02 已销毁
 17 //    self.blockColor02();
 18 //    // 分测 b:崩溃:,实质上走的 setter方法,在 setter 中再次 release 了已经销毁了的 _blockColor02
 19 //    self.blockColor02 = nil; //
 20 //    // 分测 c,不崩溃,也没任何意义
 21 //    _blockColor02 = nil;/
 22 
 23 
 24 
 25     // 测试 ④
 26     Block_release(_blockColor02);
 27     // 等价于 self.blockColor02 = nil;  或 [_blockColor02 release];
 28     // self.blockColor02 = nil; 当 self 销毁时,执行 setter 方法销毁 _blockColor02 实例
 29 
 30     [super dealloc];
 31 }
 32 
 33 // copy 特性
 34 -(void)setBlockColor02:(BlockColor)blockColor02{
 35 
 36     
 37     if (_blockColor02 != blockColor02) {
 38 
 39         NSLog(@"-------------block 点语法");;
 40         Block_release(_blockColor02);
 41         _blockColor02 = Block_copy(blockColor02);
 42     }
 43 }
 44 
 45 
 46 - (void)viewDidLoad {
 47     [super viewDidLoad];
 48 
 49 //    // 测试 ①:assign 修饰的属性且使用点语法
 50 //    SecondViewController *test1VC = self;
 51 //    NSLog(@"测试 ①:%lu",test1VC.retainCount);// N
 52 //
 53 //    self.blockColor01 = ^{
 54 //        test1VC.view.backgroundColor = [UIColor redColor];
 55 //    };
 56 //    test1VC.blockColor01();// 调用 block
 57 //    NSLog(@"测试 ①:%lu    %@",test1VC.retainCount,self.blockColor01);// N(引用计数不会+1)。   <__NSStackBlock__: 0x7ffeee58bb70>
 58 //
 59 ////    // 导航栏栈里的 subViewControllers,系统会对其自动管理,返回上级页面时自动回收
 60 ////    [test1VC release];// 触发 dealloc,同时意味着当在模拟器中返回上级页面时,程序崩溃
 61     
 62     
 63      //------------------------------------------------------------------------
 64 
 65 
 66 //    // 测试 ②:copy 修饰的属性且不使用点语法
 67 //    SecondViewController *test2VC = self;
 68 //    NSLog(@"测试 ②:%lu",test2VC.retainCount);// N
 69 //    _blockColor02 = ^(){
 70 //
 71 //        test2VC.view.backgroundColor = [UIColor redColor];
 72 //    };
 73 //    self.blockColor02();
 74 //    NSLog(@"测试 ②:%lu  %@  %@",test2VC.retainCount,_blockColor02,self.blockColor02);
 75 //    // N   <__NSStackBlock__: 0x7ffeec37db70>  <__NSStackBlock__: 0x7ffeec37db70>
 76 //    // 注意:在 dealloc 方法里调用 self.blockColor02 时会产生崩溃
 77 
 78     
 79     //------------------------------------------------------------------------
 80 
 81 
 82 //    // 测试 ③:copy 修饰的属性且走点语法
 83 //    SecondViewController *test3VC = self;
 84 //    NSLog(@"%ld",test3VC.retainCount);// N
 85 //
 86 //    test3VC.blockColor02 = ^(){
 87 //
 88 //        test3VC; // 引用了对象本身
 89 //        //test3VC.test03 = @"H";// 引用了对象的实例变量
 90 //    };
 91 //    test3VC.blockColor02();
 92 //
 93 //    // block 内部引用 对象的任一实例变量 都会导致当前的 self 引用计数 +1
 94 //    NSLog(@"测试 ③:%ld   %@",test3VC.retainCount,test3VC.blockColor02);// N + 1     <__NSMallocBlock__: 0x6000520adfd0>
 95 //    Block_release(_blockColor02);//  _blockColor02 销毁
 96 
 97 
 98 
 99     // 测试 ④:使用 __block 修饰对象,防止该对象在 block 内部引用计数 +1
100     __block SecondViewController *test4VC = self;
101     NSLog(@"测试 4:%ld",test4VC.retainCount);// N
102 
103     test4VC.blockColor02 = ^(){
104 
105         test4VC.view.backgroundColor = [UIColor grayColor];
106     };
107 
108     test4VC.blockColor02();
109     NSLog(@"测试 4:%ld     %@",test4VC.retainCount,_blockColor02); // N    <__NSMallocBlock__: 0x600051d9cfd0>
110 
111 }
112 
113 
114 
115 @end

2 - 代码示例:block 用作属性

// - LoginMarger.h

 1 #import 
 2 
 3 // 步骤 ①:使用 typedef 关键字,可以极大地简化代码
 4 typedef void(^SuccessBlock)(NSString *userName);
 5 typedef BOOL(^FailBlock)(NSString *errorMessage);
 6 
 7 @interface LoginMarger : NSObject
 8 //{
 9 //    // 声明 block 类型的实例变量
10 //    void(^_successBlock)(NSString * userName);
11 //}
12 
13 //// 如果不使用 typedef,那么码起来相当苦逼
14 //@property(nonatomic,copy)void(^successBlock)(NSString * userName);
15 //- (void)loginWithUserName:(NSString *)name passWord:(NSString *)passWord successfulBlock:(void(^)(NSString * userName))successBlock  failBlock:(BOOL (^)(NSString * errorMessage))failBlock;// 可以进行优化后,分两步骤
16 
17 
18 // 步骤 ②:把 block 声明成属性,方便在方法外部调用
19 @property(nonatomic,copy)SuccessBlock successBlock;
20 - (void)loginWithUserName:(NSString *)name passWord:(NSString *)passWord successfulBlock:(SuccessBlock)successBlock  failBlock:(FailBlock)failBlock;
21 
22 
23 @end

// - LoginMarger.m

 1 #import "LoginMarger.h"
 2 @implementation LoginMarger
 3 
 4 -(void)dealloc{
 5     
 6     Block_release(_successBlock);
 7     [super dealloc];
 8 }
 9 
10 
11 -(void)setSuccessBlock:(SuccessBlock)successBlock{
12     
13     if (_successBlock != successBlock) {
14         Block_release(_successBlock);
15         _successBlock = Block_copy(successBlock);
16     }
17 }
18 
19 
20 -(void)loginWithUserName:(NSString *)name passWord:(NSString *)passWord successfulBlock:(SuccessBlock)successBlock failBlock:(FailBlock)failBlock{
21 
22     // 简单判定:输入不为空
23     if (name.length == 0) {
24         NSLog(@"请输入账号");
25 
26     }else if(passWord.length == 0){
27         NSLog(@"请输入密码");
28 
29     }else{
30 
31         if ([name isEqualToString:@"xxx"]&&[passWord isEqualToString:@"xxx"]) {
32 
33 //            // 方式一:block 在函数内部调用
34 //            successBlock(name);
35 
36             // 方式二:block 在函数外部调用,通常把 block 声明成属性以配合使用
37             
38             self.successBlock = successBlock;// 给 block 类型的实例变量赋值,务必使用点语法,防止 block 出了该方法被销毁(copy 修饰)
39             [self callBackIt:name];// 外部调用
40 
41         }else{
42 
43             BOOL result = failBlock([NSString stringWithFormat:@"非法键入信息"]);
44             if (result) {
45                 
46                 //DoSomething...
47                 
48             }else{
49                 
50                 //DoSomething...
51             }
52         }
53     }
54 }
55 
56 // 在方法外部调用 block
57 -(void)callBackIt:(NSString*)name{
58     
59     self.successBlock(name);
60 }
61 
62 @end

// - LoginViewController.h

1 #import 
2 
3 @interface LoginViewController : UIViewController
4 
5 @property (retain, nonatomic) UITextField *nameTF;// 姓名
6 @property (retain, nonatomic) UITextField *passwordTF;// 密码
7 
8 @end

// - LoginViewController.m 

 1 #import "LoginViewController.h"
 2 #import "LoginMarger.h"
 3 #define WIDTH  [UIScreen mainScreen].bounds.size.width
 4 #define HEIGHT [UIScreen mainScreen].bounds.size.height
 5 @implementation LoginViewController
 6 -(void)dealloc{
 7 
 8     NSLog(@"销毁 %@",self);
 9     self.nameTF = nil;
10     self.passwordTF = nil;
11     [super dealloc];
12 }
13 
14 - (void)viewDidLoad {
15     [super viewDidLoad];
16 
17     // 遍历出姓名、密码控件
18     for (int i = 0; i<2; i++){
19 
20         UITextField *TF_i = [[UITextField alloc] init];
21         TF_i.frame = CGRectMake(30, 80*(i+1)+45, WIDTH-60, 45);
22         TF_i.layer.cornerRadius = 4.0f;
23         TF_i.backgroundColor = [UIColor brownColor];
24         TF_i.textColor = [UIColor blackColor];
25         [self.view addSubview:TF_i];
26 
27         if (i == 0) {
28             self.nameTF = TF_i;
29             TF_i.placeholder = @"请输入账号";
30         }else{
31             self.passwordTF = TF_i;
32             TF_i.placeholder = @"请输入密码";
33         }
34     }
35 
36     // 登录
37     UIButton *nextBT = [UIButton buttonWithType:UIButtonTypeCustom];
38     nextBT.frame = CGRectMake(100, 280, WIDTH-200, 45);
39     nextBT.backgroundColor = [UIColor redColor];
40     [nextBT setTitleColor:[UIColor yellowColor] forState:UIControlStateNormal];
41     [nextBT setTitleColor:[UIColor blackColor] forState:UIControlStateHighlighted];
42     [nextBT setTitle:@"登陆" forState:UIControlStateNormal];
43     nextBT.layer.cornerRadius = 8;
44     [nextBT addTarget:self action:@selector(makeLogin) forControlEvents:UIControlEventTouchUpInside];
45     [self.view addSubview:nextBT];
46 
47 }
48 
49 -(void)makeLogin{
50 
51     NSString *userName01 = self.nameTF.text;
52     NSString *passWord01 = self.passwordTF.text;
53     
54     LoginMarger * login = [[LoginMarger alloc] init];
55     [login loginWithUserName:userName01 passWord:passWord01 successfulBlock:^(NSString *userName) {
56 
57         NSLog(@"%@,老板在小黑屋等你",userName01);
58     } failBlock:^BOOL(NSString *errorMessage) {
59 
60         NSLog(@"%@",errorMessage);
61         return arc4random() % 2;
62     }];
63 
64 }
65 
66 
67 
68 @end