1UITableView的重用机制,ARC的基本原理,如何避免retain cycle,谈谈对MVC的理解,iOS内存管理机制
避免retain cycle
两个对象相互强引用
ARC的基本原理
- ARC是Automatic Reference Counting(自动引用计数器)的简称。
- ARC是ios5.0引入的新特性,完全消除手动管理内存的繁琐,编译器会自动在适合的代码里面插入适当的retain,release,autorelease的语句。我们不要再担心内存管理,因为编译器帮我们做了这一切。
- ARC是编译器的特性,并非运行时的特性,也不是其他编程语言中的垃圾回收器。因此自动管理和手动管理内存其实一样的,但是自动管理更加快速,因为编译器执行某些优化。
- ARC的规则就是只要对象没有强指针引用,就会被释放掉,换而言之 只要还有一个强引用指针变量指向对象,那么这个对象就会存在内存中。弱指针指向的对象,会被自动变成空指针(nil指针),从而不会引发野指针错误。
注意使用事项
- 循环引用
解决办法:一段用强引用,一段用弱引用。outlet的控件属性都是weak修饰,因为他们已经被父控件强引用。
2.ARC只对OC对象的进行内存管理,对于CoreFundation的api使用,他的对象所有权没有移交给OC对象管理,都需要手动去释放。
iOS内存管理机制
其实就是引用计数(reference count)的管理。内存管理就是在程序需要时程序员分配一段内存空间,而当使用完之后将它释放。如果程序员对内存资源使用不当,有时不仅会造成内存资源浪费,甚至会导致程序crach。我们将会从引用计数和内存管理规则等基本概念开始,然后讲述有哪些内存管理方法,最后注意有哪些常见内存问题。
Autorelease Pool
在开发中,我们常常都会使用到局部变量,局部变量一个特点就是当它超过作用域时,就会自动释放。而autorelease pool跟局部变量类似,当执行代码超过autorelease pool块时,所有放在autorelease pool的对象都会自动调用release。它的工作原理如下:
-
创建一个NSAutoreleasePool对象
-
在autorelease pool块的对象调用autorelease方法
-
释放NSAutoreleasePool对象
1.UITableview的重用机制
可以说是UIKit中最重要的一个组件,用来展示数据列表,还可以灵活使用进行页面的布局。UITableView的使用遵循MVC模式,数据模型(NSObject)、视图(UIView)和控制器(UITableViewController)分离。UITableView继承自UIScrollView,可上下滑动,可以作为跟视图也可以作为子视图组件。
reuseIdentifier顾名思义是一个复用标识符,是一个自定义的独一无二的字符串,用来唯一地标记某种重复样式的可复用UITableViewCell,系统是通过reuseIdentifier来确定已经创建了的指定样式的cell来进行复用,iOS中表格的cell通过复用来提高加载效率,因为多数情况下表格中的cell样式都是重复的,只是数据模型不同而已,因此系统可以在保证创建足够数量的cell铺满屏幕的前提下,通过保存并重复使用已经创建的cell来提高加载效率和优化内存,避免不停地创建和销毁cell元素。
通过调用当前tableView的dequeueReusableCellWithIdentifier方法看指定的reuseIdentifier是否有可以重复使用的了,如果有则会返回可复用的cell,cell就绪之后便可以开始更新cell的数据;如果还不可复用,则返回nil,然后会进入后面的if语句,此时创建新的cell并对其设置cell样式标记reuseIdentifier。注意上面的if语句并不是只要执行一次创建一次新的cell就完成任务,然后之后全部重复利用新创建的那一个cell,这是对cell复用机制的误解。
事实是要创建足够数量的可覆盖整个tableView的可复用cell之后才会开始复用之前的(UITableView中有一个visiableCells数组保存当前屏幕可见的cell,还有一个reusableTableCells数组用来保存那些可复用的cell),这个我们用下面的测试来验证。
UITableView的滚动优化主要在于以下两个方面:
- 减少cellForRowAtIndexPath代理中的计算量(cell的内容计算)
- 减少heightForRowAtIndexPath代理中的计算量(cell的高度计算)
2.线程和进程的区别和联系
1进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动。进程是系统进行资源分配和调度的一个独立单位。线程是进程的一个实体,是CPU调度和分配的基本单位,它是比进程更小的能源基本单位。线程自己基本不拥有系统资源。只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈) ,但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.
一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行.
3.并行和并发的区别
并行是指两个或者多个事件在同一时刻发生;
并发是指两个或多个事件在同一时间间隔内发生。
4.谈谈你对Block和delegate的理解
我当时是这么答的,delegate的回调更多的面向过程,而block则是面向结果的。如果你需要得到一条多步进程的通知,你应该使用delegation。而当你只是希望得到你请求的信息(或者获取信息时的错误提示),你应该使用block。(如果你结合之前的3个结论,你会发现delegate可以在所有事件中维持state,而多个独立的block却不能)delegate用时一般都是一对一
5.谈谈instancetype和id的异同
1、相同点
都可以作为方法的返回类型
2、不同点
①instancetype可以返回和方法所在类相同类型的对象,id只能返回未知类型的对象;②instancetype只能作为返回值,不能像id那样作为参数
6.category中能不能使用声明属性?为什么?如果能,怎么实现?
给分类(Category)添加属性
利用Runtime实现getter/setter 方法
1
2
3
4
5
6
7
8
9
10
11
|
@ interface ClassName (CategoryName)@property (nonatomic, strong) NSString *str;@end //实现文件#import "ClassName + CategoryName.h"#importstatic void *strKey = &strKey; @implementation ClassName (CategoryName) -( void )setStr:(NSString *)str { objc_setAssociatedObject(self, & strKey, str, OBJC_ASSOCIATION_COPY); } -(NSString *)str { return objc_getAssociatedObject(self, &strKey); } @end |
7.isKindOfClass和isMemberOfClass的区别
这个题目简单,但是就是当时紧张的情况下,别答反了。
isKindOfClass来确定一个对象是否是一个类的成员,或者是派生自该类的成员
isMemberOfClass只能确定一个对象是否是当前类的成员
8.block里面的如何防止retain cycle
使用弱引用打断block里面的retain cycle
MRC中 _block 是不会引起retain;但在ARC中 _block 则会引起retain。ARC中应该使用 _weak或__unsafe_unretained弱引用
9.iOS多线程有哪几种实现方法?GCD中有哪些队列?分别是并行还是串行?
iOS中多线程编程工具主要3有:
1.NSThread
2.NSOperation
3.GCD
dispatch queue分为下面3种:而系统默认就有一串行队列main_queue和并行队列global_queue:
GCD中有三种队列类型:
The main queue: 与主线程功能相同。实际上,提交至main queue的任务会在主线程中执行。main queue可以调用dispatch_get_main_queue()来获得。因为main queue是与主线程相关的,所以这是一个串行队列。
Global queues: 全局队列是并发队列,并由整个进程共享。进程中存在三个全局队列:高、中(默认)、低三个优先级队列。可以调用dispatch_get_global_queue函数传入优先级来访问队列。
用户队列: 用户队列 (GCD并不这样称呼这种队列, 但是没有一个特定的名字来形容这种队列,所以我们称其为用户队列) 是用函数 dispatch_queue_create
创建的队列. 这些队列是串行的。正因为如此,它们可以用来完成同步机制, 有点像传统线程中的mutex。
initialize和load的区别在于:load是只要类所在文件被引用就会被调用,而initialize是在类或者其子类的第一个方法被调用前调用。所以如果类没有被引用进项目,就不会有load调用;但即使类文件被引用进来,但是没有使用,那么initialize也不会被调用。
它们的相同点在于:方法只会被调用一次。(其实这是相对runtime来说的,后边会做进一步解释)。
文档也明确阐述了方法调用的顺序:父类(Superclass)的方法优先于子类(Subclass)的方法,类中的方法优先于类别(Category)中的方法。
不过还有很多地方是文章中没有解释详细的。所以再来看一些示例代码来明确其中应该注意的细节