iOS——Block one

这篇具有很好参考价值的文章主要介绍了iOS——Block one。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

块类似于匿名函数或闭包,在许多其他编程语言中也存在类似的概念。
可以访问上下文,运行效率高

Block

以下是块的一些基本知识:

  1. 块的定义:块是由一对花括号 {} 包围的代码片段,可以包含一段可执行的代码。块的定义使用 ^ 符号,并可以带有参数列表和返回类型。例如:
^{
    // 代码块的内容
}

  1. 块的类型:块也是一种数据类型,与函数类似。它们可以具有参数和返回值类型。可以使用 typedef 来定义块的类型。例如:
typedef returnType (^BlockTypeName)(parameterTypes);

其中 returnType 是块的返回类型,BlockTypeName 是块的类型名称,parameterTypes 是块的参数类型。
3. 块的赋值和调用:块可以赋值给变量,并且可以像函数一样进行调用。可以使用 = 运算符将块赋值给变量,然后使用该变量调用块。例如:

ReturnType (^blockName)(ParameterTypes) = ^ReturnType (Parameters) {
    // 块的内容
};
blockName(argumentValues); // 调用块

  1. 块的捕获变量:块可以捕获其定义范围内的变量,并在块内部访问这些变量。捕获的变量在块中形成了一个闭包,可以在块的[[生命周期]]内保持其状态。例如:
NSInteger outsideVariable = 10;
void (^block)(void) = ^{
    NSLog(@"Outside variable: %ld", (long)outsideVariable);
};
block(); // 输出:Outside variable: 10

在这个例子中,块捕获了外部的 outsideVariable 变量,并在块内部访问它。
默认情况下,为块所捕获的变量是不可以在块里面修改的,如果修改了outsideVariale的值,就会报错,声明变量的时候可以加上__block修饰符,这样就可以在块内修改了
外部变量如果是数组那种的话是可以使用方法来给数组添加内容的,因为此举没有改变对象原本的地址
iOS——Block one,ios,cocoa,macos

  1. 块作为参数:块可以作为方法或函数的参数进行传递,从而实现回调和异步操作等功能。可以将块作为参数声明,并在调用方法或函数时传递块。例如:
- (void)performOperationWithCompletion:(void (^)(void))completionBlock {
    // 执行操作
    // 操作完成后调用块
    completionBlock();
}

// 调用方法,传递块作为参数
[self performOperationWithCompletion:^{
    NSLog(@"Operation completed!");
}];

在这个例子中,performOperationWithCompletion: 方法接受一个块作为参数,并在操作完成后调用该块。

#pragma mark **--修改为块所捕获的变量**

    NSArray *array = @[@0, @1, @2, @3, @4, @5];

    **__block** NSInteger count = 0;

    //块作为方法的参数

    [array enumerateObjectsUsingBlock:^(NSNumber *number, NSUInteger idx, **BOOL** *stop) {

            **if** ([number compare:@2] == NSOrderedAscending) {

                count++;

            }

        NSLog(@"%ld====%@",(**unsigned** **long**)idx, number);

    }];

    NSLog(@"%ld", (**long**)count);

    //内联块的用法,传给“numberateObjectsUsingBlock:"方法的块并未先赋给局部变量,而是直接在内联函数中调用了,如果块所捕获的变量类型是对象类型的话,那么就会自动保留它,系统在释放这个块的时候,也会将其一并释放。这就引出了一个与块有关的重要问题,块本身可以视为对象,在其他oc对象能响应的选择子中,很多块也可以响应,最重要的是,块本身也会像其他对象一样,有引用计数,为0时,块就回收了,同时也会释放块所捕获的变量,以便平衡捕获时所执行的保留操作

    //如果块定义在oc类的实例方法中,那么除了可以访问类的所有实例变量之外,还可以使用self变量,块总能修改实例变量,那么除了声明时无需添加__block。不过,如果通过读取或写入操作捕获了实例变量,那么也会自动把self给捕获了,因为实例变量是与self所指代的实例关联在一起的。

    // 需要注意的是(self也是一个对象,也会被保留),如果在块内部使用了实例变量,块会自动对self进行保留操作,以确保在块执行期间保持对象的有效性。但是,如果在块内部直接使用了self,并对其进行读取或写入操作,那么self也会被捕获,从而导致循环引用的问题。为了避免循环引用,可以在块内部使用__weak修饰符来避免对self进行保留操作。

如果某个实例在执行anInstanceMethod放法,那么self变量就会指向此实例。由于块里没有明确使用self变量,所以很容易就会忘记self变量其实也为块所捕获了。直接访问实例遍历和通过self来访问时等效的:
self->_anInstanceVariable = @“someThing”;

typedef void(^SomeBlock) (void);
@property (nonatomic, copy) BlockName someBlock;
`- (void)anInstanceMethod {
     self.someBlock = ^ {
          _anInstanceVariable = @"someThing";
     }
     
 }

self也是个对象,因而块在捕获它时也会将其保留。如果self所指代的那个对象同时也保留了块,那么这种情况就会导致“保留环”
修改为以下代码即可:

typedef void(^SomeBlock) (void);
@property (nonatomic, copy) BlockName someBlock;
__weak typeof(self)weakSelf = self;
`- (void)anInstanceMethod {
     self.someBlock = ^ {
          weakSelf.anInstanceVariable = @"someThing";
     }
     
 }

块的本质

带有自动变量(局部变量)的匿名函数。

  • block本质上也是一个OC对象,它内部也有个isa指针
  • block是封装了函数调用以及函数调用环境的OC对象
  • block是封装函数及其上下文的OC对象
  • !iOS——Block one,ios,cocoa,macos

block变量捕获

为了保证block内部能够正常访问外部的变量,block有个变量捕获机制
iOS——Block one,ios,cocoa,macos

block变量与c语言变量完全相同,可以作为以下用途:

  • 自动变量
  • 函数参数
  • 静态变量
  • 静态全局变量
  • 全局变量

BLOCK的三种类型

block的类型,取决于isa指针,可以通过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型

__ NSGlobalBlock __ ( _ NSConcreteGlobalBlock ) 对象存储在数据区
__ NSStackBlock __ ( _ NSConcreteStackBlock ) 对象存储在栈区
__ NSMallocBlock __ ( _ NSConcreteMallocBlock )对象存储在堆区

iOS——Block one,ios,cocoa,macos

捕获了自动变量block就是栈类型
没有捕获就是数据区
不存在一创建就在堆区的,堆区的意义可以理解为和autorelease一样:延长作用域 Stack类型的Block进行了copy操作之后变成了堆区

  • 堆:动态分配内存,需要程序员自己申请,程序员自己管理
  • 栈:自动分配内存,自动销毁,先入后出,栈上的内容存在自动销毁的情况

NSGlobalBlock&NSStackBlock&NSMallocBlock

如果一个block没有访问外部局部变量,或者访问的是全局变量,或者 静态局部变量,此时的blcok就是一个全局block,并且储存在全局区

- (**void**)NSGlobalBlock {

    //block1没有引用到局部变量

    **int** a = 10;

    **void** (^block)(**void**) = ^{

         NSLog(@"hello world");

    };

    NSLog(@"block:%@", block);

  

    //    block2中引入的是静态变量

    **static** **int** a1 = 20;

    **void** (^block1)(**void**) = ^{

        NSLog(@"hello - %d",a1);

    };

  

    NSLog(@"block:%@", block);

  

  

}

- (**void**)NSStackBlock {

    **__block** **int** a = 10;

    **static** **int** a1 = 20;

    **void** (^**__weak** block)(**void**) = ^{

        NSLog(@"hello - %d",a);

        NSLog(@"hello - %d",a1);

    };

    NSLog(@"block:%@", block);

  

}

- (**void**)NSMallocBlock {

    **int** a = 10;

    **void** (^block1)(**void**) = ^{

        NSLog(@"%d",a);

    };

    NSLog(@"block1:%@", block1);

  

    **__block** **int** b = 10;

    **void** (^block2)(**void**) = ^{

        NSLog(@"%d",b);

    };

  

    NSLog(@"block2:%@", block2);

  

}

//block继承与nsobject

- (**void**)blockFromNSObject {

    **void** (^block1)(**void**) = ^{

        NSLog(@"block1");

    };

    NSLog(@"%@",[block1 class]);

    NSLog(@"%@",[[block1 class] superclass]);

    NSLog(@"%@",[[[block1 class] superclass] superclass]);

    NSLog(@"%@",[[[[block1 class] superclass] superclass] superclass]);

    NSLog(@"%@",[[[[[block1 class] superclass] superclass] superclass] superclass]);

  

}

iOS——Block one,ios,cocoa,macos

  • 上述代码输出了block1的类型,也证实了block是对象,最终继承NSObject

栈区block和堆区block的区别

- (**void**)diffStackAndHip {

    **__block** **int** a = 10;

    **__block** **int** b = 20;

    NSLog(@"a:%p---b:%p", &a, &b);

    **void** (^**__weak** block)(**void**) = ^{

        NSLog(@"hello - %d---%p",a, &a);

        a++;

    };

    **void** (^block1)(**void**) = ^{

        NSLog(@"hello - %d---%p",b, &b);

        b++;

    };

    block();

    block1();

    NSLog(@"block:%@---block1:%@", block, block1);

    NSLog(@"a:%d---b:%d", a, b);

    NSLog(@"a:%p---b:%p", &a, &b);
}

iOS——Block one,ios,cocoa,macos

  • 通过结果我们看到,首先block的地址是在栈区,而block1的地址是在堆区,而栈block引用的变量a的地址并没有变化,而堆block1引用的变量b的地址也相应变成了堆区`0x6,并且后面使用的b的地址都是堆区上的。
    #栈block存放在栈区,对局部变量引用只拷贝局部变量的地址,而堆block存放在堆区,并且直接将局部变量拷贝了一份到堆空间。
- (**void**)diffStackAndHip2 {

    NSObject *objc = [NSObject new];

    NSLog(@"%@---%ld",objc, CFGetRetainCount((**__bridge** CFTypeRef)(objc)));// 1

    // block 底层源码

    // 捕获 + 1

    // 堆区block

    // 栈 - 内存 -> 堆  + 1

    **void**(^strongBlock)(**void**) = ^{ // 1 - block -> objc 捕获 + 1 = 2

        NSLog(@"%@---%ld",objc, CFGetRetainCount((**__bridge** CFTypeRef)(objc)));

    };

    strongBlock();

  

    **void**(^**__weak** weakBlock)(**void**) = ^{ // + 1

        NSLog(@"%@---%ld",objc, CFGetRetainCount((**__bridge** CFTypeRef)(objc)));

    };

    weakBlock();

    **void**(^mallocBlock)(**void**) = [weakBlock copy];

    mallocBlock();

  

}

iOS——Block one,ios,cocoa,macos

奇怪为什么堆区block里面的对象引用计数加2呢?而后面的mallocBlock只加1呢?
首先objc在strongBlock里面必然会拷贝一份到堆区,所以会加1,但是他是从当前函数的栈区拷贝吗?并不是,对于堆区Block一开始编译时是栈block这时候objc对象地址拷贝了一份引用计数加1,后面从栈block变成堆block,又拷贝了一份引用计数又加1,所以这时候是3,weakBlock是栈block仅拷贝了一份,所以引用计数加1,这时候是4,mallocBlock从weakblock拷贝了一份,所以引用计数再加1,这时候是5,相当于strongBlock = weakblock + void(^mallocBlock)(void) = [weakBlock copy];

- (**void**)diffStackAndHip3 {

    NSObject *a = [NSObject alloc];

    NSLog(@"1---%@--%p", a, &a);

    **void**(^**__weak** weakBlock)(**void**) = **nil**;

    {

        // 栈区

        **void**(^**__weak** strongBlock)(**void**) = ^{

            NSLog(@"2---%@--%p", a, &a);

        };

        weakBlock = strongBlock;

        strongBlock();

        NSLog(@"3 - %@ - %@",weakBlock,strongBlock);

    }

    weakBlock();

    NSLog(@"4---%@--%p", a, &a);

  

}

iOS——Block one,ios,cocoa,macos

  • 当前是栈区strongBlock的赋值给外面的栈区weakBlock因为都是存放在栈空间的,只有当前函数结束才会被销毁,随意这边weakBlock调用并不会有什么问题。如果换成堆区block就不一样了。
  • 这边的a对象在weakBlock()调用时是nil,通过上面打印可以看出a对象在进入到strongblock里,&a拷贝了一份,拷贝的这一份地址指向的跟外面一样,但是当strongblock出了{}刚才复制的对象就要销毁了,(栈区的对象在函数结束的时候就会被销毁,函数本身会在作用域结束的时候被销毁)尽管strongblock对象不再了,但是其指向的内存空间还在,销毁之前给了外面的weakBlock,同理a也一样,对象(此时a指向的内容)不在了,但是内存空间却还在
    #这边建议打断点看一看运行流程,方便理解
- (**void**)diffStackAndHip4 {

    NSObject *a = [NSObject alloc];

    NSLog(@"1---%@--%p", a, &a);

    **void**(^**__weak** weakBlock)(**void**) = **nil**;

    {

        // 栈区

        **void**(^**__strong** strongBlock)(**void**) = ^{

            NSLog(@"2---%@--%p", a, &a);

        };

        weakBlock = strongBlock;

        strongBlock();

        NSLog(@"3 - %@ - %@",weakBlock,strongBlock);

    }

//  weakBlock();//测试的时候取消注释

    NSLog(@"4---%@--%p", a, &a);

  

}

iOS——Block one,ios,cocoa,macos文章来源地址https://www.toymoban.com/news/detail-627172.html

  • 为什么呢?因为在{}里面的堆区strongBlock出了大括号就会被销毁,此时你去调用这个block就会崩溃
  • 注意:这边weakBlock为什么也是__NSMallocBlock__,其实weakBlock相当于是指针,此时指向的是一个堆上的内存所以是__NSMallocBlock__
    iOS——Block one,ios,cocoa,macos

到了这里,关于iOS——Block one的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • iOS——Block two

    Block  的实质究竟是什么呢?类型?变量?还是什么黑科技?  Blocks 是  带有局部变量的匿名函数 在项目中添加 blocks.m 文件,并写好 block 的相关代码。 打开「终端」,执行  cd XXX/XXX  命令,其中  XXX/XXX  为 block.m 所在的目录。 继续执行 clang -rewrite-objc block.m 执行完命令之

    2024年02月14日
    浏览(26)
  • iOS——Block签名

    首先来看block结构体对象 Block_layout (等同于clang编译出来的 __Block_byref_a_0 ) 其中 Block_layout 是基础的block结构空间,而部分block则拥有 Block_descriptor_2 和 Block_descriptor_3 结构,其中的 flags 标识记录了一些信息 第1位:释放标记,一般常用BLOCK_NEEDS_FREE做位与操作,一同传入flags,

    2024年02月14日
    浏览(41)
  • iOS block以及变量捕获

    Block是什么 Block也是一个OC对象,内部也有isa指针。 扩展: instance对象的isa指向class对象 class对象的isa指向meta-class对象 meta-class对象的isa指向基类的meta-class对象 Block的类型 __NSGlobalBlock__(_NSConcreteGlobalBlock)(存放在数据区) (不访问auto变量的block 即便是访问了static局部变量 或者全

    2024年01月17日
    浏览(36)
  • 利用scrapy框架对etherscan.io中给定Block范围内的交易信息的爬取

    一、 背景介绍 Etherscan 是 2015 年推出的一个以太坊区块探索和分析的分布式智能合同平台, 由于区块链中的交易信息等数据都是公开透明的 , 而 Etherscan 作为探索以太坊的窗口, 用户可以使用其查看自己的交易详情以及以太坊中的任何信息。 我们都有过这样的经历, 打开 taoba

    2024年02月12日
    浏览(46)
  • macos编译libtiff库给IOS用

         

    2024年02月12日
    浏览(43)
  • Charles证书过期解决方法macos/ios

    今天心血来潮打开Charles想试试看抓包手机APP(ios),结果发现各种x和提示ssl错误。开始以为是和魔法的代理冲突或者ip变了,捯饬很久后发现web的也报错。 然后搜了一会原因发现时证书过期了 1、搜索“钥匙串访问”,直接搜索“charles”,找到打叉的名称,直接删掉 2、打开

    2024年02月03日
    浏览(52)
  • iOS/macOS - 逐行写入文件 (NSFileHandle)

    2024年02月15日
    浏览(46)
  • MacOS 14 系统 XCode15、 Flutter 开发 IOS

    MacOS14 Sonoma 安装 Flutter 开发环境 MacOS 系统 Flutter开发Android 环境配置 MacOS 系统 Flutter开发IOS 环境配置​​​​​​​ 前面我们已经在MacOS14 M3芯片上安装好 Flutter环境,包括开发工具 VsCode 、Android Stuiod,那么flutter如何开发IOS呢? 我们知道IOS开发语言为 objcet-c或者 swift,Flutter是

    2024年02月03日
    浏览(89)
  • macOS Sonoma编译OpenCV源码输出IOS平台库

    1.macOS下载并编译OpenCV源码:  克隆源码: 主仓: git clone https://github.com/opencv/opencv.git 扩展仓:  git clone https://github.com/opencv/opencv_contrib.git    编译xcode源码需要CMake与XCode命令行工具 确认已安装CMake  确认已安装XCode  安装xcode command line tools 确认系统已安装python环境

    2024年02月10日
    浏览(62)
  • 报错解决:java.sql.SQLRecoverableException:IO 错误:Got minus one from a read call

    应用报错: 起多个服务,最后服务的时候报这个错,无论最后的服务是啥,提供的报错日志 是java.sql.SQLRecoverableException:IO 错误:Got minus one from a read call 原因: 当应用连接数据库时,是通过连接池的机制进行连接的,数据库参数:max-session决定连接池的 大小,而应用同样也有

    2024年02月15日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包