个人技术生活分享

文明其精神,野蛮其体魄

0%

Flutter debug 与编程语言原理知识总结

一、问题背景:Flutter iOS 调试连接失败

1.1 问题现象

使用 Android Studio 运行 Flutter iOS 项目时,卡在:

1
flutter: The Dart VM service is listening on http://0.0.0.0:50464/xxx

应用白屏,无法继续。但 Xcode 直接运行或命令行 flutter run 正常。

1.2 解决方案

方案一:不暂停等待调试器

1
flutter run --no-start-paused -d <设备ID>

方案二:使用 Profile 模式

1
flutter run --profile -d <设备ID>

1.3 根本原因

  • 默认 Debug 模式:应用启动后暂停等待 IDE 调试器连接
  • AS 调试器连接失败/超时 → 应用一直阻塞
  • –no-start-paused:应用不暂停直接运行,调试器可异步连接,容错性更高

1.4 为什么 –no-start-paused 反而能成功?

**默认模式 (–start-paused)**:

  • 应用启动 → 暂停 → 等待调试器连接
  • 连接超时或失败 → 一直卡住
  • 这是「同步阻塞」过程

–no-start-paused 模式:

  • 应用启动 → 直接运行 → VM Service 同时启动
  • 调试器可以稍后连接,有更多时间和重试机会
  • 这是「异步非阻塞」过程

二、Dart VM 详解

2.1 什么是 Dart VM

概念 说明
定义 Dart Virtual Machine,Dart 虚拟机
作用 读懂 Dart 代码 → 编译成机器指令 → 执行
类比 Java 的 JVM、JavaScript 的 V8 引擎

2.2 Dart VM 的位置

Dart VM 运行在移动设备/模拟器上,不是 Mac 上!

1
2
3
4
5
6
7
8
9
iPhone/模拟器
├── Flutter App
│ └── Dart VM ← 在这里!
│ └── VM Service (监听端口)
↑
│ 网络连接
↓
Mac
└── Android Studio / VS Code (客户端,去连接设备)

2.3 Dart VM Service 的作用

功能 说明
热重载 代码修改后 1 秒内刷新 UI
断点调试 设置断点、查看变量
性能分析 CPU、内存、帧率监控
Widget 检查器 查看 Widget 树结构
日志输出 print() 语句输出

2.4 Dart VM 的核心职责

  1. 编译代码:把 Dart 代码变成机器能执行的指令
  2. 内存管理:自动分配和回收内存(垃圾回收 GC)
  3. 支持热重载:JIT 模式下可动态替换代码
  4. 提供调试接口:VM Service 让 IDE 能连接进来调试

三、编译方式:JIT vs AOT

3.1 定义

JIT (Just-In-Time) AOT (Ahead-Of-Time)
中文 即时编译 提前编译
时机 运行时边跑边编译 运行前一次性编译完
类比 同声传译 提前翻译好整本书

3.2 工作流程

AOT 编译:

  • 编译期:[全部代码] → [全部机器码] → 存储
  • 运行期:直接执行机器码

JIT 编译:

  • 运行期:执行 main() → 编译 main() → 执行;调用 foo() → 编译 foo() → 执行
  • 需要什么,编译什么!

3.3 优缺点对比

JIT AOT
启动速度 慢 快
运行性能 可优化热点代码 稳定
文件大小 较小 较大
热重载 支持 不支持
调试 方便 困难
适用场景 开发调试 发布上线

3.4 Flutter 的应用

模式 编译方式 特点
Debug JIT 支持热重载,启动慢
Profile AOT + 部分调试 性能测试用
Release AOT 启动快,无热重载

四、各平台运行机制对比

4.1 iOS

特性 说明
编译方式 纯 AOT
虚拟机 没有
JIT 禁止(Apple 安全政策)
代码形态 Swift/OC → 直接编译成 ARM 机器码

为什么 iOS 禁止 JIT?

  • 安全性:防止恶意代码注入
  • 审核:App Store 需审核所有代码,JIT 可绕过
  • 隐私:防止应用偷偷下载执行未审核的代码

唯一例外:Safari 的 JavaScript 引擎可以用 JIT(系统组件)。

Flutter 在 iOS 上:Debug 模式可用 Dart VM(开发者设备,不走 App Store);Release 模式必须 AOT 编译(符合 Apple 政策)。

4.2 Android

特性 说明
运行时 **ART (Android Runtime)**,不是标准 JVM
编译方式 AOT + JIT 混合
字节码 .dex 文件(不是 .class)

ART 工作流程:安装时 .dex → AOT 编译 → 机器码(启动快);运行时监控热点代码 → JIT 再优化(越用越快),优化结果存入 Profile 文件。

为什么 Android 不用标准 JVM?

  • 版权问题:Oracle 的 JVM 有授权问题
  • 移动优化:JVM 为服务器设计,ART 专为移动设备优化
  • 省电省内存:寄存器架构比栈架构更省资源
  • 性能:AOT 编译比纯 JIT 启动更快

4.3 Python

特性 说明
类型 解释型语言
流程 源码 → 字节码 (.pyc) → 解释器逐行执行
JIT 标准 CPython 没有(PyPy 有)
速度 较慢

为什么 Python 慢?

  1. 解释执行:一行行解释,不是直接机器码
  2. 动态类型:运行时才知道变量类型,无法优化
  3. GIL 锁:多线程无法真正并行
  4. 没有 JIT:标准 CPython 不做运行时优化

4.4 Objective-C

特性 说明
编译方式 AOT
虚拟机 没有
特殊之处 有强大的动态 Runtime
代码形态 编译时就变成机器码

OC = AOT 编译 + 动态运行时:代码在编译时已变成二进制机器码,Runtime 只负责「查表 + 调度」,不是「编译 + 解释」。


五、OC Runtime 详解

5.1 Runtime 执行时机

时机 方法 频率 说明
App 启动 +load 1 次 main() 之前,最早执行
首次使用类 +initialize 1 次 懒加载初始化
每次方法调用 objc_msgSend 最频繁 消息发送机制
方法找不到 消息转发 按需 动态处理
对象销毁 dealloc 按需 内存回收

5.2 objc_msgSend 流程

1
2
3
4
5
6
7
8
9
10
11
12
13
[obj doSomething]
│
▼
objc_msgSend(obj, @selector(doSomething))
│
├── 1. 获取对象的 isa 指针
├── 2. 查找类的方法缓存 (cache)
│ ├── 命中 → 直接调用
│ └── 未命中 ↓
├── 3. 查找类的方法列表
│ ├── 找到 → 加入缓存 → 调用
│ └── 未找到 → 查找父类 → 一直到 NSObject
└── 4. 都没找到 → 消息转发机制

5.3 消息转发三步曲

  1. resolveInstanceMethod: — 「要不要动态添加这个方法?」→ NO 则继续
  2. forwardingTargetForSelector: — 「要不要让别的对象处理?」→ nil 则继续
  3. forwardInvocation: — 「给你最后一次机会自己处理」→ 不处理则 Crash: “unrecognized selector”

5.4 Runtime 能做什么?

  • 消息发送:方法调用变成消息,可拦截
  • 方法交换:运行时替换方法实现 (Method Swizzling)
  • 动态添加:运行时给类添加方法
  • 关联对象:给对象动态添加属性
  • 类型内省:运行时检查对象类型
  • KVO/KVC:键值观察/键值编码
  • 不能:运行时编译新代码(不是 JIT)

六、静态语言 vs 动态语言

6.1 核心区别

静态语言 动态语言
类型检查 编译时 运行时
变量类型 声明时确定,不能变 随时可变
错误发现 编译就报错 运行才报错

6.2 代码示例

**静态语言 (Swift)**:

1
2
3
4
5
6
7
var name: String = "Tom"
name = 123 // 编译错误:不能把 Int 赋给 String

func greet(person: String) -> String {
return "Hello, \(person)"
}
greet(person: 123) // 编译错误:类型不对

**动态语言 (Python)**:

1
2
3
4
5
6
name = "Tom"
name = 123 # 没问题

def greet(person):
return "Hello, " + person
greet(123) # 编译不报错,运行时才报错!TypeError

6.3 常见语言分类

  • 静态语言:C/C++, Java, Swift, Kotlin, Go, Rust, Dart, TypeScript
  • 动态语言:Python, JavaScript, Ruby, PHP, Lua, Perl

6.4 优缺点对比

静态语言:编译时发现错误、IDE 补全更智能、性能更好、重构更安全;缺点:代码量更多、不够灵活、学习曲线陡。

动态语言:写起来快、灵活、学习简单、适合快速原型;缺点:运行时才发现错误、IDE 补全有限、大项目难维护、性能差。

6.5 OC 的特殊性

OC 是静态编译 + 动态特性的混合体:

1
2
3
4
5
6
7
8
// 静态部分:编译时确定类型
NSString *str = @"Hello";
str = @123; // 编译警告

// 动态部分:运行时可以做很多事
id obj = @"Hello"; // id 类型可以指向任何对象
obj = @123; // 没问题
[obj unknownMethod]; // 编译通过,运行时才报错

6.6 现代趋势:渐进式类型

很多动态语言开始支持可选的类型标注(如 Python 3.5+ 类型提示、TypeScript)。


七、Xcode 运行 vs flutter run 的区别

7.1 对比表

Xcode 直接运行 flutter run / AS
执行内容 只编译 iOS 原生壳 flutter 工具链 + iOS 编译
Dart 代码来源 用已编译的产物 实时编译
Dart VM 可能有但不连接 IDE 启动并等待 IDE 连接
热重载 没有 有
修改代码后 要重新编译运行 按 r 即刷新
适用场景 调试原生 iOS 代码 日常 Flutter 开发

7.2 Xcode 运行时 Flutter 代码从哪来?

来自之前 flutter build / flutter run 生成的产物:

1
2
3
4
5
ios/Flutter/
├── App.framework ← 你的 Dart 代码(已编译)
├── Flutter.framework ← Flutter 引擎
├── flutter_export_environment.sh
└── Generated.xcconfig

如果从未运行过 flutter 命令,直接用 Xcode 打开会报错:'Flutter/Flutter.h' file not found。

7.3 流程对比

Xcode 直接运行:Xcode 编译 → 安装到设备 → 启动应用 → 完成(不需要 Dart 调试服务,所以不会卡住)。

flutter run / AS 运行:flutter run → 编译 → 安装到设备 → 启动应用 → 启动 Dart VM 服务 → 等待 IDE 调试器连接(可能卡在这里)→ 连接成功 → 完成。


八、Flutter 架构三层结构

8.1 架构图

1
2
3
4
5
6
7
8
9
10
11
12
┌─────────────────────────────────────────┐
│ 你的代码 (Dart) │ ← lib/*.dart
│ Debug: kernel → JIT | Release: AOT │
├─────────────────────────────────────────┤
│ Flutter 框架 (Dart) │ ← Flutter SDK
│ Widget, Material, Cupertino... │
│ 和你的代码一起编译 │
├─────────────────────────────────────────┤
│ Flutter 引擎 (C++) │ ← 预编译二进制
│ Skia, Dart VM, 平台通道 │
│ 不走 kernel,是固定的二进制文件 │
└─────────────────────────────────────────┘

8.2 各层编译方式

组件 语言 Debug 模式 Release 模式
你的代码 Dart kernel → JIT AOT 机器码
Flutter 框架 Dart kernel → JIT AOT 机器码
Flutter 引擎 C++ 预编译二进制 预编译二进制

Flutter 引擎不走 kernel!它是 Google 预先用 C++ 编译好的二进制文件,包含:Skia 图形库、Dart VM、文字渲染引擎、平台通道等。

8.3 Debug 模式详细流程

Mac 端:lib/main.dart → Dart Frontend Compiler → kernel 文件 (.dill) → 通过 USB/网络发送到设备。

iPhone 端:Dart VM 接收 kernel 文件 → JIT 编译器边运行边编译成机器码 → 执行你的代码 → 调用 Flutter 引擎 → 渲染 UI。

8.4 热重载流程

修改 lib/home_page.dart → Mac: flutter 检测到文件变化 → 只编译改动的部分 → 生成增量 kernel → 通过 VM Service 发送到设备 → iPhone: Dart VM 接收增量代码 → 替换内存中的旧代码 → 重新执行 build() → UI 更新。整个过程 < 1 秒,状态还保留着!


九、总结对比表

9.1 各平台虚拟机/运行时

平台 虚拟机/运行时 JIT AOT
iOS 原生 没有 禁止 ✓
Android 原生 ART ✓ ✓
Flutter Debug Dart VM ✓
Flutter Release 没有 ✓
Python (CPython) 解释器
Java JVM ✓ ✓

9.2 语言类型

类型 特点 代表语言
静态语言 编译时检查类型,安全 Swift, Java, Dart, C++
动态语言 运行时检查类型,灵活 Python, JavaScript, Ruby
混合型 静态编译 + 动态运行时 Objective-C

9.3 编译方式对比

解释执行 JIT AOT
代表 Python Java, Dart Debug C, Swift, Dart Release
启动 慢 中 快
运行 慢 可优化 快
热更新 ✓