字节跳动

字节跳动

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原理,应用场景

  1. 动态生成子类:当你观察某个对象的属性时,Objective-C Runtime会动态创建该对象的一个子类,并重写被观察属性的setter方法。
  2. 方法重写:在重写的setter方法中,KVO机制会插入一些代码,用于通知所有观察者该属性的变化。
  3. isa指针指向:原始对象的isa指针会被修改,指向新创建的子类,以确保调用setter方法时实际调用的是重写后的方法。
  4. 通知机制:属性值变化时,KVO机制会自动通知所有注册的观察者,调用观察者的observeValueForKeyPath:ofObject:change:context:方法。

3.Runtime、应用,Runtime 交换方法需要注意什么?Runtime的 hook 和 fish hook 有什么区别?

iOS Runtime Hook

主要特点

  1. Method Swizzling: 这是最常见的Runtime Hook技术,通过交换方法实现和自定义方法实现,从而改变类的行为。
  2. 动态性强: 由于Objective-C Runtime的动态特性,可以在运行时对类和方法进行操作。
  3. 灵活性高: 可以在不修改原始代码的情况下,对应用程序的行为进行修改。
  4. 适用范围广: 适用于Objective-C编写的类和方法。

使用场景

  • 修改系统或第三方库的行为。
  • 监控方法调用和参数。
  • 在不破坏原有代码的基础上,添加额外的功能。

Fishhook

概述

Fishhook是由Facebook开源的一个库,主要用于在iOS应用中重绑定符号,从而实现对低层次的C函数进行hook。

主要特点

  1. 基于符号重绑定: Fishhook通过修改符号表来重新绑定函数指针,从而实现对函数的替换。
  2. 适用范围: 主要用于C语言层面的函数,如系统调用和标准库函数。
  3. 低层次: 适用于低层次的函数拦截,而不是面向对象的Method Swizzling。
  4. 开源库: Fishhook是一个开源库,可以方便地集成到项目中。

使用场景

  • 替换系统函数实现,如mallocfreeopen等。
  • 监控和修改底层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 仍然是一个常用的卡顿监测工具。以下是一个简单的实现思路:

  1. 观察 RunLoop 的状态:通过监听 RunLoop 的 kCFRunLoopBeforeSourceskCFRunLoopAfterWaiting 等状态,记录 RunLoop 的执行时间。
  2. 计算时间差:在每次 RunLoop 状态变化时,计算当前时间与上一次状态变化的时间差。如果时间差超过某个阈值(比如 100 毫秒),则认为发生了卡顿。
  3. 记录或上报卡顿:将卡顿信息记录到日志中,或者上报到服务器进行分析。
1
2
3
4
5
6
7
8
9
10
11
CFRunLoopObserverRef observer;
CFRunLoopObserverContext context = {0, (__bridge void *)self, NULL, NULL, NULL};
observer = CFRunLoopObserverCreate(kCFAllocatorDefault,
kCFRunLoopAllActivities,
YES,
0,
&runLoopObserverCallback,
&context);
if (observer) {
CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);
}

runLoopObserverCallback 中可以实现时间差计算和卡顿检测逻辑。

总结

虽然使用 RunLoop 来监测卡顿存在一些问题,但它仍然是一个有用的工具,尤其是在主线程的 UI 卡顿检测方面。为了提高监测的准确性和覆盖面,可以结合其他监测方法,如定期采样、堆栈追踪等,来更全面地了解应用的性能状况。

5.UIView 和 CALayer 的区别,为什么这么设计?

UIView

  1. 类型: UIView 是一个视图类,继承自 UIResponder

  2. 功能

    :

    • 事件处理: UIView 负责处理用户交互事件,如触摸、手势等。
    • 视图层次结构: UIView 管理视图层次结构,可以包含其他视图(子视图)并且可以被其他视图包含(父视图)。
    • 布局和自动布局: UIView 支持自动布局(Auto Layout)和布局约束,并且可以响应布局变化。
    • 动画: UIView 提供了动画接口,可以对视图进行动画处理。
    • 渲染: UIView 本身并不直接进行渲染操作,而是通过其内部的 CALayer 来进行渲染。

CALayer

  1. 类型: CALayer 是 Core Animation 框架中的一个类,负责管理和渲染内容。
  2. 功能:
    • 内容渲染: CALayer 负责渲染视图的内容,并且可以直接绘制图形、设置背景颜色、边框、阴影等。
    • 动画: CALayer 提供了强大的动画功能,支持隐式动画(Implicit Animations)和显式动画(Explicit Animations)。
    • 图形操作: CALayer 支持复杂的图形操作,如变换(旋转、缩放、平移)、遮罩(Masking)、滤镜(Filters)等。
    • 不处理事件: CALayer 不处理用户交互事件,这是 UIView 的职责。
    • 不参与自动布局: CALayer 不参与 UIView 的布局系统,它的布局需要手动设置。

关系

  • 层次结构: 每个 UIView 都包含一个 CALayer 实例作为其 layer 属性,UIView 通过这个 CALayer 来进行内容的实际渲染。
  • 委托关系: UIViewCALayer 的委托(delegate),CALayer 的一些操作(如绘制内容)可以委托给 UIView 来处理。

典型用法

  • UIView:
    • 创建和管理界面元素。
    • 响应用户交互。
    • 布局和子视图管理。
  • CALayer:
    • 执行复杂的动画和图形操作。
    • 提供低级别的绘图和渲染支持。
    • 作为 UIView 的底层实现,提升渲染性能。

在 iOS 开发中,UIViewCALayer 的设计遵循了一系列面向对象设计原则,这些原则确保了代码的灵活性、可维护性和性能。以下是一些关键的设计原则以及它们在 UIViewCALayer 中的体现:

1. 单一职责原则(Single Responsibility Principle, SRP)

定义: 每个类应该只有一个引起变化的原因,即每个类应该只有一个职责。

体现:

  • UIView 主要负责处理用户交互事件、管理视图层次结构和布局。
  • CALayer 专注于内容的渲染和动画。

通过将视图的管理和渲染职责分离,可以让每个类更加专注于自身的功能,简化了类的设计和职责。

2. 开放-封闭原则(Open/Closed Principle, OCP)

定义: 软件实体(类、模块、函数等)应该可以扩展,但不可修改。

体现:

  • UIView 通过其 layer 属性委托渲染任务给 CALayer。如果需要扩展渲染功能,可以通过自定义 CALayer 而不是修改 UIView
  • CALayer 的功能可以通过子类化和委托模式来扩展,而不需要修改现有的类。

这种设计允许在不修改现有代码的情况下,轻松添加新功能。

3. 里氏替换原则(Liskov Substitution Principle, LSP)

定义: 子类对象必须能够替换其父类对象,并且不会导致程序行为的改变。

体现:

  • UIViewCALayer 都可以被子类化,子类可以替换父类的实例并正常工作。
  • UIView 的子类可以扩展用户交互和布局功能,而 CALayer 的子类可以扩展渲染和动画功能。

4. 依赖倒置原则(Dependency Inversion Principle, DIP)

定义: 高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。

体现:

  • UIView 并不直接依赖低层的渲染实现,而是通过 CALayer 进行抽象。
  • CALayer 提供了渲染的抽象接口,具体的渲染细节可以通过子类和委托模式来实现。

5. 接口隔离原则(Interface Segregation Principle, ISP)

定义: 客户端不应该被强迫依赖它们不使用的方法;一个类对另一个类的依赖应该建立在最小接口上。

体现:

  • UIViewCALayer 的接口设计相对独立,UIView 聚焦在用户交互和布局接口上,而 CALayer 聚焦在渲染和动画接口上。
  • UIView 的用户只需要关心用户交互和布局相关的方法,而不需要了解渲染和动画的细节。

6. 组合优于继承(Composition over Inheritance)

定义: 应该优先使用组合而不是继承来达到代码复用和扩展的目的。

体现:

  • UIView 通过组合 CALayer 来实现复杂的功能,而不是通过继承来扩展一个巨大的视图类。
  • 这种设计允许在 UIViewCALayer 之间进行灵活的组合和配置,以适应不同的需求。

7. 性能和效率

定义: 在设计中考虑性能和效率问题,确保应用程序运行高效。

体现:

  • CALayer 直接与 Core Animation 交互,提供硬件加速的动画和渲染性能。
  • UIView 通过 CALayer 进行渲染,可以减少视图树的复杂度并提高渲染效率。

通过遵循这些设计原则,UIViewCALayer 实现了职责分离、代码复用、灵活扩展和高效渲染,形成了一个强大且易于维护的视图和动画系统。这种设计不仅提升了开发体验,还提高了应用程序的性能和稳定性。

6.显式动画和隐式动画的区别

在 iOS 开发中,动画可以分为显式动画(Explicit Animations)和隐式动画(Implicit Animations)。这两种动画方式在实现、使用场景和控制粒度上有所不同。

隐式动画(Implicit Animations)

隐式动画是由 Core Animation 框架自动处理的动画效果。它们不需要显式地创建动画对象,系统会自动为你生成动画效果。

特点:

  1. 自动触发: 隐式动画通常在更改 CALayer 的可动画属性时自动触发,如 positionopacitybackgroundColor 等。
  2. 简洁: 隐式动画不需要显式地声明动画对象,代码更简洁。
  3. 默认持续时间: 隐式动画有默认的动画持续时间,通常为 0.25 秒。
  4. 可定制: 可以通过事务(CATransaction)来定制隐式动画的属性,如持续时间、动画曲线等。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import UIKit

class ViewController: UIViewController {
var myLayer: CALayer!

override func viewDidLoad() {
super.viewDidLoad()

myLayer = CALayer()
myLayer.frame = CGRect(x: 50, y: 50, width: 100, height: 100)
myLayer.backgroundColor = UIColor.blue.cgColor
view.layer.addSublayer(myLayer)

// 使用隐式动画更改属性
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
CATransaction.begin()
CATransaction.setAnimationDuration(2.0)
self.myLayer.position = CGPoint(x: 200, y: 300)
self.myLayer.backgroundColor = UIColor.red.cgColor
CATransaction.commit()
}
}
}

显式动画(Explicit Animations)

显式动画是开发者明确创建并添加到图层上的动画对象。它们提供了更高的控制粒度,可以对动画的各个方面进行精细控制。

特点:

  1. 明确声明: 显式动画需要显式地创建动画对象,如 CABasicAnimationCAKeyframeAnimationCAAnimationGroup 等。
  2. 精细控制: 显式动画允许精细控制动画的各个方面,包括起始值、结束值、持续时间、重复次数、动画曲线等。
  3. 复合动画: 可以组合多个动画,形成复杂的动画效果。
  4. 更多类型: 提供了多种类型的动画,如基础动画、关键帧动画、路径动画、组动画等。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import UIKit

class ViewController: UIViewController {
var myLayer: CALayer!

override func viewDidLoad() {
super.viewDidLoad()

myLayer = CALayer()
myLayer.frame = CGRect(x: 50, y: 50, width: 100, height: 100)
myLayer.backgroundColor = UIColor.blue.cgColor
view.layer.addSublayer(myLayer)

// 创建显式动画
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
let positionAnimation = CABasicAnimation(keyPath: "position")
positionAnimation.fromValue = self.myLayer.position
positionAnimation.toValue = CGPoint(x: 200, y: 300)
positionAnimation.duration = 2.0

let colorAnimation = CABasicAnimation(keyPath: "backgroundColor")
colorAnimation.fromValue = UIColor.blue.cgColor
colorAnimation.toValue = UIColor.red.cgColor
colorAnimation.duration = 2.0

// 添加动画到图层
self.myLayer.add(positionAnimation, forKey: "positionAnimation")
self.myLayer.add(colorAnimation, forKey: "colorAnimation")

// 更新图层属性
self.myLayer.position = CGPoint(x: 200, y: 300)
self.myLayer.backgroundColor = UIColor.red.cgColor
}
}
}

总结

  • 隐式动画:
    • 自动触发,代码简洁。
    • 适用于简单的动画效果。
    • 可以通过事务(CATransaction)进行有限的定制。
  • 显式动画:
    • 需要显式地创建动画对象。
    • 提供更高的控制粒度,适用于复杂的动画效果。
    • 支持多种动画类型和复合动画。

选择使用隐式动画还是显式动画取决于具体的需求和场景。如果需要实现简单的动画效果,隐式动画通常足够且更简洁;如果需要复杂的、多步骤的动画效果,显式动画则提供了更强大的控制能力。

7.了解的设计原则和设计模式,结合项目

设计原则

单一职责:一个类只负责一件事,UIView和CALayer

开闭原则:对修改关闭,对扩展开放 Cagrgory

接口隔离原则:使用多个专门的协议,而不是一个庞大臃肿的协议,协议中的方法尽量少 UITableView

依赖倒置原则:抽象不应该依赖于具体实现,具体实现可以依赖于抽象

里氏替换原则:父类可以被子类无缝替换,且原有功能不受任何影响 KVO

迪米特法则:一个对象应当对其他对象尽可能少的了解,高内聚、低耦合

8.block的本质

9.算法:查找字符串的无重复字符子串的最大长度

无重复字符的最长子串-力扣

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* @param {string} s
* @return {number}
*/
var lengthOfLongestSubstring = function(s) {
let len = s.length;
let set = new Set();
let i = 0, j = 0;
let maxLenth = 0;
while (i < len && j < len) {
if (!set.has(s[j])) {
set.add(s[j]);
j++;
maxLenth = Math.max(maxLenth, j - i);
} else {
set.delete(s[i]);
i++;
}
}
return maxLenth;
};
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×