`

Objective-C的Block,递归与泛型

 
阅读更多


Objective-C的Block
Apple在C,Objective-C和C++中扩充了Block这种文法的,并且在GCC4.2中进行了支持。现在我们可以在Mac 10.6和iOS 4中使用。如果是Mac 10.6 或 iOS 4.0 之前的平台,据说可以用http://code.google.com/p/plblocks/这个项目来支持Block语法。
Apple在 Snow Leopard中所用到的Grand Central Dispatch(GCD)就是基于Blocks实现的。Grand Central Dispatch是苹果开发的工具,目的是帮助开发者更容易的利用多核处理器的并行处理功能。关于Blocks以及GCD在苹果官方的介绍,请见:Introducing Blocks and Grand Central Dispatch。
你可以把它理解为函数指针,匿名函数,闭包,lambda表达式,这里暂且用块对象来表述,因为它们之间还是有些许不同的。
块对象
块对象是C级别的文法,同时也是一种运行时特征,即它允许您把函数表达式组合在一起,组合结果可作为参数传递也可保存,还可供多个线程使用。块对象的函数表达式可引用或持有局部变量。在其他的语言环境中,块对象有时也被称为closure或者lambda。如果您需创建可如数值般传递的工作单元(即代码段),则可使用块对象,它可为您提供更多的编程灵活性和更强大的功能。如需编写回调函数或对某个群体的所有项执行某种操作,也可使用块对象。
声明一个块
如果以内联方式使用块对象,则无需声明。块对象声明语法与函数指针声明语法相似,但是块对象应使用脱字符(^)而非星号指针 (*)。下面的代码声明一个aBlock变量,它标识一个需传入三个参数并具有float返回值的块。
float (^aBlock)(const int*, int, float);
创建一个块
块使用脱字符(^)作为起始标志,使用分号作为结束标志。下面的例子声明一个简单块,并且将其赋给之前声明的block变量(oneFrom)。
int (^oneFrom)(int);
oneFrom = ^(int anInt) {
    return anInt - 1;
};
结尾处的分号是标准C的行结束标志。如果未显式声明块表达式的返回值,则编译器会根据块内容自动进行推导。
块可变变量
如果某个局部变量使用__block存储修饰符,则表示块应使用此变量的引用,并可更改它的值。对变量的任何改变都只在块的语法作用域内部,以及该作用域中定义的其它块中起作用。
块对象的特性
Blocks比C++0x中的lambda表达式更强的一点是,它可以是个数组类型:
int main(void)
{
    void (^p[2])(void) = { ^(void){ puts("Hello, world!"); }, ^(void){ puts("Goodbye!"); } };
    p[0](), p[1]();
}
这里p的类型为void(^[2])(void),表示含有2个void(^)(void)块引用元素的变量。下面谈谈函数块对其外部临时变量的可访问情况。
static int global = 100;
int main(void)
{
    int local = 200;
    void (^p)(void) = ^(void){ printf("The answer is: %dn", global + local); };
    p();
}
对于FP比较熟悉的朋友可能会想到,如果一个外部变量能够随随便便被一个函数块修改的话,那么对于其本身的副作用仍然无法进行方便地多核并行编程。那么我们不妨试试看吧:
static int global = 100;
int main(void)
{
    int local = 200;
    void (^p)(void) = ^(void){
        printf("The answer is: %dn", global + local);
        global++;
        local--;    // compiling error:error: decrement of read-only variable \'local\'
    };
    p();
printf("After modified, global is: %d and local is %dn", global, local);
}
对于全局变量可以进行修改,但是对于main函数中的局部变量则不行。如果对local修改则无法通过编译。很显然,Blocks对此已经有了相应的机制。 那么我们如何能够对local进行修改呢?
static int global = 100;
int main(void)
{
    __block int local = 200;
    static int s = 10;
   
    void (^p)(void) = ^(void){
        printf("The answer is: %dn", global + local);
        global++;
        s++;
        local--;
    };
    p();
    printf("After modified, global is: %d and local is %d and s is: %dn", global, local, s);
}
这里引入了一个新的关键字 ——__block,用此声明一个局部变量可以被函数块修改。
Block是否可以递归
如果要使Blocks能够递归,那么在函数块中必须能够引用函数块的入口地址。我做了一些尝试,当函数块引用是全局的或static的,即函数块内所引用的函数 块引用变量的值在初始时就已经确定的,那么可以使用递归。
int main(void)
{
    void (^p)(int) = 0;
    static void (^ const blocks)(int) = ^(int i){ if(i > 0){ puts("Hello, world!"); blocks(i - 1); } };
    p = blocks;
    p(2);
}
如果在上述代码中将blocks前的static去掉,那么在运行时就会出错,因为blocks在被函数块引用时是未初始化值,所以调用它的话就访问了无效地址,或者所要执行的指令是未定义的。
Blocks结合泛型
由于泛型能够使我们更高效、合理地管理好自己的代码,同时也为部件化提供了许多便利之处。那么Blocks与泛型结合会产生什么新元素呢?
我们先举一个简单例子:
#import <Foundation/Foundation.h>
 
template <void pBlock(void)>
void BlockTest(void)
{
    pBlock();
}
 
void Hi(void)
{
    NSLog(@"Hi, there!");
}
 
int main(int argc, const char* argv[])
{
    BlockTest<Hi>();
}
上述代码中尚未出现Blocks,但是我们可以看到,一般的外部函数能够作为模板参数。那么Blocks是否可以这么做呢?我们不妨尝试一下:
#import <Foundation/Foundation.h>
 
template <void (^pBlock)(void)>
void BlockTest(void)
{
    pBlock();
}
 
int main(int argc, const char* argv[])
{
    BlockTest<^(void) { NSLog(@"Hi, there!"); }>();
}
编译时会在第11行出现error: no matching function for call to \'BlockTest()\'。C++标准中明确指出,模板参数必须为常量表达式,如果是函数的话必须是带有外部连接(即external-linkage)的函数指针。而Blocks表达式首先 就不是一个常量表达式,然后它也没有外部连接。我们下面看第二个例子:
#import <Foundation/Foundation.h>
 
template <typename T>
void BlockTest(void (&pBlock)(T))
{
    pBlock(T());
}
 
static void Hi(int a)
{
    NSLog(@"The value is: %dn", a);
}
 
int main(int argc, const char* argv[])
{
    BlockTest(Hi);
}
上述代码中使用了函数引用作为函数参数,然后由实参类型演绎出模板类型。这段代码将能正常地通过编译、连接并正常运行。那么我们下面再看一看 Blocks是否具有这个泛型特性:
#import <Foundation/Foundation.h>
 
template <typename T>
void BlockTest(void (^pBlock)(T))
{
    pBlock(T());
}
 
int main(int argc, const char* argv[])
{
    BlockTest(^(int a) { NSLog(@"The value is: %dn", a); });
}
编译后出现 error: no matching function for call to \'BlockTest(void (^)(int))\'。即使显式地将<int>模板实参加上也没用。也就是说Blocks的参数类型包括返回类型不能是一个泛型。我们再看第三个例子:
#import <Foundation/Foundation.h>
#include <iostream>
#include <typeinfo>
using namespace std;
 
template <typename T>
void BlockTest(T pBlock)
{
    pBlock();
    cout << "The type is: " << typeid(T).name() << endl;
}
 
static void Hi(void)
{
    NSLog(@"Hi, there!");
}
 
int main(int argc, const char* argv[])
{
 BlockTest(Hi);
}
这段代码展示了整个函数指针类型演绎出模板实参。对于目前已被很多编译器所实现的Lambda表达式,这是与泛型挂钩的唯一桥梁,那么Blocks是否具备这个特性呢?
#import <Foundation/Foundation.h>
#include <iostream>
#include <typeinfo>
using namespace std;
 
template <typename T>
void BlockTest(T pBlock)
{
    pBlock();
    cout << "The type is: " << typeid(T).name() << endl;
}
 
int main(int argc, const char* argv[])
{
    BlockTest(^(void) { NSLog(@"Hi, there!"); });
}
恭喜,我们成功了。这段代码能够正常编译和运行。各位可以自己看看输出结果。其中,类型信息是被压缩过的:F表示函数,P表示指针,v表示void类型。Blocks 与C++0x中的lambda表达式一样,必须作为一个完整的类型。对其类型做拆分进行泛型化是非法的。由于C++0x的Lambda表达式的具体类型不对程序员开放,因此它即不能作为模板形参亦无法作为模板函数的形参,但是它可以在模板函数内使用泛型。
#include <iostream>
using namespace std;
 
template <typename T>
void LambdaTest(T)
{
    T a = 100;
    auto ref = [a](const T& b) -> T { return a + b; };
    cout << "The value is: " << ref(200) << endl;
}
int main(void)
{
    LambdaTest((short)0);
}
上述代码可以在VS2010以及Intel C++ Compiler11.0通过编译并正常运行。然而比较奇怪的是Blocks在模板函数内的表现就非常不好——
template <typename T>
void BlockTest(void)
{
    void (^pBlocks)(void) = ^{};
}
int main(int argc, const char* argv[])
{
    BlockTest<void>();
}
上面这段代码中,模板函数BlockTest中pBlocks根本就没用泛型,也无法通过编译,而且报的错误是internal error: segmentation fault。而只有下面这种情况才能通过编译,但实际上是没有任何意义的:
#import <Foundation/Foundation.h>
template <typename T>
void BlockTest(void)
{
    void (^pBlock)(void) = nil;
}
 
int main(int argc, const char* argv[])
{
    BlockTest<void>();
}
可见,Blocks作为Lambda表达式而言已经是非常棒的,但与lambda函数功能相比就稍逊一些,尤其是与泛型结合时,表现得很一般。其实这也是与 Blocks的实现有关的。Blocks仍然像Objective-C的很多特性那样,主要是靠动态实现的,在编译时所花的精力较少。能够在C以及Objective-C中用上Lambda特性也很不错。
小结
Blocks在作用域,内存管理等方面具有自己独特的特性和需要留意的地方,在许多场景也很多实用的功能。虽然有时理解起来有点费劲,但是用好了,相信用好了可以大大提高代码可读性和效率等。

本文出自 “ArthurChen” 博客,请务必保留此出处http://arthurchen.blog.51cto.com/2483760/616735

分享到:
评论

相关推荐

    objc swift函数式编程+进阶 (swift 4.0)

    例如,Objective-C中的Block与Swift中的闭包有相似之处,但Swift的闭包语法更为简洁和强大,更易于在函数式编程场景下使用。 综上所述,这个主题会涵盖Swift 4.0中的函数式编程技巧,包括但不限于高阶函数的应用、...

    swifter-tips.pdf

    Swift与Objective-C和C语言高度兼容,可以轻松地在Swift项目中使用这两种语言的代码。 #### Selector Selector是Objective-C中的概念,但在Swift中同样可以使用。它可以用来表示方法签名,用于动态方法调用。 ###...

    Origin教程009所需练习数据

    Origin教程009所需练习数据

    大模型的稀疏激活方法及其高效推理应用研究:基于dReLU激活函数

    内容概要:本文提出了一个新的激活函数dReLU,用于提高大语言模型(LLM)的稀疏激活水平。dReLU可以显著减少模型推理过程中激活的参数数量,从而实现高效的模型推理。通过在Mistral-7B和Mixtral-47B模型上的实验,验证了dReLU的有效性。结果表明,使用dReLU的模型在性能上与原始模型相当甚至更好,同时减少了计算资源的需求,达到了2-5倍的推理加速。 适合人群:对深度学习、大语言模型和模型优化感兴趣的机器学习研究人员和技术开发者。 使用场景及目标:适用于需要高效推理的大语言模型应用场景,特别是资源受限的设备,如移动电话。目标是减少模型的计算资源消耗,提高推理速度。 其他说明:本文详细探讨了dReLU的设计和实验验证,提供了大量的实验数据和对比结果,展示了dReLU在多种任务上的优越表现。

    STM32F103+PWM+DMA精准控制输出脉冲的数量和频率 源程序

    最近参加一个农业机器人的比赛,由于今年的题目是蔬菜幼苗自动搬运,因此搬运部分需要用到一个三轴运动的装置,我们参考了3D打印机的原理,上面通过步进电机控制丝杆和皮带从而带动我们的抓手来抓举幼苗。因为比赛的幼苗和幼苗的基质比较小,这个过程需要精度比较高,查询了一些资料后,我想到了用dma来给STM32单片机的定时器寄存器ARR发送数据来精准控制输出pwm的数量,从而可以精准控制步进电机转动的度数,可以十分方便的计算出到某个位置需要的脉冲。

    白色大气风格的商务团队公司模板下载.zip

    白色大气风格的商务团队公司模板下载.zip

    2023-04-06-项目笔记 - 第三百五十八阶段 - 4.4.2.356全局变量的作用域-356 -2025.12.25

    2023-04-06-项目笔记-第三百五十八阶段-课前小分享_小分享1.坚持提交gitee 小分享2.作业中提交代码 小分享3.写代码注意代码风格 4.3.1变量的使用 4.4变量的作用域与生命周期 4.4.1局部变量的作用域 4.4.2全局变量的作用域 4.4.2.1全局变量的作用域_1 4.4.2.356局变量的作用域_356- 2024-12-25

    白色大气风格的个人摄影图片博客网站源码下载.zip

    白色大气风格的个人摄影图片博客网站源码下载.zip

    白色大气风格的木材加工行业网站模板下载.zip

    白色大气风格的木材加工行业网站模板下载.zip

    PCle AI加速卡在智能制造中的应用.docx

    PCle AI加速卡在智能制造中的应用.docx

    VB+SQL房地产评估系统设计(源代码+系统)(2024d7).7z

    1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于计算机科学与技术等相关专业,更为适合;

    白色风格的后台管理系统模板下载.rar

    白色风格的后台管理系统模板下载.rar

    网络热点采集与分析系统的构建及应用

    内容概要:本文详细介绍了网络热点采集系统的开发过程,包括网页抓取、数据去重、清洗、分类和可视化的各个环节。系统使用 Python 的 requests 和 BeautifulSoup 库抓取指定关键词相关的网页内容,通过集合数据结构去重,利用正则表达式清洗数据,根据关键词匹配进行分类,最后通过 matplotlib 和 wordcloud 库进行数据可视化,展示热点信息。此外,文章还介绍了多线程抓取、数据缓存、异常处理等性能优化方法,以及系统的部署和运行步骤。 适合人群:具有 Python 编程基础的开发人员和技术爱好者。 使用场景及目标:该系统适用于需要实时监控网络热点话题的个人或企业,帮助他们快速了解和分析热点信息的趋势和分布,辅助决策。 阅读建议:在学习本文时,建议读者跟随每一步代码实现,理解各个模块的设计思路和技术细节,并尝试在自己的环境中搭建和运行整个系统,以便更好地掌握网络热点采集系统的开发流程。

    白色大气风格的KTV美女麦霸网站模板下载.zip

    白色大气风格的KTV美女麦霸网站模板下载.zip

    白色简洁风的博客网站模板下载.zip

    白色简洁风的博客网站模板下载.zip

    梧州市五险一金办事指南.docx

    梧州市五险一金办事指南

    白色简洁风格的餐厅服务团队整站网站源码下载.zip

    白色简洁风格的餐厅服务团队整站网站源码下载.zip

    白色大气风格的境外游景区模板下载.zip

    白色大气风格的境外游景区模板下载.zip

    白色大气风格的美食厨师展示模板下载.zip

    白色大气风格的美食厨师展示模板下载.zip

    VB+access综合人事管理系统的设计与实现(论文+系统+开题报告+答辩PPT+摘要及目录)(2024h1).7z

    1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于计算机科学与技术等相关专业,更为适合;

Global site tag (gtag.js) - Google Analytics