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 分钟前
配置锁状态那一栏查的是什么:报告里几项各自指向什么风险
android·前端·安全·ios
努力努力再努力FFF6 分钟前
题目:2026AI 证书选购指南:不同基础、不同诉求如何抉择
学习
只愿云淡风清18 分钟前
正则表达式学习笔记-01 正则表达式的来源
笔记·学习·正则表达式
m4Rk_41 分钟前
【论文阅读】Agent 记忆机制(97):MCMA——让 Agent 学会如何抽象记忆,实现跨任务经验迁移
论文阅读·人工智能·学习·开源·github
晓德44 分钟前
7、LLM 模型是怎么训练生成的?(4)指令微调
人工智能·学习·算法·信息安全
问天_观心1 小时前
Mistral-7B QLoRA 实战(二):官方权重转换、4-bit NF4 加载与 Base Model 基线测试
人工智能·深度学习·学习·大模型·transformer
andrsted1 小时前
练题簿在线免费刷题:选择、填空、简答与排序,多种题型练出不同的掌握程度
学习·微信小程序·小程序·学习方法·刷题
Carson带你学Android1 小时前
OpenAI Dots 登陆手机端:手机上派任务,AI 在云端写代码
android·ios
乌暮1 小时前
# Spring 入门与 Spring MVC 实战指南:从零理解 IoC 到 RESTful 图书管理
java·开发语言·后端·学习·spring·mvc·restful
小弥儿1 小时前
PPT Master:用 AI 把PDF/Word一键转成可编辑 PPT
学习·开源·github