zoukankan      html  css  js  c++  java
  • iOS

    前言

    • Block 是一段预先准备好的代码,可以在需要的时候执行,可以当作参数传递。Block 可以作为函数参数或者函数的返回值,而其本身又可以带输入参数或返回值。Block 是 C 语言的,类似于一个匿名函数,它和传统的函数指针很类似,但是 Block 是 inline(内联函数)的,并且默认情况下它对局部变量是只读的。

    • 苹果官方建议尽量多用 Block。在多线程、异步任务、集合遍历、集合排序、动画转场用的很多。

    • Block 语法

      	// Block as a local variable
      	returnType (^blockName)(parameterTypes) = ^returnType(parameters) {...};
      
      	// Block as a property
      	@property (nonatomic, copy) returnType (^blockName)(parameterTypes);
      
      	// Block as a method parameter
      	- (void)someMethodThatTakesABlock:(returnType (^)(parameterTypes))blockName;
      
      	// Block as an argument to a method call
      	[someObject someMethodThatTakesABlock: ^returnType (parameters) {...}];
      
      	// Block as typedef
      	typedef returnType (^TypeName)(parameterTypes);
      	TypeName blockName = ^returnType(parameters) {...};
      

    1、Block 的使用

    1.1 Block 的定义

    • Block 的简单定义

      	// 定义 Block
      	/*
      		定义了一个名叫 MySum 的 Block 对象,它带有两个 int 型参数,返回 int 型。等式右边就是 Block 的具体实现,大括号后需加分号
      	*/
      	int (^MySum)(int, int) = ^(int a, int b){
      	    
      		return a + b;
      	};
      	 
      	// 调用 Block
      	int sum = MySum(10, 12);
      
    • Block 数据类型的定义

      	// 定义 block 数据类型 MyBlock
      	typedef int (^MyBlock)(int, int);
      	
      	// 定义 MyBlock 的变量
      	MyBlock myblock;
      	
      	// 实现 MyBlock 的变量 1
      	myblock = ^(int a, int b){
      	
      		return a + b;
      	};
      	    
      	// 调用 MyBlock 的变量 1
      	int sum = myblock(10, 12);
      	
      	// 实现 MyBlock 的变量 2
      	myblock = ^(int a, int b){
      	
      		return a - b;
      	};
      	    
      	// 调用 MyBlock 的变量 2
      	int minus = myblock(13, 2);
      

    1.2 Block 访问局部变量

    • Block 可以访问局部变量,但是不能修改,如果要修改需加关键字 __block(双下划线)。

      	// 这样定义时,局部变量 sum 只能读取值不能修改,编译时会报错
      	// int sum = 10;
      	
      	// 这样定义时,局部变量 sum 既可以读取值又能修改
      	__block int sum = 10;
      	
      	int (^MyBlock)(int) = ^(int a){
      		
      		// 对局部变量值修改
      		sum ++;
      		
      		// 读取局部变量的值
      		return a * sum;
      	};
      	
      	int result = MyBlock(5);
      

    2、Block 的回调

    2.1 Block 回调使用

    	// Block1.h
    	
    		// block 属性变量定义
    		/*
    			要使用 copy 类型,格式:@property (nonatomic, copy) 返回值类型 (^变量名) (参数类型列表);
    		*/
    		@property (nonatomic, copy) void (^completion) (NSString *);
    		
    		// 调用 block 代码段声明
    		- (void)useBlock;
    
    	// Block1.m
    
    		// 调用 block 代码段
    		- (void)useBlock {
    			    
    			// 设置 block 的回调值
    			    
    			// 判断是否设置了 block
    			if (self.completion != nil) {
    			
    				// 设置回调值
    				self.completion(@"hello world");
    			}
    		}
    
    	// Block.m
            
    		#import "Block1.h"
    		
    		Block1 *block = [[Block1 alloc] init];
    		    
    		// 设置 block 代码段
    		block.completion = ^(NSString *str) {
    			
    			// 结果:string = @"hello world"
    			NSString *string = str;
    		};
    		    
    		// 调用 block 代码段
    		[block useBlock];
    

    2.2 Block 回调封装

    	// Block2.h
    	
    		// block 方法参数定义
    		
    		// 类方法定义
    		+ (Block2 *)blockWithCompletion:(void (^) (NSString *)) completion;
    		
    		// 调用 block 代码段声明
    		- (void)useBlock;
    
    	// Block2.m
    
    		// block 属性变量定义
    		
    		// 要使用 copy 类型,格式:@property (nonatomic, copy) 返回值类型 (^变量名) (参数类型列表);
    		@property (nonatomic, copy) void (^completion) (NSString *);
    		
    		// 调用 block 代码段
    		- (void)useBlock {
    			    
    			// 设置 block 的回调值
    			    
    			// 判断是否设置了 block
    			if (self.completion != nil) {
    			
    				// 设置回调值
    				self.completion(@"hello world");
    			}
    		}
    		
    		// 类方法实现
    		+ (Block2 *)blockWithCompletion:(void (^)(NSString *))completion {
    			    
    			Block2 *bl = [[Block2 alloc] init];
    			    
    			// 设置属性的值
    			bl.completion = completion;
    			    
    			return bl;
    		}
    
    	// Block.m
            
    		#import "Block2.h”
    		
    		// 设置 block 代码段
    		Block2 *block = [Block2 blockWithCompletion:^(NSString *str) {
    			    
    			// 结果:string = @"hello world"
    			NSString *string = str;
    		}];
    		    
    		// 调用 block 代码段
    		[block useBlock];
    

    3、Block 属性定义中为什么使用 copy 修饰

    • ARC 开发的时候,编译器底层对 block 做过一些优化,使用 copy 修饰可以防止出现内存泄漏。

    • 从内存管理的角度而言,程序员需要管理的内存只有堆区的。如果用 strong 修饰,相当于强引用了一个栈区的变量。

    • 而使用 copy 修饰,在设置数值的时候,可以把局部变量从栈区复制到堆区。

      	// 用 copy 修饰定义属性
      	@property (nonatomic, copy) void (^myTask)();
      	
      	// 定义,myBlock 是保存在栈区的,出了作用域,就应该被销毁
      	void (^myBlock)() = ^ {
      	
      		NSLog(@"hello");
      	};
      	    
      	// 用属性记录
      	self.myTask = myBlock;
      	
      	// 执行
      	self.myTask();
      

    4、循环引用

    • 在 Block 中调用 self 容易产生循环引用,无法释放对象,在程序中可以使用析构方法判断是否产生了循环引用。

      	@implementation ViewController
      	
      	// 在 Block 中调用 self 容易产生循环引用
      	[[QWebImageManager sharedManager] downloadImage:self.urlStr completion:^(UIImage *image) {
      		self.image = image;
      	}];
      	
      	@end
      
      	// 判断是否存在循环引用,无法释放时即存在循环引用
      	- (void)dealloc {                                               
      		NSLog(@"成功退出");
      	}
      
    • 可以使用关键字 __weak 声明一个弱变量,或者为属性指定 weak 特性。如:

      	@implementation ViewController
      	
      	// 弱引用 self,typeof(self) 等价于 ViewController
      	__weak typeof(self) weakSelf = self;
      	
      	[[QWebImageManager sharedManager] downloadImage:self.urlStr completion:^(UIImage *image) {
      		weakSelf.image = image;
      	}];
      	
      	@end
      
  • 相关阅读:
    Codeforces 385C
    Codeforces 496C
    HDU 6114 Chess
    Codeforces 839B
    Codeforces 483B
    Codeforces 352B
    Codeforces 768B
    Codeforces 38B
    Codeforces 735B
    Codeforces 534B
  • 原文地址:https://www.cnblogs.com/QianChia/p/5782876.html
Copyright © 2011-2022 走看看