6

我正在尝试CLLocationManagerDelegate通过协议扩展来实现协议要求,但位置管理器在协议扩展中看不到它并且失败了。但是,它在移入类时使用相同的代码。

这就是我正在做的事情:

class ViewController: UIViewController, MyLocationProtocol {
    let locationManager = CLLocationManager()

    override func viewDidLoad() {
        super.viewDidLoad()

        locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
        locationManager.distanceFilter = 1000.0
        locationManager.delegate = self

        // Below crashes when implementation in protocol extension
        locationManager.requestLocation()
    }

}

protocol MyLocationProtocol: CLLocationManagerDelegate {
    func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
    func locationManager(manager: CLLocationManager, didFailWithError error: NSError)
}

extension MyLocationProtocol /*where Self: UIViewControll*/ { // Tried using where clause but still no go :(

    // Not being triggered by CLLocationManagerDelegate! :(
    // Move to ViewController class and error goes away
    func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        print("MyLocationProtocol: locationManager: didUpdateLocations")
    }

    func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
        print("MyLocationProtocol: locationManager: didFailWithError")
    }

}

请注意,extension MyLocationProtocol我将didUpdateLocationsanddidFailWithError实现放在那里。他们从来没有被触发并且实际上崩溃说:'Delegate must respond to locationManager:didUpdateLocations:'。如果我将相同的didUpdateLocations代码didFailWithError移动到ViewController,一切都会按预期工作。

关于为什么这不能通过协议扩展工作,我有什么遗漏吗?该类被识别为符合CLLocationManagerDelegate,否则它将失败locationManager.delegate = self。关于如何进行这项工作的任何想法或某处是否存在错误?

4

3 回答 3

2

协议扩展是纯 Swift 员工。协议扩展的调度规则是:

如果变量的推断类型是协议

  • 并且该方法在原始协议中定义,然后调用运行时类型的实现,无论扩展中是否存在默认实现
  • 并且该方法未在原始协议中定义,然后调用默认实现。

IF 推断的变量类型是类型 THEN 调用该类型的实现。

考虑到这一切...

import XCPlayground
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true

import Foundation
import MapKit


class Lm: NSObject, CLLocationManagerDelegate {
    func locationManager(manager: CLLocationManager, didUpdateLocations locations: [AnyObject]) {
        print("\(locations)")
    }
}


class C:Lm {
    let lm = CLLocationManager()
    override init() {
        super.init()
        lm.delegate = self
        lm.startUpdatingLocation()
    }

}
let c = C()
/*
2016-02-22 08:41:56.506 Untitled Page 10[32000:11547708] ### Failed to load Addressbook class CNContactNameFormatter
[<+48.71408491,+21.20868516> +/- 65.00m (speed -1.00 mps / course -1.00) @ 22/02/16 08 h 41 min 57 s Central European Standard Time]
[<+48.71408491,+21.20868516> +/- 65.00m (speed -1.00 mps / course -1.00) @ 22/02/16 08 h 41 min 57 s Central European Standard Time]
[<+48.71415732,+21.20859246> +/- 65.00m (speed -1.00 mps / course -1.00) @ 22/02/16 08 h 41 min 57 s Central European Standard Time]
....
*/

另一种选择是做类似...

import XCPlayground
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true

import Foundation
import MapKit

class MyLocationManager: CLLocationManager, CLLocationManagerDelegate {
    override init() {
        super.init()
        delegate = self
    }
}
extension MyLocationManager {
    func locationManager(manager: CLLocationManager, didUpdateLocations locations: [AnyObject]) {
        print("\(locations)")
    }
}
class C {
    let lm = MyLocationManager()
    init() {
        lm.startUpdatingLocation()
    }
}

如果在编译时所有类型都是已知的,并且协议中没有“可选”方法,则它“按预期”工作

protocol P { func foo() }
extension P { func foo() { print("p") } }
protocol P1: P {}
extension P1 { func foo() { print("p1") } }
class C: P {}
class C1: P1 {}
let c = C()
let c1 = C1()
let p:P = C()
let p1:P = C1()

c.foo()  // p
c1.foo() // p1
p.foo()  // p
p1.foo() // p1

如需进一步阅读,请参阅

于 2016-02-22T08:18:40.910 回答
0

我觉得这种行为有点奇怪。但是,对于您当前的问题,您可以使用此解决方案。

基本上,这个想法是引入一个代理对象,您可以在其中实现处理协议中位置的逻辑并编写一个符合该协议的类。然后在您的 ViewController 中创建类的实例(代理)并实现CLLocationManagerDelegate方法并将回调方法传递给代理对象。这将帮助您从 ViewController 中分离出位置处理逻辑。您仍然可以在 Watch 应用程序中独立使用 MyLocationManagerDelegateClass。

希望这可以帮助。

class ViewController: UIViewController, CLLocationManagerDelegate
{
    let locationManager = CLLocationManager()
    let proxyLocationManagerDelegate = MyLocationManagerDelegateClass()

    override func viewDidLoad() {
        super.viewDidLoad()
        locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
        locationManager.distanceFilter = 1000.0
        // Below crashes when implementation in protocol extension
        locationManager.delegate = self
        locationManager.requestLocation()
    }

    func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        print("ViewController: locationManager: didUpdateLocations")
        proxyLocationManagerDelegate.locationManager(manager, didUpdateLocations: locations)
    }

    func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
        print("ViewController: locationManager: didFailWithError")
        proxyLocationManagerDelegate.locationManager(manager, didFailWithError: error)
    }
}

class MyLocationManagerDelegateClass : NSObject, MyCLLocationManagerDelegate {

}

protocol MyCLLocationManagerDelegate : CLLocationManagerDelegate  {
    func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
    func locationManager(manager: CLLocationManager, didFailWithError error: NSError)
}

extension MyCLLocationManagerDelegate /*where Self: UIViewControll*/ { // Tried using where clause but still no go :(

    // Not being triggered by CLLocationManagerDelegate! :(
    // Move to ViewController class and error goes away
    func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        print("MyLocationProtocol: locationManager: didUpdateLocations")
    }

    func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
        print("MyLocationProtocol: locationManager: didFailWithError")
    }

}
于 2016-03-07T06:56:58.537 回答
0

协议扩展是 swift 2.0 的新事物之一。这允许您自己定义协议的行为。如果您在确认类和协议扩展中都实现了方法定义,则在运行时将调用确认类中的方法实现。但可选协议实际上是目标 c 的副产品。这意味着如果您想快速定义可选协议,您需要@objc在协议声明之前插入属性。当我像您解释的那样试验协议扩展时,当协议不是可选的时它运行良好。但是当协议是可选的时,应用程序就崩溃了。CLLocationManagerDelegate委托方法被声明为可选的。我想这可能是原因。

视图控制器 1

class ViewController: UIViewController,MyViewControllerProtocol {

var myViewController:NewViewController?

override func viewDidLoad() {
    super.viewDidLoad()

    let sb = UIStoryboard(name: "Main", bundle: nil)
    myViewController = sb.instantiateViewControllerWithIdentifier("newViewController") as? NewViewController
    myViewController?.delegate = self
    myViewController?.view.frame = self.view.bounds
    self.view.addSubview((myViewController?.view)!)
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
}

func newViewControllerLoaded(){
    print("protocol definition called inside class")
}

}

protocol MyViewControllerProtocol: NewViewControllerProtocol {
   func newViewControllerLoaded()
}

extension MyViewControllerProtocol{

func newViewControllerLoaded(){
   print("protocol definition called inside extension")
}

}

视图控制器 2

protocol NewViewControllerProtocol {

func newViewControllerLoaded()
}

class NewViewController: UIViewController {

var delegate: NewViewControllerProtocol?

override func viewDidLoad() {
    delegate?.newViewControllerLoaded()
}

}

带有协议实现的输出ViewController

protocol definition called inside class

删除协议实现后的输出ViewController

protocol definition called inside extension

解决问题的一种方法是将扩展应用到您的类,然后协议实现将在您的类中。但这不是协议扩展。

extension ViewController /*where Self: UIViewControll*/ { // Tried using where clause but still no go :(

// Not being triggered by CLLocationManagerDelegate! :(
// Move to ViewController class and error goes away
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
    print("MyLocationProtocol: locationManager: didUpdateLocations")
}

func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
    print("MyLocationProtocol: locationManager: didFailWithError")
}

}
于 2016-03-02T13:27:16.193 回答