2024面试周期总结一

前言

笔者一直很懒,恰逢最近又一次离职,躺平了一段时间后,终于出来面试了。所以整理了一些面试遇见的问题,希望自己也希望大家能用得到。

一、iOS的三大特性

  • 封装:是指将对象的属性和方法隐藏在对象内部,并通过对象提供的接口进行访问。这种特性有助于降低代码之间的耦合度,提高代码的可维护性和安全性。在iOS开发中,封装意味着通过访问修饰符(如public、private、protected)来控制类成员(包括属性和方法)的可见性和可访问性。封装的好处包括提高数据的安全性、降低代码的耦合率,以及允许代码的重复使用。

  • 继承:是面向对象编程中的一个概念,它允许一个类(称为子类)继承另一个类(称为父类)的属性和方法。这样,子类可以重用父类的代码,同时还可以添加自己特有的属性和方法。继承有助于减少代码冗余,提高代码的可重用性,并支持代码的模块化设计。

  • 多态:是指允许用父类类型的引用来操作子类的对象,这是通过方法的动态绑定实现的。在运行时,根据对象的实际类型来确定调用哪个实现的方法。多态提供了更大的灵活性,使得程序可以根据需要使用不同类型的对象,而无需修改原有的代码。

这些特性共同构成了面向对象编程的基础,使得iOS开发能够更加灵活、高效地处理复杂的问题和需求。

二、六大设计原则

  • 单一职责原则:每个类或对象应该只有一个引起变化的原因,即每个类应该只有一个职责。例如,在iOS开发中,CALayer负责动画和视图的显示,而UIView则负责事件传递和响应,两者各有其明确的职责。

  • 开闭原则:系统应对扩展开放,对修改关闭。这意味着当需求变化时,应通过添加新的代码来扩展功能,而不是修改已有的代码。

  • 依赖倒置原则:高层模块不应该依赖于低层模块的实现细节,而应该依赖于抽象。这有助于减少代码之间的耦合,提高系统的可维护性。

  • 里氏替换原则:子类必须能够替换它们的基类。这意味着在软件设计中,如果基类能够正常工作的代码,那么用它的子类替换后也应该能够正常工作。

  • 接口隔离原则:客户端不应该依赖于它不需要的接口。通过将庞大的接口拆分成更小的、更具体的接口,可以提高系统的可维护性和可扩展性。

  • 迪米特法则:一个对象应当对其他对象尽可能少的了解。这有助于减少对象之间的耦合,提高系统的内聚性和可维护性。

这些原则是iOS开发中的基本指导原则,旨在提高软件的可维护性、可扩展性和稳定性。遵循这些原则可以帮助开发者构建高质量的应用程序。

三、AppDelegate的生命周期

AppDelegate的生命周期可以概括为以下几个主要方法:

  • application:didFinishLaunchingWithOptions:
    应用程序启动后,系统调用这个方法。可以在这里进行一些初始化设置,例如添加窗口、设置根视图控制器等。

  • applicationDidBecomeActive:
    应用程序进入前台时,系统调用这个方法。这是开始执行应用程序核心逻辑的好地方。

  • applicationWillResignActive:
    应用程序即将进入非活跃状态(如进入后台)时,系统调用这个方法。通常在这里保存用户数据、暂停游戏或多媒体播放。

  • applicationDidEnterBackground:
    应用程序已经进入后台时,系统调用这个方法。这是进行资源释放、保存状态等操作的好地方。

  • applicationWillEnterForeground:
    应用程序即将进入前台(从后台返回到前台)时,系统调用这个方法。

  • applicationDidReceiveMemoryWarning:
    应用程序接收到内存警告时,系统调用这个方法。在这里,你可以释放缓存、关闭不需要的视图控制器等,以减少内存使用。

  • applicationWillTerminate:
    应用程序即将终止时,系统调用这个方法。这是清理工作的最后机会,比如保存用户设置、结束网络连接等。

四、单例的优缺点

  • 单例模式的优点:包括提供全局唯一实例的访问点,确保一个类只有一个实例存在,节省系统资源,避免多个实例之间的冲突和不一致性,管理共享的状态或数据,以及延迟实例化(只在需要时创建实例,避免不必要的资源消耗)。此外,单例模式还提供了一个全局访问点,使得其他对象可以方便地获取单例类的实例,确保了系统的性能提升,因为只创建一次实例,所以会常驻内存之中,在频繁使用该类的时候可以提升系统的性能。单例模式适用于需要共享一份资源的情况,如系统的UIApplication、NSNotificationCenter、NSBundle、NSFileManager、NSUserDefaults等,以及用户信息管理、网络请求、数据查询、日志管理等场景。

  • 单例模式的缺点:首先,它的可扩展性较差,不易于进行重写或扩展,但可以通过分类来实现一定的扩展。其次,单例类的实例是全局共享的,对单例类的修改可能会影响到系统的其他部分,使得扩展和维护变得困难。此外,单例模式违背了单一原则,因为只存在一个实例,职责过重。最后,单例实例一旦创建,对象指针被保存在静态区,在堆区申请分配的内存空间只有当程序运行结束后才会被释放,这可能导致内存管理上的问题。

综上所述,单例模式在iOS开发中具有其独特的优势和适用场景,但同时也需要注意其潜在的问题和限制,以确保代码的可维护性和扩展性。

五、自动释放池

六、isa指针的结构

七、引用计数的原理

八、load 和 initialize 的区别

九、Swift和OC的区别

十、综合题

  1. 问:下面代码输出的结果是什么?(这是这些天面试,遇见中最喜欢的一道题。)
- (void)textAction {
    dispatch_queue_t queue = dispatch_queue_create("myqueue", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"1");
    dispatch_async(queue, ^{
        dispatch_async(queue,^{
            NSLog(@"3");
        });
        [self performSelector:@selector(action:) withObject:@4 afterDelay:0];
        [self performSelector:@selector(action:) withObject:@5];
        dispatch_sync(queue, ^{
            NSLog(@"6");
        });
        NSLog(@"7");
    });
    NSLog(@"8");
}

- (void)action:(NSNumber*)num {
    NSLog(@"%ld",num.integerValue);
}

解析:因为没有固定答案,所以一点点说。

  • 首先输出1,这没有什么好说的;

  • 然后说3,3是异步线程,所以3的位置也不确定。如果忽略1和8,那么3可能出现在3~7中的任一位置。

  • 4很有意思,4不会出现。因为afterDelay:等同于NSTimer,子线程中创建的NSTimer需要加入到对应线程的RunLoop中,而子线程RunLoop的运行需要手动开启[[NSRunLoop currentRunLoop] run];

  • 其次是5,在3~7的子线程中,串行执行,所以正常输出,没什么好说的。

  • 6在同步线程中,所以在3~7的子线程中,依然串行执行,所以6在5之后正常输出,没什么好说的。

  • 最后是7,和5一样,在3~7的子线程中,串行执行,所以7在6之后正常输出。

  • 最后的最后,我们说说8。并发队列,且1和8中间代码为异步线程,所以并发、异步导致8并不确定。8有两种触发方式:
    1.是8先触发,3至7的异步线程后执行,那么此时8在1之后,排第二位:1、8、3至7。
    2.是8后触发,3至7的异步线程先执行,恶心的地方来了,因为3本身还是异步线程,所以3不确定,那么就有可能出现1、3、8、5、6、7;(且因为3至7是并发、异步子线程,考虑到并发的机制是来回线程切换执行,所以我认为8可能出现在1之后的任一位置(未验证))。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容