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;
}
相关推荐
nashane1 小时前
HarmonyOS 6学习:画中画(PiP)状态同步与场景化实战指南
学习·pip·harmonyos·harmonyos 5
_李小白1 小时前
【android opencv学习笔记】Day 8: remap(像素位置重映射)
android·opencv·学习
勤劳的进取家1 小时前
数据链路层基础
网络·学习·算法
美狐美颜SDK开放平台1 小时前
多场景美颜SDK解决方案:直播APP(iOS/安卓)开发接入详解
android·人工智能·ios·音视频·美颜sdk·第三方美颜sdk·短视频美颜sdk
d111111111d1 小时前
直流电机位置式 PID 控制 和 舵机的区别
笔记·stm32·单片机·嵌入式硬件·学习
y = xⁿ3 小时前
Redis八股学习日记:布隆过滤器
数据库·redis·学习
wuxianda10303 小时前
苹果App上架4.3a被拒解决方案汇报总结
ios·uni-app·objective-c·cocoa·苹果上架·4.3a
Cho1yon3 小时前
【第15期:车机CarPlay使用中语音唤醒失效问题分析与解决方案】
macos·车载系统·objective-c·cocoa
d111111111d4 小时前
了解Modbus
网络·笔记·stm32·单片机·嵌入式硬件·学习
charlie1145141914 小时前
通用GUI编程技术——图形渲染实战(三十八)——顶点缓冲与输入布局:GPU的第一个三角形
开发语言·c++·学习·图形渲染·win32