Swift:构造或不构造

时间:2017-07-28 15:09:32

标签: ios swift class struct

我终于完成了从Objective-C到Swift的转换。我正在为我的客户创建一个视图布局系统,使他们的应用程序在布局上更灵活,而不使用自动布局,因为他们想要远程设计他们的屏幕,自动布局对他们来说太复杂了。我尝试使用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协议和ViewGroupModel协议来使用协议来处理它,但我发现它创建了大量重复(属性)。有更好的方法吗?在这种情况下使用类会被视为一种好习惯吗?

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

3 个答案:

答案 0 :(得分:3)

如果问题只是如何实现协议的属性,我不一定会在structclass之间选择。如果您有struct类型必须实现的各种属性,则有两个基本选项:

  1. 如果您正在谈论一些属性,只需在符合该协议的struct类型中实现这些少数属性。我们一直这样做。例如。在定义符合MKAnnotation的自定义类型时,我们只需实现三个必需的属性。

    当然,如果我们谈论的是一组更大的属性,这会变得乏味,但编译器会抓住这个过程,确保我们不会错过任何东西。所以挑战是相当适度的。

  2. 虽然我不喜欢这种方法,但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的类型中单独实现所有这些属性。

  3. 所以,让我们把财产问题放在一边。真正的问题是您是应该使用值类型(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中结构和类之间的主要区别是

  1. 类对象作为引用存储/传递,其中结构实例作为值存储/传递
  2. 引用计数允许对类实例进行多次引用。
  3. 对于Class,我们有标识运算符===和!==,它们用于检查两个变量或常量是否引用同一个Class实例。对于身份运算符的问题不会出现在Struct中,因为两个不同的Struct变量或常量不能指向同一个实例。您可以尝试将标识运算符应用于Struct类型。您将收到编译时错误。
  4. 继承允许一个类继承另一个类的特性。
  5. 类型转换使您可以在运行时检查和解释类实例的类型。
  6. Deinitializers允许类的实例释放它已分配的任何资源。
  7. 有关详细讨论,您可以通过我的帖子Struct Vs Classes in Swift