2016-02-20 6 views
6

Próbuję zaimplementować wymagania protokołu CLLocationManagerDelegate za pośrednictwem rozszerzenia protokołu, ale menedżer lokalizacji nie widzi go w rozszerzeniu protokołu i kończy się niepowodzeniem. Jednak po przeniesieniu do klasy działa z tym samym kodem.Rozszerzenia protokołu nie mogą spełniać zgodności CLLocationManagerDelegate?

Oto co robię:

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") 
    } 

} 

Wskazówki w extension MyLocationProtocol Kładę implementacje didUpdateLocations i didFailWithError tam. Nigdy się nie uruchamiają i nie rozbijają, mówiąc: 'Delegate must respond to locationManager:didUpdateLocations:'. Jeśli przeniesię ten sam kod didUpdateLocations i didFailWithError do ViewController, wszystko będzie działać zgodnie z oczekiwaniami.

Czy jest coś, czego mi brakuje, dlaczego nie działa przez rozszerzenia protokołu? Ta klasa jest uznawana za zgodną z CLLocationManagerDelegate, w przeciwnym razie może zawieść pod adresem locationManager.delegate = self. Jakieś pomysły na to, jak to działa, czy gdzieś tam jest błąd?

+0

gdzie jest funkcja requestLocation() z? – user3441734

+0

Nowe API iOS 9. – TruMan1

Odpowiedz

1

Rozszerzenie protokołu to czysty personel Swift. Zasady wysyłki dla rozszerzeń protokołu są:

JEŻELI wywnioskować typ zmiennej jest protokół

  • a metoda jest zdefiniowana w oryginalnym protokole WTEDY realizacja typu Runtime za nazywa, niezależnie czy istnieje domyślna implementacja w rozszerzeniu.
  • I metoda NIE jest zdefiniowana w oryginalnym protokole, TO jest wywoływana domyślna implementacja.

JEŻELI wywnioskować typ zmiennej jest typ WTEDY realizacja typu nazywa.

Biorąc to wszystko na koncie ...

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] 
.... 
*/ 

Inną opcją jest zrobić coś takiego ...

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

jeśli wszystkie typy są znane w czasie kompilacji i tam nie jest " opcjonalna "metoda w protokole, działa" zgodnie z oczekiwaniami "

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 

do dalszego czytania zobacz this i

+0

Dzięki za wyjaśnienia i artykuły, ale nadal nie widzę, jak to nie jest możliwe. Ponieważ przekazuję 'self' do właściwości delegata' CLLocationManagerDelegate', czy nie powinien 'self', protokół, który jest zgodny, i jego rozszerzenia protokołu są dostępne? – TruMan1

+0

@ Funkcje TruMan1 w CLLocationManagerDelegate są "opcjonalne". oznacza to, że powinna istnieć pewna domyślna implementacja, która jest wykonywana i nie robi nic. jeśli taka implementacja nie istnieje, wówczas wykonywana jest funkcja z twojego rozszerzenia protokołu. zobacz, że nie masz żadnej kontroli nad typem właściwości delegata, to CLLocationManagerDelegate – user3441734

0

Rozszerzenie protokołu to jedna z nowych rzeczy od Swift 2.0. To pozwala na zdefiniowanie zachowania na samym protokole. Jeśli implementujesz definicję metody zarówno w klasie potwierdzającej, jak i rozszerzeniu protokołu, w czasie wykonywania zostanie wywołana implementacja metody w klasie potwierdzającej. Opcjonalny protokół jest jednak produktem ubocznym obiektywnego c. Oznacza to, że jeśli chcesz zdefiniować opcjonalny protokół w swift, musisz wstawić atrybut @objc przed deklaracją protokołu. Kiedy eksperymentowałem z rozszerzeniem protokołu, tak jak to wyjaśniłeś, działa ono dobrze, gdy protokół nie jest opcjonalny. Ale gdy protokół jest opcjonalny, aplikacja uległa awarii. CLLocationManagerDelegate Metody delegatów są zadeklarowane jako opcjonalne. Myślę, że to może być powód.

widok kontroler 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") 
} 

} 

Zobacz kontroler 2

protocol NewViewControllerProtocol { 

func newViewControllerLoaded() 
} 

class NewViewController: UIViewController { 

var delegate: NewViewControllerProtocol? 

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

} 

wyjście z implementacji protokołu w ViewController

protocol definition called inside class 

wyjście po usunięciu implementacji protokołu z ViewController

protocol definition called inside extension 

Jednym ze sposobów rozwiązania problemu jest zastosowanie rozszerzenia do klasy, a implementacja protokołu będzie znajdować się w klasie. Ale to nie jest rozszerzenie protokołu.

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") 
} 

} 
+0

Dzięki za próbę, powodem, dla którego chcę trzymać się rozszerzeń protokołów, jest to, że chciałbym zaimplementować to w systemie iOS i watchOS, który nie udostępnia żadnych klas przodków. – TruMan1

0

Uważam, że to zachowanie jest nieco dziwne. Jednak w przypadku obecnego problemu możesz użyć tego rozwiązania.

Zasadniczo chodzi o wprowadzenie obiektu proxy, w którym zaimplementowano logikę obsługi lokalizacji w protokole i napisano klasę zgodną z tym protokołem. Następnie w swoim kontrolerze ViewController utworzymy instancję klasy (proxy), a także zaimplementujemy metody CLLocationManagerDelegate i po prostu przekazujemy metody wywołania zwrotnego do obiektu proxy. Pomoże to oddzielić logikę obsługi lokalizacji od ViewController. Nadal możesz korzystać z MyLocationManagerDelegateClass niezależnie w aplikacji Watch.

Mam nadzieję, że to pomoże.

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") 
    } 

} 
Powiązane problemy