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;
}
相关推荐
M78佐菲1 小时前
ARM学习笔记(1)
linux·arm开发·笔记·嵌入式硬件·学习
无敌贵点大王3 小时前
RTThread学习记录11——RT-Thread 设备模型吃透:UART/ADC/PWM/PIN 到底有什么区别?
c语言·stm32·学习·链表
2501_915918414 小时前
iOS编程用什么软件好?主流工具Xcode、AppCode、CodeRunner与新兴快蝎对比
ide·vscode·ios·objective-c·个人开发·swift·敏捷流程
是翎5 小时前
深度长文|2026产品经理AI实践手册
人工智能·深度学习·学习·自然语言处理·数据挖掘
Linux-lucky5 小时前
32-38-Linux学习之旅之MySQL综合
linux·运维·学习·mysql·ubuntu
知识分享小能手6 小时前
C++ 学习教程,从入门到精通,C++ 入门知识 — 完整知识点(1)
开发语言·c++·学习
AI职业加油站9 小时前
数据要素价值释放年:大数据治理工程师,站上职业新风口
人工智能·学习·职场和发展·数据分析·职场发展
啄缘之间9 小时前
1.【SVA】什么是SVA?
经验分享·学习·测试用例·verilog·sva
sunoo-22910 小时前
【嵌入式裸机开发Day04】i.MX6ULL 蜂鸣器驱动详解:从引脚地址查找到代码实现全流程
arm开发·单片机·嵌入式硬件·学习·串口·裸机
Purple Coder10 小时前
stm32
学习