2014-09-14 2 views
8

Это то, что я разместил в качестве возможного решения Traverse view controller hierarchy in Swift (слегка модифицированный):Дополнительное связывание успешно, если он не должен

extension UIViewController { 

    func traverseAndFindClass<T where T : UIViewController>(T.Type) -> T? { 
     var currentVC = self 
     while let parentVC = currentVC.parentViewController { 
      println("comparing \(parentVC) to \(T.description())") 
      if let result = parentVC as? T { // (XXX) 
       return result 
      } 
      currentVC = parentVC 
     } 
     return nil 
    } 
} 

Метод должен пройти вверх по иерархии родительского зрения контроллера и возвращает первый экземпляр данного класса, или nil, если ни один не найден.

Но это не сработает, и я не могу понять, почему. Необязательное связывание с пометкой (XXX)всегда преуспевает, так что первый родительский контроллер представления возвращается , даже если он не является экземпляром T.

Это может быть легко воспроизведен: Создание проекта с "IOS Master-Detail Application" шаблон в Xcode 6 GM, и добавьте следующий код viewDidLoad() класса MasterViewController:

if let vc = self.traverseAndFindClass(UICollectionViewController.self) { 
    println("found: \(vc)") 
} else { 
    println("not found") 
} 

self представляет собой MasterViewController (подкласс UITableViewController), а его контроллер представления родительского вида - это UINavigationController. Существует нет UICollectionViewController в родительском представлении иерархии контроллеров , поэтому я ожидаю, что метод возвращает nil, а выход «не найден».

Но это то, что происходит:

comparing <UINavigationController: 0x7fbc00c4de10> to UICollectionViewController 
found: <UINavigationController: 0x7fbc00c4de10> 

Это, очевидно, неправильно, потому что UINavigationController не подкласс UICollectionViewController. Возможно, я сделал какую-то глупую ошибку, но я не мог ее найти.


Для того, чтобы выделить эту проблему, я также пытался воспроизвести его с моим собственным классом иерархии, независимо от UIKit:

class BaseClass : NSObject { 
    var parentViewController : BaseClass? 
} 

class FirstSubClass : BaseClass { } 

class SecondSubClass : BaseClass { } 

extension BaseClass { 

    func traverseAndFindClass<T where T : BaseClass>(T.Type) -> T? { 
     var currentVC = self 
     while let parentVC = currentVC.parentViewController { 
      println("comparing \(parentVC) to \(T.description())") 
      if let result = parentVC as? T { // (XXX) 
       return result 
      } 
      currentVC = parentVC 
     } 
     return nil 
    } 
} 

let base = BaseClass() 
base.parentViewController = FirstSubClass() 

if let result = base.traverseAndFindClass(SecondSubClass.self) { 
    println("found: \(result)") 
} else { 
    println("not found") 
} 

И угадайте, что? Теперь он работает так, как ожидалось! Выход

comparing <MyApp.FirstSubClass: 0x7fff38f78c40> to MyApp.SecondSubClass 
not found 

UPDATE:

  • Удаление ограничение типа в родовом метод

    func traverseAndFindClass<T>(T.Type) -> T? 
    

    как предложено @POB в комментарий делает работу как и ожидалось.

  • Замена дополнительного связывания с помощью «двухступенчатого связывания»

    if let result = parentVC as Any as? T { // (XXX) 
    

    как предложено @vacawama в своем ответе также делает его работу, как и ожидалось.

  • Изменение конфигурации сборки с «Отладки» на «Выпуск» также делает метод работать должным образом. (Я тестировал это только в симуляторе iOS до сих пор.)

Последнее, что может означать, что это компилятор Swift или ошибка времени выполнения. И я все еще не вижу, почему проблема возникает с подклассами UIViewController, но не с подклассами моего BaseClass. Поэтому я буду держать вопрос открытым для , прежде чем принимать ответ.


UPDATE 2: Это было исправлено в Xcode 7.

С окончательным выпуском Xcode 7 проблема больше не возникает. Необязательное связывание if let result = parentVC as? T в методе traverseAndFindClass() теперь работает (и не работает), как ожидалось, как в конфигурации Release, так и Debug.

+1

Если вы пытаетесь 'FUNC traverseAndFindClass (T.Type) -> T? {/*...*/} 'вместо' func traverseAndFindClass (T.Type) -> T? {/*...*/} ', он будет печатать« Не найден »для' if let vc = self.traverseAndFindClass (UICollectionViewController.self) '. Однако, если пусть vc = self.traverseAndFindClass (UIViewController.self) 'и' если пусть vc = self.traverseAndFindClass (UINavigationController.self) 'будет как печатать" Найдено: ". –

+0

@POB: Это действительно работает (как и решение, предложенное в ответе vacawama), но я все еще не понимаю * почему *. –

+0

Может быть что-то неясное, вы пробовали просить на форумах? –

ответ

3

Если попытаться условно бросить объект типа UINavigationController к UICollectionViewController в Playground:

var nc = UINavigationController() 

if let vc = nc as? UICollectionViewController { 
    println("Yes") 
} else { 
    println("No") 
} 

Вы получаете эту ошибку:

исполнение площадка не удалось:: 33: 16: ошибка: «UICollectionViewController» не является подтипом «UINavigationController» , если пусть vc = nc as? UICollectionViewController {

но если вместо этого вы делаете:

var nc = UINavigationController() 

if let vc = (nc as Any) as? UICollectionViewController { 
    println("Yes") 
} else { 
    println("No") 
} 

печатает "Нет".

Так что я предлагаю попробовать:

extension UIViewController { 

    func traverseAndFindClass<T where T : UIViewController>(T.Type) -> T? { 
     var currentVC = self 
     while let parentVC = currentVC.parentViewController { 
      println("comparing \(parentVC) to \(T.description())") 
      if let result = (parentVC as Any) as? T { // (XXX) 
       return result 
      } 
      currentVC = parentVC 
     } 
     return nil 
    } 
} 
+0

Это действительно работает так, как ожидалось (как и решение, предложенное @POB в комментарии), но я все еще не понимаю * почему *. И где разница с моим вторым примером BaseClass/FirstSubClass/SecondSubClass? Btw. 'if let result = (parentVC как AnyObject) как? T'. * Нет * работа. –

+0

Та же проблема по-прежнему возникает с последним Xcode 6.3.1, и вы также продолжаете работать. Кажется, лучшего решения нет! –

Смежные вопросы