iOSSharing #6 | 2019-04-28

栏目: IOS · 发布时间: 6年前

内容简介:我们在上面的代码中创建了一个Light(光)类,并声明两个属性color(颜色)及block。然后我们实例化一个对象loveLight并对其属性赋值,实现并调用block,造成循环引用。 然后我们通过clang代码,了解这段代码内部的部分实现:通过clang后的源码我们可以知道:同时面试中面试官还可能会询问你如何检测到内存泄漏,我们可以通过Instruments中的Leaks进行检测,也可以选择facebook发布的FBRetainCycleDetector内存泄漏检测工具。

1. block的循环引用是如何造成的?

------------Light.h------------
#import <Foundation/Foundation.h>
@interface Light : NSObject
@property (nonatomic, copy) NSString *color;
@property (nonatomic, copy) void (^block)(void);
@end

------------Light.m------------
#import "Light.h"
@implementation Light
@end

------------main.m------------
#import "Light.h"

int main(int argc, char * argv[]) {
    @autoreleasepool {
        Light *loveLight = [Light alloc] init];
        loveLight.color = @"green";
        
        loveLight.block = ^{
            NSLog(@"%@",loveLight.color);
        };
        
        loveLight.block();
    }
}
复制代码

我们在上面的代码中创建了一个Light(光)类,并声明两个属性color(颜色)及block。然后我们实例化一个对象loveLight并对其属性赋值,实现并调用block,造成循环引用。 然后我们通过clang代码,了解这段代码内部的部分实现:

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  RMPerson *__strong person;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Light *__strong _loveLight, int flags=0) : loveLight(_loveLight) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  Light *__strong loveLight = __cself->loveLight; // bound by copy

            NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_0xn052bn6dgb9z7pfk8bbg740000gn_T_main_d61985_mi_0,((int (*)(id, SEL))(void *)objc_msgSend)((id)loveLight, sel_registerName("color")));
        }

static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {

_Block_object_assign((void*)&dst->loveLight, (void*)src->loveLight, 3/*BLOCK_FIELD_IS_OBJECT*/);

}

...后省略
复制代码

通过clang后的源码我们可以知道:

  • 对象的创建本身就是强引用(默认strong修饰)。
  • 对象对block属性赋值,在ARC下,block作为返回值时或者赋值给一个strong/copy修饰的对象会自动调用copy, loveLight强引用block
  • 对象的block在其内部捕获了对象本身,block在自动调用copy的时候,_Block_object_assign(clang源码最后一行)会根据捕获变量的所有权修饰符,来对变量的引用计数进行操作。此处loveLight本身是strong修饰,则引用计数+1, block强引用loveLight对象
  • 所以双方互相引用,造成了循环引用。

同时面试中面试官还可能会询问你如何检测到内存泄漏,我们可以通过Instruments中的Leaks进行检测,也可以选择facebook发布的FBRetainCycleDetector内存泄漏检测工具。

2. 使用UIView Animation的block回调时,是否需要考虑循环引用的问题?为什么?

首先UIView Animation使用时,不需要考虑循环引用的问题。

UIKit将动画直接集成到UIView的类中,当内部的一些属性发生改变时,UIView将为这些改变提供动画支持,并以类方法的形式提供接口。

而block造成循环引用的主要原因是对象与block的相互持有,UIView Animation的block本身处于类方法中,在使用时并不属于调用控制器。同时控制器也无法强引用一个类,所以不会造成循环引用的问题。

3. block属性是否可以用strong修饰?

block属性可以使用strong属性修饰符修饰,但是不推荐,会有内存泄漏的隐患。

首先,ARC中block用copy属性修饰符修饰是MRC时代延续的产物,提醒开发者可能存在的内存问题。同时copy的确是可以用strong来替代的。

我们都知道block在OC中有三种类型:

  • _NSConcreateGlobalBlock 全局的静态block,不会访问任何外部变量。
  • _NSConcreateStackBlock 栈区的block,当函数返回时会被销毁。
  • _NSConcreateMallocBlock 堆区的block,当引用计数为0时被销毁。

block在MRC下可以存在于全局区、栈区和堆区,而在ARC下,block会自动从栈区拷贝到堆区(除了裸写block实现块),所以只存在于全局区和堆区。 所以对于栈区block,MRC下处于栈区,想在作用域外调用就得copy到堆区;ARC则自动copy堆区。

那么这个时候问题就来了,strong属性修饰符并不能拷贝,就会有野指针错区的可能,造成Crash。这种情况很少见,但是不代表不可能发生,所以最好还是使用copy属性修饰符。

4. 什么场景下才需要对变量使用__block?

赋值场景下使用__block,使用场景下不需要。 我们来对比下赋值场景和使用场景

//赋值场景
NSMutableArray *__block array = nil;

void(^Block)(void) = ^{
    array = [NSMutableArray array];
};

Block();
    
//使用场景
NSMultableArray *array = [NSMultableArray array];

void(^Block)() = ^{
    [array addObject:@"1"];
};

Block();
复制代码

5. 运行以下GCD多线程代码,控制台将打印什么?

dispatch_queue_t gQueue= dispatch_get_global_queue(0, 0);
    
NSLog(@"1");
dispatch_sync(gQueue, ^{
    NSLog(@"2");
    dispatch_sync(gQueue, ^{
        NSLog(@"3");
     });
    NSLog(@"4");
    });
NSLog(@"5");
复制代码

答案:12345

首先打印1,全局队列本质是并发队列也就是并发同步,同步任务不开起线程,在主线程执行打印2。 然后全局队列执行同步任务,依旧不开启线程,在主线程执行打印3。 同步任务完成,依旧存在于全局队列同步执行,打印4. 同步任务完成,打印5。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Haskell

Haskell

Simon Thompson / Addison-Wesley / 1999-3-16 / GBP 40.99

The second edition of Haskell: The Craft of Functional Programming is essential reading for beginners to functional programming and newcomers to the Haskell programming language. The emphasis is on th......一起来看看 《Haskell》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具