面试题:Block循环引用的理解
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面试题:Block循环引用的理解相关的知识,希望对你有一定的参考价值。
参考技术A 在ARC下一直说使用block会要注意循环应用的问题,所有有碰到block我都会 __block 一下,表示我弱引用了。参考 http://www.cnblogs.com/xujiahui/p/6026043.html
不是所有的block调用类或者其属性的时候都要用 __weak ,只有这个block是被定义成属性,copy类型,被他所属的类强引用了,这个时候这个block引用的时候需要 __weak 。
我们知道我们定义block为属性的时候,block用strong和copy来修饰效果是一样的,他都会拷贝出来分配一个新的地址给他,会把block放到堆区里面,官方建议使用copy。但是如果使用assgin或者retain,retain他只是把block的计数+1,效果和assgin一样,这个block还是在栈区里面。
在堆区:内存释放由用户自己管理。
在栈区:内存释放由系统管理,block可能是被系统给释放回收了。
所以循环引用是,我们对象A用copy修饰定义了属性Block-B,这个时候A强引用了B【A->B】,然后A对象作为参数或者附属属性使用在C对象中,C引用了A【C->A】,C这个使用调用A的Block,block这个时候引用C这个对象或者其属性【B->C】,
所以 就是 A->B->C->A.... 然后他们之间相互强引用,这个时候C控制器pop了要释放了,因为ABC之间造成了强引用,所以他们一直会释放不掉。
下面循环引用出现了,我们要怎么处理,我们一般都是用__weak 和 __block 来弱引用block中要调用的对象或者属性信息。
参考 http://www.jianshu.com/p/f27038845143
__weak 和 __block 的区别
__weak 是ARC下使用
__block 在ARC和MRC下都可以使用
__weak 只能修饰对象信息 不能修饰基本类型
__block 对象和基本类型都可以修饰
__block 修饰的对象可以在block里面重新被赋值 __weak 不可以。
__block 修饰对象会增加引用
__weak 修饰对象不会增加引用
在MRC下使用 __Block 是可以避免循环引用的 但是在ARC下使用 __block 的话来修饰对象的话,
在ARC下使用 __block typeof(self)weakSelf = self;
因为block是用过添加引用来访问实例变量的,所以self会被retain一次,block也是一个强引用,会引起循环引用。使用 __weak 的话,当self释放的话,weakSelf也会置空。
22iOS面试题·自整理·Four
1.为什么代理要用weak?代理的delegate和dataSource有什么区别?block和代理的区别?
A:为了避免循环引用。weak指明该对象并不负责保持delegate这个对象,delegate这个对象的销毁由外部控制。strong该对象强引用delegate,外界不能销毁delegate对象,会导致循环引用。DataSource是关于View的内容的东西包括属性,数据等等,而Delegate则是一些我们可以调用的方法,全是操作。block和代理都能解决对象间交互的问题,block更轻型,更简单,能够直接访问上下文,代码通常在同一个地方,这样读代码也连贯。缺点是容易引起循环引用。delegate更重一些,需要实现接口,它的方法分开来,很多时候需要存储一些临时数据,另外相关的代码需要分离到各处没有block好读,其优点就是它是用weak关键字修饰的,不会引起循环引用。
2.属性的实质是什么?包括哪几个部分?属性默认的关键字都有哪些?@dynamic关键字和@synthesize关键字是用来做什么的?
A:属性的本质是@property = ivar+getter+setter,也就是说@property系统会自动生成getter和setter方法。属性默认的关键字包括atomic,nonatomic,@synthesize,@dynamic,getter=getterName,setter=setterName,readwrite,readonly,assign,retain,copy。
@dynamic:表示变量对应的属性访问器方法,是动态实现的,你需要在 NSObject 中继承而来的 +(BOOL) resolveInstanceMethod:(SEL) sel 方法中指定 动态实现的方法或者函数。
@synthesize:如果没有实现setter和getter,编译器能够自动实现getter和setter方法。
3.NSString为什么要用copy关键字,如果用strong会有什么问题?(注意:这里没有说用strong就一定不行。使用copy和strong是看情况而定的)
A:针对于当把NSMutableString赋值给NSString的时候,才会有不同,用copy的话NSString的值不会发生变化,用strong则会发生变化,随着NSMutableString的值变化。如果是赋值是NSString对象,那么使用copy还是strong,结果都是一样的,因为NSString对象根本就不能改变自身的值,他是不可变的。
4.如何令自己所写的对象具有拷贝功能?
A:若想让自己写的对象具有拷贝功能,则需要实现NSCopying协议。如果自定义的对象分为可变版本和非可变版本,那么就要同时实现NSCopying和NSMutableCopying协议,不过一般没什么必要,实现NSCopying协议就够了。
5.可变集合类 和 不可变集合类的 copy 和 mutablecopy有什么区别?如果是集合是内容复制的话,集合里面的元素也是内容复制么?
A:对于不可变对象,copy操作是浅复制,mutableCopy是深复制。对于不可变对象,mutableCopy不仅仅是深复制,返回的对象类型还是不可变对象类型相应的可变对象的类型。内容复制也就是深拷贝,集合的深复制有两个方法,可以用initWithArray:copyItems:将第二个参数设置为YES即可进行深复制,如:NSDictionary shallowCopyDict = [NSDictionary alloc]initWithDictionary:someDictionary copyItems:YES];如果用这个方法深复制,集合里的每个元素都会收到copyWithZone:消息。如果集合里的对象遵循NSCopying协议,那么对象就会深复制到新的集合。如果对象没有遵循NSCopying协议,而尝试用这种方法进行深复制则会出错。copyWithZone:这种拷贝方式只能提供一层内存拷贝,而非真正的深拷贝。第二种方法是将集合进行归档解档,如:NSArray trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:oldArray]];
6.为什么IBOutlet修饰的UIView也适用weak关键字?
A:因为既然有外链那么视图在xib或者storyboard中肯定存在,视图已经对它有一个强引用了。
7. nonatomic和atomic的区别?atomic是绝对的线程安全么?为什么?如果不是,那应该如何实现?
A:nonatomic和atomic的区别在于两者自动生成getter和setter的方法不一样,如果你自己写getter和setter方法,那么(getter,setter,retain,copy,assign)只起提示作用,写不写都一样。
对于atomic的属性,系统生成的getter和setter会保证get,set的操作完整性,不受其他线程影响。比如线程A的getter方法运行到一半,线程B调用了setter,那么线程A的getter还是能得到一个完整的对象。
而nonatomic就没有这个保证了,所以速度要比atomic快。
不过atomic可不能保证线程安全,如果线程A调用了getter,与此同时线程B和线程C都调了setter,那最后线程Aget到的值,三种都有可能:可能是B,C set之前原始的值,也可能是B set的值,也可能是C set的值。同时这个最终的值,也可能是B set的值,也可能是C set的值。要保证安全,可以使用线程锁。
8.UICollectionView自定义layout如何实现?
A:UICollectionViewLayoutAttributes,UICollectionViewFlowLayout。
9.用StoryBoard开发界面有什么弊端?如何避免?
A:难以维护,如果需要改动全局的一个字体,如果是代码的话就很好办,pch或头文件中改动就好了。如果是storyboard中就需要一个一个改动很麻烦。
如果storyboard中scene太多,打开storyboard会比较慢。
错误定位比较困难,好多错误提示模棱两可。
10.进程和线程的区别?同步异步的区别?并行和并发的区别?
A:进程是一个内存中运行的应用程序,比如在Windows系统中,一个运行的exe就是一个进程。
线程是指进程中的一个执行流程。
同步是顺序执行,执行完一个再执行下一个。需要等待,协调运行。
异步就是彼此独立,在等待某事件的过程中继续做自己的事,不需要等待这些事件完成后再工作。
并行和并发 是前者相当于三个人同时吃一个馒头,后者相当于一个人同时吃三个馒头。
并发性(Concurrence):指两个或两个以上的事件或活动在同一时间间隔内发生。并发的实质是一个物理CPU(也可以多个物理CPU) 在若干道程序之间多路复用,并发性是对有限物理资源强制行使多用户共享以提高效率。
并行性(parallelism)指两个或两个以上事件或活动在同一时刻发生。在多道程序环境下,并行性使多个程序同一时刻可在不同CPU上同时执行。
区别:(并发)一个处理器同时处理多个任务和(并行)多个处理器或者是多核的处理器同时处理多个不同的任务。
11.线程间通信?
A:NSThread、GCD、NSOperation。
12.GCD的一些常用的函数?(group,barrier,信号量,线程同步)
A:1.延迟执行任务函数:dispatch_after(.....)。
2.一次性执行dispatch_once(...)。
3.栅栏函数dispatch_barrier_async/dispatch_barrier_sync。
4.队列组的使用dispatch_group_t。
5.GCD定时器。
13.如何使用队列来避免资源抢夺?
A:dispatch_barrior_async 作用是在并行队列中,等待前面两个操作并行操作完成。
14.数据持久化的几个方案(fmdb用没用过)
A:Coredata,realm,fmdb。
15.说一下AppDelegate的几个方法?从后台到前台调用了哪些方法?第一次启动调用了哪些方法?从前台到后台调用了哪些方法?
A:
1.当程序第一次运行并且将要显示窗口的时候执行,在该方法中我们完成的操作
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
2.程序进入后台的时候首先执行程序将要取消活跃该方法
- (void)applicationWillResignActive:(UIApplication *)application
3.该方法当应用程序进入后台的时候调用
- (void)applicationDidEnterBackground:(UIApplication *)application
4.当程序进入将要前台的时候调用
- (void)applicationWillEnterForeground:(UIApplication *)application
5.应用程序已经变得活跃(应用程序的运行状态)
- (void)applicationDidBecomeActive:(UIApplication *)application
6.当程序将要退出的时候调用,如果应用程序支持后台运行,该方法被applicationDidEnterBackground:替换
- (void)applicationWillTerminate:(UIApplication *)application
16.NSCache优于NSDictionary的几点?
A:NSCache 是一个容器类,类似于NSDIctionary,通过key-value 形式存储和查询值,用于临时存储对象。
注意一点它和NSDictionary区别就是,NSCache 中的key不必实现copy,NSDictionary中的key必须实现copy。
NSCache中存储的对象也不必实现NSCoding协议,因为毕竟是临时存储,类似于内存缓存,程序退出后就被释放了。
17.知不知道Designated Initializer(指定初始化函数)?使用它的时候有什么需要注意的问题?
A:比如:
- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;
18.实现description方法能取到什么效果?
A:1.NSLog(@"%@", objectA);这会自动调用objectA的description方法来输出ObjectA的描述信息.
2.description方法默认返回对象的描述信息(默认实现是返回类名和对象的内存地址)
3.description方法是基类NSObject 所带的方法,因为其默认实现是返回类名和对象的内存地址, 这样的话,使用NSLog输出OC对象,意义就不是很大,因为我们并不关心对象的内存地址,比较关心的是对象内部的一些成变量的值。因此,会经常重写description方法,覆盖description方法的默认实现。
19.objc使用什么机制管理对象内存?
A:通过 retainCount 的机制来决定对象是否需要释放。 每次 runloop 的时候,都会检查对象的 retainCount,如果retainCount 为 0,说明该对象没有地方需要继续使用了,可以释放掉了。
20.block的实质是什么?一共有几种block?都是什么情况下生成的?
A:block对象就是一个结构体,里面有isa指针指向自己的类(global malloc stack),有desc结构体描述block的信息,forwarding指向自己或堆上自己的地址,如果block对象截获变量,这些变量也会出现在block结构体中。最重要的block结构体有一个函数指针,指向block代码块。block结构体的构造函数的参数,包括函数指针,描述block的结构体,自动截获的变量(全局变量不用截获),引用到的block变量。(block对象也会转变成结构体)
block代码块在编译的时候会生成一个函数,函数第一个参数是前面说到的block对象结构体指针。执行block,相当于执行block里面forwarding里面的函数指针。
以上是关于面试题:Block循环引用的理解的主要内容,如果未能解决你的问题,请参考以下文章