iOS开发:Worker类Delegate未调用问题求助
Delegate未触发:Stack Overflow API数据无法传递到ViewController
问题场景
已成功通过Worker类调用Stack Overflow API获取数据,但尝试用Delegate将数据传递至ViewController时,getListModels方法始终不执行。点击搜索按钮会触发Worker的fetch逻辑,但Delegate回调无响应。
相关代码
ViewController类
import UIKit protocol WorkerDelegate: AnyObject { func getListModels(noteModels: [CellModel]) } class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, WorkerDelegate{ private var listModels: [CellModel] = [] private var searchText = UITextField() private var searchButton = UIButton() private var table = UITableView() private let worker = Worker() override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .systemBackground let worker = Worker() worker.workerDelegate = self setupSearchText() setupSearchButton() table.register(CustomCell.self, forCellReuseIdentifier: "Cell") table.delegate = self table.dataSource = self setupUI() } func getListModels(noteModels: [CellModel]) { listModels += noteModels table.reloadData() } private func setupSearchText() { searchText.translatesAutoresizingMaskIntoConstraints = false searchText.font = UIFont.systemFont(ofSize: 14, weight: .bold) searchText.textColor = .black searchText.backgroundColor = .white searchText.borderStyle = UITextField.BorderStyle.roundedRect searchText.layer.borderWidth = 1 searchText.layer.borderColor = UIColor.black.cgColor view.addSubview(searchText) searchText.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true searchText.leadingAnchor.constraint( equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 20 ).isActive = true searchText.trailingAnchor.constraint( equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -70 ).isActive = true } private func setupSearchButton() { searchButton.translatesAutoresizingMaskIntoConstraints = false searchButton.backgroundColor = .white searchButton.layer.borderWidth = 1 searchButton.setTitle("OK", for: .normal) searchButton.setTitleColor(.black, for: .normal) searchButton.layer.borderColor = UIColor.black.cgColor view.addSubview(searchButton) searchButton.addTarget(self, action: #selector(didSearchButtonnTap(_:)), for: .touchUpInside) searchButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true searchButton.leadingAnchor.constraint( equalTo: searchText.safeAreaLayoutGuide.trailingAnchor, constant: 10 ).isActive = true searchButton.trailingAnchor.constraint( equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -20 ).isActive = true } private func setupUI() { table.translatesAutoresizingMaskIntoConstraints = false table.separatorStyle = .none view.addSubview(table) table.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true table.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true table.topAnchor.constraint(equalTo: searchText.bottomAnchor,constant: 20).isActive = true table.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { listModels.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { guard let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as? CustomCell else { fatalError("не CustomCell") } cell.selectionStyle = .none cell.model = listModels[indexPath.row] cell.layer.cornerRadius = 14 cell.layer.shadowRadius = 14 return cell } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {} @objc private func didSearchButtonnTap(_ sender: Any) { worker.fetch(question: searchText.text ?? "") table.reloadData() } }
Worker类
import Foundation class Worker { let session: URLSession var workerDelegate: WorkerDelegate? func fetch(question: String) { var listModels: [CellModel] = [CellModel(title: "123",name: "312",date: Date.now, answer_count: Int32(0))] if let url = createURLComponents(question: question) { url.asyncDownload { data, _, error in guard let data = data else { print("URLSession dataTask error:", error ?? "nil") return } do { let jsonObject = try JSONDecoder().decode(SearchModels.self, from: data) _ = String(data: data, encoding: .utf8) for object in jsonObject.items { let timeInterval = TimeInterval(object.creationDate) let newDate = Date(timeIntervalSince1970: timeInterval) var model = CellModel( title: object.title, name: object.owner.displayName, date: newDate, answer_count: Int32(object.answerCount) ) listModels.append(model) } DispatchQueue.main.async { self.workerDelegate?.getListModels(noteModels: listModels) } } catch { print(error.localizedDescription) } } } } init(session: URLSession = URLSession(configuration: .default)) { self.session = session } private func createURLComponents(question: String) -> URL? { var urlComponents = URLComponents() urlComponents.scheme = "https" urlComponents.host = "api.stackexchange.com" urlComponents.path = "/2.3/search" urlComponents.queryItems = [ URLQueryItem(name: "order", value: "desc"), URLQueryItem(name: "sort", value: "activity"), URLQueryItem(name: "intitle", value: question), URLQueryItem(name: "site", value: "stackoverflow") ] return urlComponents.url! } private func createURLRequest(question: String) -> URLRequest { var request = URLRequest(url: createURLComponents(question: question)!) request.httpMethod = "GET" return request } } extension URL { func asyncDownload(completion: @escaping (_ data: Data?, _ response: URLResponse?, _ error: Error?) -> Void) { URLSession.shared .dataTask(with: self, completionHandler: completion) .resume() } }
问题原因
ViewController中存在两个独立的Worker实例:
- 类属性
private let worker = Worker():后续调用fetch方法的实例,但从未设置它的workerDelegate - viewDidLoad中的局部实例
let worker = Worker():给这个实例设置了workerDelegate = self,但实例在viewDidLoad执行完毕后就被销毁,未被实际使用
最终导致调用fetch的Worker实例的workerDelegate为nil,无法触发回调。
修复方案
删除viewDidLoad中的局部Worker实例,直接给类属性的worker设置delegate:
override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .systemBackground worker.workerDelegate = self setupSearchText() setupSearchButton() table.register(CustomCell.self, forCellReuseIdentifier: "Cell") table.delegate = self table.dataSource = self setupUI() }
同时移除搜索按钮点击事件中多余的table.reloadData()——此时数据还未更新,调用无意义,Delegate回调中已经包含了数据更新后的刷新逻辑:
@objc private func didSearchButtonnTap(_ sender: Any) { worker.fetch(question: searchText.text ?? "") }
内容的提问来源于stack exchange,提问作者termyter
相关产品推荐
相关产品推荐

