首页 手机 iOS 查看内容

iOS 开发者 2019 面试总结

2019-4-26 17:14 |来自: 互联网 2651 0

摘要: 在投递简历之前,就是所谓的寒冬将至,开个年会都是守望寒冬,然后我身边的准备跳槽的大佬们,都是有几分凉意,不过我还好,总感觉一个人吃饱,全家不饿,O(∩_∩)O哈!没想那么多,直接就全身投入,找工作。现在做个回顾吧,为自己,也为路过的各位大侠。
关键词:ios

前言

在投递简历之前,就是所谓的寒冬将至,开个年会都是守望寒冬,然后我身边的准备跳槽的大佬们,都是有几分凉意,不过我还好,总感觉一个人吃饱,全家不饿,O(∩_∩)O哈!没想那么多,直接就全身投入,找工作。现在做个回顾吧,为自己,也为路过的各位大侠。


先说一个问题,是寒冬吗?我真没觉得,说自己的一个亲身体会,不夸张的说,基本上是每天2家,且持续一个月,当然是距离可以接受,公司小中大都有的,我感觉不是互联网的寒冬,是自己的寒冬,有一句说的很好,人生就两季,努力是旺季,不努力是淡季!我感觉很有道理~~~~


现在面试要求高在要会各种语言,另外要很深入,要够底层,要懂数据结构与算法之美(面试过的都会体会什么是真是一言难尽吧),看一些大佬,进入一个大厂,也写了自己的准备,我感觉真是有付出有回报的,也看出自己的一些不足吧!so,革命尚未成功,同志们仍需努力伐!!

知识点总结

因为自己水平有限,可能有些路过的大佬感觉比较简单,我也总结了下,请飘过~~还有一些答案仅供参考,如有错误,请不吝赐教,在此谢过---->

+(void)initinstance 与 +(void)load两个方法的区别于比较//小红书面试问题\

先看下面表格两者的区别,后续会继续介绍

+load+initialize
调用时机被添加runtime时收到第一条消息时,可能永远不调用
调用顺序父类->子类->分类父类->子类
调用次数1次多次
是否需要显示调用父类实现
是否沿用父类的实现
分类中的实现类和分类都执行

相同点:

  1. 系统都执行一次。

  2. 假如父类和子类都被调用,父类在子类之前被调用

不同点:

  1. load 方法会在加载类的时候就被调用,也就是 ios 应用启动的时候,就会加载所有的类,就会调用每个类的 + load 方法。

  2. +initialize 这个方法会在 第一次初始化这个类之前 被调用,我们用它来初始化静态变量

  3. load 会在main()函数之前调用。initialize 则在类实例化 或 类方法被调用时调用;

  4. 如果子类中没有initialize方法,则会再次调用父类的initialize方法,类别会覆盖主类的initialize,load则不会被覆盖

  5. load顺序在 initialize之前;

  6. •  initialize 方法的调用看起来会更合理,通常在它里面写代码比在 + load 里写更好,因为它是懒调用的,也有可能完全不被调用。类第一次被加载时,

  7. 类接收消息时,运行时会先检查 + initialize 有没有被调用过。如果没有,会在消息被处理前调用

--->>>>

initialize 最终是通过 objc_msgSend 来执行的,objc_msgSend 会执行一系列方法查找,并且 Category 的方法会覆盖类中的方法

load 是在被添加到 runtime 时开始执行,父类最先执行,然后是子类,最后是 Category。又因为是直接获取函数指针来执行,不会像 objc_msgSend 一样会有方法查找的过程。

---->>>>

怎么实现单例, 2种方法实现//喜马拉雅面试问题\

//第一种方式:线程安全的单例2(不推荐 效率低)
+ (instancetype)shareSingleton2 {
@synchronized(self) {
if (!singleton) {
singleton = [[self alloc]init];
}
}
return singleton;
}

//第二种方式 线程安全的单例
+ (instancetype)shareSingleton {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
singleton = [[self alloc]init];
});
return singleton;
}

然而仅仅知道这些是不够的,说了上面的,面试官会继续问单例,怎么实现的,加锁了吗?单例什么时候释放?然后你就会一脸懵~有同感的举个手

  • 单例,怎么实现的,加锁了吗?单例什么时候释放

    其实在上面的两个单例的创建中,@synchronized是一个锁,后面会讲到,就是说第一种是通过加锁的方式来实现,而第二种解析如下:

    GCD创建:dispatch_once中dispatch_once_t类型为typedef long

    •   onceToken= 0,线程执行dispatch_once的block中代码

    •   onceToken= -1,线程跳过dispatch_once的block中代码不执行

    •   onceToken= 其他值,线程被线程被阻塞,等待onceToken值改变

    用途:限制创建,提供全局调用,节约资源和提高性能。 参考

    常见的应用场景:

    •   UIApplication

    •   NSNotificationCenter

    •   NSFileManager

    •   NSUserDefaults

    •   NSURLCache

    •   NSHTTPCookieStorage

那么单例是怎么销毁的呢?如下:

方法一:
+(void)attemptDealloc{
[_instance release]; //mrc 需要释放,当然你就不能重写release的方法了.
_instance = nil;
}


方法二:
1. 必须把static dispatch_once_t onceToken; 这个拿到函数体外,成为全局的.
2.
+(void)attempDealloc{
onceToken = 0; // 只有置成0,GCD才会认为它从未执行过.它默认为0.这样才能保证下次再次调用shareInstance的时候,再次创建对象.
[_instance release];
_instance = nil;
}

数据持久化

下面说下数据持久化吧?如果是在2年前,你说了数据持久化有NSUserDefaults,plist,归档,CoreData巴拉巴拉,感觉这位童靴还阔以,但是现在就有点low了,你懂得~

面试大佬会问有几种?然后每种有什么不同?什么能存储什么不能存储?每个在具体使用应该注意什么?等等,问到你怀疑人生

  • 属性列表(plist存储)通常叫做plist文件,用于存储在程序中不经常修改、数据量小的数据,不支持自定义对象存储,支持数据存储的类型为:Array,Dictionary,String,Number,Data,Date,Boolean,通常用来存放接口名、城市名、银行名称、表情名等极少修改的数据

    plist文件是将某些特定的类,通过xml的方式保存在目录中。

  • 偏好设置(NSUserDefaults)

    用于存储用户的偏好设置,同样适合于存储轻量级的用户数据,数据会自动保存在沙盒的Libarary/Preferences目录下,本质上就是一个plist文件,所以同样的不支持自定义对象存储,支持数据存储的类型为:Array,Dictionary,String,Number,Data,Date,Boolean,可以用做检查版本是否更新、是否启动引导页、自动登录、版本号等等,需要注意的是NSUserDefaults是定时的将缓存中的数据写入磁盘,并不是即时写入,为了防止在写完NSUserDefaults后,程序退出导致数据的丢失,可以在写入数据后使用synchronize强制立即将数据写入磁盘

    如果这里你没有调用synchronize方法的话,系统会根据I/O情况不定时刻地保存到文件中。所以如果需要立即写入文件的就必须调用synchronize方法。

PS: 在这里说了小问题,就是有面试官会问,你在开发中用NSUserDefaults有没有什么坑?你可以这样答:比如你存储一个值时,没有进行及时的调用synchronize方法,然后此时程序就crash了或者强制杀死,那么你再下次去取值的时候,就会取不到你之前存储的值,路过的大佬可以试下~~

  • 归档序列化存储

    归档可以直接将对象存储为文件,也可将文件直接解归档为对象,相对于plist文件与偏好设置数据的存储更加多样,支持自定义的对象存储,归档后的文件是加密的,也更加的安全,文件存储的位置可以自定义。

    遵守NSCoding或者NSSecureCoding协议

  • 沙盒存储

    可以提高程序的体验度,为用户节约数据流量,主要在用户阅读书籍、听音乐、看视频等,在沙盒中做数据的存储,主要包含文件夹:Documents: 最常用的目录,存放重要的数据,iTunes同步时会备份该目录Library/Caches: 一般存放体积大,不重要的数据,iTunes同步时不会备份该目录Library/Preferences: 存放用户的偏好设置,iTunes同步时会备份该目录tmp: 用于存放临时文件,在程序未运行时可能会删除该文件夹中的数据,iTunes同步时不会备份该目录

  • Core Data

    Core Data是框架,并不是数据库,该框架提供了对象关系的映射功能,使得能够将OC对象转换成数据,将数据库中的数据还原成OC对象,在转换的过程中不需要编写任何的SQL语句,在Core Data中有三个重要的概念:

    NSPersistentStoreCoordinator:持久化存储协调器,在NSPersistentStoreCoordinator中包含了持久化存储区,在持久化存储区中包含了数据表中的很多数据,持久化存储区的设置通常选择NSSQLiteStoreType,也就是选择SQLite数据库

    NSManagedObjectModel:托管对象模型,用于描述数据结构的模型

  • SQLite3

    SQLite是轻量级的数据库,占用资源很少,最初是用于嵌入式的系统,在iOS中使用SQLite,需要加入"libsqlite3.tbd"依赖库并导入头文件。不应该频繁的打开关闭数据库,有可能会影响性能, 应在启动程序时打开数据库,在退出程序是关闭数据库

  • FMDB

    FMDB以OC的方式封装了SQLite的C语言API,减去了冗余的C语言代码,使得API更具有OC的风格,更加的面向对象,相对于Core Data框架更加的轻量级,FMDB还提供了多线程安全的数据库操作方法,在FMDB中有三个重要的概念:

    FMDatabase:一个FMDatabase就代表一个SQLite数据库,执行sql语句

    FMResultSet:执行查询后的结果集

    FMDatabaseQueue:用于在多线程中执行多个查询或更新,安全的

===

紧接着说下CoreData吧?它总是比你知道的还要多?

CoreData中的多线程问题

主要推荐的实施方案,也是最优方案,如下:

1.使用一个NSPersistentStoreCoordinator,以及两个独立的Contexts,一个context负责主线程与UI协作,一个context在后台负责耗时的处理,用Notifications的方式通知主线程的NSManagedObjectContext进行mergeChangesFromContextDidSaveNotification操作

2.后台线程做读写更新,而主线程只读

3.CoreData中的NSManagedObjectContext在多线程中不安全,如果想要多线程访问CoreData的话,最好的方法是一个线程一个NSManagedObjectContext,每个NSManagedObjectContext对象实例都可以使用同一个NSPersistentStoreCoordinator实例,这个实例可以很安全的顺序访_问永久存储,这是因为NSManagedObjectContext会在便用NSPersistentStoreCoordinator前上锁。ios5.0为NSManagedObjectContext提供了initWithConcurrentcyType方法,其中的一个NSPrivateQueueConcurrencyType,会自动的创建一个新线程来存放NSManagedObjectContext而且它还会自动创建NSPersistentStoreCoordinator,

CoreData里面还带有一个通知NSManagedObjectContextDidSaveNotification,主要监听NSManagedObjectContext的数据是否改变,并合并数据改变到相应context。

面试官问的Context是那两种?这个面试官问的应该是用到的那两个Type?

答:NSConfinementConcurrencyType   NSMainQueueConcurrencyType

//创建并行的NSManagedObjectContext对象
[[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];

ps:NSConfinementConcurrencyType (或者不加参数,默认就是这个)NSMainQueueConcurrencyType (表示只会在主线程中执行)

接着谈谈数据库的优化问题,可以通过以下几点进行优化

  • FMDB事务批量更新数据库速度问题。(亲测可以呀---740条数据用和不用事务效率差别20倍+)

  • 写同步(synchronous)

    在SQLite中,数据库配置的参数都由编译指示(pragma)来实现的,而其中synchronous选项有三种可选状态,分别是full、normal、off

    设置为synchronous OFF (0)时,SQLite在传递数据给系统以后直接继续而不暂停

  • 一条SQL语句插入多条数据

  • 在事务中进行插入处理。

  • 数据有序插入。

再说下什么是事务?\英语流利说总监面试问题//


事务:

  • 作为单个逻辑工作单元执行的一系列操作,而这些逻辑工作单元需要具有原子性,一致性,隔离性和持久性

  • 是并发控制的基本单元。所谓的事务,它是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单元。例如,银行转账工作:从一个账号扣款并使另一个账号增款,这两个操作要么都执行,要么都不执行。所以,应该把它们看成一个事务。

  • 事务是一种机制,用于维护数据库的完整性

事务基本特征:

  • 原子性(Atomicity):事务的个元素是不可分的,事务是一个完整的操作,一个操作序列,要么都执行,要么都不执行

  • 一致性(Consistemcy):事务完成时,数据必须是一致的,保证数据的无损

  • 隔离性(Isolation):多个事务彼此隔离,事务必须是独立的,任何事务都不应该受影响

  • 持久性(Durability):事务完成之后,它对于系统的影响是永久的,该修改即使出现系统故障也将一直保留,真实的修改了数据库

五种 Mach-O 类型的浅要分析

这个面试题针对我自己的简历,可略过~

在制作Framework时,可以设置framework中的Mach-O Type,不手动修改的默认配置即为 Dynamic Library,在SDK中默认使用的是 Relocatable Object File

Executable: 可执行二进制文件

dynamic Library 动态库

Bundle :非独立二进制文件,显示加载

static Library 静态库

Relocatable Object File: 可重定位的目标文件,中间结果

Relocatable Object File 是组装静态库和动态库的零件,而静态库和动态库就是可执行二进制文件的组件。这里用了零件和组件的概念,零件是不可缺少的,组件则是可选的

Dynamic Library 更灵活;复用性更强;且就安全来说,统一放置在 Payload/Framework 目录下的自建的动态库,不参与应用的加壳操作,安全性稍逊一筹

Relocatable Object File 以及 Static Library 都是在编译后直接合并到最后的可执行文件中的,缺点相对不够灵活,但安全性稍强。

如果要偏向静态的方案,应该选择 Relocatable Object File 还是 Static Library?

使用 Relocatable Object File 可以减少二进制文件的大小

动态库和静态库的区别:

如果使用动态库,需要考虑的是:

  1. 对于启动速度的影响。

  2. 对于保密要求高的线下渠道 SDK,可能会被从 .app/ 中单独拿出来,反编译研究具体实现。静态库则比较安全一点。

内存管理

Objective-C的内存管理主要有三种方式ARC(自动内存计数)、手动内存计数、内存池。

1). 自动内存计数ARC:由Xcode自动在App编译阶段,在代码中添加内存管理代码。

2). 手动内存计数MRC:遵循内存谁申请、谁释放;谁添加,谁释放的原则。

3). 内存释放池Release Pool:把需要释放的内存统一放在一个池子中,当池子被抽干后(drain),池子中所有的内存空间也被自动释放掉。内存池的释放操作分为自动和手动。自动释放受runloop机制影响。

有一个很经典的面试题,考察自动释放池的如下:

 for (int i = 0; i < MAXFLOAT; i++) {

NSString *string = @"stdy";
string = [string lowercaseString];
string = [string stringByAppendingString:@"123"];
NSLog(@"--%@", string);
}

上述的这种写法,会使内存慢慢增加,如何解决呢,面试官想要的答案就是用自动释放池,你也可以改成其他的,但不是面试官要的,你懂的,修改如下:

for (int i = 0; i < MAXFLOAT; i++) {
@autoreleasepool {
NSString *string = @"stdy";
string = [string lowercaseString];
string = [string stringByAppendingString:@"123"];
NSLog(@"--%@", string);
}
}
  • 什么时间会创建自动释放池?*

    从程序启动到加载完成是一个完整的运行循环,然后会停下来,等待用户交互,用户的每一次交互都会启动一次运行循环,来处理用户所有的点击事件、触摸事件,运行循环检测到事件并启动后,就会创建自动释放池。

    子线程的 runloop 默认是不工作,无法主动创建,必须手动创建。

    自定义的 NSOperation 和 NSThread 需要手动创建自动释放池。比如:自定义的 NSOperation 类中的 main 方法里就必须添加自动释放池。否则出了作用域后,自动释放对象会因为没有自动释放池去处理它,而造成内存泄露。

声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系 [邮箱地址] 删除


路过

雷人

握手

鲜花

鸡蛋

最新评论

返回顶部