переопределение свойства вызывает бесконечную рекурсию - PullRequest
0 голосов
/ 05 сентября 2018

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


@interface TestProp: NSObject
@property (nonnull, nonatomic) NSString *prop;
@end

@interface TestBase: NSObject
@end

@interface TestClass: TestBase
- (nullable TestProp *)testGetter;
- (void)testSetter:(nullable TestProp *)test;
@property (nullable, nonatomic, readonly, getter=testGetter) TestProp *test;
@end

@implementation TestProp
@end

@implementation TestBase
@end

@implementation TestClass { TestProp *_test; }
- (TestProp *)testGetter { return _test; }
- (void)testSetter:(TestProp *)test { _test = test; }
@end

Я написал быстрое расширение, чтобы сделать свойство доступным в базовом классе.


extension TestBase {
    var test: TestProp? {
        guard let testClass = self as? TestClass else { return nil }
        return testClass.test
    }
}

Проблема в том, что testClass.test вызывает геттер из TestBase вместо TestClass. Это приводит к бесконечной рекурсии, которая разбивает стек.


Это дефект? Есть ли способ, которым я могу заставить это работать?


UPDATE

Когда я впервые подумал об этом, я надеялся, что смогу воспринимать это как функцию.

extension TestBase {
    var test: TestProp? {
        return nil
    }
}

и произойдет следующее:

func callback(_ testBase: TestBase) {
    guard let prop = testBase.prop else { return }

    …
}

callback(TestBase()) // uses TestBase.prop
callback(TestClass()) // uses TestClass.prop

Это не сработало. Фактическое поведение было

callback(TestBase()) // uses TestBase.prop
callback(TestClass()) // uses TestBase.prop

Свойства не работают как функции. Так что расширение

extension TestBase {
    var test: TestProp? {
        guard let testClass = self as? TestClass else { return nil }
        return testClass.test
    }
}

была моей попыткой заставить TestCase.prop.

Я пытаюсь избежать

func callback(_ testBase: TestBase) {
    guard let prop = (testBase as? TestClass)?.prop else { return }

    …
}

ФИНАЛЬНОЕ ОБНОВЛЕНИЕ

Я обнаружил метод, который работал, смотрите мой ответ, если вам интересно.

Однако, оно того не стоит. Решение было слишком сложным. Я создал частную глобальную функцию.

private func prop(_ testBase: TestBase) -> TestProp {
    return (testBase as? TestClass)?.prop
}

затем в обратных вызовах

func callback(_ testBase: TestBase) {
    guard let prop = prop(testBase) else { return }

    …
}

Не объектно-ориентированный, а простой.

Ответы [ 2 ]

0 голосов
/ 06 сентября 2018

Благодаря подсказке @Cristik о статической и динамической отправке мне удалось заставить ее работать.

protocol TestBaseProp {
    var test: TestProp? { get }
}

extension TestBaseProp {
    var test: TestProp? {
        guard let testClass = self as? TestClass else { return nil }
        return testClass.test
    }
}

extension TestBase: TestBaseProp { }

ПРИМЕЧАНИЕ: я пробовал

extension TestBaseProp {
    var test: TestProp? { return nil }
}

но это не сработало.

0 голосов
/ 06 сентября 2018

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

Это приводит к тому, что Swift разрешает адрес получателя из адреса базового класса, поскольку вещи, объявленные в расширениях, не могут быть переопределены: Переопределяющие методы в расширениях Swift , http://blog.flaviocaetano.com/post/this-is-how-to-override-extension-methods/.

Вот почему вы получаете бесконечную рекурсию.

Решение простое, объявить свойство в базовом классе, вернуть nil и фактическое свойство в подклассе.

...