Swift中使用WikipediaKit与代理模式的网络请求问题排查
问题分析与解决方案
核心问题
- 代理失效原因:你在
TopTenViewController中调用的albumManager和AlbumInfoViewController里初始化的albumManager是两个完全独立的实例。发起网络请求时,前者的delegate为nil,而后者设置代理的是另一个实例,所以回调永远不会触发。 - 时序问题:网络请求是异步操作,你先调用
getAlbumInfo立刻执行performSegue,此时请求还未完成,新视图已经加载,且因为实例不共享,后续结果也无法传递。
修复方案:让新视图负责发起请求(更符合职责划分)
步骤1:修改TopTenViewController,传递专辑标题到新视图
extension TopTenViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { performSegue(withIdentifier: "topTenAlbumInfoSeg", sender: indexPath) } } // 添加prepare方法传递数据 extension TopTenViewController { override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "topTenAlbumInfoSeg", let indexPath = sender as? IndexPath, let destVC = segue.destination as? AlbumInfoViewController { // 把选中的专辑标题传给新视图 destVC.targetAlbumTitle = topTenList[indexPath.row][1] } } }
步骤2:修改AlbumInfoViewController,接收标题并发起请求
class AlbumInfoViewController: UIViewController, AlbumManagerDelegate { @IBOutlet weak var albumDescriptionLabel: UILabel! @IBOutlet weak var myWebView: WKWebView! // 添加属性接收专辑标题 var targetAlbumTitle: String = "" var albumManager = AlbumManager() override func viewDidLoad() { super.viewDidLoad() albumManager.delegate = self // 拿到标题后发起请求 albumManager.getAlbumInfo(albumTitle: targetAlbumTitle) } func didUpdateAlbumInfo(albumInfo: String) { DispatchQueue.main.async { print("This worked: \(albumInfo)") self.albumDescriptionLabel.text = "Hi this is: \(albumInfo)" } } }
步骤3:优化AlbumManager避免循环引用
protocol AlbumManagerDelegate: AnyObject { // 限制仅类遵守协议 func didUpdateAlbumInfo(albumInfo: String) } struct AlbumManager { weak var delegate: AlbumManagerDelegate? // 用weak避免循环引用 func getAlbumInfo(albumTitle: String) { let wikipedia = Wikipedia.shared let language = WikipediaLanguage("en") let _ = wikipedia.requestArticle(language: language, title: albumTitle, imageWidth: 5) { [weak self] result in guard let self = self else { return } switch result { case .success(let article): print(article.displayTitle) self.delegate?.didUpdateAlbumInfo(albumInfo: article.displayTitle) print("delegate should be updated") case .failure(let error): print(error) } } } }
额外优化:用Completion Handler替代代理(更简洁)
如果你觉得代理繁琐,可以用闭包代替,代码更紧凑:
修改AlbumManager的getAlbumInfo方法:
struct AlbumManager { func getAlbumInfo(albumTitle: String, completion: @escaping (Result<String, Error>) -> Void) { let wikipedia = Wikipedia.shared let language = WikipediaLanguage("en") let _ = wikipedia.requestArticle(language: language, title: albumTitle, imageWidth: 5) { result in switch result { case .success(let article): completion(.success(article.displayTitle)) case .failure(let error): completion(.failure(error)) } } } }
然后AlbumInfoViewController里直接调用:
override func viewDidLoad() { super.viewDidLoad() albumManager.getAlbumInfo(albumTitle: targetAlbumTitle) { [weak self] result in guard let self = self else { return } DispatchQueue.main.async { switch result { case .success(let title): self.albumDescriptionLabel.text = "Hi this is: \(title)" case .failure(let error): print(error) self.albumDescriptionLabel.text = "加载失败" } } } }
这样就不需要代理协议,代码更简洁,也彻底避免了代理实例不匹配的问题。
内容的提问来源于stack exchange,提问作者Jake Jake
相关产品推荐
相关产品推荐

