![](/img/trans.png)
[英]Var in extension class show error “Extensions must not contain stored properties”
[英]"Extensions must not contain stored properties" preventing me from refactoring code
我有一个 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()
}
要使 menuTappedAction 函数正常工作,我必须像这样声明一个弱变量:
extension UIViewController {
weak var coordinator: MainCoordinator?
但是这样做我得到错误Extensions must not contain stored properties
到目前为止我尝试过的:
1) 删除weak
关键字会在我的所有应用程序中引起冲突。 2)这样声明:
weak var coordinator: MainCoordinator?
extension UIViewController {
将消除错误,但协调器不会执行任何操作。 任何建议如何解决这个问题?
您可以将addMenuButton()
函数移动到具有协议扩展名的协议。 例如:
@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)
}
}
不幸的是,您不能将@objc
方法添加到类扩展中(请参阅: this stackoverflow question ),因此您仍然必须像这样设置您的视图控制器:
class SomeViewController: UIViewController, Coordinated {
weak var coordinator: MainCoordinator?
/// Launches the MenuViewController
@objc func menuTappedAction() {
coordinator?.openMenu()
}
}
它将为您节省一些代码,并允许您重构更大的函数addMenuButton()
。 希望这可以帮助!
为了让它在扩展中工作,你必须像这样使它计算属性: -
extension ViewController {
// Make it computed property
weak var coordinator: MainCoordinator? {
return MainCoordinator()
}
}
您可以使用 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) }
}
}
发生这种情况是因为extension
不是类,因此它不能包含存储的属性。 即使它们是weak
属性。
考虑到这一点,您有两个主要选择:
1.1. 声明你的协议
protocol CoordinatorProtocol: class {
var coordinator: MainCoordinator? { get set }
func menuTappedAction()
}
1.2. 创建协议扩展,以便您可以预先实现addMenuButton()
方法
extension CoordinatorProtocol where Self: UIViewController {
func menuTappedAction() {
// Do your stuff here
}
}
1.3. 声明weak var coordinator: MainCoordinator?
在将采用此协议的课程中。 不幸的是,你不能跳过这个
class SomeViewController: UIViewController, CoordinatorProtocol {
weak var coordinator: MainCoordinator?
}
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) }
}
}
你可以通过子类化来做到这一点
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.
}
}
使用NSMapTable
为您的扩展创建一个状态容器,但请确保您指定对键使用弱引用。
创建一个要在其中存储状态的类。 我们称之为ExtensionState
,然后在扩展文件中创建一个映射作为私有字段。
private var extensionStateMap: NSMapTable<TypeBeingExtended, ExtensionState> = NSMapTable.weakToStrongObjects()
那么你的扩展可以是这样的。
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
}
}
这适用于 iOS 和 macOS 开发,但不适用于服务器端 Swift,因为那里没有NSMapTable
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.