iOS面试问题第2部分:ObjectiveC🖥

在前面的第1部分“差异化”中,我们讨论了许多术语,这些术语用于更好地说明情况。 在这一部分中,我们将重点介绍一些特定的Objective-C和iOS应用开发概念。

@synchronized指令是在Objective-C代码中动态创建互斥锁的便捷方法。 @synchronized指令执行任何其他互斥锁将执行的操作- 防止不同的线程同时获取同一锁。

官方Apple文件

选择器是用于选择要为对象执行的方法的名称,或者是唯一的标识符,用于在编译源代码时替换该名称。 选择器本身不会执行任何操作。 它仅标识一种方法。 使选择器方法名称不同于纯字符串的唯一事情是编译器确保选择器是唯一的

详细信息:Apple Doc

捆绑软件是用于在Mac OS X上打包软件的结构。应用程序,框架和插件是所有不同种类的捆绑软件。 如果您愿意,捆绑软件可能包含可执行代码,资源,头文件和其他内容(包括其他捆绑软件)。

捆绑软件以具有定义结构的目录树的形式实现。 应用程序,框架和插件在树的结构上各有不同。 但是,对于Finder,捆绑包看起来像单个文件。

主捆绑包只是正在运行的应用程序的捆绑包。 因此,例如,Apple邮件程序的主要捆绑包是/Applications/Mail.app。

苹果开发者文档:NSBundle和mainBundle

“ id”是Objective-C中对象标识符的数据类型 ,它可以用于任何类型对象,无论其本机或原始类型是什么类。 “ id”是所有对象的最终超类型。

详细实施。

借助UIApplicationMain函数将控件移交给UIKit框架。

它创建或启动了我们应用程序的几个核心对象。 它从可用的Storyboard加载UI 。 它允许自定义代码(AppDelegate)进行一些初始设置 。 它还通过将应用程序运行循环设置为运动来启动应用程序 。 我们需要为UIAplicationMain函数提供Storyboard文件和自定义初始化代码。

它是iOS应用程序的心脏,使系统与应用程序的其他对象之间的交互变得容易。

使用NSUserDefaults类,可以保存与应用程序或用户数据有关的设置和属性 。 例如,您可以保存用户设置的配置文件图像或应用程序的默认配色方案。 对象将保存在iOS的“默认系统”中 。 iOS默认系统在您应用中的所有代码中都可用,并且保存到默认系统的所有数据将在应用程序会话中保留。 这意味着,即使用户关闭了您的应用程序或重新启动了手机,下一次打开该应用程序时,保存的数据仍然可用!

使用NSUserDefaults,可以保存以下类类型的对象: NSData,NSString,NSNumber,NSDate,NSArray,NSDictionary

如果要存储任何其他类型的对象,例如UIImage,则通常需要将其归档或包装在NSData,NSNumber或NSString的实例中。

苹果开发人员文档

根据更新的 Apple文档 ,大小为4KB。

  • 对于常规的远程通知,最大大小为4KB(4096字节)
  • 对于互联网协议语音(VoIP)通知,最大大小为5KB(5120字节)。

注意:如果您使用旧版APNs二进制接口发送通知而不是HTTP / 2请求,则最大有效负载大小为2KB(2048字节)

服务质量 API实际上是在iOS 8中引入的。QoS也可以在整个iOS上应用。 可以区分队列,线程对象,调度队列和POSIX线程的优先级。 这很重要,因为异步工作通常分散在所有这些技术中。 通过为这些方法执行的工作分配正确的优先级,iOS应用程序将保持快速,灵活和响应的状态。

  • 用户交互 :在主线程上发生的工作,例如动画或绘图操作。
  • 用户发起的用户启动的工作,应立即产生结果。 必须完成此工作,用户才能继续。
  • 实用程序 :可能需要一点时间并且不需要立即完成的工作。 类似于进度条和导入数据。
  • 背景 :用户看不到此项工作。 备份,同步,索引编制等
  • 默认值 :此QoS的优先级介于用户启动和实用程序之间。 未分配QoS信息的工作被视为默认工作,并且GCD全局队列在此级别运行。
  • 未指定 :这表示没有QoS信息,提示系统应该推断环境QoS。

官方文档可以在这里找到。 优势很少:

  • 收到VoIP推送后,如果应用未运行,则会自动重新启动该应用
  • 仅在进行VoIP推送时才唤醒设备(节省电池)
  • VoIP推送直接进入您的应用程序进行处理,并且立即交付
  • 收到VoIP推送后,如果应用未运行,则会自动重新启动该应用

Apple为我们提供了一个名为PushKit的框架,以支持使用此VoIP推送功能。 我们还需要生成VoIP推送证书以进行实施。

动态调度是选择哪种实现过程
是在运行时调用的方法或函数的多态操作。
这意味着,当我们想调用对象方法之类的方法时,Swift不支持直接动态分配。

  • 我们创建对象,然后将它们序列化到磁盘。
  • 这是很好的,非常有限的用例。
  • 我们显然不能使用复杂的查询来过滤结果。
  • 非常慢
  • 每当我们需要某些东西时,我们都需要对其进行序列化或反序列化。
  • 它不是线程安全的。

在Swift中,错误是在do-catch块内引发和处理的。

由于代码被调度到后台线程,因此我们需要捕获对正确对象的引用

深度链接是一种将数据从任何平台(如网站或任何其他应用程序)传递到您的应用程序的方法。 通过点击一次链接,您可以将必要的数据传递到您的应用程序。

设置子类存储属性后,我们使用super关键字调用父类初始化程序

  • 我们可以添加音频,视频和图像。
  • 我们可以为通知创建自定义界面。
  • 我们可以通过通知中心中的界面管理通知。
  • 新的通知扩展允许我们在交付远程通知有效负载之前对其进行管理。

位置,日历和时间间隔 。 当手机上的GPS处于某个位置或地理区域时,会触发位置通知。 日历触发器基于细分为日期组件的日历数据。 时间间隔是秒数,直到计时器关闭。

通过向对象发送自动释放消息,该消息将被添加到本地AutoReleasePool中,您不再需要担心,因为当AutoReleasePool被销毁(在主运行循环上)时,该对象将收到释放消息(保留计数为如果RetainCount变为零,则垃圾回收器将破坏bject。 @ Kibitz503

释放:当我们向对象发送释放消息时,保留计数减少一。

当池本身耗尽时, 自动 release池将存储对象并发送release消息。 如果使用自动引用计数(ARC),则不能直接使用自动释放池。 而是使用@autoreleasepool块。 例如,代替:

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // Code benefitting from a local autorelease pool. [pool release];

你会写:

@autoreleasepool { // Code benefitting from a local autorelease pool. }

@autoreleasepool块比直接使用NSAutoreleasePool实例更有效; 即使不使用ARC,也可以使用它们。

自动释放池块提供了一种机制,您可以通过该机制放弃对象的所有权,但可以避免将其立即释放的可能性(例如,从方法返回对象时)。 该池的 drain 操作将在每个主运行循环结束时进行 。 这种延迟释放在开发中是必需的,有时我们希望变量的生存期比其范围长(例如被调用函数),因此我们可以在以后继续使用它。

在Swift中,UIApplicationMain处理应用程序的自动释放池,我们不必自己使用自动释放。 但是在某些特殊情况下,我们可能需要添加自己的自动释放池。

在哪里使用自动释放池? 考虑需要循环处理许多图像的情况。

 func processDirectory() { 
if let path = NSBundle.mainBundle()
.pathForResource("directory", ofType: "txt") {
for i in 1...90000 {
let data = NSData.dataWithContentsOfFile(
path, options: nil, error: nil)
self.processData()
}
}
}

在这里,我们在循环中使用大小很大的数据变量。 这可能会占用大量设备内存。 为了优化此代码,我们可以如下添加自动释放池:

 func processDirectory() { 
if let path = NSBundle.mainBundle()
.pathForResource("directory", ofType: "txt") {
for i in 1...90000 {
autoreleasepool { () -> Result in
let data = NSData.dataWithContentsOfFile(
path, options: nil, error: nil)
self.processData()
}
}
}
}

在这里,我们在自动释放池中使用数据,当for循环的作用域结束时,for循环的每次迭代都会消耗掉数据。 由于在每个循环中释放一小部分内存可能会导致性能问题,因此我们可以进一步对其进行优化,以在每n次迭代中使用自动释放池,而不是在每次迭代中使用。

注意:@autoreleasepool的作用域结束时,该作用域中的所有对象将发送到主Autoreleasepool,当当前 运行循环 终止 时,该对象将被清空

混乱是通常通过在运行时将方法的实现替换为另一方法来更改方法功能的行为。 一个人可能想使用混乱的原因有很多:自省,覆盖默认行为甚至动态方法加载。 这种动态程序修改类似于其他修补语言支持的概念“猴子修补”。

Michael Marvis的实现示例。

  • 苹果文件
  • NSHispter

通过使用__block存储类型。 详细信息:Apple Doc

主队列非常适合更新UI和获取图像,而任何其他繁重的任务都应在后台线程上运行。

 dispatch_async(dispatch_get_global_queue(0, 0), ^{ 
//load your data here.
dispatch_async(dispatch_get_main_queue(), ^{
//update UI in main thread.
});
});

几个可可类,包括收集类,都采用NSFastEnumeration协议。 您可以使用它来检索实例持有的元素,其语法类似于标准C for循环的语法,如以下示例所示:

  NSArray * anArray = //获取一个数组; 
对于(anArray中的id元素){
/ *作用于元素的代码* /
}

在Objective-C中, 实例方法以“-”开头, 类级别方法以“ +”开头

实例方法是属于特定类,结构或枚举的实例的函数。 它们通过提供访问和修改实例属性的方式,或者通过提供与实例目的相关的功能,来支持那些实例的功能。

类方法是在类型本身上调用的,也称为类型方法。 您可以通过在方法的func关键字之前编写关键字class来指示类的类型方法,并通过在方法的func关键字之前编写关键字static来指示结构和枚举的类型方法。

子类可以覆盖 class 方法,因为它们是动态分派的 。 我们不能覆盖 static 方法class理论上讲, class属性将以相同的方式起作用(子类可以覆盖它们),但是在Swift中尚无法实现。 静态的可以认为是“班级决赛”

苹果文件

CocoaPods是 Swift和Objective-C Cocoa项目的依赖项管理器 。 CocoaPods是使用Ruby构建的,并且可以使用OS X上可用的默认Ruby安装。

项目的依赖关系在称为Podfile的单个文本文件中指定。 CocoaPods将解决库之间的依赖关系,获取生成的源代码,然后将其链接到Xcode工作区中以构建您的项目。

安装指南。

注意:依赖关系管理器使添加,删除,更新和管理应用程序使用的第三方依赖关系变得容易。

TestFlight Beta Testing是一种Apple产品,可让您轻松邀请用户测试您的iOS,watchOS和tvOS应用程序,然后再将其发布到App Store中。 仅使用他们的电子邮件地址,您最多可以邀请10,000名测试人员。

TestFlight SDK还允许开发人员接收远程日志,崩溃报告和测试人员反馈。

苹果文件

HTTP是网站用来将数据从Web服务器传输到客户端的应用程序协议或规则集。 客户端(您的Web浏览器或应用)用于指示所需的操作:

  • GET :用于检索数据,例如网页,但不更改服务器上的任何数据。
  • HEAD :与GET相同,但仅发送回标头,不发送任何实际数据。
  • POST :用于将数据发送到服务器,通常在填写表单并单击提交时使用。
  • PUT :用于将数据发送到提供的特定位置。
  • 删除 :从提供的特定位置删除数据。

框架具有三个主要目的:

  • 代码封装
  • 代码模块化
  • 代码重用

您可以与其他应用程序,团队成员或iOS社区共享框架。 与Swift的访问控制结合使用时,框架可帮助定义代码模块之间强大的,可测试的接口。

  • 位置发生重大变化-大约每500米(通常可达1公里)交付一次位置
  • 区域监视—跟踪半径等于或大于100m的圆形区域的进/出事件。 区域监视是继GPS之后最精确的API。
  • 拜访事件—监视场所拜访事件是从场所(家庭/办公室)进入或离开的事件。

持续集成(CI)是一种开发实践,要求开发人员每天几次将代码集成到共享存储库中。 然后,每个签入均由自动构建进行验证,从而使团队可以及早发现问题。 有很多持续集成工具,例如Xcode服务器,Jenkins,Travis,Fastlane等。

钥匙串是一种用于将数据安全存储在iOS App中的API。 有一个很好的图书馆— 锁匠

没有键盘输入。 不推荐滚动视图和多步操作

我们不能将动态文本与辅助功能一起使用。

accessibilityHint 描述与用户界面元素进行交互的结果。 当交互结果从元素标签中不明显时, 应提供提示。

UNNotification Content将通知内容存储在计划的或已交付的通知中。 它是只读的。

UIKit提供了两个用于创建粒子效果的类: CAEmitterLayerCAEmitterCellCAEmitterLayer是发射,设置动画和渲染粒子系统的层。 CAEmitterCell表示源,并定义所发射粒子的方向和属性。

  • 易耗品:可以多次购买,用过的物品必须重新购买。
  • 非消耗性产品:如果用户出于任何原因需要重新安装该应用,则将来可以恢复此功能。 我们还可以向我们的应用添加订阅。
  • 非续订订阅 :用于一定时间和某些内容。
  • 自动续订 :用于定期每月订阅。

HealthKit是iOS上的框架。 它在中央位置存储健康和健身数据。 它从多个来源(可能是不同的设备)接收数据。 它允许用户控制对其数据的访问,并维护用户数据的隐私。 数据在手机和手表之间同步。

目前,神经网络和深度学习为图像识别,语音识别和自然语言处理中的许多问题提供了最佳解决方案。

Core ML是iOS 11附带的iOS框架,有助于处理应用中的模型以进行人脸检测。 有关更多信息,请遵循此指南https://developer.apple.com/machine-learning/

这些文件将帮助我们通过应用内购买对我们的收据验证文件进行设备验证。

Xcode服务器将自动签出我们的项目,构建应用程序,运行测试,并存档应用程序以进行分发。

我们可以创建,播放音频和视频媒体。 AVFoundation使我们可以处理基于时间的视听数据的详细级别。 有了它,我们可以创建,编辑,分析和重新编码媒体文件。 AVFoundation有两套API,一套是视频,一套是音频。

Xcode项目模板将其声明为UIResponder的子类。

屏幕上一个或多个视图的呈现由UIWindow对象协调。

图层对象是代表可视内容的数据对象。 视图使用图层对象来呈现其内容。 也可以添加它们以实现复杂的动画和其他类型的复杂视觉效果。

iOS 10,11 (Swift 3.1,Swift 4.0)

根据Apple开发人员文档中的UIViewController

1. loadView():如果子类不使用笔尖,则应在此处创建其自定义视图层次结构。 永远不要直接调用。

2. loadViewIfNeeded():如果尚未设置视图控制器的视图,则将其加载。

3. viewDidLoad():在加载视图后调用。 对于用代码创建的视图控制器,该命令位于-loadView之后。 对于从笔尖未存档的视图控制器,这是在设置视图之后。

4. viewWillLayoutSubviews():在调用视图控制器的视图的layoutSubviews方法之后立即调用。 子类可以根据需要实现。

5. viewDidLayoutSubviews():在将大小,位置和约束应用于所有对象时调用。

6. viewWillAppear(_ animation:Bool):在视图将变为可见时调用。 默认不执行任何操作

7. viewDidAppear(_ animation:Bool):当视图完全过渡到屏幕上时调用。 默认不执行任何操作

8. viewWillDisappear(_ animation:Bool):在关闭,覆盖或隐藏视图时调用。 默认不执行任何操作

9. viewDidDisappear(_ animation:Bool ):在视图被关闭,覆盖或其他隐藏之后调用。 默认不执行任何操作

10. viewWillTransition(大小:CGSize,协调器:UIViewControllerTransitionCoordinator):在视图过渡时调用。

11. willMove(toParentViewController parent:UIViewController?)

12. didMove(toParentViewController parent:UIViewController?)

在子控制器之间转换时,这两个方法是公共的,供容器子类调用。 如果它们被覆盖,则覆盖应确保调用超级。

当从其父级中移除子级时,这两种方法的父级参数均为nil。 否则,它等于新的父视图控制器。

13. didReceiveMemoryWarning():在父应用程序收到内存警告时调用。 在iOS 6.0上,默认情况下将不再清除视图。

未运行:该应用尚未启动或正在运行,但已被系统终止。

不活动(前台):该应用程序在前台运行,但当前未接收事件。 (尽管它可能正在执行其他代码。)应用程序通常仅在过渡到其他状态时短暂停留在此状态。

活动(前台):该应用程序正在前台运行并正在接收事件。 这是前台应用程序的正常模式。

后台:该应用程序在后台执行代码。 大多数应用在暂停状态下都会短暂进入此状态。 但是,请求额外执行时间的应用程序可能会在此状态下保留一段时间。 此外,直接在后台启动的应用会进入此状态,而不是处于非活动状态。 有关如何在后台执行代码的信息,请参阅后台执行。

已暂停:该应用程序在后台运行,但未执行代码。 系统会自动将应用程序移至此状态,并且在执行操作之前不会通知它们。 挂起时,应用程序仍保留在内存中,但不执行任何代码。

application:willFinishLaunchingWithOptions: —此方法是您的应用在启动时执行代码的第一个机会。

application:didFinishLaunchingWithOptions: —使用此方法,您可以在将应用显示给用户之前执行任何最终初始化。

applicationDidBecomeActive :—让您的应用知道它即将成为前台应用。 在任何最后的准备中都可以使用此方法。

applicationWillResignActive :—让您知道您的应用程序正在从作为前台应用程序过渡。 使用此方法可使您的应用程序进入静态状态。

applicationDidEnterBackground :—让您知道您的应用程序现在正在后台运行,并且可能随时挂起。

applicationWillEnterForeground :—让您知道您的应用程序正在移出后台,又移回了前台,但尚未激活。

applicationWillTerminate :—让您知道您的应用程序正在终止。 如果您的应用已暂停,则不会调用此方法。

Apple App生命周期文档

保留周期两个对象相互引用并保留时的条件由于两个对象都试图相互保留,因此无法释放, 因此创建了一个保留周期

Matt Gallagher的精彩文章。


谢谢阅读! 我希望这将有助于理解或理解某些iOS概念。 请在下面的评论中分享您的反馈,对任何主题的疑问。 直到addio ! 💚💚💚💚💚💚💚