OC 基础-Block类型

2021-03-07  本文已影响0人  我是卖报的小行家

OC基础-Block(3)Block的类型
Block有三种类型,可以通过调用class方法或者isa指针查看具体类型,最终都是继承NSBlock类型
NSGlobalBlock (NSConcreteGlobalBlock) 没有访问auto变量
NSStackBlock (NSConcreteStackBlock) 访问了auto变量
NSMallocBlock (NSConcreteMallocBlock) __NSStackBlock调用了copy
看图更清楚

一般全局变量存于数据段,alloc出来的存于堆段,局部变量存于栈段(一般数据不宜很大),类对象内存一般存于数据段
Block做为函数返回值的时候,(ARC情况下)系统会自动给block做copy处理

每一种类型的block调用copy后的结果如下图所示
Block类型 副本源的配置存储域 复制效果
_NSConcreteStackBlock 栈 从栈复制到堆
_NSConcreteGlobalBlock 程序的数据区域 什么也不用做
_NSConcreteMallocBlock 堆 引用计数增加

block类型图 内存分配地质图

栈上的内存系统会自动回收

栈空间的block 不会对 对象进行强引用

堆空间的block 可能会对对象产生强引用:

如果是weak指针,不会强引用

如果是strong指针,会强引用

堆上的内存是由程序员控制,所以一般将block 拷贝到堆上,让程序员控制他与内部变量的生命周期
//上代码
1.NSGlobalBlock

#import <Foundation/Foundation.h>

typedef void(^MJBlock)(void);

MJBlock myBlock(){
    
    return ^{
        NSLog(@"-----------");
    };
}


int main(int argc, const char * argv[]) {
    @autoreleasepool {

        MJBlock block = myBlock();
        block();
        
        NSLog(@"%@",[block class]);
        
    }
    return 0;
}

//打印结果
2021-03-07 21:19:31.897780+0800 DoBlock[74885:2090486] -----------
2021-03-07 21:19:31.898430+0800 DoBlock[74885:2090486] __NSGlobalBlock__

原因分析:因为里面没有访问到任何的auto变量,所以MJBlock为NSGlobalBlock

代码2.NSMallocBlock

#import <Foundation/Foundation.h>

typedef void(^MJBlock)(void);

MJBlock myBlock(){
    
    int age = 20;
    return ^{
        NSLog(@"-----------age%d",age);
    };
}


int main(int argc, const char * argv[]) {
    @autoreleasepool {

        MJBlock block = myBlock();
        block();
        
        NSLog(@"%@",[block class]);
        
    }
    return 0;
}

//打印结果
2021-03-07 21:23:36.045133+0800 DoBlock[74961:2094241] -----------age = 20
2021-03-07 21:23:36.045757+0800 DoBlock[74961:2094241] __NSMallocBlock__

原因分析:咦,不是访问了auto变量,应该是NSStackBlock吗?其实是Block做为返回值,ARC环境下默认帮我们做了一次copy处理,__NSStackBlock调用了copy处理所以就是NSMallocBlock类型咯
此时我们做一个总结

在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上,比如以下情况

1)block作为函数返回值时
2)将block赋值给__strong指针时

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int age = 50;
        MJBlock block = ^{
            NSLog(@"----- = %d",age);
        };//强引用
        NSLog(@"%@",[block class]);
        
    }
    return 0;
}
//打印结果:2021-03-07 21:39:42.777653+0800 DoBlock[75249:2109355] __NSMallocBlock__


int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int age = 30;
        NSLog(@"%@",[^{
            NSLog(@"%d",age);
        } class]);

    }
    return 0;
}
//因为只是引用了auto变量所以
//打印结果:2021-03-07 21:40:37.273735+0800 DoBlock[75269:2110555] __NSStackBlock__

3)block作为Cocoa API中方法名含有usingBlock的方法参数时

        NSArray * array = @[];
        
        [array enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                    
        }];
//block最终会搬到堆上面去

4)block作为GCD API的方法参数时
所以在这些情况下block类型都为NSMallocBlock类型。

Tip:
在使用clang转换OC为C++代码时,可能会遇到以下问题
cannot create __weak reference in file using manual reference

解决方案:支持ARC、指定运行时系统版本,比如
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m

上一篇下一篇

猜你喜欢

热点阅读