1.项目选一点来说,技术指标?了解业务指标吗?
2.KVO原理,应用场景
3.Runtime、应用,Runtime 交换方法需要注意什么?Runtime 的 hook 和 fish hook 有什么区别?
4.Runloop、应用、Runloop 来监测卡顿有什么问题?
5.UIView 和 CALaye r的区别,为什么这么设计?
6.显式动画和隐式动画的区别
7.了解的设计原则和设计模式,结合项目
8.block 的本质
9.算法:查找字符串的无重复字符子串的最大长度
参考答案
1.项目选一点来说,技术指标?了解业务指标吗?
2.KVO原理,应用场景
- 动态生成子类:当你观察某个对象的属性时,Objective-C Runtime会动态创建该对象的一个子类,并重写被观察属性的setter方法。
- 方法重写:在重写的setter方法中,KVO机制会插入一些代码,用于通知所有观察者该属性的变化。
- isa指针指向:原始对象的
isa
指针会被修改,指向新创建的子类,以确保调用setter方法时实际调用的是重写后的方法。 - 通知机制:属性值变化时,KVO机制会自动通知所有注册的观察者,调用观察者的
observeValueForKeyPath:ofObject:change:context:
方法。
3.Runtime、应用,Runtime 交换方法需要注意什么?Runtime的 hook 和 fish hook 有什么区别?
iOS Runtime Hook
主要特点
- Method Swizzling: 这是最常见的Runtime Hook技术,通过交换方法实现和自定义方法实现,从而改变类的行为。
- 动态性强: 由于Objective-C Runtime的动态特性,可以在运行时对类和方法进行操作。
- 灵活性高: 可以在不修改原始代码的情况下,对应用程序的行为进行修改。
- 适用范围广: 适用于Objective-C编写的类和方法。
使用场景
- 修改系统或第三方库的行为。
- 监控方法调用和参数。
- 在不破坏原有代码的基础上,添加额外的功能。
Fishhook
概述
Fishhook是由Facebook开源的一个库,主要用于在iOS应用中重绑定符号,从而实现对低层次的C函数进行hook。
主要特点
- 基于符号重绑定: Fishhook通过修改符号表来重新绑定函数指针,从而实现对函数的替换。
- 适用范围: 主要用于C语言层面的函数,如系统调用和标准库函数。
- 低层次: 适用于低层次的函数拦截,而不是面向对象的Method Swizzling。
- 开源库: Fishhook是一个开源库,可以方便地集成到项目中。
使用场景
- 替换系统函数实现,如
malloc
、free
、open
等。 - 监控和修改底层C函数的行为。
- 在动态加载的库中对特定函数进行hook。
总结
- iOS Runtime Hook主要用于Objective-C的方法替换,通过Runtime机制实现,适用于面向对象的环境。
- Fishhook主要用于C函数的符号重绑定,通过修改符号表实现,适用于底层C函数的拦截和替换。
4.Runloop、应用、Runloop 来监测卡顿有什么问题?
使用 RunLoop 来监测卡顿是一个常见的方法,但它也有一些局限性和潜在的问题。以下是一些主要问题:
1. 精度问题
RunLoop 的监测精度有限。RunLoop 的每一次循环都有其自身的周期,如果要监测短暂的卡顿(比如几毫秒),RunLoop 可能无法捕捉到。
2. 影响性能
频繁监测 RunLoop 的状态可能会对应用性能产生影响。尤其在主线程上进行监测时,可能会增加额外的开销,从而影响应用的响应速度。
3. 误报问题
使用 RunLoop 来检测卡顿时,可能会出现误报的情况。例如,后台任务、I/O 操作或者网络请求等可能导致 RunLoop 的阻塞,这些情况未必是真正的 UI 卡顿。
4. 无法检测异步任务
RunLoop 主要监测的是同步任务的执行情况,对于异步任务(如 GCD 的 dispatch_async
或者 NSOperationQueue
)的监测能力有限。这可能导致一些卡顿问题被遗漏。
5. 复杂的实现逻辑
为了准确监测卡顿,可能需要实现复杂的逻辑来处理各种边界情况和特例。这不仅增加了代码的复杂性,也增加了维护的难度。
6. 应用场景有限
RunLoop 监测方法主要应用于主线程的 UI 卡顿检测,对于多线程环境下的卡顿监测效果不佳。因此,如果应用中存在大量的多线程操作,单纯依赖 RunLoop 进行监测可能无法全面覆盖。
典型实现方法
尽管存在上述问题,RunLoop 仍然是一个常用的卡顿监测工具。以下是一个简单的实现思路:
- 观察 RunLoop 的状态:通过监听 RunLoop 的
kCFRunLoopBeforeSources
和kCFRunLoopAfterWaiting
等状态,记录 RunLoop 的执行时间。 - 计算时间差:在每次 RunLoop 状态变化时,计算当前时间与上一次状态变化的时间差。如果时间差超过某个阈值(比如 100 毫秒),则认为发生了卡顿。
- 记录或上报卡顿:将卡顿信息记录到日志中,或者上报到服务器进行分析。
1 | CFRunLoopObserverRef observer; |
runLoopObserverCallback
中可以实现时间差计算和卡顿检测逻辑。
总结
虽然使用 RunLoop 来监测卡顿存在一些问题,但它仍然是一个有用的工具,尤其是在主线程的 UI 卡顿检测方面。为了提高监测的准确性和覆盖面,可以结合其他监测方法,如定期采样、堆栈追踪等,来更全面地了解应用的性能状况。
5.UIView 和 CALayer 的区别,为什么这么设计?
UIView
类型:
UIView
是一个视图类,继承自UIResponder
。功能
:
- 事件处理:
UIView
负责处理用户交互事件,如触摸、手势等。 - 视图层次结构:
UIView
管理视图层次结构,可以包含其他视图(子视图)并且可以被其他视图包含(父视图)。 - 布局和自动布局:
UIView
支持自动布局(Auto Layout)和布局约束,并且可以响应布局变化。 - 动画:
UIView
提供了动画接口,可以对视图进行动画处理。 - 渲染:
UIView
本身并不直接进行渲染操作,而是通过其内部的CALayer
来进行渲染。
- 事件处理:
CALayer
- 类型:
CALayer
是 Core Animation 框架中的一个类,负责管理和渲染内容。 - 功能:
- 内容渲染:
CALayer
负责渲染视图的内容,并且可以直接绘制图形、设置背景颜色、边框、阴影等。 - 动画:
CALayer
提供了强大的动画功能,支持隐式动画(Implicit Animations)和显式动画(Explicit Animations)。 - 图形操作:
CALayer
支持复杂的图形操作,如变换(旋转、缩放、平移)、遮罩(Masking)、滤镜(Filters)等。 - 不处理事件:
CALayer
不处理用户交互事件,这是UIView
的职责。 - 不参与自动布局:
CALayer
不参与UIView
的布局系统,它的布局需要手动设置。
- 内容渲染:
关系
- 层次结构: 每个
UIView
都包含一个CALayer
实例作为其layer
属性,UIView
通过这个CALayer
来进行内容的实际渲染。 - 委托关系:
UIView
是CALayer
的委托(delegate),CALayer
的一些操作(如绘制内容)可以委托给UIView
来处理。
典型用法
- UIView:
- 创建和管理界面元素。
- 响应用户交互。
- 布局和子视图管理。
- CALayer:
- 执行复杂的动画和图形操作。
- 提供低级别的绘图和渲染支持。
- 作为
UIView
的底层实现,提升渲染性能。
在 iOS 开发中,UIView
和 CALayer
的设计遵循了一系列面向对象设计原则,这些原则确保了代码的灵活性、可维护性和性能。以下是一些关键的设计原则以及它们在 UIView
和 CALayer
中的体现:
1. 单一职责原则(Single Responsibility Principle, SRP)
定义: 每个类应该只有一个引起变化的原因,即每个类应该只有一个职责。
体现:
UIView
主要负责处理用户交互事件、管理视图层次结构和布局。CALayer
专注于内容的渲染和动画。
通过将视图的管理和渲染职责分离,可以让每个类更加专注于自身的功能,简化了类的设计和职责。
2. 开放-封闭原则(Open/Closed Principle, OCP)
定义: 软件实体(类、模块、函数等)应该可以扩展,但不可修改。
体现:
UIView
通过其layer
属性委托渲染任务给CALayer
。如果需要扩展渲染功能,可以通过自定义CALayer
而不是修改UIView
。CALayer
的功能可以通过子类化和委托模式来扩展,而不需要修改现有的类。
这种设计允许在不修改现有代码的情况下,轻松添加新功能。
3. 里氏替换原则(Liskov Substitution Principle, LSP)
定义: 子类对象必须能够替换其父类对象,并且不会导致程序行为的改变。
体现:
UIView
和CALayer
都可以被子类化,子类可以替换父类的实例并正常工作。UIView
的子类可以扩展用户交互和布局功能,而CALayer
的子类可以扩展渲染和动画功能。
4. 依赖倒置原则(Dependency Inversion Principle, DIP)
定义: 高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。
体现:
UIView
并不直接依赖低层的渲染实现,而是通过CALayer
进行抽象。CALayer
提供了渲染的抽象接口,具体的渲染细节可以通过子类和委托模式来实现。
5. 接口隔离原则(Interface Segregation Principle, ISP)
定义: 客户端不应该被强迫依赖它们不使用的方法;一个类对另一个类的依赖应该建立在最小接口上。
体现:
UIView
和CALayer
的接口设计相对独立,UIView
聚焦在用户交互和布局接口上,而CALayer
聚焦在渲染和动画接口上。UIView
的用户只需要关心用户交互和布局相关的方法,而不需要了解渲染和动画的细节。
6. 组合优于继承(Composition over Inheritance)
定义: 应该优先使用组合而不是继承来达到代码复用和扩展的目的。
体现:
UIView
通过组合CALayer
来实现复杂的功能,而不是通过继承来扩展一个巨大的视图类。- 这种设计允许在
UIView
和CALayer
之间进行灵活的组合和配置,以适应不同的需求。
7. 性能和效率
定义: 在设计中考虑性能和效率问题,确保应用程序运行高效。
体现:
CALayer
直接与 Core Animation 交互,提供硬件加速的动画和渲染性能。UIView
通过CALayer
进行渲染,可以减少视图树的复杂度并提高渲染效率。
通过遵循这些设计原则,UIView
和 CALayer
实现了职责分离、代码复用、灵活扩展和高效渲染,形成了一个强大且易于维护的视图和动画系统。这种设计不仅提升了开发体验,还提高了应用程序的性能和稳定性。
6.显式动画和隐式动画的区别
在 iOS 开发中,动画可以分为显式动画(Explicit Animations)和隐式动画(Implicit Animations)。这两种动画方式在实现、使用场景和控制粒度上有所不同。
隐式动画(Implicit Animations)
隐式动画是由 Core Animation 框架自动处理的动画效果。它们不需要显式地创建动画对象,系统会自动为你生成动画效果。
特点:
- 自动触发: 隐式动画通常在更改
CALayer
的可动画属性时自动触发,如position
、opacity
、backgroundColor
等。 - 简洁: 隐式动画不需要显式地声明动画对象,代码更简洁。
- 默认持续时间: 隐式动画有默认的动画持续时间,通常为 0.25 秒。
- 可定制: 可以通过事务(
CATransaction
)来定制隐式动画的属性,如持续时间、动画曲线等。
示例:
1 | import UIKit |
显式动画(Explicit Animations)
显式动画是开发者明确创建并添加到图层上的动画对象。它们提供了更高的控制粒度,可以对动画的各个方面进行精细控制。
特点:
- 明确声明: 显式动画需要显式地创建动画对象,如
CABasicAnimation
、CAKeyframeAnimation
、CAAnimationGroup
等。 - 精细控制: 显式动画允许精细控制动画的各个方面,包括起始值、结束值、持续时间、重复次数、动画曲线等。
- 复合动画: 可以组合多个动画,形成复杂的动画效果。
- 更多类型: 提供了多种类型的动画,如基础动画、关键帧动画、路径动画、组动画等。
示例:
1 | import UIKit |
总结
- 隐式动画:
- 自动触发,代码简洁。
- 适用于简单的动画效果。
- 可以通过事务(
CATransaction
)进行有限的定制。
- 显式动画:
- 需要显式地创建动画对象。
- 提供更高的控制粒度,适用于复杂的动画效果。
- 支持多种动画类型和复合动画。
选择使用隐式动画还是显式动画取决于具体的需求和场景。如果需要实现简单的动画效果,隐式动画通常足够且更简洁;如果需要复杂的、多步骤的动画效果,显式动画则提供了更强大的控制能力。
7.了解的设计原则和设计模式,结合项目
设计原则
单一职责:一个类只负责一件事,UIView和CALayer
开闭原则:对修改关闭,对扩展开放 Cagrgory
接口隔离原则:使用多个专门的协议,而不是一个庞大臃肿的协议,协议中的方法尽量少 UITableView
依赖倒置原则:抽象不应该依赖于具体实现,具体实现可以依赖于抽象
里氏替换原则:父类可以被子类无缝替换,且原有功能不受任何影响 KVO
迪米特法则:一个对象应当对其他对象尽可能少的了解,高内聚、低耦合
8.block的本质
9.算法:查找字符串的无重复字符子串的最大长度
1 | /** |