Swift实现委托模式时WeatherManager的delegate始终为nil
问题原因
delegate始终为nil的核心原因是你创建了两个完全独立的WeatherManager实例,设置delegate和发起网络请求根本不是同一个对象:
WeatherViewController中声明的let weatherManager = WeatherManager()实例,你在viewDidLoad里确实给它设置了delegate为当前控制器,但这个实例全程没有被调用过fetchWeather方法,完全闲置。CoordinateManager中声明的let weathetManager = WeatherManager()是一个全新的独立实例,你实际拼接完天气URL后发起请求用的是这个实例,这个实例的delegate属性从未被赋值,自然为nil,回调无法触发。
另外注意这里weathetManager存在拼写错误(漏了字母r),后续维护容易踩坑。
修复步骤
- 移除
CoordinateManager内部自行创建的WeatherManager实例,改为接收外部传入的、已经绑定好delegate的实例:struct CoordinateManager { // 移除原有的let weathetManager = WeatherManager() var weatherManager: WeatherManager let coordinateURL = "https://api.openweathermap.org/geo/1.0/direct?limit=1&appid=c8e60c6317c653a1789294c00f54ae19" init(weatherManager: WeatherManager) { self.weatherManager = weatherManager } // 其余原有方法保持不变,仅把parseJSON方法里调用的weathetManager改为self.weatherManager func parseJSON(JSONobject: Data) { let decoder = JSONDecoder() do { let decodedData = try decoder.decode([CoordinateData].self, from: JSONobject) let name = decodedData[0].name let lat = decodedData[0].lat let lon = decodedData[0].lon let coordinateModel = CoordinateModel(name: name, lat: lat, lon: lon) let weatherURL = coordinateModel.urlString // 改为用传入的weatherManager发起请求 self.weatherManager.fetchWeather(urlString: weatherURL) } catch { print(error) } } // ...... 其余CoordinateManager原有方法不变 } - 在
WeatherViewController中初始化CoordinateManager时,把当前控制器持有的、已经绑定好delegate的weatherManager实例传入:class WeatherViewController: UIViewController, UITextFieldDelegate, WeatherManagerDelegate { // ...... 原有IBOutlet保持不变 let weatherManager = WeatherManager() var coordinateManager: CoordinateManager! override func viewDidLoad() { super.viewDidLoad() searchTextField.delegate = self searchTextField.keyboardType = .asciiCapable weatherManager.delegate = self // 传入已绑定delegate的weatherManager实例 coordinateManager = CoordinateManager(weatherManager: weatherManager) } // ...... 其余原有方法不变 }
补充优化
didUpdateWeather回调是在URLSession的后台子线程执行的,如果回调内需要更新UI,必须手动切换到主线程,否则会出现UI更新延迟、偶现崩溃的问题:
func didUpdateWeather(inputWeatherModel: WeatherModel) { DispatchQueue.main.async { print(inputWeatherModel.temperature) // 此处编写温度标签、城市名、天气图标的UI更新逻辑 } }
内容的提问来源于stack exchange,提问作者ADAM _YONEDA
相关产品推荐
相关产品推荐

