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 #import2 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 #import2 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 #import2 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