Как инициировать мой подкласс экземпляром его суперкласса?

В своем приложении я читаю события календаря типа EKEvent и сделал расширение с большим количеством вычисляемых переменных, поэтому я могу легко получить продолжительность, количество человеко-часов и т. д. для каждого события в календаре. Но в больших масштабах производительность плохая, поэтому вместо этого я хочу использовать ленивые переменные, чтобы кэшировать все мои дополнительные данные.

Поэтому я хочу создать подкласс EKEvent, называемый CustomEvent, который добавляет ленивые переменные, но моя проблема в том, что EKEventStore всегда возвращает EKEvents, и мне нужно преобразовать его в экземпляры моего подкласса CustomEvent, чтобы иметь возможность получить доступ к ленивым варам и т. д.

Простого приведения типов недостаточно, и я попробовал на игровой площадке посмотреть, что может сработать, но ничего полезного не получил. Мне нужен специальный конструктор для CustomRectangle, который может инициализировать CustomRectangle из NativeRectangle. Альтернативным решением является создание класса-оболочки, который содержит исходный объект как свойство, но это не было бы моим любимым решением, поскольку тогда мне пришлось бы отображать все методы и свойства.

class NativeRectangle: NSObject {
    var width: Int
    var height: Int

    init(width: Int, height: Int) {
        self.width = width
        self.height = height
        super.init()
    }
}

class CustomRectangle: NativeRectangle {
    var area: Int { return width * height}
}

let rect = NativeRectangle(width: 100, height: 20)

let customRect = CustomRectangle(rect) // This fails, i need a constructor

print(customRect.area)

person Esben von Buchwald    schedule 26.08.2017    source источник


Ответы (3)


В Swift (и вообще в большинстве объектно-ориентированных языков) нет способа использовать существующий экземпляр объекта базового класса при создании экземпляра дочернего класса.

С общей точки зрения программирования в этой ситуации у вас есть два варианта:

  1. Используйте композицию: сделайте так, чтобы CustomRectangle содержал NativeRectangle и передайте ему все необходимые методы.

  2. Используйте карту, чтобы связать NativeRectangles с дополнительной информацией. В Objective C и Swift вы можете использовать objc_AssociationPolicy, чтобы проще всего иметь такую ​​внутреннюю карту. См. https://stackoverflow.com/a/43056053/278842.

Кстати. Невозможно увидеть какое-либо ускорение от «кэширования» простого вычисления как width * height.

person Christopher Oezbek    schedule 26.08.2017
comment
Композиция — это обычно первая мысль, которая приходит мне в голову, когда мне приходится делать подобные вещи. - person Abizern; 26.08.2017
comment
Спасибо. Думаю, когда мне придется создать класс-оболочку и сопоставить свойства... Я надеялся на более элегантное решение. Простое кэширование ширины * высоты было просто для примера... - person Esben von Buchwald; 27.08.2017

Если вы уже работаете в мире Objective-C, есть возможность обернуть нативный класс и автоматически пересылать все (кроме добавленных) сообщения:

- (NSMethodSignature*) methodSignatureForSelector: (SEL) selector
{
    NSMethodSignature *ours = [super methodSignatureForSelector:selector];
    return ours ?: [wrappedObject methodSignatureForSelector:selector];
}

Я не могу вспомнить, все ли это было необходимо для работы переадресации, но это должно быть довольно близко. Кроме того, я не знаю, как это будет работать со Swift, поэтому я думаю, что мы могли бы считать это интересной мелочью времен Objective-C и искать лучшее решение…


Второй, также слегка хакерский вариант, который приходит на ум, — это использование Функция ассоциированных объектов для связывания кэшированных данных с исходным экземпляром. Таким образом, вы можете сохранить свой подход к расширениям.

person zoul    schedule 26.08.2017

Вы создали свой собственный CustomRectangle(object: rect) , поэтому Swift больше не будет предоставлять init() по умолчанию. Вам явно нужно вызвать один из ваших собственных, владеющих вашей собственностью, и сделать вызов super.init(), так как ваш класс также наследуется от суперкласса. –

class NativeRectangle: NSObject {
    var width: Int
    var height: Int

    // Super class custom init()
    init(width: Int, height: Int) {
        self.width = width
        self.height = height
        super.init()
    }
}

class CustomRectangle: NativeRectangle {

    // computed property area
    var area: Int { return width * height}

    // Sub class Custom Init
    init(object:NativeRectangle) {
        // call to super to check proper initialization
        super.init(width: object.width, height: object.height)
    }
}

let rect = NativeRectangle(width: 100, height: 20)

let customRect = CustomRectangle(object: rect)

print(customRect.area) //2000
person Tushar Sharma    schedule 26.08.2017
comment
Я не думаю, что это решение для EKEvents, поскольку у них нет подходящего конструктора. Но для других типов это может быть полезно. Я буду придерживаться рекомендации @Christopher Oezbek - person Esben von Buchwald; 27.08.2017
comment
@Esben von Buchwald не очень хорошо осведомлен о EKEvents или о том, как для него работает инициализация. Но приведенный выше ответ - это базовый процесс инициализации, выполняемый быстро или, скорее всего, на любых других языках. Действительно, вы можете придерживаться решения Oezbek. - person Tushar Sharma; 27.08.2017