iOS笔试面试题(5)--OC语法

运行时(runtime)是OC在运行时的一些机制和特性,包括动态类型,动态绑定,动态加载。运行时系统指的是实现OC动态特性的底层C语言库。

为什么说OC是动态语言:因为在OC中,数据类型的确定和方法的调用都从编译时推迟到了运行时。动态类型如id类型(用了id类型要做判断isKindOfClass再去接收数据和调用方法,避免crash);动态绑定如@selector/SEL,

(2)动态绑定

动态绑定(dynamic binding)貌似比较难记忆,但事实上很简单,只需记住关键词@selector/SEL即可。OC跳过编译阶段,用SEL变量在运行时动态绑定一个方法。SEL变量是存储方法编号的整数,通过整数来查找方法速度更快,@selector()就是取类方法的ID。

2. 属性( @property )与成员变量(ivar)的关系 :

属性对成员变量扩充了存取方法 . 属性默认会生成带下划线的成员变量 .

历史:  早期要手写成员变量的存取方法,  后来可以用 @synthesize 关键字自动合成成员变量的存取方法. 现在用@property关键字就能同时生成存取方法和带下划线的成员变量.

 注: @dynamic 关键字是告诉编译器在运行时手动来实现成员变量的存取方法 , @synthesize 是让 Xcode 自动生成 .

3. CoreAnimation 和 CoreGraphics这两个框架的区别

CoreGraphics底层绘制框架,我们实际会用到的也就是CG开头的一些底层绘制函数和变量,这是一个纯C语言框架。

QuartzCore是包含了CoreAnimation的框架,是iOS系统的基本渲染框架,是一个OC语言框架,是一套基于CoreGraphics的OC语言封装,封装出了基本渲染类CALayer。

4. 面向对象有哪些特征以及你对这些特征的理解。

继承:子类继承父类, 也会把父类的属性,方法,协议一起继承. 可以让类之间产生联系, 简化代码.

封装:封装是把数据和操作数据的方法对外隐藏,对外只公开接口。外界调用接口(方法)就能实现功能,但不必知道内部如何实现, 可以保证数据安全性, 而且更加面向对象. 我们在类中编写的方法就是对实现细节的一种封装;我们编写一个类就是对数据和数据操作的封装。

多态性:多态性是指允许不同子类型的对象对同一消息作出不同的响应。简单的说就是用同样的对象引用调用同样的方法但是做了不同的事情。分为编译时的多态性和运行时的多态性。方法重载(overload)实现的是编译时的多态性(也称为前绑定),而方法重写(override)实现的是运行时的多态性(也称为后绑定)。要实现多态需要做两件事:1. 方法重写(子类重写父类中已有的或方法);2. 对象造型(用父类指针指向子类)。

抽象:抽象是将一类对象的共同特征抽象成一个类,包括数据抽象和行为抽象两方面。只关注对象有的属性和行为,不关注细节。

5. 为什么说OC是动态运行时语言?

因为在OC 中, 通过自身的多态特性和底层运行时系统, 把数据类型的确定和函数绑定由编译时推迟到了运行时.(对比C++在编译时就将函数名进行绑定, OC把某些编译时报错延迟到运行时)  简单来说, 运行时机制使我们知道程序运行时才确定一个对象的类型,以及调用对象的哪个方法, 

6. atomic 和 nonatomic 

1. 定义:  atomic和nonatomic用来决定编译器生成的getter和setter是否为原子操作。

3. 对比: (1)前者读写慢, 更安全,  后者读写快, 不安全.  (2)Mac OS 用前者, iOS 用后者居多。

2. 由来:  一种线程保护技术(加锁, 但不绝对安全), 属性写值未完成时,  其他线程无法读取,避免数据读取错误。这种机制耗性能,所以在iPhone上,如没有使用多线程间的通讯编程,那么用nonatomic声明属性(OC默认atomic是因为mac平台也用OC写)

4. 原理: 简单来说,就是 atomic 会加一个锁来保障线程安全,并且引用计数会 +1,来向调用者保证这个对象会一直存在。假如不这样做,如有另一个线程调 setter,可能会出现线程竞态,导致引用计数降到0,原来那个对象就释放掉了。但是在这种加锁机制下, 只能说属性是读/写安全的,但并不是线程安全的,因为别的线程还能进行读写之外的其他操作。线程安全需要开发者自己来保证。

7. 简述NotificationCenter、KVC、KVO、Delegate? 并说明它们之间的区别? (重点)

1. KVO(Key-Value- Observing):一对多的观察者模式, 提供了观察对象某一属性变化的方法,极大简化了代码。

2. KVC(Key-Value-Coding):是键值编码, 一个对象在调用setValue的时候,检查是否存在相应key的set方法,存在就调用set方法, 不存在就查找_key的成员变量是否存在 --> 存在就直接赋值, 不存在就查找相同名称的key.   --> 存在就赋值,  没有就调用valueForUndefinedkey和setValue:forUndefinedKey。

3. Delegate: 直接的一对一关系。代理的目的是改变或传递控制链。允许一个类在某些特定时刻通知到其他类,而不需要获取到那些类的指针。可以减少框架复杂度。消息的发送者(sender)告知接收者(receiver)某个事件将要发生,delegate同意然然后发送者响应事件,delegate机制使得接收者可以改变发送者的行为。

4. Notification: 观察者模式, 间接的多对多关系。 A(通知中心单例)发送通知,B(任意对象)收到通知开始做事情(调用新方法或者block),  但是B并不能反过来影响A的行为。

对比: 效率肯定是delegate比NSNotification高。

delegate方法比notification更加直接,需要关注返回值,所以delegate方法往往包含should这个很传神的词。相反的,notification最大的特色就是不关心结果。所以notification往往用did这个词汇。两个模块之间联系不是很紧密,就用notification传值,例如多线程之间传值用notificaiton。

delegate只是一种较为简单的回调,且主要用在一个模块中,例如底层功能完成了,需要把一些值传到上层去,就事先把上层的函数通过delegate传到底层,然后在底层call这个delegate,它们都在一个模块中,完成一个功能,例如说 NavgationController 从 B 界面到A 点返回按钮 (调用popViewController方法), 就用delegate

8. 懒加载

1. 懒加载:用到某个对象时再进行初始化, 就是重写get方法, 也叫延时加载。举例: tableView中图片的加载显示, 避免内存过高。

2. 异步加载:开启子线程处理耗时操作,比如加载网络图片、音频, 避免阻塞主线程使得界面卡顿。

9. OC有没有多继承? 没有的话有什么替代方法?

没有, 可以用分类和协议来代替。

protocol(协议)可以实现多个接口,通过实现多个接口可以完成多继承;

Category(类别)一般使用分类,用Category去重写类的方法,仅对本Category有效,不会影响到其他类与原有类的关系。

10. 分别描述分类(categories)和延展(extensions)是什么? 以及两者的区别? 继承和分类在实现中有何区别? 为什么Category只能为对象添加方法, 却不能添加成员变量?

1. 定义:(1)分类(categories): 在没有原类.m文件的基础上, 新建分类文件给本类添加方法; (2)延展(extensions): 分类的特例, 直接在类的.m文件里给类添加私有方法和私有变量

2. 分类和延展的区别: 延展可以添加属性,是一个私有分类。分类只能添加方法, 添加属性。

3. 分类和继承在实现中的区别:当类别有和本类中同名的方法, 则类别将覆盖本类方法, 因为类别具有更高的优先级。继承可以增加,修改删除方法,添加属性。

4. 分类只能为对象添加方法, 却不能添加成员变量的原因: 如果可以添加成员变量,添加的成员变量没有办法初始化。

11. Objective-C有私有方法么? 私有变量呢? 如果没有,有什么代替的方法?

没有真正的私有方法,objective-c类里面的方法只有两种, 类方法和对象方法。但是可以通过把方法的声明和定义都放在.m文件中来实现一个表面上的私有方法,但是通过运行时系统可以调用任何方法。有私有变量, 可以通过@private来修饰,或者把成员变量声明放到.m文件中,使其成为私有变量。在Objective‐C中,所有实例变量默认都是私有的, 所有实例方法默认都是公有的。

12. include和#import的区别? @class和#import的区别?

(1)import指令是OC针对C中的#include指令的改进版本, 避免交叉包涵,交叉编译;

(2)#import链接了该头文件的全部信息,包括方法和属性,而@class只是告诉编译器其后申明一个类,避免编译报错,不用关心类中细节。一般.h中用@class, .m文件中用#import。

13. Objective-C与C、C+++之间的联系和区别?

Objective-C和C++都是C的面向对象的超集

Object与C++的区别主要点:Objective-C是完全动态的,支持在运行时动态类型决议(dynamic typing),动态绑定(dynamic binding)以及动态装载(dynamic loading);而C++是部分动态的,编译时静态绑定,通过嵌入类(多重继承)和虚函数(虚表)来模拟实现。

Objective-C 在语言层次上支持动态消息转发,其消息发送语法为 [object function]; 而且C++ 为 object->function()。 两者的语义也不同,在 Objective-C 里是说发送消息到一个对象上,至于这个对象能不能响应消息以及是响应还是转发消息都不会 crash; 而在 C++ 里是说对象进行了某个操作,如果对象没有这个操作的话,要么编译会报错(静态绑定),要么程序会 crash 掉的(动态绑定)。

14. 目标-动作机制

目标:接收事件反馈的id类型的对象。在代码中通常用target来表示目标对象。

动作:事件反馈将触发的方法。动作对象为SEL类型,代表一个方法。

SEL类型是OC语言为存储方法名创造的变量类型。同时还创造了一个运算符,用于把一个方法名转换成一个SEL值。

SEL test = @selector(方法名);

应用场景:能够设置反馈目标和反馈动作的组件我们称为具有目标动作回调接口的组件,如UI控件

15. Objective-C优点和缺点

优点:(1).Category和Extension(实用的扩展机制,为类添加方法和属性);

(2). 动态语言(动态类型识别,动态绑定,动态加载功能);

(4). 方便的通过操作指针来操作对象;

(5). C语言超集,上手快,混编。

缺点: (1).不支持命名空间(所以苹果类库都要带一些奇怪的UI,NS前缀);

(3).不支持多重继承

(4).运行时特性让方法调用跳过编译时, 用不到编译时优化方法(如内联函数等), 影响性能

16. C语言的函数调用和oc的消息机制有什么区别?

C语言的函数调用在编译时就决定调用哪个函数,编译后直接按顺序执行。

OC的消息机制是一个动态调用过程,决定调用哪个函数从编译时延迟到运行时。(在编译时并不能决定真正调用哪个函数。事实证明,在编译阶段,OC可以调用任何函数,即使这个函数并未实现,只要申明过就不会报错。而C语言在编译阶段就会报错。只有在真正运行时才会根据函数的名称找到对应的函数来调用。)

17. 什么是谓词?

谓词就是通过NSPredicate给定的逻辑条件作为约束条件,完成对数据的筛选。

18. 常见的oc数据类型哪些,和c的基本类型有啥区别?

常见的:NSInteger CGFloat NSString NSNumber NSArray NSDate

NSInteger根据32或者64位系统决定本身是int还是long,CGFloat根据32或者64位系统决定本身是float还是double

NSString NSNumber NSArray NSDate是对象,存储在堆中,c语言中的char int是一定字节的内存空间,用于存储数据,存储在栈中。

19. id和nil代表什么?

id类型的指针可以指向任何OC对象

nil代表空值(空指针的值,0),在Objective-C中,nil对象调用任何方法表示什么也不执行,也不会崩溃。

20. nil和NULL的区别?

nil表示对象的指针(即对象的引用)为空;

null表示指向基本数据类型变量 (即c语言变量)的指针为空

注意:在MRC中可互换,ARC中 普通指针和对象引用被严格限制,不能互换!

NULL是宏,是对于C语言指针而使用的,表示空指针

nil是宏,是对于Objective-C中的对象而使用的,表示对象为空

Nil是宏,是对于Objective-C中的类而使用的,表示类指向空

NSNull是类类型,是用于表示空的占位对象,与JS或者服务端的null类似的含意

21.向一个nil对象发送消息会发生什么?

向nil发送消息是有效的,只是在运行时不会有任何作用。

果一个方法返回值是一个对象、指针或结构体,那么发送给nil的消息将返回0(nil),如果返回值不是上述情况,那么发送给nil的消息的返回值将是未定义的。(如果方法返回值为指针类型,其指针大小为小于或者等于sizeof(void*),float,double,long double 或者long long的整型标量,发送给nil的消息将返回0。如果方法返回值为结构体,正如在《Mac OS X ABI 函数调用指南》,发送给nil的消息将返回0。结构体中各个字段的值将都是0。其他的结构体数据类型将不是用0填充的。)

22. self.和self->的区别?

self.是调用get或者set方法;self->是直接访问成员变量

 前者通过成员变量的存取方法取值赋值,我们可以在两个方法中过滤不合法的访问,保证成员变量的封装性;

后者随意拿到成员变量赋值,不安全。而且后者对成员变量的访问,要求成员变量是@public的,那么就暴露给外界了,也不安全。

23. 类方法和对象方法的区别和联系?

类方法                                                                             实例方法

属于类对象                                                                      属于实例对象

只能类对象调用                                                               只能实例对象调用

self指类对象                                                                     self指实例对象

类方法不能访问成员变量                                                 实例方法可以访问成员变量

内部不可直接调用对象方法(创建对象再调用)             内部可直接调用类方法(通过类名)

24. _block/_weak修饰符区别?

(1)_block在arc和mrc环境下都能用,_weak只能在arc环境下使用。

(2)_block能修饰对象和基本数据类型,_weak只能修饰对象。

(3)_block修饰的对象可以在block中重新赋值,_weak不行。

25. init和initWithObjc区别(语法)?

都是构造方法,后者给属性赋值。

26、@property的本质是什么?ivar、getter、setter是如何生成并添加到这个类中的?

(1)@property的本质:@property = ivar(实例变量) +  getter/setter(存取方法)。【“属性” (property)有两大概念:ivar(实例变量)、存取方法(access method = getter + setter)】;

(2)ivar、getter、setter如何生成并添加到类中:

我们写上@property + 属性名,编译器根据属性名自动合成带下划线的ivar和getter/setter方法,(默认为@synthesize propertyName = _propertyName);

(3)拓展:1. 手写@synthesize 属性名 = _zhangsan,可以给成员变量改名成_zhangsan(一般不这么瞎搞,应保持系统默认做法);2. 手写getter/setter方法,则不会有生成成员变量。

27. 这个写法会出什么问题:@property (copy) NSMutableArray *array ?

第一, 没注明nonatomic,会影响性能。(没有指明为nonatomic,就是默认的atomic原子操作,会在创建属性时生成一些额外的代码用于加锁,保证线程安全,这会带来性能问题。通过声明nonatomic可以节省这些虽然很小但是不必要额外开销。iOS中几乎都是使用nonatomic来声明的,因为使用atomic也并不能保证绝对的线程安全,对于要绝对保证线程安全的操作,还需要使用更高级的方式来处理,比如NSSpinLock、@syncronized等)

第二,copy一个可变数组将会得到一个不可变数组。如果对copy出来的数组进行增删改操作,会crash。

28. @protocol和category中怎么使用 @property ?

在protocol中使用@property只会生成setter和getter方法声明,并要求遵守协议的对象能实现该属性;

category使用@property只会生成setter和getter方法的声明,如果要给category增加属性的实现,需要借助于运行时的两个函数:objc_setAssociatedObject,objc_getAssociatedObject。

29. @property中有哪些属性关键字?

原子性 (atomic,nonatomic)

读写(readwrite, readonly)

内存管理(assign, strong, weak, unsafe_unretained,copy)

重命名 getter、setter

30. 如何访问并修改一个类的私有属性?

通过KVC获取,通过runtime访问并修改私有属性。

Objective-C 中,meta-class 指的是什么?

meta-class 是类对象的类,为这个Class类存储类方法,当一个类发送消息时,就去这个类对应的meta-class中查找那个消息,每个Class都有不同的meta-class,所有的meta-class都使用基类的meta-class(假如类继承NSObject,那么他所对应的meta-class也是NSObject)作为他们的类

对于语句NSString *obj = [[NSData alloc] init]; ,编译时和运行时obj分别是什么类型?

编译时是NSString类型 ,运行时是NSData类型.

@synthesize和@dynamic分别有什么作用?

- @property有两个对应的词,一个是 @synthesize,一个是 @dynamic。如果 @synthesize和 @dynamic都没写,那么默认的就是@syntheszie var = _var;

- @synthesize 的语义是如果你没有手动实现 setter 方法和 getter 方法,那么编译器会自动为你加上这两个方法。

- @dynamic 告诉编译器:属性的 setter 与 getter 方法由用户自己实现,不自动生成。(当然对于 readonly 的属性只需提供 getter 即可)。假如一个属性被声明为 @dynamic var,然后你没有提供 @setter方法和 @getter 方法,编译的时候没问题,但是当程序运行到 instance.var = someVar,由于缺 setter 方法会导致程序崩溃;或者当运行到 someVar = var 时,由于缺 getter 方法同样会导致崩溃。编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定。

NSString 的时候用copy和strong的区别?

简言之,copy修饰NSString会多一次判断,strong则不会,如果NSString很多,性能差。(OC中NSString为不可变字符串的时候,用copy和strong都是只分配一次内存,但是如果用copy的时候,需要先判断字符串是否是不可变字符串,如果是不可变字符串,就不再分配空间,如果是可变字符串才分配空间。如果程序中用到NSString的地方特别多,每一次都要先进行判断就会耗费性能,影响用户体验,用strong就不会再进行判断,所以,不可变字符串可以直接用strong。)

NSArray、NSSet、NSDictionary与NSMutableArray、NSMutableSet、NSMutableDictionary的特性和作用(遇到copy修饰产生的变化)

特性:

NSArray表示不可变数组元素有序只能存对象,可通过下标访问元素,而且元素类型可以不一样,但是不能进行增、删、改操作;NSMutableArray是可变数组,能进行增、删、改操作。通过索引查询值很快,但是插入、删除等效率很低。(数组用于处理一组有序的数据集,比如常用的tableview的dataSource要求有序,可通过索引直接访问,效率高。)

NSSet表示不可变集合,具有确定性、互异性、无序性的特点,只能访问而不能修改集合;NSMutableSet表示可变集合,可以对集合进行增、删、改操作。集合通过值查询很快,插入、删除操作极快。

集合在iOS开发中比较少用,多点触控时的多个UITouch对象就是NSSet形式存在的。

字典是键值对数据集,值是无序的。JSON转字典,字典转模型就是其中一种应用。

weak和assign区别?

weak:一般修饰控件,代表指向对象的是弱指针,引用计数不会+1,指向的对象销毁时指针自动清空;

assign,一般修饰int float double等基本数据类型,引用计数不会+1,指向的对象销毁时指针不会清空;

frame和bounds的区别?

frame以父控件左上角为坐标原点,bounds以自己的左上角为坐标原点。两者都是描述一块区域的可视范围。前者决定自己在父控件中的位置,后者决定子控件在自己中的位置。

你可能感兴趣的:(iOS笔试面试题(5)--OC语法)