Это то, что я разместил в качестве возможного решения 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.
Если вы пытаетесь '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) 'будет как печатать" Найдено: ". –
@POB: Это действительно работает (как и решение, предложенное в ответе vacawama), но я все еще не понимаю * почему *. –
Может быть что-то неясное, вы пробовали просить на форумах? –