如何防止外部通过UIView的subviews属性访问私有子视图?
要阻止外部通过subviews属性访问你的私有CustomTextField,可以从几个层面入手,下面是几种可行的实现方式:
1. 用私有容器视图包裹子视图
最直接的方式是把私有子视图放到一个私有容器视图里,这样外部遍历MyView的subviews时,只能拿到容器视图,无法直接访问到内部的CustomTextField。
修改MyView的代码:
class MyView: UIView { // 私有容器视图 private lazy var containerView: UIView = { let view = UIView(frame: self.bounds) return view }() private lazy var textField: CustomTextField = { let textField = CustomTextField(frame: CGRect(x: 95, y: 150, width: 200, height: 44)) textField.backgroundColor = .white textField.borderStyle = .roundedRect textField.text = "hello world!" return textField }() override init(frame: CGRect) { super.init(frame: frame) containerView.addSubview(textField) self.addSubview(containerView) } required init?(coder: NSCoder) { fatalError("error") } }
这样外部调用customView.subviews时,只会返回containerView,而textField是containerView的子视图,外部无法直接获取到。
2. 重写subviews属性,过滤私有子视图
如果你不想额外加容器,可以重写MyView的subviews属性,返回不包含私有子视图的列表。不过要注意,这种方式要确保不会影响系统对视图层级的正常处理(比如自动布局、渲染等)。
修改MyView的代码:
class MyView: UIView { private lazy var textField: CustomTextField = { let textField = CustomTextField(frame: CGRect(x: 95, y: 150, width: 200, height: 44)) textField.backgroundColor = .white textField.borderStyle = .roundedRect textField.text = "hello world!" return textField }() override init(frame: CGRect) { super.init(frame: frame) self.addSubview(textField) } required init?(coder: NSCoder) { fatalError("error") } // 重写subviews,过滤掉私有textField override var subviews: [UIView] { return super.subviews.filter { $0 !== textField } } }
此时外部遍历customView.subviews时,不会拿到textField,自然无法访问它的text属性。
3. 限制CustomTextField的属性访问权限(辅助方案)
如果担心外部通过类型转换绕过容器限制,可以给CustomTextField的text属性加上更严格的访问控制。比如把text属性设为私有,只允许MyView通过内部方法来操作:
class CustomTextField: UITextField { // 把text属性设为私有,外部无法直接访问 private override var text: String? { get { super.text } set { super.text = newValue } } // 提供仅MyView能调用的方法来修改文本 func setInternalText(_ text: String?) { super.text = text } // 如果需要MyView读取文本,也可以提供对应的方法 func getInternalText() -> String? { return super.text } }
然后在MyView里操作文本时,调用这些内部方法:
// 在MyView的某个方法里 textField.setInternalText("new text") let currentText = textField.getInternalText()
这种方式下,即使外部意外拿到了CustomTextField实例,也无法直接访问text属性,只能通过你暴露的方法操作(而这些方法可以限制访问范围,比如用fileprivate或模块级访问控制)。
需要注意的是,Swift的访问控制是编译时的限制,理论上可以通过反射等runtime手段绕过,但对于常规的业务代码,上面的方案已经足够阻止无意的访问。
内容的提问来源于stack exchange,提问作者AKK

