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;
}
相关推荐
xian_wwq6 小时前
【学习笔记】AGC协调控制系统概述
笔记·学习
憧憬成为java架构高手的小白7 小时前
docker学习笔记(基于b站多个视频学习)【未完结】
笔记·学习
辰海Coding7 小时前
MiniSpring框架学习-完成的 IoC 容器
java·spring boot·学习·架构
AGoodrMe8 小时前
swift基础之async/await
前端·ios
闫记康8 小时前
Linux学习day5
linux·chrome·学习
hhb_6189 小时前
Swift核心技术难点与实战案例解析
开发语言·ios·swift
一楼的猫9 小时前
从工具链视角对比:番茄作家助手 vs 第三方写作辅助方案
java·服务器·开发语言·前端·学习·chatgpt·ai写作
他们叫我阿冠9 小时前
实习前自我培训-Day1学习
学习
Upsy-Daisy9 小时前
AI Agent 项目学习笔记(七):RAG 高级扩展——过滤检索、PgVector 与云知识库
人工智能·笔记·学习
人月神话-Lee10 小时前
【图像处理】饱和度——颜色的浓淡与灰度化
图像处理·人工智能·ios·ai编程·swift