代码之家  ›  专栏  ›  技术社区  ›  Jeffery Thomas

重写属性会导致无限递归

  •  1
  • Jeffery Thomas  · 技术社区  · 6 年前

    我有一个从基类派生的类,基类有一个属性。


    @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
    

    我编写了一个swift扩展,使该属性在基类中可用。


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

    问题是 testClass.test 呼叫来自的getter TestBase 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
    

    属性不像函数那样工作。那么延长线呢

    扩展测试库{
    var测试:TestProp?{
    返回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 回复  |  直到 6 年前
        1
  •  2
  •   Cristik    6 年前

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

    Overriding methods in Swift extensions , http://blog.flaviocaetano.com/post/this-is-how-to-override-extension-methods/ .

    这就是为什么你得到无限递归。

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

        2
  •  0
  •   Jeffery Thomas    6 年前

    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 }
    }
    

    但那没用。