我正在尝试通过协议扩展实现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
我将didUpdateLocations
和didFailWithError
实施放在那里。他们永远不会触发并实际崩溃说:'Delegate must respond to locationManager:didUpdateLocations:'
。如果我将相同的didUpdateLocations
和didFailWithError
代码移至ViewController
,则一切都会按预期运行。
为什么这不通过协议扩展工作,我有什么遗漏?该类被识别为符合CLLocationManagerDelegate
,否则将在locationManager.delegate = self
失败。关于如何使这项工作或某处有错误的任何想法?
答案 0 :(得分:2)
协议扩展是纯粹的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
答案 1 :(得分:0)
协议扩展是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")
}
}
答案 2 :(得分: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")
}
}