简体   繁体   English

“扩展不能包含存储的属性”阻止我重构代码

[英]"Extensions must not contain stored properties" preventing me from refactoring code

I have a 13 lines func that is repeated in my app in every ViewController, which sums to a total of 690 lines of code across the entire project!我有一个 13 行的 func,它在我的应用程序中的每个 ViewController 中重复,在整个项目中总共有 690 行代码!

/// Adds Menu Button
func addMenuButton() {
    let menuButton = UIButton(frame: CGRect(x: 0, y: 0, width: 30, height: 30))
    let menuImage = UIImage(named: "MenuWhite")
    menuButton.setImage(menuImage, for: .normal)

    menuButton.addTarget(self, action: #selector(menuTappedAction), for: .touchDown)
    self.navigationItem.rightBarButtonItem = UIBarButtonItem(customView: menuButton)
}
/// Launches the MenuViewController
@objc func menuTappedAction() {
    coordinator?.openMenu()
}

for menuTappedAction function to work, I have to declare a weak var like this:要使 menuTappedAction 函数正常工作,我必须像这样声明一个弱变量:

extension UIViewController {

weak var coordinator: MainCoordinator?

But by doing this I get error Extensions must not contain stored properties What I tried so far:但是这样做我得到错误Extensions must not contain stored properties到目前为止我尝试过的:

1) Removing the weak keyword will cause conflicts in all my app. 1) 删除weak关键字会在我的所有应用程序中引起冲突。 2) Declaring this way: 2)这样声明:

weak var coordinator: MainCoordinator?
extension UIViewController {

Will silence the error but the coordinator will not perform any action.将消除错误,但协调器不会执行任何操作。 Any suggestion how to solve this problem?任何建议如何解决这个问题?

You can move your addMenuButton() function to a protocol with a protocol extension.您可以将addMenuButton()函数移动到具有协议扩展名的协议。 For example:例如:

@objc protocol Coordinated: class {
    var coordinator: MainCoordinator? { get set }
    @objc func menuTappedAction()
}

extension Coordinated where Self: UIViewController {
    func addMenuButton() {
        let menuButton = UIButton(frame: CGRect(x: 0, y: 0, width: 30, height: 30))
        let menuImage = UIImage(named: "MenuWhite")
        menuButton.setImage(menuImage, for: .normal)

        menuButton.addTarget(self, action: #selector(menuTappedAction), for: .touchDown)
        self.navigationItem.rightBarButtonItem = UIBarButtonItem(customView: menuButton)
    }
}

Unfortunately, you can't add @objc methods to class extensions (see: this stackoverflow question ), so you'll still have to setup your view controllers like this:不幸的是,您不能将@objc方法添加到类扩展中(请参阅: this stackoverflow question ),因此您仍然必须像这样设置您的视图控制器:

class SomeViewController: UIViewController, Coordinated {
    weak var coordinator: MainCoordinator?
    /// Launches the MenuViewController
    @objc func menuTappedAction() {
        coordinator?.openMenu()
    }
}

It'll save you some code, and it will allow you to refactor the bigger function addMenuButton() .它将为您节省一些代码,并允许您重构更大的函数addMenuButton() Hope this helps!希望这可以帮助!

For it to work in an extension you have to make it computed property like so : -为了让它在扩展中工作,你必须像这样使它计算属性: -

extension ViewController {

   // Make it computed property
    weak var coordinator: MainCoordinator? {
        return MainCoordinator()
    }

}

You could use objc associated objects.您可以使用 objc 关联对象。

extension UIViewController {
    private struct Keys {
        static var coordinator = "coordinator_key"
    }

    private class Weak<V: AnyObject> {
        weak var value: V?

        init?(_ value: V?) {
            guard value != nil else { return nil }
            self.value = value
        }
    }

    var coordinator: Coordinator? {
        get { (objc_getAssociatedObject(self, &Keys.coordinator) as? Weak<Coordinator>)?.value }
        set { objc_setAssociatedObject(self, &Keys.coordinator, Weak(newValue), .OBJC_ASSOCIATION_RETAIN_NONATOMIC) }
    }
}

This happens because an extension is not a class, so it can't contain stored properties.发生这种情况是因为extension不是类,因此它不能包含存储的属性。 Even if they are weak properties.即使它们是weak属性。

With that in mind, you have two main options:考虑到这一点,您有两个主要选择:

  1. The swift way: Protocol + Protocol Extension快捷方式:协议+协议扩展
  2. The nasty objc way: associated objects讨厌的 objc 方式:关联对象

Option 1: use protocol and a protocol extension:选项 1:使用协议和协议扩展:

1.1. 1.1. Declare your protocol声明你的协议

protocol CoordinatorProtocol: class {
    var coordinator: MainCoordinator? { get set }
    func menuTappedAction()
}

1.2. 1.2. Create a protocol extension so you can pre-implement the addMenuButton() method创建协议扩展,以便您可以预先实现addMenuButton()方法

extension CoordinatorProtocol where Self: UIViewController {
    func menuTappedAction() {
        // Do your stuff here
    }
}

1.3. 1.3. Declare the weak var coordinator: MainCoordinator?声明weak var coordinator: MainCoordinator? in the classes that will be adopting this protocol.在将采用此协议的课程中。 Unfortunately, you can't skip this不幸的是,你不能跳过这个

class SomeViewController: UIViewController, CoordinatorProtocol {
    weak var coordinator: MainCoordinator?
}

Option 2: use objc associated objects (NOT RECOMMENDED)选项 2:使用 objc 关联对象(不推荐)

extension UIViewController {
    private struct Keys {
        static var coordinator = "coordinator_key"
    }

    public var coordinator: Coordinator? {
        get { objc_getAssociatedObject(self, &Keys.coordinator) as? Coordinator }
        set { objc_setAssociatedObject(self, &Keys.coordinator, newValue, .OBJC_ASSOCIATION_ASSIGN) }
    }
}

You can do it through subclassing你可以通过子类化来做到这一点

class CustomVC:UIViewController {

    weak var coordinator: MainCoordinator?

    func addMenuButton() {
        let menuButton = UIButton(frame: CGRect(x: 0, y: 0, width: 30, height: 30))
        let menuImage = UIImage(named: "MenuWhite")
        menuButton.setImage(menuImage, for: .normal)

        menuButton.addTarget(self, action: #selector(menuTappedAction), for: .touchDown)
        self.navigationItem.rightBarButtonItem = UIBarButtonItem(customView: menuButton)
    }
    /// Launches the MenuViewController
    @objc func menuTappedAction() {
        coordinator?.openMenu()
    }

}

class MainCoordinator {

    func openMenu() {

    }
}


class ViewController: CustomVC {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }

}

Use a NSMapTable to create a state container for your extension, but make sure that you specify to use weak references for keys.使用NSMapTable为您的扩展创建一个状态容器,但请确保您指定对键使用弱引用。

Create a class in which you want to store the state.创建一个要在其中存储状态的类。 Let's call it ExtensionState and then create a map as a private field in extension file.我们称之为ExtensionState ,然后在扩展文件中创建一个映射作为私有字段。

private var extensionStateMap: NSMapTable<TypeBeingExtended, ExtensionState> = NSMapTable.weakToStrongObjects()

Then your extension can be something like this.那么你的扩展可以是这样的。

extension TypeBeingExtended {
    private func getExtensionState() -> ExtensionState {
        var state = extensionStateMap.object(forKey: self)

        if state == nil {
            state = ExtensionState()
            extensionStateMap.setObject(state, forKey: self)
        }

        return state
    }

    func toggleFlag() {
        var state = getExtensionState()
        state.flag = !state.flag
    }
}

This works in iOS and macOS development, but not on server side Swift as there is no NSMapTable there.这适用于 iOS 和 macOS 开发,但不适用于服务器端 Swift,因为那里没有NSMapTable

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 扩展 class 中的 Var 显示错误“扩展不得包含存储的属性” - Var in extension class show error “Extensions must not contain stored properties” 想要保存核心数据,但“扩展不得包含存储的属性” - Want to save Core Data but “Extensions must not contain stored properties” 扩展名可能不包含存储的属性 - Extensions May not contain Stored properties 收到错误消息:“扩展名可能不包含存储的属性” - Getting an error: “Extensions may not contain stored properties” 具有存储属性的扩展 - Extensions with stored properties Objective C&Swift互操作性导致错误“扩展可能不包含存储的属性” - Objective C & Swift Interoperability causes error “Extensions may not contain stored properties” “扩展可能不包含存储的属性”,除非你是 Apple? 我错过了什么? - "Extensions may not contain stored properties" unless your are Apple? What am I missing? 有什么解决方法可以快速将存储的属性放入扩展中? - is there any workaround to put stored properties into extensions in swift? 为什么Swift不允许在扩展中存储属性? - Why does Swift not allow stored properties in extensions? 为什么@NSManaged 属性可以存储在扩展中? - Why can @NSManaged properties be stored in extensions?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM