Objective-C学习 块的概念即基本语法

文章目录

块的概念

块(Block)是Objective-C对ANSIC所做的扩展, 使用块可以更好地简化Objective-C编程, 而且Objective-C的很多都API都依赖于块

块的基本语法

objective-c 复制代码
^[块返回值类型] (形参类型1 形参1, 形参类型2 形参2, ...) {
    块执行体 
}

从上面的已语法格式可以看出, 定义块的语法非常像定义一个函数, 但知识定义一个匿名函数, 但定义块与定义函数的语法格式存在如下差异:

定义块必须以^开头

定义块的返回值可以省略, 而且经常都会省略声明块的返回值类型

定义块无须指定名字

如果块没有返回值, 块无须带参数, 通常建议使用``void` 作为占位符

如果程序需要以后多次调用已经定义的块, 那么程序应该将块赋给一个块变量, 定义块变量的语法格式如下:

objective-c 复制代码
块返回值类型 (^块变量名)(形参类型1 形参1, 形参类型2 形参2, ...); 

定义块变量时, 无须声明形参名, 只要指定形参类型即可, 类似的, 如果该块不需要形参, 则建议使用 void 作为占位符

例如: 下面是有参和无参两种块的的定义和调用

objective-c 复制代码
#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 定义不带参数, 无返回值的块
        void (^printStr)(void) = ^void (void) {
            NSLog(@"我正在开始学习");
        };
        // 使用printStr调用块
        printStr();
        
        //  定义带参数, 有返回值的块
        double (^hypot)(double, double) = ^double (double num1, double num2) {
            return sqrt(num1 * num1 + num2 * num2);
        };
        // 调用块,并输出块的返回值
        NSLog(@"%g", hypot(3, 4));
        
        // 也可以先只定义块变量, 定义带参数, 无返回值的块
        void (^print)(NSString* info);
        print = ^void (NSString* info) {
            NSLog(@"info 的参数为: %@", info);
        };
        // 调用块
        print(@"hello");
    }
    return EXIT_SUCCESS;
}

块与局部变量

块可以快速访问程序中局部变量的值, 当块访问局部变量的值的时候, 不允许修改局部变量的值

objective-c 复制代码
#import<Foundation/Foundation.h>
int main(int argc, const char* argv[]) {
    @autoreleasepool {
        int my = 20;    // 定义局部变量
        void (^printmy)(void) = ^void(void) {
            my = 30; // 出错, 不可以修改
            NSLog(@"%d", my);
        };
        printmy();
    }
    return 0;
}
objective-c 复制代码
#import<Foundation/Foundation.h>
int main(int argc, const char* argv[]) {
    @autoreleasepool {
        int my = 20;    // 定义局部变量
        void (^printmy)(void) = ^void(void) {
            NSLog(@"%d", my);
        };
        my = 30; 
        printmy();
    }
    return 0;
}
objective-c 复制代码
输出: 20

上面程序仍然输出20, 而不是30, 这是因为当程序使用块访问局部变量的时候, 系统在定义块的时候就会把局部变量的值保存在块中, 而不是等到执行的时候在去访问, 获取变量的值. 上面的程序虽然将my变量的值赋值为30, 但这条赋值语句位于块定义之后, 因此, 在块定义中my变量的值已经固定为20, 后面程序对my变量进行修改后, 对块不存在任何影响

如果不想要在定义块的时候就把局部变量的值复制到块中, 而是等到执行的时候再去访问,获取局部变量的值, 甚至希望块也可以改变局部变量的值, 此时可以考虑使用__block(注意这里block前面是两个"_")修饰局部变量

__block修饰后可以在块内修改局部变量的值

objective-c 复制代码
#import<Foundation/Foundation.h>
int main(int argc, const char* argv[]) {
    @autoreleasepool {
        // 用__block修饰局部变量
        __block int my = 20;
        void (^printmy)(void) = ^void (void) {
            my = 30;
            NSLog(@"%d", my);
        };
        printmy();
        my = 45;
        printmy(); 
    }
    return 0;
}
objective-c 复制代码
输出 : 30
       30

在调用块的时候再去获取局部变量的值

objective-c 复制代码
#import<Foundation/Foundation.h>
int main(int argc, const char* argv[]) {
    @autoreleasepool {
        // 用__block修饰局部变量
        __block int my = 20;
        void (^printmy)(void) = ^void (void) {
            NSLog(@"%d", my);
        };
        printmy();
        my = 45;
        printmy();
    }
    return 0;
}
objective-c 复制代码
输出: 20 
    45

直接使用块作为参数

当把块作为方法参数的时候, 无须定义块变量, 直接把块作为参数传给指定的方法即可,需要指出的是, 块只能作为方法的最后一个参数

使用typedef定义块变量类型

使用typedef可以定义块变量类型, 一旦定义了块变量类型, 该块变量主要有两个用途:

  • 复用块类型变量, 使用块变量类型可以重复定义多个块变量
  • 使用块变量类型定义函数参数, 这样即可定义带块参数的函数
objective-c 复制代码
#import<Foundation/Foundation.h>
int main(int argc, const char* argv[]) {
    @autoreleasepool {
        typedef void (^FKPrintBlock) (NSString*);
        FKPrintBlock print = ^void(NSString* info) {
            NSLog(@"%@", info);
        };
        
        FKPrintBlock loopPrint = ^void(NSString* info) {
            for (int i = 0; i < 3; i++) {
                NSLog(@"%@", info);
            }
        };
        print(@"hello world");
        loopPrint(@"hehe"); 
    }
    return 0;
}

利用typedef定义的块变量类型即可为函数声明块变量类型的形参, 这要求调用函数时必须传入块变量

objective-c 复制代码
#import<Foundation/Foundation.h>
    typedef void (^FKPrintBlock)(int);
    // 使用FKPrintBlock定义最后一个参数类型为块
    void processArray(int array[], unsigned int len, FKPrintBlock process) {
        for (int i = 0; i < len; i++) {
            process(array[i]);  // 将数组元素作为参数调用块
        }
    }
    int main(int argc, const char* argv[]) {
        @autoreleasepool {
            int arr[] = {2, 3, 4};
            processArray(arr, 3, ^(int num) {
                NSLog(@"元素平方为: %d", num * num);
            });
        }
        return 0;
}
相关推荐
宇文诸2 小时前
jmeter-beanshell学习17-上传文件接口删请求头
学习·jmeter
观书喜夜长2 小时前
web网络安全-每日一练-Training-WWW-Robots
学习·web安全
Briue7Rua2 小时前
关于使用Visual Studio编译时产生的编码问题与解决方案
c语言·ide·utf-8·学习·visual studio·gbk
·中年程序渣·2 小时前
Spring AI Alibaba入门学习(二)
学习
deng-c-f2 小时前
Linux C/C++ 学习日记(70):grpc(三):基于grpc编写同步的server、client
网络协议·学习·rpc
HalvmånEver2 小时前
Linux:初始网络(下)
linux·运维·网络·学习
2501_915918412 小时前
苹果应用开发编译流程,用快蝎(kxapp)工具完成 iOS 构建与调试
ide·vscode·ios·objective-c·个人开发·swift·敏捷流程
开心就好20253 天前
iOS App 安全加固流程记录,代码、资源与安装包保护
后端·ios