使用perform调用ViewController中@objc方法触发崩溃求助
问题分析与解决
从崩溃日志里的核心报错信息就能定位问题:
-Business_Name.RPLabel test]: unrecognized selector sent to instance 0x7fd444d03e30
简单说就是:系统尝试给一个RPLabel对象发送test方法,但RPLabel根本没实现这个方法。
为啥会搞成这样?看你写的调用代码:
perform(#selector(MainScreenViewController().test))
这里犯了两个关键错误:
- 你用
MainScreenViewController()创建了一个全新的、完全没经过VC生命周期初始化的实例——这个对象和你界面上正在显示的MainScreenViewController是八竿子打不着的两个东西。 - 你调用的
perform(_:)如果不指定target,默认会把消息发给当前调用这个方法的对象。从崩溃栈的调用路径能看到,这个perform是在RPLabel的setup方法里触发的,所以消息被发给了RPLabel实例,而不是你想要的MainScreenViewController,自然就触发了“未识别选择器”的崩溃。
解决方法
方法1:获取正确的MainScreenViewController实例直接调用
你得先拿到界面上正在运行的MainScreenViewController对象,而不是随便new一个。比如如果它是TabBarController的第一个子控制器,可以这么获取:
// 假设当前调用者是某个VC,通过tabBarController获取目标VC if let tabBarVC = self.tabBarController, let mainVC = tabBarVC.viewControllers?.first as? MainScreenViewController { // 直接调用方法 mainVC.test() // 要是需要延迟调用等场景,再用perform // mainVC.perform(#selector(MainScreenViewController.test)) }
如果是通过导航栈跳转的,可以从导航栈里查找:
if let mainVC = navigationController?.viewControllers.first(where: { $0 is MainScreenViewController }) as? MainScreenViewController { mainVC.test() }
方法2:用更健壮的跨VC通信方式(推荐)
直接硬编码获取VC实例的耦合性太高,推荐用通知中心或者代理模式来实现:
通知中心实现
在MainScreenViewController里注册通知:
override func viewDidLoad() { super.viewDidLoad() // 注册通知 NotificationCenter.default.addObserver(self, selector: #selector(test), name: NSNotification.Name(rawValue: "TriggerMainScreenTest"), object: nil) } deinit { // 记得销毁通知,避免内存泄漏 NotificationCenter.default.removeObserver(self) }
然后在需要触发调用的地方发送通知:
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "TriggerMainScreenTest"), object: nil)
代理模式实现
先定义代理协议:
protocol TestTriggerDelegate: AnyObject { func triggerTest() }
在MainScreenViewController里实现代理:
class MainScreenViewController: UIViewController, TestTriggerDelegate { func triggerTest() { test() } // 其他业务代码... }
在需要触发调用的对象里添加代理属性:
class YourOtherVC: UIViewController { weak var delegate: TestTriggerDelegate? // 需要调用的地方 func callTest() { delegate?.triggerTest() } }
最后在初始化YourOtherVC的时候设置代理:
let otherVC = YourOtherVC() otherVC.delegate = self // 这里的self是MainScreenViewController实例 navigationController?.pushViewController(otherVC, animated: true)
方法3:明确指定perform的target
如果你一定要用perform方法,必须明确指定目标对象:
// 先拿到正确的mainVC实例 let mainVC = ... // 获取当前界面的MainScreenViewController实例 mainVC.perform(#selector(MainScreenViewController.test))
内容的提问来源于stack exchange,提问作者Joe Alex
相关产品推荐
相关产品推荐

