—— unrecognized selector applicationDidFinishLaunching 的根因分析与终极解法

技术栈:Flutter 3.x / macOS 13 / Xcode 15 / Swift / Vision


一、背景说明

最近在做一个 Flutter macOS 项目,目标是:

  • 使用 macOS 摄像头

  • 基于 Vision(VNDetectHumanHandPoseRequest)进行手势识别

  • 通过 手指移动 控制 Flutter 页面中的 小球左右移动与缩放

整体技术结构如下:

  • Flutter

    • 页面 UI

    • 动画渲染

    • 玻璃球效果

  • macOS 原生(Swift)

    • AVFoundation:摄像头采集

    • Vision:手势关键点识别

    • MethodChannel:与 Flutter 通信


二、问题现象:程序能编译,但一启动就崩

程序可以正常执行:

 flutter run -d macos

但启动后立刻崩溃:

 -[hand_gesture_control.AppDelegate applicationDidFinishLaunching:]
 unrecognized selector sent to instance

特点:

  • Swift 编译正常

  • AppDelegate 文件存在

  • Target Membership 正确

  • DerivedData 多次清理无效


三、根因分析

核心结论一句话

Flutter macOS 项目中,不要 override AppDelegate 的启动生命周期方法。

Flutter macOS 使用的是 FlutterAppDelegate,其内部已经接管了:

  • applicationDidFinishLaunching

  • Flutter Engine 初始化

  • Window / Plugin 生命周期

在此基础上再 override,会导致 Objective‑C selector 在最终二进制中缺失或冲突,从而产生运行时崩溃。


四、错误做法(请全部避免)

 ❌ override applicationDidFinishLaunching
 ❌ override applicationDidBecomeActive
 ❌ override applicationWillTerminate
 ❌ 在 AppDelegate 中初始化 Camera / Vision / Channel

这些在原生 macOS App 中是对的,但在 Flutter macOS 中是错误的。


五、唯一正确且稳定的解决方案

正确切入点

MainFlutterWindow.awakeFromNib()

这是 Flutter 官方模板中:

  • Flutter engine 已创建

  • FlutterViewController 已就绪

  • Plugin 已注册完成

  • 不涉及 ObjC selector 派发


六、最终正确工程结构

1. AppDelegate.swift(最小化)

 @main
 class AppDelegate: FlutterAppDelegate {
   override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
     true
   }
 }

2. MainFlutterWindow.swift(关键)

 class MainFlutterWindow: NSWindow {
   override func awakeFromNib() {
     let flutterViewController = FlutterViewController()
     self.contentViewController = flutterViewController
     RegisterGeneratedPlugins(registry: flutterViewController)
 ​
     HandGestureService.shared.wireIfNeeded(
       binaryMessenger: flutterViewController.engine.binaryMessenger
     )
 ​
     super.awakeFromNib()
   }
 }

3. 原生服务单例(推荐)

 final class HandGestureService {
   static let shared = HandGestureService()
   func wireIfNeeded(binaryMessenger: FlutterBinaryMessenger) {
     // init camera, vision, channel
   }
 }

七、经验总结(必看)

  • Flutter macOS ≠ 原生 macOS App

  • 不要接管 AppDelegate 生命周期

  • 原生初始化放在 MainFlutterWindow

  • selector 问题请直接检查最终二进制


八、结语

这个问题不是代码能力问题,而是 架构认知问题。 一旦理解 Flutter macOS 的真实运行机制,后续所有原生扩展都会非常顺畅。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐