【问题标题】:Protocol extensions cannot fulfill CLLocationManagerDelegate conformance?协议扩展不能满足 CLLocationManagerDelegate 一致性?
【发布时间】:2016-02-20 15:08:55
【问题描述】:

我正在尝试通过协议扩展实现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 我将didUpdateLocationsdidFailWithError 实现放在那里。他们永远不会触发并实际上崩溃说:'Delegate must respond to locationManager:didUpdateLocations:'。如果我将相同的didUpdateLocationsdidFailWithError 代码移动到ViewController,一切都会按预期进行。

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

【问题讨论】:

  • requestLocation() 函数来自哪里?

标签: ios swift swift2 cllocationmanager protocol-extension


【解决方案1】:

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

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

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

如果推断的变量类型是类型 然后调用类型的实现。

考虑到这一切......

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

更多阅读请参阅thisthis

【讨论】:

  • 感谢您的解释和文章,但我仍然看不出这是不可能的。既然我将self 传递给CLLocationManagerDelegate 委托属性,那么self、它所遵循的协议及其协议扩展不应该可用吗?
  • CLLocationManagerDelegate 中的 @TruMan1 函数是“可选的”。这意味着,它应该存在一些默认实现,它被执行并且什么都不做。如果不存在这样的实现,则执行协议扩展中的函数。看,您对委托属性的类型没有任何控制权,它是 CLLocationManagerDelegate
【解决方案2】:

协议扩展是 swift 2.0 的新功能之一。这允许您自己定义协议的行为。如果您在确认类和协议扩展中都实现了方法定义,则在运行时将调用确认类中的方法实现。但可选协议实际上是目标 c 的副产品。这意味着如果您想在 swift 中定义可选协议,您需要在协议声明之前插入 @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")
}

}

【讨论】:

  • 感谢您的尝试,我之所以要坚持使用协议扩展是因为我想在不共享任何祖先类的 iOS 和 watchOS 上实现这一点。
【解决方案3】:

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

基本上,这个想法是引入一个代理对象,您可以在其中实现处理协议中位置的逻辑,并编写一个符合该协议的类。然后在您的 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")
    }

}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多