iOS高级开发工程师面试——Objective-C 语言特性
- 一、多态
- 二、继承
- 三、代理(Delegate)
- 四、通知(NSNotificationCenter)
- 五、KVC (Key-value Coding)
- 六、属性
- 七、`@property` [ˈprɒpəti]的本质是什么?ivar 、 setter 、getter 是如何生成并添加到这个类中的?
- 八、@synthesize 、@dynamic 的区别
- 九、UIView 和 CALayer 的关系?
- 十、ViewController 不走 dealloc 的情况
- 十一、viewController生命周期
- 十二、NSCache 优于 NSDictionary 的几点?
- 十三、id 和 instanceType 有什么区别?
- 十四、self 和 super 的区别 ?
- 十五、setNeedsDisplay 和 layoutIfNeeded 两者是什么关系?
- 十六、KVO键值观察
- 十七 自动释放池
- 十八 App启动到UI显示,期间做了什么?
- 十九、容器(数组)里面的内容的深浅拷贝
一、多态
多态表现为了三个方面:动态类型
、动态绑定
、动态加载
。之所以叫做多态,是因为必须到运行时(run time)才会做一些事情。
-
动态类型:
编译器编译的时候是不能被识别的(如 id 类型),要等到运行时(run time),即程序运行的时候才会根据语境来识别。所以这里面就有两个概念要分清:编译时跟运行时。
-
动态绑定 :
动态绑定(dynamic binding)貌似比较难记忆,但事实上很简单,只需记住关键词
@selector/SEL
即可。而在OC中,其实是没有函数的概念的,我们叫
消息机制
,所谓的函数调用就是给对象发送一条消息。这时,动态绑定的特性就来了。OC可以先跳过编译,到运行的时候才动态地添加函数调用,在运行时才决定要调用什么方法,需要传什么参数进去。这就是动态绑定,要实现他就必须用SEL变量绑定一个方法,最终形成的这个SEL变量就代表一个方法的引用。动态绑定的特定不仅方便,而且效率更高。 -
动态加载 :
让程序在运行时添加代码模块以及其他资源。用户可以根据需要加载一些可执行代码和资源,而不是在启动时就加载所有组件。可执行代码中可以含有和程序运行时整合的新类。
二、继承
OC 不支持多继承
,但是可以用 代理(Delegate
) 来实现多继承。runtime
消息转发等实现伪多继承。
三、代理(Delegate)
代理是一种设计模式,以 @protocol
形式体现,一般是一对一传递
。
1. 代理为什么用 weak 修饰呢?block和代理的区别?
-
一般以
weak
关键词以规避循环引用- 用
weak
修饰指明该对象并不负责保持delegate
这个对象,delegate 这个对象的销毁由外部控制。 - 用
strong
修饰该对象强引用delegate
,外界不能销毁 delegate对象,会导致循环引用。
- 用
-
block 和代理的区别:
运行成本
:代理运行成本低,block 运行成本高。
因为block出栈需要将使用的数据从栈内存拷贝到堆内存,如果本身就在堆内存的话计数器会+1,使用完或block置为nil后才消除;
delegate 只保留了一个指针对象,直接回调,没有额外的消耗。写法更简练,更紧凑
。block 注重结果的传输
。block 要防止循环引用,善用 __weak 和 __strong
。公共接口,当方法较多后者调用太频繁建议用delegate
。
四、通知(NSNotificationCenter)
使用观察者模式
来实现的用于跨层传递信息的机制。传递方式是一对多
的。
如果实现通知机制?
五、KVC (Key-value Coding)
键值编码
是一种间接访问对象的属性,使用字符串来标识属性,而不是通过调用存取方法,直接或通过实例变量访问的机制。非对象类型的变量将被自动封装或者解封成对象,很多情况下会简化程序代码。
1. KVC 底层实现原理:
当一个对象调用setValue:forKey:
方法时,方法内部会做以下操作:
- 判断有没有指定
key
的set
方法,如果有set
方法,就会调用set
方法,给该属性赋值 - 如果没有
set
方法,判断有没有跟key
值相同且带有下划线的成员属性(_key
) 如果有,直接给该成员属性进行赋值 - 如果没有成员属性
_key
,判断有没有跟key 相同名称的属性。如果有,直接给该属性进行赋值 - 如果都没有,就会调用
valueforUndefinedKey
和setValue:forUndefinedKey:
方法
2. KVC 使用场景:
- KVC 属性赋值
- 添加私有成员变量
- 字典和模型之间的互转
六、属性
OC 中,基本数据类型的默认关键字是atomic
, readwrite
, assign
;普通属性的默认关键字是atomic
, readwrite
, strong
。
1. 读写权限:readonly
,readwrite(默认)
2. 原子性: atomic(默认)
,nonatomic
。atomic
读写线程安全,但效率低,而且不是绝对的安全,比如如果修饰的是数组,那么对数组的读写是安全的,但如果是操作数组进行添加移除其中对象的话,就不保证安全了。nonatomic
禁止多线程,变量保护,提高性能。
3. 引用计数
-
retain/strong
:表示指向并拥有该对象。其修饰的对象引用计数会增加1。该对象只要引用计数不为0则不会被销毁。当然强行将其设为nil
可以销毁它。(当有一个新的指针指向这个对象时,引用计数会加1,当某个指针不再指向这个对象时,引用计数会减1
) -
assign:修饰基本数据类型,修饰对象类型时,不改变其引用计数,会产生悬垂指针,修饰的对象在被释放后,assign指针仍然指向原对象内存地址,如果使用
assign
指针继续访问原对象的话,就可能会导致内存泄漏或程序异常。这些数值主要存在于栈上。 -
weak
:不改变被修饰对象的引用计数,所指对象在被释放后,weak指针会自动置为nil
,不会造成野指针。比如自定义 IBOutlet 控件属性也是用 weak (因为父控件的 subViews 数组已经对它有了一次强引用)。 -
copy
:分为深拷贝和浅拷贝-
浅拷贝
:对内存地址的复制,让目标对象指针和原对象指向同一片内存空间,会增加引用计数。 -
深拷贝
:对对象内容的复制,开辟新的内存空间。
-
可变对象的
copy
和mutableCopy
都是深拷贝;
不可变对象的copy
是浅拷贝,mutableCopy
是深拷贝
copy
方法返回的都是不可变对象
mutableCopy
方法返回的都是可变对象
-
七、@property
[ˈprɒpəti]的本质是什么?ivar 、 setter 、getter 是如何生成并添加到这个类中的?
1. @property 的本质:
@property = ivar(成员变量) + setter + getter
即:@property
等于声明了ivar(成员变量)
,并实现了该属性的存取方法(setter + getter
)。
2. @property 作为 OC 的一项特性,主要用于封装对象中的数据。
OC 通常把其所需要的各种数据保存为各种实例变量。实例变量一般通过“存取方法”(access method
)来访问。其中,“获取方法” (getter
)用于读取变量值,而“设置方法” (setter
)用于写入变量值。
八、@synthesize 、@dynamic 的区别
@synthesize [ˈsɪnθəsaɪz]
: 系统会自动生成该属性的 setter
和 getter
方法。
@dynamic [daɪˈnæmɪk]
: 系统不会自动生成该属性的 setter
和 getter
方法,需要用户自己去实现。
九、UIView 和 CALayer 的关系?
1. UIView :
UIView
是 iOS 系统中的界面元素的基础,所有的界面元素都继承自它;- 它本身完全是由
CoreAnimation
来实现的; - 它真正绘图部分,是由
CALayer
(CoreAnimation Layer) 类来管理的; UIView
本身更像一个CALayer
的管理器,访问它的根绘图和根坐标有关的属性(如:frame、bounds 等),实际上内部都是在访问他所包含的 CALayer 的相关属性;UIView
的属性layer
,对应的是他的CALayer
实例。UIView
可以响应时间,Layer
不可以,因为UIView
继承自UIResponder
。
2. CALayer :
CALayer
类似于UIView
的子View
树形结构,也可以向它的layer
上添加子layer
,来完成某些特殊的表示;UIView
的layer
树形在系统内部分别是:- 逻辑树,这里的代码是可操控的;
- 动画树,是一个中间层,系统就在这一层上更改属性,进行各种渲染操作;
- 显示树,其内容就是当前正在被显示在屏幕上的内容。
- 动画的运作:对
UIView
的subLayer
(非主 Layer)属性进行更改,系统将会自动进行动画生成。 - 坐标系统:
CALayer
的坐标系统比UIView
多了一个anchorPoint
属性,使用CGPoint
结构标识,值域是0 ~ 1
,是个比例值。