ios - Swift:构建还是不构建

标签 ios swift class struct

我终于从 Objective-C 转向了 Swift。我正在为我的客户创建一个 View 布局系统,使他们的应用程序在布局上更加灵活,而不使用自动布局,因为他们想远程设计他们的屏幕,而自动布局对他们来说太复杂了。我尝试使用 structsprotocols 来做到这一点,但我发现它很笨拙,所以我怀疑我没有以正确的方式思考它。

对于类,结构如下:

class ViewModel {
    var frame: CGRect = .zero
}

class ViewGroupModel: ViewModel {
    var weight: Int = 1
    var children:[ViewModel] = [ViewModel]()
}

class HorizontalViewGroupModel: ViewGroupModel {
}

class VerticalViewGroupModel: ViewGroupModel {
}

我试图通过定义一个 ViewModel 协议(protocol)和一个 ViewGroupModel 协议(protocol)来使用协议(protocol)来处理它,但我发现它创建了很多重复(属性)。有更好的方法吗?在这种情况下使用类会被认为是一种好的做法吗?

编辑:如果不使用类会更好,我正在寻找一个答案,在 structsprotocols 方面给我一个具体的解决方案。

最佳答案

如果问题仅仅是如何实现协议(protocol)的属性,我不一定会让那影响我在 structclass 之间的选择。如果您的 struct 类型必须实现各种属性,您有两个基本选择:

  1. 如果您谈论的是几个属性,只需在符合该协议(protocol)的 struct 类型中实现这几个属性即可。我们一直这样做。例如。在定义符合 MKAnnotation 的自定义类型时,我们只需实现三个必需的属性。

    当然,如果我们讨论的是一组更大的属性,这会变得乏味,但编译器会在整个过程中牵着我们的手,确保我们不会遗漏任何东西。所以挑战相当小。

  2. 虽然我不喜欢这种方法,https://stackoverflow.com/a/38885813/1271826表明您可以将共享属性实现为一个组件,其中您有 struct 来包装所有这些属性,然后在扩展中为您的协议(protocol)实现默认计算属性:

    enum SubviewArrangement {
        case none
        case horizontal
        case vertical
        case flow
    }
    
    struct ViewComponent {
        var frame = CGRect.zero
        var weight = 1
        var subviews = [ViewModel]()
        var subviewArrangement = SubviewArrangement.none
    }
    
    protocol HasViewComponent {
        var viewComponent: ViewComponent { get set }
    }
    
    protocol ViewModel: HasViewComponent { }
    
    extension ViewModel {
        var frame: CGRect {
            get { return viewComponent.frame }
            set { viewComponent.frame = newValue }
        }
        var weight: Int {
            get { return viewComponent.weight }
            set { viewComponent.weight = newValue }
        }
        var subviews: [ViewModel] {
            get { return viewComponent.subviews }
            set { viewComponent.subviews = newValue }
        }
        var subviewArrangement: SubviewArrangement {
            get { return viewComponent.subviewArrangement }
            set { viewComponent.subviewArrangement = newValue }
        }
    }
    

    在那里,您可以创建一个符合 ViewModel 的实例,如下所示:

    struct LabelModel: ViewModel {
        var viewComponent = ViewComponent()
    }
    
    var label = LabelModel()
    label.weight = 2
    print(label.weight)
    

    我必须承认,这不是最优雅的方法。 (我什至不愿展示它。)但它避免了必须在符合 ViewModel 的类型中单独实现所有这些属性。

所以,让我们把属性问题放在一边。真正的问题是您应该使用值类型 (struct) 还是引用类型 (class)。我认为在 Protocol-Oriented Programming in Swift 接近尾声 (@42:15) 时考虑 Apple 关于值(value)与引用语义的讨论很有启发性。视频。他们触及了您实际上可能仍想使用类的那些情况。例如,他们建议您可能希望在“复制或比较实例没有意义”时使用引用类型。他们建议在处理“Window”实例时可以应用此规则。这同样适用于此。

最重要的是,在我看来,使用值类型来表示 View 层次结构并没有多大好处, View 层次结构是引用类型对象的集合。这只会让人更加困惑。我会坚持使用 class 类型,因为它会准确地反射(reflect)它所代表的 View 层次结构。

不要误会我的意思:我们已经习惯使用引用类型,所以我认为挑战我们先入为主的观念并仔细研究值类型是否可以更好地解决这种情况总是好的。不过,在这种情况下,我根本不会担心它,只是坚持使用反射(reflect)您正在建模的那些对象的层次结构的 class 层次结构。


话虽如此,您在问题中提出的类层次结构也感觉不太对。感觉很奇怪,你实际上可以实例化一个 ViewModel,你以后不能添加 subview (而所有 UIView 对象都有 subview 属性)。此外,您的水平和垂直组类型也感觉不正确。例如,它是否应该是具有某些“轴”属性的单一类型,如 UIStackView 或其他一些“排列”属性,以扩大捕获 UICollectionView 布局的概念?。正如您将在我上面的 ViewComponent 示例中看到的那样,考虑到这两个注意事项,我将其扁平化了一点,但是您可以按照自己认为合适的方式进行操作。

关于ios - Swift:构建还是不构建,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45376913/

相关文章:

ios - 按下后按钮变为白色。 ( iOS ) 刷新后恢复正常

ios - 无法符号化崩溃报告

ios - 如何解释 AudioBuffer 并获得权力?

ios - Swift 中带有部分和自定义对象的 UITableView

arrays - Swift2 GeneratorOfOne.next() 发生错误 : cannot use mutating member on immutable value: function call returns immutable value

javascript - 将 $.getJSON 的结果传递给同一个类的另一个方法

c++ - 无法访问类的成员;段错误

ios - 编辑一个单元格时,Swift tableview 会更改随机单元格的值

ios - 是否可以像在 Vuforia 中那样在 ARKit 中跟踪对象?

python - 从类方法访问类名