Частичное обновление решения в конце!
В приложении приведен код, который вызывает странное поведение.Я скопировал его из быстрой игровой площадки, чтобы он работал в одном штрафе.
Я создал подкласс в своем проекте и передал его в мой общий класс в качестве конкретного типа.Однако я быстро заметил, что вызываются только методы базового класса.Это показано с myBase
и mySub
ниже.Несмотря на то, что базовый класс создается как <mySub>
, вызываются только базовые методы.Строки печати для подкласса никогда не показываются.
Ну, я нашел простой способ обойти это, и это не наследовать от NSObject.Когда я использовал быстрые собственные классы, методы подкласса фактически вызывались.Это secondBase и secondSub.
Как передать подкласс в универсальный класс и получить фактический подкласс для приема вызовов при наследовании от NSObject?
И почему поведение будет другим?
import Foundation
// The Protocol
protocol P {
init ()
func doWork() -> String
}
// Generic Class
class G<T: P> {
func doThing() -> String {
let thing = T()
return thing.doWork()
}
}
// NSObject Base Class with Protocol
class A1: NSObject, P {
override required init() {
super.init()
}
func doWork() -> String {
return "A1"
}
}
// NSObject Sub Class
class B1: A1 {
required init() {
super.init()
}
override func doWork() -> String {
return "B1"
}
}
// Swift Base Class
class A2: P {
required init() {
}
func doWork() -> String {
return "A2"
}
}
// Swift Sub Class
class B2: A2 {
required init() {
super.init()
}
override func doWork() -> String {
return "B2"
}
}
print ("Sub class failure with NSObject")
print ("Recieved: " + G<B1>().doThing() + " Expected: B1 - NSObject Sub Class Generic (FAILS)")
print ("\nSub class success with Swift Native")
print ("Recieved: " + G<B2>().doThing() + " Expected: B2 - Swift Sub Class Generic (SUCCEEDS)")
print("")
#if swift(>=5.0)
print("Hello, Swift 5.0")
#elseif swift(>=4.1)
print("Hello, Swift 4.1")
#elseif swift(>=4.0)
print("Hello, Swift 4.0")
#elseif swift(>=3.0)
print("Hello, Swift 3.x")
#else
print("Hello, Swift 2.2")
#endif
Вывод:
Sub class failure with NSObject
Recieved: A1 Expected: B1 - NSObject Sub Class Generic (FAILS)
Sub class success with Swift Native
Recieved: B2 Expected: B2 - Swift Sub Class Generic (SUCCEEDS)
Hello, Swift 5.0
Частичное обновление решения :
Перемещение соответствия протокола из базового класса в подкласс позволяет подклассувести себя правильно.Определения становятся следующими:
class A1: NSObject
class B1: A1, P
Проблема в том, что базовый класс больше не может использоваться напрямую, если не требуется никаких функциональных возможностей, кроме него.Это в основном проблема, если соответствующий протокол имеет связанный тип.Когда это так, вы должны иметь конкретный класс, соответствующий протоколу для использования в обобщениях.
Один из вариантов использования здесь предполагает ожидание базового класса в обобщениях (с протоколом, включающимсвязанный тип), который позволяет чему-то функционировать, не заботясь о том, в каком фактическом подклассе он был передан. В действительности это в конечном итоге является формой стирания типа бедного человека.И вы все еще можете использовать тот же универсальный с подклассом.
G<A1>()
G<B1>()
Это было получено из аналогичного вопроса здесь: Универсальный класс не перенаправляет вызовы делегатов конкретному подклассу
Возможны следующие варианты:
- удалить NSObject и использовать только быстрые собственные классы
- , когда требуется NSObject, попытаться отделить соответствие протокола от наследования NSObject
ОБНОВЛЕНИЕ НА НИЖЕ ИДЕИ: Не работает
Я собираюсь проверить, меняет ли поведение предоставление дополнительного слоя.В основном есть 3 уровня: базовый класс, наследующий от NSObject, базовый класс протокола, добавляющий протокол, но наследующий от базового и , затем определенных классов.Если в этом случае он сможет различить базовый класс протокола и конкретный подкласс, это будет функциональным обходным решением во всех случаях использования.(и может объяснить, почему Apple NSManagedObject работает нормально)
Тем не менее, похоже, что это ошибка.