2014-11-25 3 views
5

ОБНОВЛЕНИЕ: Вопрос и название были пересчитаны на основании изменений, которые были сделаны в ответ на исходный вопрос.Невозможно вызвать инициализатор для подкласса типового типа

У меня есть базовый класс, который реализует универсальный метод класса для создания новых экземпляров. Упрощенная логика этого метода класса заключается в следующем

class MyBaseClass { 

    required init(_ record:MyRecordType) { 
     println("Entered MyBaseClass.init") 
     // Initialize base class from record 
    } 

    class func retrieveInstance<T:MyBaseClass>(name:String, callback:(T) ->()) { 
     let myRecord:MyRecordType = ... // Perform fetch to get a record for name 
     let result = (T.self as T.Type)(myRecord) // Code currently crashes with BAD_ACCESS at this line 
     callback(result) 
    } 
} 

Я тогда реализовать подкласс этого базового класса с логикой как следующий

class MySubClass : MyBaseClass { 

    required init(_ record:MyRecordType) { 
     println("Entered MySubClass.init") 
     // Initialize subclass from record 
     super.init(record) 
    } 
} 

Далее, я пытаюсь вызвать метод общего класса

class AnotherClass { 
    func successCallback(result:MySubclass) { 
     // Handle callback 
    } 
    MySubClass.retrieveInstance("objectName", callback:successCallback) 
} 

При создании экземпляра класса - строки, помеченной комментарием, идентифицирующим местоположение сбоя - я ожидаю, что метод init от MySubClass до вызывается. (Это нечетное обозначение основано на обходном пути ошибки, предложенном в ответах)

Вместо вызова метода init для MySubClass этот код вылетает с BAD_ACCESS. Я не смог найти ничтожные ссылки или что-то еще, что могло бы объяснить эту катастрофу.

+0

Я не могу воспроизвести BAD_ACCESS. Я предполагаю, что вы используете какой-то асинхронный вызов, может быть, проблема там. – rintaro

+0

@ rintaro - Наконец-то я смог изолировать проблему. Он оказывается не связанным с асинхронностью и вместо этого относится к смешению переопределенного инициализатора с требуемым инициализатором (я опубликую полное описание ниже). Поскольку это был ваш ответ, по крайней мере, я указал в правильном направлении, я хотел бы дать вам правильный ответ, но вы, кажется, удалили свой ответ. Не стесняйтесь пересказывать, если вы хотите, чтобы я дал вам кредит на вашу помощь. –

ответ

5

С большой помощью из ответа, первоначально отправленного @rintaro, я смог решить эту проблему, хотя по-прежнему есть странность, которую я опубликую по отдельному вопросу.

Как выясняется, @rintaro был абсолютно прав в необходимости инициализировать экземпляр универсального типа, используя следующий синтаксис:

let result = (T.self as T.Type)(myRecord) 

Это работает до тех пор, как базовый класс MyBaseClass объявляет этот инициализатору с required тег:

public class MyBaseClass { 
    public required init(_ record:MyRecordType) { 
     ... 
    } 
} 

и подкласс MySubClass реализует соответствующий инициализатор:

public class MySubClass : MyBaseClass { 
    public required init (_ record:MyRecordType) { 
     ... 
     super.init(record) 
    } 
} 

Если что-то не удается, однако, когда у меня есть иерархия классов из 3 уровней, а иерархия инициализатора - override. Чтобы представить себе это, рассмотрим набор класса, который представляет узлы в древовидной структуре:

public class Node { 
    public init(_ record:MyRecordType) { 
     ... 
    } 
} 

public class RootNode : Node { 
    override public init(_ record:MyRecordType) { 
     ... 
     super.init(record) 
    } 
    public class func <T:RootNode>retrieveAll(success:(T) ->()) { 
     // Retrieve all instances of root node subclass T, and invoke success callback with new T instance 
    } 
} 

public class ChildNode : Node { 
    public init(_ record:MyRecordType, parentNode:Node) { 
     ... 
     super.init(record) 
    } 
    public class func <T:ChildNode>retrieveChildren(parent:Node, success:(T) ->()) { 
     // Retrieve all child T instances of parent node, and invoke success callback with new T instance 
    { 
} 

Проблема возникает в реализации retrieveAll метода RootNode класса. Для того, чтобы он работал, как описано @rintaro, мне нужен init в RootNode, который будет помечен ключевым словом required. Но поскольку он также отменяет инициализатор от Node, он также должен иметь ключевое слово override. Поэтому я стараюсь использовать как ключевые слова в объявлении:

override required public init(_ record:MyRecordType) { 
    ... 
} 

Свифт компилятор принимает это, но когда я использую его для инициализации экземпляра внутри метода retrieveAll, он падает с BAD_ACCESS.

я смог обойти эту проблему, лишь слегка изменив метод подписи NodeClass так, что его RootNode подкласс не нужно переопределить:

public class Node { 
    public init(record:MyRecordType) { 
     ... 
    } 
} 
public class RootNode { 
    public required init(_ record:MyRecordType) { 
     ... 
     super.init(record:record) 
    } 
} 

Делая это обходной путь, мои подклассы RootNode может правильно реализовать требуемый инициализатор, а метод retrieveAll в RootNode может корректно создавать экземпляры этих подклассов.

+1

Как только вы добавите требуемый инициализатор в базовый класс и подклассы, вы можете просто использовать 'T()'. –

+0

@seanwoodward - К сожалению, это не похоже на правду. Когда я вызываю инициализатор с помощью T (myRecord), он терпит неудачу, когда я называю это так, как это сделал rintaro. Сбой с T (myRecord), по-видимому, происходит из-за исключения BAD_ACCESS при получении свойства «record» экземпляра, которое должно быть установлено на значение «myRecord», которое было передано. –

+1

Спасибо, что заметили это. Вызов универсального инициализатора 'T()' работал для меня в простых тестах. Я надеюсь, что проблемы могут быть решены в будущих версиях компилятора. –

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