Working with Blocks

简介

Blocks是C语言层级语法和运行时特性。 它们类似于标准C函数,但是除了可执行代码之外,它们还可以保存堆栈变量。 因此,块可以保存数据,在代码执行时使用。
1、Block可以作为函数数调用、作为函数参数、作为方法参数。
2、因为独立完整可以在多线程中使用;
3、因为拥有回调时需要执行的代码和执行代码时需要的数据,常常被用来实现回调Callback。
由于Objective-C和C++都是从C派生,因此三种语言均可以使用(Objective-C使用更多)。iOS从4.0版本开始支持。在其他语言环境中有时被称为“闭包”。

特点

Block是一个匿名的内联代码集合,有以下特点:
1、像函数一样用参数列表
2、有可推断或者声明的返回值类型
3、可以从定义它作用域捕获数据
4、可选的可以修改捕获到的数据
5、同一作用域中的其它block共享捕获的数据
6、作用域的堆栈被销毁后,仍然可以继续共享定义其范围定义的数据
由于compiler 和 runtime 保证block和其相关的数据的生命周期,因此可以copy一份传递到其他地方使用。

声明和创建

声明

首先说明下函数指针的声明格式

返回值类型 ( * 指针变量名) ([形参列表]);

Block variables保存着block的引用。可以像声明函数指针一样声明block变量,但是要把*换成^,例如以下均为有效的声明

void (^blockReturningVoidWithVoidArgument)(void);
int (^blockReturningIntWithIntAndCharArguments)(int, char);
void (^arrayOfTenBlocksReturningVoidWithIntArgument[10])(int);

Blocks支持可变的参数列表,当没有参数时必须写void。
通过为编译器提供block使用的数据,传递参数,返回值,block设计完全类型安全。可以把block引用强制转换成任意类型的指针,反之亦然。但是不能使用操作符*来访问值,因为block的值在编译期无法计算。
理解:假如每次方法调用当做一次消息发送(一般底层会有很多次),把block中的所以消息发送按照顺序放到一种能够先进先出的数据结构–比方说实现栈特性的结构体;那么block变量^{ ... }作用一样都是指向结构体的首地址;传递首地址跟其他对象指针、函数指针用法很像。

类型定义简化用法

如果经常重复使用同一个类型的block,你可以定义自己的block类型,例如

//返回值类型:float 两个参数类型:float 变量名称:MyBlockType
typedef float (^MyBlockType)(float, float);

MyBlockType myFirstBlock = // ... ;
MyBlockType mySecondBlock = // ... ;

创建

^表示block的开始,接一个返回值类型(可选,默认不写)再接一个(参数列表),后边接一个{代码块};例如

int multiplier = 7;
int (^myBlock)(int) = ^(int num) {
    return num * multiplier;
};

解读如下图所示

如果没有明确声明返回值类型,则根据block代码内容推断具体类型,例如

int multiplier = 7;

//未指明返回值类型
int (^myBlock)(int) = ^(int num) {
    return num * multiplier;
};

//指明返回值类型
int (^myBlock)(int) =  ^ int (int num) {
   return multiplier;
};

Blocks和变量

首先可以像函数一样引用三种标准类型的变量:
1、全局变量,包括静态局部变量
2、全局函数
3、作用域内的局部变量和参数
其次Blocks还支持另外两种类型:
4、__block修饰的变量
5、const导入的
所以Blocks内部处理变量的五种情况:
1、全局变量(包括作用域内的静态局部变量)可以直接访问
2、Blocks的参数可以直接访问
3、作用域内非静态局部变量,作为const变量引用(只读),强行修改编译器报错
4、被__block修饰的作用域内非静态局部变量,可以直接访问
5、Blocks内部声明的局部变量,可以直接访问
示例代码如下

int global_var = 1;

- (void)viewDidLoad {
[super viewDidLoad];


static int static_var = 2;

__block int loacal_var = 3;

__block int const_var = 3;

void (^myBlock)(int) = ^(int number) {

   global_var = global_var * 10;
   static_var = static_var * 10;
   loacal_var = loacal_var * 10;
   number     = number * 10;
   NSLog(@"局部变量:%d ,说明局部变量可以读取",const_var);
   NSLog(@"参数变量原:4 修改后%d,说明可以正常访问",number);

};
myBlock(4);

NSLog(@"全局变量原:1 修改后:%d,说明可以正常访问 ",global_var);
NSLog(@"静态变量原:2 修改后:%d,说明可以正常访问 ",static_var);
NSLog(@"__block修饰的局部变量原:3 修改后:%d,说明可以正常访问 ",loacal_var);
}

//输出结果
局部变量:3 ,说明局部变量可以读取
参数变量原:4 修改后40,说明可以正常访问
全局变量原:1 修改后:10,说明可以正常访问 
静态变量原:2 修改后:20,说明可以正常访问 
__block修饰的局部变量原:3 修改后:30,说明可以正常访问 

Blocks和对象

对象通过Properties来引用Blocks。语法和定义Blocks语法类似;例如

@interface XYZObject : NSObject
@property (copy) void (^blockProperty)(void);
@end

self.blockProperty = ^{
   ...
};
self.blockProperty();

注意:此处应该用copy,因为block需要被copy来保存引用的外界的变量,无需关心引用计数问题,编译器自动进行管理。

还可以通过自定义类型简化

typedef void (^XYZSimpleBlock)(void);

@interface XYZObject : NSObject
@property (copy) XYZSimpleBlock blockProperty;
@end

避免循环引用

当变量变成实例变量的时候,需要避免循环引用的问题。由于Blocks会持有使用的变量,比如在Viewcontroller中,block作为一个property被self持有,block中使用self调用方法,这样便造成循环引用的问题使得二者在不使用时均得不到释放。解决方法是在block中使用self的弱引用,用__weak修饰。

@interface XYZBlockKeeper : NSObject
@property (copy) void (^block)(void);
@end

- (void)configureBlock {
    XYZBlockKeeper * __weak weakSelf = self;
    self.block = ^{
        [weakSelf doSomething];   // capture the weak reference
                                  // to avoid the reference cycle
    }
}

用法

作为变量

如果声明Block作为一个变量,可以像函数一样调用。例如

int (^oneFrom)(int) = ^(int anInt) {
    return anInt - 1;
};

printf("1 from 10 is %d", oneFrom(10));
// Prints "1 from 10 is 9"

float (^distanceTraveled)(float, float, float) =
                         ^(float startingSpeed, float acceleration, float time) {

    float distance = (startingSpeed * time) + (0.5 * acceleration * time * time);
    return distance;
};

float howFar = distanceTraveled(0.0, 9.8, 1.0);
// howFar = 4.9

作为函数参数

可以像传递其它参数一样传递block。大多数的情况不需要声明,只需要作为参数以内联的方式实现。例如

char *myCharacters[3] = { "TomJohn", "George", "Charles Condomine" };

//根据首字符进行一次排序 
qsort_b(myCharacters, 3, sizeof(char *), ^(const void *l, const void *r) {
    char *left = *(char **)l;
    char *right = *(char **)r;
    //此函数表示:以参数3的长度计算前2个参数的差值
    return strncmp(left, right, 1);
});

// Block implementation ends at "}"

// myCharacters is now { "Charles Condomine", "George", "TomJohn" }

作为方法参数

Cocoa Touch 提供了很多方法使用Blocks。可以传递block作为参数使用。例如

//是否包含指定字符串
__block BOOL found = NO;
NSSet *aSet = [NSSet setWithObjects: @"Alpha", @"Beta", @"Gamma", @"X", nil];
NSString *string = @"gamma";

[aSet enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
    if ([obj localizedCaseInsensitiveCompare:string] == NSOrderedSame) {
        *stop = YES;
        found = YES;
    }
}];

// At this point, found == YES

作用

很大程度上起到了简化的作用。

简化回调

Blocks可以用处替换传统回调的原因如下:
1、方法调用和回调代码集中在一起;还常作为framework methods的参数。
2、可以直接访问局部变量。

简化枚举

对一个数组进行枚举,常用的方法有3种
1、for循环
2、for的泛型遍历for (type *object in collection)
3、使用带有block的简化方法。例如- (void)enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block;

NSArray *array = @[@"A", @"B", @"C", @"A", @"B", @"Z", @"G", @"are", @"Q"];

//for循环
for (int i = 0; i < array.count; i ++)
{

   NSString *item = array[i];
   NSLog(@"index:%d value:%@",i,item);
}

 //泛型遍历   
for (NSString *item in array)
{
   NSLog(@"value:%@",item);
}

 //枚举方法   
[array enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
   NSLog(@"index:%ld value:%@",idx,obj);

}];

简化并发任务

每个block都是独立的工作单元,包含可执行代码和保存使用的数据,这使得它能够在多线程开发中被异步调用。
系统提供了一系列的多线程编程技术,其中包括两种任务调度机制:Operation queues 和 Grand Central Dispatch(GCD)。不像Thread关注怎么样管理运行,把需要执行的任务打包到blocks中,然添加到队列中,然后交给系统根据当时的资源自动执行。

简化Operation Queues
//定义任务
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
    ...
}];

// 主线程执行
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
[mainQueue addOperation:operation];

// 后台执行
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:operation];

更多知识请阅读Operation Queues

简化Grand Central Dispatch
//获取并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

//定义任务添加到队列并执行
dispatch_async(queue, ^{
    NSLog(@"Block for asynchronous execution");
});

更多知识请阅读 Dispatch Queues

参考文献:Blocks Programming TopicsWorking with Blocks

### Split Blocks in Programming or Data Structures In programming and data structures, split blocks refer to dividing a block of elements into smaller sub-blocks based on specific criteria. This concept is closely related to handling grouped values rather than individual ones[^1]. For instance, when working with parameter blocks or structured types like records in Pascal or structures in C, splitting these blocks allows developers to process subsets independently. #### Definition and Usage Splitting blocks involves partitioning an array or structure into multiple segments according to predefined rules. The primary purpose is to facilitate easier manipulation, analysis, or transformation of large datasets by breaking them down into manageable chunks. In some cases, it aligns well with recurrent neural network architectures where sequential inputs need segmentation before further computation occurs within loops iterating over those parts[^2]. Here’s how you might implement basic functionality using Python: ```python def split_blocks(data_list, size_per_block): """Splits given list 'data_list' into equal-sized lists.""" return [data_list[i:i + size_per_block] for i in range(0, len(data_list), size_per_block)] example_data = ['a', 'b', 'c', 'd', 'e'] result = split_blocks(example_data, 2) print(result) # Output will be [['a','b'], ['c','d'], ['e']] ``` This function demonstrates simple logic behind creating separate sections out of continuous series while maintaining order among original entries during division operations performed programmatically via slicing techniques applied directly onto iterable objects such as lists here shown above. Monitoring tools play significant roles too; they provide valuable feedback regarding system behaviors under various conditions which could lead towards identifying potential bottlenecks affecting overall efficiency levels achieved after implementing any changes suggested accordingly through detailed reports generated thereafter analyzing gathered metrics effectively enhancing final outcomes reached eventually post adjustments made precisely targeting areas needing most attention identified earlier stages throughout entire lifecycle involved managing complex software systems successfully overcoming challenges faced along way ensuring optimal results obtained consistently meeting expectations set initially project commencement phase itself.[^3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值