2014-12-12 3 views
4

Я новичок в Swift, поэтому я мог бы делать что-то глупое. Если это так, отлично: пожалуйста, скажите мне, где!Быстрый вызов виртуального метода приводит к segfault

В следующем коде, вы увидите, что класс Derived наследует от общего класса Base<T>:

class Base<T> { 
    func method(Int) -> T { 
    fatalError("Subclasses must override method.") 
    } 
} 

class Derived<T> : Base<Int> { 
    override func method(input:Int) -> Int { 
    return input 
    } 
} 

В Swift 1.1, это не возможно для не-универсальный класс наследовать от общий. Следовательно, Derived в этом случае имеет переменную типа фиктивного типа.

Если я теперь использовать эти классы:

class Container { 
    let item: Base<Int> 
    init(item:Base<Int>) { 
    self.item = item 
    } 
    func method(input:Int) -> Int { 
    return item.method(input) 
    }  
} 
let a = Derived<Int>() 
let b = Container(item:a) 
let test = b.method(42) 

код компилируется нормально, но я получаю Segfault когда Derived<Int>.method вызывается. Основываясь на моей отладке до сих пор, похоже, что указатель self верен в пределах Container.method, но неправильно, когда мы попадаем в Derived<Int>.method. Возможно, происходит некоторая коррупция в стеке?

Различные незначительные изменения в этом коде заставляют его работать нормально (хотя и с другой семантикой). Может ли кто-нибудь объяснить, что здесь происходит? Я не решаюсь предложить ошибку компилятора, пока не узнаю немного о Свифте.

ответ

1

Вы определенно нашли ошибку в компиляторе или во время выполнения - не должно быть возможности получить код записи segfault, подобный этому. Он должен либо работать нормально, либо не компилироваться. Вам даже не нужен класс контейнера, вы можете воспроизвести ошибку с

let d: Base = Derived<Int>() 
d.method(2) 

Однако, даже если это была ошибка во время выполнения, который получил фиксированное, я бы сказал, что это, вероятно, не очень хорошая практика, чтобы сделать это. Требование генерировать производные классы generic существует по какой-то причине, и использование шаблона фиктивного шаблона для его использования, вероятно, не является хорошей идеей. Например, что бы это значит, объявить let d: Base = Derived<String>()?

Если Base не имеет фактических реализаций и является просто абстрактным (или даже если это возможно, и вы можете его разложить), вам может быть лучше заменить базу протоколом, возможно, связанным типом для представления T:

protocol P { 
    // instead of T, best to call it 
    // something with a meaningful name... 
    typealias T 

    func method(input: Int) -> T 
} 

class D : P { 
    typealias T = Int 

    func method(input: Int) -> Int { 
     return input * 2 
    } 
} 

class Container<U: P> { 
    let item: U 
    init(item: U) { 
     self.item = item 
    } 
    func method(input: Int) -> U.T { 
     return item.method(input) 
    } 
} 

let d = D() 
let c = Container(item: d) 
c.method(2) 

Этот подход имеет дополнительное преимущество, что позволяет D быть тип значения (т.е. структура), если вы хотите, чтобы это было, так как они не могут наследовать, но они могут.

+0

Спасибо за подробный ответ. Вы отмечаете, что «Требование сделать производные классы родовыми есть по какой-то причине». Вы знаете, что это за причина, кроме недостающей функции? – olliew

+0

Я также ценю вашу рекомендацию по дизайну кода, который я разместил. К сожалению, мне нужно использовать вызовы виртуальных методов в реальном коде, который я создаю. Протокол с 'typealias' в нем не может рассматриваться как абстрактный класс и только как ограничение типа. Вот почему я переработал свой код, чтобы использовать наследование, и вот тут эта ошибка появилась. – olliew

+0

Я отчасти догадываюсь, так как официальные документы не говорят, но мое объяснение того, почему производные классы дженериков должны быть общими, будет: общие классы могут различаться по размеру - то есть класс 'A {let t: T} 'будет другого размера в зависимости от того, был ли T« Int »(размером 8) или« String »(размер которого равен 24). Производный класс также может быть разным по размеру, потому что его база может и, следовательно, учитывать это, должна быть общей. –

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