2017-07-28 65 views
1

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

随着类,该结构将是如下:

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方面。

+0

“在这种情况下使用类是否被认为是一种好的做法?” - 我会说“是”,因为那样您就可以使用ObjectMapper将JSON对象映射到您的模型 –

+1

请注意,CGRect已经实现了您的模型。 – Sulthan

+5

@mag_zbc仅仅因为你想使用一个库而改变设计就像是最糟糕的原因。 – Sulthan

回答

1

如果担心的仅仅是如何实现协议的属性,我不一定会让我选择在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的类型中单独实现所有这些属性。

所以,让我们把性能问题放在一边。真正的问题是你是否应该使用值类型(struct)或引用类型(class)。我认为,考虑苹果在视频结尾(@ 42:15)Protocol-Oriented Programming in Swift视频附近对值与参考语义的讨论,这是很有启发性的。它们涉及那些你实际上可能仍然想要使用类的情况。例如,他们建议您在“复制或比较实例无效”时可能需要使用引用类型。他们建议这个规则可能适用于处理“窗口”实例。这里同样适用。

最重要的是,在我看来,使用值类型来表示视图层次结构(它是引用类型对象的集合)有很多好处。它只会让它更加混乱。我会坚持使用class类型,因为它会精确地反映它所代表的视图层次结构。

不要误解我们的意思:我们习惯于使用引用类型,我认为挑战我们先入为主的概念总是很好,并且长时间努力研究值类型是否能更好地解决问题。不过,在这种情况下,我根本不会担心它,只是坚持反映您正在建模的这些对象层次结构的层次结构。


这样说了,你提出的问题中的类层次结构也不太对劲。奇怪的是,你实际上可以实例化一个ViewModel,你不能以后添加子视图(而所有的UIView对象有subview属性)。此外,你的水平和垂直组类型也感觉不到正确。例如,它应该是带有“轴”属性的单一类型,如UIStackView或其他某些“排列”属性,以扩大概念以捕获UICollectionView布局?正如你将在我上面的ViewComponent例子中看到的那样,我已经将这一点弄平了,考虑到这两个注意事项,但是请做你认为合适的事情。

+0

感谢您的出色答案(我收到的最好答案之一)。是的,我在这个问题上提出的结构并不合适。我猜没有子视图的ViewModel来自Android模型,其中View没有子视图。我仍然在玩这个模型,试图找到一个适合所有三个大平台的模型。但是,是的,我现在使用的行为就像你所建议的“集合”类。 –

2

在一般情况下,使用一个类只有在需要的类的特殊功能,它们分别是:

  • 一个类可以有超类和/或子类;一个结构不能。

  • 一个类是reference type,而struct是一个值类型。 Objective-C可以反省一个类(尤其是如果它是从NSObject派生的),而它甚至不能看到在Swift中声明的结构。

0

它总是很好的代码接口/协议比类/结构。这是你的模型明智。 为了达到这个目的,你可以很好地利用泛型。我希望这会为你节省很多变数和重复。 为了布局的目的,在我看来,结合协议和泛型的结构将会是一个很好的设计。我不认为你需要在你的案例中使用类。 它总是很好地了解一个功能,以更好地使用它。在夫特结构和类之间的主要区别是

  1. 类对象被存储/为其中作为结构体实例被存储/传递作为值
  2. 引用计数允许多于一个引用类实例的引用传递。
  3. 对于Class,我们有身份运算符===和!==,用于检查两个变量或常量是否引用Class的同一个实例。由于两个不同的结构体变量或常量不能指向同一个实例,所以对于结构体不存在同一运算符的问题。您可以尝试将身份运算符应用于Struct类型。你会得到编译时错误。
  4. 继承使一个类能够继承另一个类的特性。
  5. 类型转换使您能够在运行时检查和解释类实例的类型。
  6. 取消初始化程序可使类的实例释放已分配的任何资源。

如需了解详细情况,您可以通过我的文章Struct Vs Classes in Swift

+0

我知道这些事实。我更多地寻找一种方法来实现我在结构和协议方面所写的内容。 –