个人技术生活分享

文明其精神,野蛮其体魄

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
启动
运行 可优化
热更新