You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS开发:String扩展为何在AppDelegate方法前执行?

iOS多语言适配:Extension代码早于didFinishLaunchingWithOptions执行的原因与解决办法

嘿,这个问题我做多语言适配的时候也踩过一模一样的坑!咱们先把原因说清楚,再给你几个可行的解决方案。

为什么Extension的代码执行更早?

iOS App的启动流程是有固定顺序的:

系统先加载App的基础资源(比如Storyboard/XIB、全局变量、静态属性)→ 初始化界面控件 → 然后才会调用AppDelegate的didFinishLaunchingWithOptions方法

你写的String extension的localized属性,应该是被界面上的控件直接调用了(比如Storyboard里的UILabel的text绑定了"some_key".localized)。这些控件的初始化是在didFinishLaunchingWithOptions之前完成的,所以当localized去UserDefaults里取值时,你还没在didFinishLaunchingWithOptions里把selectedLocale存进去,自然就拿到nil了。

看你的代码也能印证这一点:你在didFinishLaunchingWithOptions里才把Singleton.sharedInstance.currentLocale存到UserDefaults,但界面控件早就已经调用localized去取值了。

解决方案

1. 给Extension加默认值兜底

先在localized属性里判断,如果UserDefaults里取不到值,就用系统当前Locale或者预设的默认值,避免nil的情况:

extension String {
    var localized: String {
        // 先从UserDefaults取,取不到就用系统Locale判断,默认给fr
        let locale = UserDefaults.standard.value(forKey: USER_DEFAULT.selectedLocale) as? String ?? {
            let currentCollator = NSLocale.current.collatorIdentifier
            return currentCollator?.contains("en") ?? false ? "en" : "fr"
        }()
        
        if let path = Bundle.main.path(forResource: locale, ofType: "lproj") {
            let bundle = Bundle(path: path)!
            return NSLocalizedString(self, tableName: nil, bundle: bundle, value: "", comment: "")
        }
        return self
    }
}

2. 启动完成后刷新界面

虽然默认值能解决nil的问题,但你在didFinishLaunchingWithOptions里设置的正确Locale还没生效,这时候需要通知所有界面控件重新加载本地化文本:

  • 在AppDelegate的didFinishLaunchingWithOptions最后添加通知:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    // 你的原有代码...
    UserDefaults.standard.setValue(Singleton.sharedInstance.currentLocale, forKey: USER_DEFAULT.selectedLocale)
    
    // 发送通知,告诉界面刷新本地化内容
    NotificationCenter.default.post(name: NSNotification.Name("LocaleDidSet"), object: nil)
    return true
}
  • 在每个ViewController里监听通知,刷新控件:
class YourViewController: UIViewController {
    @IBOutlet weak var titleLabel: UILabel!
    @IBOutlet weak var descLabel: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 监听通知
        NotificationCenter.default.addObserver(self, selector: #selector(refreshLocalizedContent), name: NSNotification.Name("LocaleDidSet"), object: nil)
        // 初始化时先加载一次
        refreshLocalizedContent()
    }
    
    @objc private func refreshLocalizedContent() {
        titleLabel.text = "title_key".localized
        descLabel.text = "desc_key".localized
        // 其他需要本地化的控件都在这里重新赋值
    }
    
    deinit {
        // 记得移除观察者
        NotificationCenter.default.removeObserver(self)
    }
}

3. 纯代码界面用懒加载

如果你的界面是纯代码创建的,把控件改成懒加载属性,这样控件的text赋值会在didFinishLaunchingWithOptions之后执行:

class YourViewController: UIViewController {
    lazy var titleLabel: UILabel = {
        let label = UILabel()
        label.text = "title_key".localized // 这里的localized会在控件第一次被访问时调用,也就是didFinishLaunchingWithOptions之后
        // 其他布局设置...
        return label
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(titleLabel)
        // 布局代码...
    }
}

这样调整后,就能保证localized属性能拿到正确的Locale值了。

内容的提问来源于stack exchange,提问作者sanjaykmwt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 06:46:02