【问题标题】:unrecognized selector sent to instance-Swift无法识别的选择器发送到 instance-Swift
【发布时间】:2018-12-28 13:48:50
【问题描述】:

大家好:我有一个通过 API 下载数据(纬度、经度)的函数,它可以 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'

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

这是我的模型类

//  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()
    }
}
}

我的常量是:

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($0)! }
        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 选择器语法是正确的,但我仍然收到错误(无法识别的选择器发送到实例)你能帮我吗??

【问题讨论】:

  • 什么是被调用方法的参数中的“DisplayAlertMessage”?
  • 仍然是同样的问题和同样的崩溃错误(无法识别的选择器发送到实例 0x7ffa4a51cb00)
  • 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) }

标签: ios swift swift3 swift4


【解决方案1】:

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

但是在你的代码中:

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:)

当你为action方法指定#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 类型的参数(在 Objective-C 中是 NSTimer。)

因此,您可能需要定义一个与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)

抱歉,由于显示的答案不完整,请尝试在我更新的代码中填写...

【讨论】:

  • 现在尝试这个之后我仍然会崩溃,但它不是(无法识别的选择器发送到实例 0x7ffa4a51cb00)它现在是空日志???
  • 我没有完全测试你的代码,所以你的代码可能有很多其他的错误。如果你没有更多的无法识别的选择器,你最好用你的最新代码开始一个新线程。
  • 好的,非常感谢您,我将尝试找出新崩溃的原因。非常感谢
  • 哦,我在你的代码中发现了其他错误,我应该在写答案之前找到它。请停止发起新话题。
  • OOPer 非常感谢您,现在它 100% 正确,没有任何崩溃或错误非常感谢您的出色解释和完美答案
【解决方案2】:

你在这里犯的错误是

  1. 您将 target 作为 self 传递,这意味着您的选择器应该在 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")
        })

【讨论】:

    【解决方案3】:

    您确定的计时器目标是错误的。你应该设置定时器和调用函数。

    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)
    

    【讨论】:

    • 此显示 8 个错误和 1 个警告
    • 在我的情况下,这是因为我忘记更改自定义类 (Main.storyboard) 中的模块,因为我将一个项目重用于新项目
    【解决方案4】:

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

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

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

    【讨论】:

      【解决方案5】:

      当你每 5 秒调用一次函数时,替换为这一行,并请遵循 Swift 的约定。类名的首字母大写。

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

      【讨论】:

      • 还是同样的错误(无法识别的选择器发送到实例 0x7f9ff9d198d0)
      • 触发 callFuncTimer 计时器。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多