如何通过Bundle Identifier定位NSBundle?bundleWithIdentifier方法解惑
问题解析与解决方案
一、关于bundleWithIdentifier:的文档矛盾
文档的两条描述并不矛盾,需结合场景理解:
- 「找不到对应Bundle时返回nil」:当系统中不存在该Bundle ID对应的Bundle文件时,返回nil。
- 「无现有实例则创建新Bundle对象」:仅当系统中确实存在该Bundle,但当前进程还未加载过它的实例时,才会创建新的Bundle对象返回;若Bundle本身不存在,依然返回nil。
本质上,这个方法的核心是「管理已加载到当前进程的Bundle实例」,而非「搜索系统中所有Bundle」。
二、bundleWithIdentifier:失效的原因
[NSBundle bundleWithIdentifier:]只能找到已被当前进程加载的Bundle,包括:
- 当前App自身的Bundle
- 已加载的系统框架(如
Foundation、AppKit) - 手动通过
bundleWithPath:或load方法加载过的第三方Bundle
像Preview这类未被当前进程加载的独立App,无论权限、运行位置如何,该方法都无法找到它——因为它的Bundle从未被加载到你的TesterApp进程中。
三、通过Bundle ID获取Bundle路径的解决方案
要获取系统中任意Bundle(包括未加载的App)的路径,需使用专门的搜索类API:
针对App Bundle(macOS)
使用NSWorkspace的公开API:
#import <Cocoa/Cocoa.h> NSString *targetBundleID = @"com.apple.Preview"; NSURL *appURL = [[NSWorkspace sharedWorkspace] URLForApplicationWithBundleIdentifier:targetBundleID]; if (appURL) { NSBundle *appBundle = [NSBundle bundleWithURL:appURL]; NSLog(@"Bundle executable: %@", appBundle.executablePath); } else { NSLog(@"No application found for bundle identifier: %@", targetBundleID); }
针对非App Bundle(如系统框架、插件)
使用CoreServices框架的CFBundleCopyBundleURLForBundleIdentifier函数:
#import <CoreServices/CoreServices.h> CFStringRef targetBundleID = CFSTR("com.apple.Foundation"); CFURLRef bundleURL = CFBundleCopyBundleURLForBundleIdentifier(targetBundleID); if (bundleURL) { NSBundle *bundle = [NSBundle bundleWithURL:(__bridge NSURL *)bundleURL]; NSLog(@"Executable path: %@", bundle.executablePath); CFRelease(bundleURL); }
四、测试场景验证说明
你之前用bundleWithPath:能成功,是因为直接指定路径强制将Preview的Bundle加载到了进程中;而bundleWithIdentifier:返回nil,完全是因为Preview的Bundle从未被当前进程加载,和权限、运行目录无关。
内容的提问来源于stack exchange,提问作者Motti Shneor
相关产品推荐
相关产品推荐

