覆盖属性会导致无限递归

时间:2018-09-05 16:44:25

标签: objective-c swift

我有一个类,该类派生自具有属性的基类。


@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.testTestBase而不是TestClass调用吸气剂。这将导致无限递归,从而破坏堆栈。


这是缺陷吗?有什么方法可以使它正常工作吗?


更新

当我第一次想到它时,我希望可以将其视为一个函数。

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 :(得分:2)

撇开代码“闻起来”的事实(从超类引用子类,将属性添加到子类,然后在扩展中添加到基类,而不是直接在基类上添加属性-在一个超类),这是因为Swift和其他许多编程语言一样都是静态调度的,这意味着它将急切地将方法调用(在这种情况下为getter)解析为固定的内存地址。

这导致Swift将基类中的getter地址解析为一个地址,因为扩展中声明的内容不可覆盖:Overriding methods in Swift extensionshttp://blog.flaviocaetano.com/post/this-is-how-to-override-extension-methods/

这就是为什么您获得无限递归的原因。

解决方案很简单,在基类中声明属性,在基类中返回nil并在子类中返回实际属性。

答案 1 :(得分:0)

感谢@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 }
}

但这没用。