无法识别的选择器发送到实例-Swift

unrecognized selector sent to instance-Swift

大家好:我有一个功能可以点击 API 下载数据 (LAT,LONG),它 100% 正常工作。但问题是我想每 5 秒调用一次这个函数,在我每次尝试 运行 它 运行 时添加一个计时器来执行此操作非常好,5 秒后我崩溃了

unrecognized selector sent to instance 0x7ffa4a51cb00
2018-07-20 11:05:31.191467+0200 Interactive Bus[684:6752] *** 
Terminating app due to uncaught exception 'NSInvalidArgumentException', 
reason: '-[Interactive_Bus.MapVC 
downloadBusDataWithUserId:warning:delegate:completed:]: unrecognized 
selector sent to instance 0x7ffa4a51cb00'

我尝试清理然后构建并且没有代码错误我不知道为什么会发生此崩溃

这是我的模特Class

//  busLocationModel.swift
//  Interactive Bus
import UIKit
import Alamofire
import SwiftyJSON

class busLocationModel {

var route_pins: String?
var busLatitude: Double?
var busLongitude: Double?

@objc func downloadBusData(userId: String,warning: @escaping (String,String,String) -> Void, delegate : MapVC ,completed : @escaping DownloadCompleted) {

    let Parameters = ["parent_id": userId]
    print(#function)
    Alamofire.request(busLocationUrl, method: .get, parameters: Parameters).responseJSON { (response) in

        switch response.result {

        case .failure(let error):

            print(error)

            let alertControllerTitle = "Warning"
            let actionButtonTitle = "Ok"
            let alertMessage = "Some Thing Went Wrong Please Try Agin Later "

            return warning(alertControllerTitle, actionButtonTitle, alertMessage)

        case .success(let Value):

            let json = JSON(Value)
            //print(json)

            let status = json["status"].boolValue

            if status != false {

                for locations in json["data"].arrayValue {

                    let busPins = locations["route_pins"].stringValue
                    let bus_lat = locations["bus_lat"].doubleValue
                    let bus_long = locations["bus_long"].doubleValue

                    delegate.busPins = busPins
                    delegate.currentBusLate = bus_lat
                    delegate.currentBusLong = bus_long

                    print(delegate.busPins ?? "HH")
                    print("the bus lat is \(bus_lat)")
                    print("the bus long is \(bus_long)")
                }

            }
        }
        completed()
    }
}
}

我的 Const 是:

typealias DownloadCompleted = () -> ()

我的 MapVC 是:

//
import UIKit
import MapKit
import CoreLocation

class MapVC: UIViewController {

@IBOutlet weak private var busMapView: MKMapView!

var locationManager = CLLocationManager()
//var locationManager: CLLocationManager!
let authorizationStatus = CLLocationManager.authorizationStatus()
fileprivate let regionRadius: Double = 1000 //Meter's From UP,Right,Down and Left
fileprivate var busInfoObject: busLocationModel!

var busPins: String!
var currentBusLate: CLLocationDegrees?
var currentBusLong: CLLocationDegrees?

var callFuncTimer: Timer!



override func viewDidLoad() {
    super.viewDidLoad()

    locationManager.delegate = self
    busMapView.delegate = self
    busInfoObject = busLocationModel()

    let myActivity = CreatActivityIndicator()


    busInfoObject.downloadBusData(userId: "366", warning: DisplayAlertMessage, delegate: self) {

        self.drawLine()
        self.RemoveActivityIndicator(ActivityIndicator: myActivity)

        guard let latitude = self.currentBusLate else { return }
        guard let longitude = self.currentBusLong else { return }

        let Location = CLLocation(latitude: latitude, longitude: longitude)

        self.centerMapOnBusLocation(location: Location)

        self.callFuncTimer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(self.busInfoObject.downloadBusData(userId:warning:delegate:completed:)), userInfo: nil, repeats: true)

    }
}

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(true)

    configureLocationServices()
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(true)

    callFuncTimer.invalidate()
}

@IBAction func centerMapBtnPressed(_ sender: Any) {

    if authorizationStatus == .authorizedAlways {

        guard let latitude = self.currentBusLate else { return }
        guard let longitude = self.currentBusLong else { return }

        let Location = CLLocation(latitude: latitude, longitude: longitude)

        centerMapOnBusLocation(location: Location)
    }
}
}
extension MapVC: MKMapViewDelegate {

fileprivate func centerMapOnBusLocation(location: CLLocation) {

    //guard let currtntLocationCoordinate = locationManager.location?.coordinate else { return }
    let coordinateRegion = MKCoordinateRegionMakeWithDistance(location.coordinate , regionRadius * 2.0, regionRadius * 2.0)
    busMapView.setRegion(coordinateRegion, animated: true)
}


func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {

    if (overlay is MKPolyline) {
        let pr = MKPolylineRenderer(overlay: overlay)
        pr.strokeColor = UIColor.blue
        pr.lineWidth = 5
        return pr
    }
    return MKPolylineRenderer()
}
}

extension MapVC {

func drawLine() {

    let coordinates = busPins.components(separatedBy: "#").dropFirst().map { (pin) -> CLLocationCoordinate2D in
        let latLng = pin.components(separatedBy: ",").map{ CLLocationDegrees([=14=])! }
        return CLLocationCoordinate2D(latitude: latLng[0], longitude: latLng[1])
    }

    let polyLine = MKPolyline(coordinates: coordinates , count: coordinates.count)
    self.busMapView.add(polyLine)
}

}

extension MapVC: CLLocationManagerDelegate {

fileprivate func configureLocationServices() {

    if authorizationStatus == .notDetermined {
        locationManager.requestAlwaysAuthorization()
    } else {

        return
    }
}

func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {

    guard let latitude = self.currentBusLate else { return }
    guard let longitude = self.currentBusLong else { return }

    let Location = CLLocation(latitude: latitude, longitude: longitude)

    centerMapOnBusLocation(location: Location)
}
}

这是 DisplayAlertMessage:

    func DisplayAlertMessage(alertControllerTitle: String , actionButtonTitle: String , alertMessage: String) -> Void{


    let alertcontroller = UIAlertController(title: alertControllerTitle, message: alertMessage , preferredStyle: .alert)

    let okaction = UIAlertAction(title: actionButtonTitle, style: .default, handler: nil)

    alertcontroller.addAction(okaction)
    self.present(alertcontroller, animated: true, completion: nil)
}

我看不到代码错误我执行了@OBJC 选择器语法是正确的但我仍然收到错误(无法识别的选择器发送到实例)你能帮我吗??

您设定的定时器目标有误。您应该为定时器设置功能并调用。

func timerUpdates(_ timer : Timer)
{ 
  self.busInfoObject.downloadBusData(userId:warning:delegate:completed:)
}
self.callFuncTimer = Timer.scheduledTimer(timeInterval: 5, target:#selector(self.timerUpdates:) self, selector: #selector(), userInfo: nil, repeats: true)

您调用的方法缺少一些参数

func downloadBusData(userId: String,warning: @escaping (String,String,String) -> Void, delegate : MapVC ,completed : @escaping DownloadCompleted)

根据您的声明,它应该有 userId、warning、delegate 和完成处理程序

在目标-动作模式中,目标对象需要实现动作方法。

但是在你的代码中:

self.callFuncTimer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(self.busInfoObject.downloadBusData(userId:warning:delegate:completed:)), userInfo: nil, repeats: true)

您使用 self 作为目标,它是 MapVC 的实例,未实现方法 downloadBusData(userId:warning:delegate:completed:).

当你为动作方法指定#selector(someInstance.methodName(...))时,你需要将someInstance传递给目标对象。在你的情况下 someInstanceself.busInfoObject.

这意味着创建 Timer 的行应该变成这样:

self.callFuncTimer = Timer.scheduledTimer(timeInterval: 5, target: self.busInfoObject, selector: #selector(self.busInfoObject.downloadBusData(userId:warning:delegate:completed:)), userInfo: nil, repeats: true)

但这不起作用。


我太蠢了,差点忘了告诉你目标-行动模式中的另一件重要的事情。

也就是说,

action方法的签名肯定是根据target固定的

使用Timer时,签名需要和这个一样:

class func scheduledTimer(timeInterval: TimeInterval, target: Any, selector: Selector, userInfo: Any?, repeats: Bool) -> Timer

- (void)timerFireMethod:(NSTimer *)timer

符号是Objective-C格式,但是Timer的动作方法需要有一个且只有一个Timer类型的参数(在[中是NSTimer =66=].)

因此,您可能需要定义一个方法来匹配您 MapVC:

中的签名
func timerFired(_ timer: Timer) {
    self.busInfoObject.downloadBusData(userId: ...,
                                       warning: {_, _, _ in
                                           ...
                                       }, 
                                       delegate: self,
                                       completed: {
                                           ...
                                       })
}

并将设置定时器的行更改为:

self.callFuncTimer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(self.timerFired(_:)), userInfo: nil, repeats: true)

抱歉,给你一个不完整的答案,请尝试在我更新的代码中填充 ...

当您每 5 秒调用一次函数时,替换为此行,请遵循 Swift 的约定。为 class 姓名写首字母大写。

self.callFuncTimer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(BusLocationModel.downloadBusData(userId:warning:delegate:completed:)), userInfo: nil, repeats: true)

你在这里犯的错误是

  1. 您将目标作为自身传递,这意味着您的选择器应该在 MapVC 中

  2. 您传递的选择器不正确。根据 Apple 文档,您的选择器应具有签名

    -(void)timerFireMethod:(NSTimer *)timer

参考这个https://developer.apple.com/documentation/foundation/timer/1412416-scheduledtimer

因此,要使其正常工作,请执行以下操作:

func timerFireMethod(_ timer: Timer?) {

   self.busInfoObject.downloadBusData(your parameters)

}

//注册定时器

self.callFuncTimer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(self.timerFireMethod), userInfo: nil, repeats: true)

为了方便你可以使用这个方法

self.callFuncTimer = Timer(timeInterval: 5, repeats: true, block: { (timer) in
            print("timer")
        })