我终于完成了从Objective-C到Swift的转换。我正在为我的客户创建一个视图布局系统,使他们的应用程序在布局上更灵活,而不使用自动布局,因为他们想要远程设计他们的屏幕,自动布局对他们来说太复杂了。我尝试使用structs
和protocols
来做这件事,但我发现它非常笨拙,所以我怀疑我没有以正确的方式思考它。
对于类,结构如下:
class ViewModel {
var frame: CGRect = .zero
}
class ViewGroupModel: ViewModel {
var weight: Int = 1
var children:[ViewModel] = [ViewModel]()
}
class HorizontalViewGroupModel: ViewGroupModel {
}
class VerticalViewGroupModel: ViewGroupModel {
}
我尝试通过定义ViewModel
协议和ViewGroupModel
协议来使用协议来处理它,但我发现它创建了大量重复(属性)。有更好的方法吗?在这种情况下使用类会被视为一种好习惯吗?
编辑:如果不使用课程会更好,我正在寻找一个答案,它给出了structs
和protocols
方面的具体解决方案。
答案 0 :(得分:3)
如果问题只是如何实现协议的属性,我不一定会在struct
与class
之间选择。如果您有struct
类型必须实现的各种属性,则有两个基本选项:
如果您正在谈论一些属性,只需在符合该协议的struct
类型中实现这些少数属性。我们一直这样做。例如。在定义符合MKAnnotation
的自定义类型时,我们只需实现三个必需的属性。
当然,如果我们谈论的是一组更大的属性,这会变得乏味,但编译器会抓住这个过程,确保我们不会错过任何东西。所以挑战是相当适度的。
虽然我不喜欢这种方法,但https://stackoverflow.com/a/38885813/1271826表明您可以将共享属性实现为组件,您可以struct
来包装所有这些属性,并且然后在扩展中实现协议的默认计算属性:
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
)。我认为考虑Apple最近(@ 42:15)Protocol-Oriented Programming in Swift视频对价值与参考语义的讨论是很有启发性的。他们触及那些你可能仍然想要使用类的情况。例如,他们建议您在“复制或比较实例没有意义”时使用引用类型。他们建议在处理“Window”实例时可能会应用此规则。这同样适用于此。
最重要的是,在我看来,使用值类型来表示视图层次结构有很多好处,视图层次结构是引用类型对象的集合。它只会让它更加混乱。我会坚持使用class
类型,因为它将准确地镜像它所代表的视图层次结构。
不要误会我的意思:我们已经习惯使用引用类型了,我认为挑战我们先入为主的观念总是好的,并且需要长时间仔细研究一种值类型是否能更好地解决这种情况。但是,在这种情况下,我根本不会担心它,只是坚持使用class
层次结构来反映您正在建模的那些对象的层次结构。
话虽如此,你问题中提出的阶级等级也不太正确。您实际上可以实例化以后无法添加子视图的ViewModel
(而所有UIView
对象都具有subview
属性),这感觉很奇怪。此外,您的水平和垂直组类型也感觉不正确。例如,它是否应该是具有某些“轴”属性的单一类型,如UIStackView
或其他“排列”属性,以扩大捕获UICollectionView
布局的概念?正如您在我上面的ViewComponent
示例中所看到的那样,我已经将这一点弄平了,记住了这两点,但做了你认为合适的事情。
答案 1 :(得分:2)
通常,只有在需要类的特殊功能时才使用类:
一个类可以有一个超类和/或一个子类;结构不能
类是reference type,而结构是值类型。
Objective-C可以内省一个类(特别是如果它派生自NSObject),而它甚至看不到在Swift中声明的结构。
答案 2 :(得分:0)
编码接口/协议而不是类/结构总是很好。明智地说这是你的模特。 为此,您可以精美地使用泛型。我希望它能为你节省很多变量和重复。 对于布局目的,在我看来结构与协议和泛型相结合将是一个伟大的设计。我不认为你需要在你的情况下使用类。 它总是很好地了解一个功能,以更好地使用它。 Swift中结构和类之间的主要区别是
有关详细讨论,您可以通过我的帖子Struct Vs Classes in Swift。