面试题

1.Class 中包含属性 Name 如何定义为对外只读对内可读写?

在头文件中将属性定义为readonly, 在.m文件中将属性重新定义为readwrite

2.什么情况使用 weak 关键字,相比 assign 有什么不同?

  1. 在 ARC 中,在有可能出现循环引用的时候,往往要通过让其中一端使用 weak 来解决,比如: delegate 代理属性
  2. 自身已经对它进行一次强引用,没有必要再强引用一次,此时也会使用 weak,自定义 IBOutlet 控件属性一般也使用 weak;当然,也可以使用strong。在下文也有论述:

不同点:

  1. weak 此特质表明该属性定义了一种“非拥有关系” (nonowning relationship)。为这种属性设置新值时,设置方法既不保留新值,也不释放旧值。此特质同assign类似,然而在属性所指的对象遭到摧毁时,属性值也会清空(nil out)。而 assign 的“设置方法”只会执行针对“纯量类型” (scalar type,例如 CGFloat 或 NSlnteger 等)的简单赋值操作。
  2. assigin 可以用非 OC 对象,而 weak 必须用于 OC 对象

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

添加,删除,修改数组内的元素的时候,程序会因为找不到对应的方法而崩溃。

//如:-[__NSArrayI removeObjectAtIndex:]: unrecognized selector sent to instance 0x7fcd1bc30460

// copy后返回的是不可变对象(即 arr 是 NSArray 类型,NSArray 类型对象不能调用 NSMutableArray 类型对象的方法)

原因:是因为 copy 就是复制一个不可变 NSArray 的对象,不能对 NSArray 对象进行添加/修改。

4.举例说明浅复制和深复制的区别?

浅复制 你和你的影子 你挂了 他也挂了

深复制 你和你的克隆体 你挂了 他还存在

浅拷贝只是增加了一个指针指向已经存在的内存,而深拷贝就是增加一个指针并且申请一个新的内存,使这个增加的指针指向这个新的内存

5.iOS 开发中数据持久性有哪几种?你用到哪些?

据存储的核心都是写文件。

  • 属性列表:只有NSString、NSArray、NSDictionary、NSData可writeToFile;存储依旧是plist文件。plist文件可以存储的7中数据类型:array、dictionary、string、bool、data、date、number。
  • 对象序列化(对象归档):对象序列化通过序列化的形式,键值关系存储到本地,转化成二进制流。通过runtime实现自动化归档/解档,请参考这个文章。实现NSCoding协议必须实现的两个方法:
  • 编码(对象序列化):把不能直接存储到plist文件中得到数据,转化为二进制数据,NSData,可以存储到本地;
  • 解码(对象反序列化):把二进制数据转化为本来的类型。
  • SQLite 数据库:大量有规律的数据使用数据库。
  • CoreData :通过管理对象进行增、删、查、改操作的。它不是一个数据库,不仅可以使用SQLite数据库来保持数据,也可以使用其他的方式来存储数据。如:XML。

6.UITableViewCell 上有个 UILabel,显示 NSTimer 实现的秒表时间,手指滚动 cell 过程中,label 是否刷新,为什么?

这是否刷新取决于timer加入到Run Loop中的Mode是什么。Mode主要是用来指定事件在运行循环中的优先级的,分为:

  • NSDefaultRunLoopMode(kCFRunLoopDefaultMode):默认,空闲状态
  • UITrackingRunLoopMode:ScrollView滑动时会切换到该Mode
  • UIInitializationRunLoopMode:run loop启动时,会切换到该mode
  • NSRunLoopCommonModes(kCFRunLoopCommonModes):Mode集合

苹果公开提供的Mode有两个:

  • NSDefaultRunLoopMode(kCFRunLoopDefaultMode)
  • NSRunLoopCommonModes(kCFRunLoopCommonModes)
  • 在编程中:如果我们把一个NSTimer对象以NSDefaultRunLoopMode(kCFRunLoopDefaultMode)添加到主运行循环中的时候, ScrollView滚动过程中会因为mode的切换,而导致NSTimer将不再被调度。当我们滚动的时候,也希望不调度,那就应该使用默认模式。但是,如果希望在滚动时,定时器也要回调,那就应该使用common mode。

7.UIView 和 CALayer 之间的关系?

  • 每个 UIView 内部都有一个 CALayer 在背后提供内容的绘制和显示,并且 UIView 的尺寸样式都由内部的 Layer 所提供。两者都有树状层级结构,layer 内部有 SubLayers,View 内部有 SubViews.但是 Layer 比 View 多了个AnchorPoint
  • 在 View显示的时候,UIView 做为 Layer 的 CALayerDelegate,View 的显示内容由内部的 CALayer 的 display
  • CALayer 是默认修改属性支持隐式动画的,在给 UIView 的 Layer 做动画的时候,View 作为 Layer 的代理,Layer 通过 actionForLayer:forKey:向 View请求相应的 action(动画行为)
  • layer 内部维护着三分 layer tree,分别是 presentLayer Tree(动画树),modeLayer Tree(模型树), Render Tree (渲染树),在做 iOS动画的时候,我们修改动画的属性,在动画的其实是 Layer 的 presentLayer的属性值,而最终展示在界面上的其实是提供 View的modelLayer
  • 两者最明显的区别是 View可以接受并处理事件,而 Layer 不可以

8.UIWebView 有哪些性能问题?有没有可替代的方案。

使用UIWebView来显示图片和动态图,会占用很多内存,内存占用多了之后就会出现卡顿。如果播放的图片比较少占的内存比较小或者比较常用,可以选择用imageNamed:方式获取图片。

9.有 a、b、c、d 4个异步请求,如何判断 a、b、c、d都完成执行?如果需要 a、b、c、d顺序执行,该如何实现?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
> dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
>
> dispatch_group_t group = dispatch_group_create();
>
> dispatch_group_async(group, queue, ^{ /*任务a */ });
>
> dispatch_group_async(group, queue, ^{ /*任务b */ });
>
> dispatch_group_async(group, queue, ^{ /*任务c */ });
>
> dispatch_group_async(group, queue, ^{ /*任务d */ });
>
> dispatch_group_notify(group,dispatch_get_main_queue(), ^{
>
> // 在a、b、c、d异步执行完成后,会回调这里
>
> });
>

>

10.Scoket 连接和 HTTP 连接的区别?

  • HTTP是短连接,Socket(基于TCP协议的)是长连接。尽管HTTP1.1开始支持持久连接,但仍无法保证始终连接。而Socket连接一旦建立TCP三次握手,除非一方主动断开,否则连接状态一直保持。
  • HTTP连接服务端无法主动发消息,Socket连接双方请求的发送先后限制。这点就比较重要了,因为它将决定二者分别适合应用在什么场景下。HTTP采用“请求-响应”机制,在客户端还没发送消息给服务端前,服务端无法推送消息给客户端。必须满足客户端发送消息在前,服务端回复在后。Socket连接双方类似peer2peer的关系,一方随时可以向另一方喊话。

11.如何把一个包含自定义对象的数组序列化到磁盘?

  • 自定义对象需要实现NSCoding协议
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
> - (void)viewDidLoad{
> [super viewDidLoad];
> User user = [User new];
> Acac= [Acnew];
> NSArray userArray = @[user, account];
> // 存到磁盘
> NSData tempArchive = [NSKeyedArchiver archivedDataWithRootObject: userArray];
> }
>
> // 代理方法
> -(instancetype)initWithCoder:(NSCoder )coder{
> self = [super initWithCoder:coder];
> if (self) {
> self.user = [aDecoder decodeObjectForKey:@"user"];
> self.ac= [aDecoder decodeObjectForKey:@"account"];
> }
> return self;
> }
> // 代理方法
> -(void)encodeWithCoder:(NSCoder )aCoder{
> [aCoder encodeObject:self.user forKey:@"user"];
> [aCoder encodeObject:self.acforKey:@"account"];
> }
>

或者

1
2
3
4
5
6
7
8
9
10
> +(BOOL)writeToPlistFile:(NSDictionary*)settings fileName:(NSString*)filename{
>
> NSData * data = [NSKeyedArchiver archivedDataWithRootObject:settings];
> NSArray * paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
> NSString * documentsDirectory = [paths objectAtIndex:0];
> NSString * path = [documentsDirectory stringByAppendingPathComponent:filename];
> BOOL didWriteSuccessfull = [data writeToFile:path atomically:YES];
> return didWriteSuccessfull;
> }
>

12.iOS 的沙盒目录结构是怎样的? App Bundle 里面都有什么?

沙盒结构

  • Application:存放程序源文件,上架前经过数字签名,上架后不可修改
  • Documents:常用目录,iCloud备份目录,存放数据,这里不能存缓存文件,否则上架不被通过
  • Library
    • Caches:存放体积大又不需要备份的数据,SDWebImage缓存路径就是这个
    • Preference:设置目录,iCloud会备份设置信息
  • tmp:存放临时文件,不会被备份,而且这个文件下的数据有可能随时被清除的可能

App Bundle

  • Info.plist:此文件包含了应用程序的配置信息.系统依赖此文件以获取应用程序的相关信息
  • 可执行文件:此文件包含应用程序的入口和通过静态连接到应用程序target的代码
  • 资源文件:图片,声音文件一类的
  • 其他:可以嵌入定制的数据资源

13.如何把一张大图缩小为 1/4 大小的缩略图?

1
2
3
> UIImagePNGRepresentation(UIImage __nonnull image);
> UIImageJPEGRepresentation(UIImage __nonnull image, CGFloat compressionQuality);
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
> //等比例缩放图片
> +(UIImage *)cropImage:(UIImage *)image scale:(CGFloat)scale{
> CGSize newSize = CGSizeMake(image.size.widthscale, image.size.heightscale);
>
> UIGraphicsBeginImageContext(newSize);
>
> [image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
>
> image = UIGraphicsGetImageFromCurrentImageContext();
>
> UIGraphicsEndImageContext();
>
> return image;
>
> }
>

14.Objective-C 的 class 是如何实现的?Selector 是如何被转化为 C 语言的函数调用的?

Class是一个objc_class结构类型的指针;

类是通过struct objc_class结构实现的

1
2
> typedef struct objc_class *Class;
>

15.dispatch_async 与 dispatch_sync 解释一下?

asyne 是异步 sync 是同步

1
2
3
4
5
NSLog(@"1");
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"2");
});
NSLog(@“3”);

打印结果是?

​ 132

​ 把 async 换成 sync 打印结果是?

打印结果是1 然后崩溃了(线程死锁)

​ dispatch_async中加入dispatch_get_main_queue中,是异步运行中把主队列中的运行任务放到主线程中去运行。

而dispatch_sync中的dispatch_get_main_queue,是在主线程中运行主队列,当把主队列中得任务放到主线程时,会和dispatch_sync进行线程争夺。这时就会产生线程死锁。