可选数组vs空数组

时间:2022-11-24 11:12:56

I have a simple Person class in Swift that looks about like this:

我在Swift有一个简单的Person类,大概是这样的:

class Person {
    var name = "John Doe"
    var age = 18
    var children = [Person]?

    \\ init function goes here, but does not initialize children array
}

Instead of declaring children to be an optional array, I could simply declare it and initialize it as an empty array like this:

我可以简单地声明子数组并将其初始化为空数组,而不是将子数组声明为可选数组:

var children = [Person]()

I am trying to decide which approach is better. Declaring the array as an optional array means that it will not take up any memory at all, whereas an empty array has at least some memory allocated for it, correct? So using the optional array means that there will be at least some memory saving. I guess my first question is: Is there really any actual memory saving involved here, or are my assumptions about this incorrect?

我正试图决定哪种方法更好。将数组声明为可选数组意味着它不会占用任何内存,而空数组至少有一些内存分配给它,对吗?因此,使用可选数组意味着至少会节省一些内存。我想我的第一个问题是:这里面真的包含了内存节省吗?还是我的假设是错误的?

On the other hand, if it is optional then each time I try to use it I will have to check to see if it is nil or not before adding or removing objects from it. So there will be be some loss of efficiency there (but not much, I imagine).

另一方面,如果它是可选的,那么每次我尝试使用它时,我必须在添加或删除对象之前检查它是否为nil。因此,那里将会有一些效率的损失(我想,不会有太大的损失)。

I kind of like the optional approach. Not every Person will have children, so why not let children be nil until the Person decides to settle down and raise a family?

我有点喜欢可选的方法。并不是每个人都会有孩子,所以为什么不让孩子成为零,直到这个人决定安定下来并组建家庭?

At any rate, I would like to know if there are any other specific advantages or disadvantages to one approach or the other. It is a design question that will come up over and over again.

无论如何,我想知道一种方法或另一种方法是否有其他特定的优点或缺点。这是一个反复出现的设计问题。

6 个解决方案

#1


15  

The ability to choose between an empty array or an optional gives us the ability to apply the one that better describe the data from a semantic point of view.

在空数组和可选数组之间进行选择的能力使我们能够应用从语义角度更好地描述数据的数组。

I would choose:

我会选择:

  • An empty array if the list can be empty, but it's a transient status and in the end it should have at least one element. Being non optional makes clear that the array should not be empty
  • 如果列表可以为空,则为空数组,但它是一个暂态状态,最后应该至少有一个元素。非可选使得数组不应该为空
  • An optional if it's possible for the list to be empty for the entire life cycle of the container entity. Being an optional makes clear that the array can be empty
  • 一个可选的,如果这个列表可能是空的,整个生命周期的容器实体。如果是可选的,则表示数组可以为空

Let me make some examples:

让我举几个例子:

  • Purchase order with master and details (one detail per product): a purchase order can have 0 details, but that's a transient status, because it wouldn't make sense having a purchase order with 0 products
  • 带有主和详细信息的采购订单(每个产品有一个详细信息):一个采购订单可以有0个详细信息,但是这是一个暂时的状态,因为有一个包含0个产品的采购订单是没有意义的
  • Person with children: a person can have no children for his entire life. It is not a transient status (although not permanent as well), but using an optional it's clear that it's legit for a person to have no children.
  • 有孩子的人:一个人一生都不能有孩子。这不是一种短暂的状态(虽然也不是永久的),但是使用一个可选的选项很明显,一个人没有孩子是合法的。

Note that my opinion is only about making the code more clear and self-explainatory - I don't think there is any significant difference in terms of performance, memory usage, etc. for choosing one option or the other.

请注意,我的意见只是让代码更清晰和自我解释——我认为在选择一个选项或另一个选项时,在性能、内存使用等方面没有任何显著差异。

#2


16  

I'm going to make the opposite case from Yordi - an empty array just as clearly says "this Person has no children", and will save you a ton of hassle. children.isEmpty is an easy check for the existence of kids, and you won't ever have to unwrap or worry about an unexpected nil.

我将从Yordi(一个空的数组)中得出相反的情况,就像很清楚地说“这个人没有孩子”,并将为您省去许多麻烦。的孩子。isEmpty是一种很容易检查是否存在的孩子,你永远都不用打开或担心一个意想不到的nil。

Also, as a note, declaring something as optional doesn't mean it takes zero space - it's the .None case of an Optional<Array<Person>>.

另外,声明一个可选的东西并不意味着它占用零空间——它是可选的 >的. none情况。

#3


9  

Interestingly enough, we have recently had few discussions regarding this very same question at work.

有趣的是,我们最近在工作中很少讨论这个问题。

Some suggest that there are subtle semantic differences. E.g. nil means a person has no children whatsoever, but then what does 0 mean? Does it mean "has children, the whole 0 of them"? Like I said, pure semantics "has 0 children" and "has no children" makes no difference when working with this model in code. In that case why not choosing more straightforwards and less guard-let-?-y approach?

有些人认为存在微妙的语义差异。例如,nil是指一个人没有孩子,那么0是什么意思呢?它的意思是“有孩子,全部都是0”吗?如我所说,纯语义“有0个子项”和“没有子项”在代码中使用这个模型时没有区别。既然如此,为什么不选择更直的和更少的保护呢?可能是方法?

Some suggest that keeping a nil there may be an indication that, for example, when fetching model from backend something went wrong and we got error instead of children. But I think model should not try to have this type of semantics and nil should not be used as indication of some error in the past.

有些人认为保持nil可能意味着,例如,当从后端取回模型时出错了,我们得到的是错误而不是子错误。但是我认为模型不应该尝试使用这种类型的语义,并且在过去不应该使用nil作为一些错误的指示。

I personally think that the model should be as dumb as possible and the dumbest option in this case is empty array.

我个人认为模型应该尽可能地愚蠢,在这种情况下最愚蠢的选项是空数组。

Having an optional will make you drag that ? until the end of days and use guard let, if let or ?? over and over again.

有一个可选的会让你拖?直到日子的尽头和使用的护卫队让,如果让还是?一遍又一遍。

You will have to have extra unwrapping logic for NSCoding implementation, you will have to do person.children?.count ?? 0 instead of straightforward person.children.count when you display that model in any view controller.

您将不得不为NSCoding实现提供额外的解包逻辑,您将不得不处理person.children?数? ?0而不是简单的人,孩子们。在任何视图控制器中显示模型时都要计数。

The final goal of all that manipulation is to display something on UI. Would you really say

所有这些操作的最终目标是在UI上显示一些东西。你真的会说

"This person has no children" and "This person has 0 children" for nil and empty array correspondingly? I hope you would not :)

“这个人没有孩子”和“这个人有0个孩子”对应的空数组?我希望你不要:

Last Straw

Finally, and this is really the strongest argument I have

最后,这是我最有力的论点

There's tons of examples like this in Cocoa framework: UIViewController::childViewControllers and more.

在Cocoa框架中有很多这样的例子UIViewController: childViewControllers等等。

Even from pure Swift world: Dictionary::keys though this may be a bit far fetched.

即使是纯粹的斯威夫特世界:字典::钥匙,尽管这可能有点太牵强。

Why is it OK for person to have nil children, but not for SKNode? For me the analogy is perfect. Hey, even the SKNode's method name is children :)

为什么一个人可以有nil的孩子,而不是SKNode?对我来说,这个类比是完美的。嘿,连SKNode的方法名都是children:)

My view: there must be an obvious reason for keeping those arrays as optionals, like a really good one, otherwise empty array offers same semantics with less unwrapping.

我的观点是:一定有一个明显的理由将这些数组保留为选项,比如一个非常好的选项,否则空数组提供了相同的语义,而且不需要展开。

The Last Last Straw

Finally, some references to very good articles, each of those

最后,我还提到了一些非常好的文章

In Natasha's post, you will find a link to NSHipster's blog post and in Swiftification paragraph you can read this:

在娜塔莎的博文中,你可以找到NSHipster博客的链接,在Swiftification段落中,你可以看到:

For example, instead of marking NSArray return values as nullable, many APIs have been modified to return an empty array—semantically these have the same value (i.e., nothing), but a non-optional array is far simpler to work with

例如,不是将NSArray返回值标记为nullable,而是对许多api进行了修改,以返回一个空数组——从语义上讲,这些api具有相同的值(例如,但非可选数组要简单得多

#4


1  

Swift is designed to take advantage of optional value's and optional unwrapping.

Swift旨在利用可选值和可选解包。

You could also declare the array as nil, as it will save you a very small (almost not noticable) amount of memory.

您还可以将数组声明为nil,因为它将为您节省非常小的(几乎是不可见的)内存。

I would go with an optional array instead of an array that represents a nil value to keep Swift's Design Patterns happy :)

我会选择一个可选的数组,而不是一个表示nil值的数组,这样才能让Swift的设计模式满意:)

I also think

我也认为

if let children = children {

}

looks nicer than :

看起来比:

if(children != nil){

}

#5


1  

Sometimes there's a difference between something not existing and being empty.

有时候不存在的东西和空的东西是有区别的。

Let's say we have an app where a user can modify a list of parsed phone numbers and we save said modifications. If no modification has ever occurred the array should be nil. If the user has modified the parsed numbers by deleting them all the array should be empty.

假设我们有一个应用,用户可以修改解析后的电话号码列表,并保存上面的修改。如果没有发生任何修改,则数组应该为nil。如果用户通过删除解析后的数字来修改,那么所有的数组都应该是空的。

When we can't differentiate between a property being empty or not existing empty is the way to go. If a Person were to lose their only child we should simply have to remove that child and have an empty array rather than have to check if the count is 1 then set the entire array to nil.

当我们不能区分一个属性是空的还是不存在的空的时候,就应该这样做。如果一个人失去了他的独子,我们只需删除这个子元素,并拥有一个空数组,而不必检查计数是否为1,然后将整个数组设置为nil。

#6


1  

I always use empty arrays.

我总是使用空数组。

In my humble opinion, the most important purpose of optionals in Swift is to safely wrap some value that may be nil. An array already act as this type of wrapper - you can ask the array if it has anything inside & access its value(s) safely with for loops, mapping, etc. Do we need to put a wrapper within a wrapper? I don't think so.

在我看来,在Swift中,最重要的选择是安全地包装一些可能为零的值。数组已经作为这种类型的包装器—您可以询问数组内部是否有任何内容,并使用for循环、映射等安全地访问其值。是否需要在包装器中放置包装器?我不这么想。

#1


15  

The ability to choose between an empty array or an optional gives us the ability to apply the one that better describe the data from a semantic point of view.

在空数组和可选数组之间进行选择的能力使我们能够应用从语义角度更好地描述数据的数组。

I would choose:

我会选择:

  • An empty array if the list can be empty, but it's a transient status and in the end it should have at least one element. Being non optional makes clear that the array should not be empty
  • 如果列表可以为空,则为空数组,但它是一个暂态状态,最后应该至少有一个元素。非可选使得数组不应该为空
  • An optional if it's possible for the list to be empty for the entire life cycle of the container entity. Being an optional makes clear that the array can be empty
  • 一个可选的,如果这个列表可能是空的,整个生命周期的容器实体。如果是可选的,则表示数组可以为空

Let me make some examples:

让我举几个例子:

  • Purchase order with master and details (one detail per product): a purchase order can have 0 details, but that's a transient status, because it wouldn't make sense having a purchase order with 0 products
  • 带有主和详细信息的采购订单(每个产品有一个详细信息):一个采购订单可以有0个详细信息,但是这是一个暂时的状态,因为有一个包含0个产品的采购订单是没有意义的
  • Person with children: a person can have no children for his entire life. It is not a transient status (although not permanent as well), but using an optional it's clear that it's legit for a person to have no children.
  • 有孩子的人:一个人一生都不能有孩子。这不是一种短暂的状态(虽然也不是永久的),但是使用一个可选的选项很明显,一个人没有孩子是合法的。

Note that my opinion is only about making the code more clear and self-explainatory - I don't think there is any significant difference in terms of performance, memory usage, etc. for choosing one option or the other.

请注意,我的意见只是让代码更清晰和自我解释——我认为在选择一个选项或另一个选项时,在性能、内存使用等方面没有任何显著差异。

#2


16  

I'm going to make the opposite case from Yordi - an empty array just as clearly says "this Person has no children", and will save you a ton of hassle. children.isEmpty is an easy check for the existence of kids, and you won't ever have to unwrap or worry about an unexpected nil.

我将从Yordi(一个空的数组)中得出相反的情况,就像很清楚地说“这个人没有孩子”,并将为您省去许多麻烦。的孩子。isEmpty是一种很容易检查是否存在的孩子,你永远都不用打开或担心一个意想不到的nil。

Also, as a note, declaring something as optional doesn't mean it takes zero space - it's the .None case of an Optional<Array<Person>>.

另外,声明一个可选的东西并不意味着它占用零空间——它是可选的 >的. none情况。

#3


9  

Interestingly enough, we have recently had few discussions regarding this very same question at work.

有趣的是,我们最近在工作中很少讨论这个问题。

Some suggest that there are subtle semantic differences. E.g. nil means a person has no children whatsoever, but then what does 0 mean? Does it mean "has children, the whole 0 of them"? Like I said, pure semantics "has 0 children" and "has no children" makes no difference when working with this model in code. In that case why not choosing more straightforwards and less guard-let-?-y approach?

有些人认为存在微妙的语义差异。例如,nil是指一个人没有孩子,那么0是什么意思呢?它的意思是“有孩子,全部都是0”吗?如我所说,纯语义“有0个子项”和“没有子项”在代码中使用这个模型时没有区别。既然如此,为什么不选择更直的和更少的保护呢?可能是方法?

Some suggest that keeping a nil there may be an indication that, for example, when fetching model from backend something went wrong and we got error instead of children. But I think model should not try to have this type of semantics and nil should not be used as indication of some error in the past.

有些人认为保持nil可能意味着,例如,当从后端取回模型时出错了,我们得到的是错误而不是子错误。但是我认为模型不应该尝试使用这种类型的语义,并且在过去不应该使用nil作为一些错误的指示。

I personally think that the model should be as dumb as possible and the dumbest option in this case is empty array.

我个人认为模型应该尽可能地愚蠢,在这种情况下最愚蠢的选项是空数组。

Having an optional will make you drag that ? until the end of days and use guard let, if let or ?? over and over again.

有一个可选的会让你拖?直到日子的尽头和使用的护卫队让,如果让还是?一遍又一遍。

You will have to have extra unwrapping logic for NSCoding implementation, you will have to do person.children?.count ?? 0 instead of straightforward person.children.count when you display that model in any view controller.

您将不得不为NSCoding实现提供额外的解包逻辑,您将不得不处理person.children?数? ?0而不是简单的人,孩子们。在任何视图控制器中显示模型时都要计数。

The final goal of all that manipulation is to display something on UI. Would you really say

所有这些操作的最终目标是在UI上显示一些东西。你真的会说

"This person has no children" and "This person has 0 children" for nil and empty array correspondingly? I hope you would not :)

“这个人没有孩子”和“这个人有0个孩子”对应的空数组?我希望你不要:

Last Straw

Finally, and this is really the strongest argument I have

最后,这是我最有力的论点

There's tons of examples like this in Cocoa framework: UIViewController::childViewControllers and more.

在Cocoa框架中有很多这样的例子UIViewController: childViewControllers等等。

Even from pure Swift world: Dictionary::keys though this may be a bit far fetched.

即使是纯粹的斯威夫特世界:字典::钥匙,尽管这可能有点太牵强。

Why is it OK for person to have nil children, but not for SKNode? For me the analogy is perfect. Hey, even the SKNode's method name is children :)

为什么一个人可以有nil的孩子,而不是SKNode?对我来说,这个类比是完美的。嘿,连SKNode的方法名都是children:)

My view: there must be an obvious reason for keeping those arrays as optionals, like a really good one, otherwise empty array offers same semantics with less unwrapping.

我的观点是:一定有一个明显的理由将这些数组保留为选项,比如一个非常好的选项,否则空数组提供了相同的语义,而且不需要展开。

The Last Last Straw

Finally, some references to very good articles, each of those

最后,我还提到了一些非常好的文章

In Natasha's post, you will find a link to NSHipster's blog post and in Swiftification paragraph you can read this:

在娜塔莎的博文中,你可以找到NSHipster博客的链接,在Swiftification段落中,你可以看到:

For example, instead of marking NSArray return values as nullable, many APIs have been modified to return an empty array—semantically these have the same value (i.e., nothing), but a non-optional array is far simpler to work with

例如,不是将NSArray返回值标记为nullable,而是对许多api进行了修改,以返回一个空数组——从语义上讲,这些api具有相同的值(例如,但非可选数组要简单得多

#4


1  

Swift is designed to take advantage of optional value's and optional unwrapping.

Swift旨在利用可选值和可选解包。

You could also declare the array as nil, as it will save you a very small (almost not noticable) amount of memory.

您还可以将数组声明为nil,因为它将为您节省非常小的(几乎是不可见的)内存。

I would go with an optional array instead of an array that represents a nil value to keep Swift's Design Patterns happy :)

我会选择一个可选的数组,而不是一个表示nil值的数组,这样才能让Swift的设计模式满意:)

I also think

我也认为

if let children = children {

}

looks nicer than :

看起来比:

if(children != nil){

}

#5


1  

Sometimes there's a difference between something not existing and being empty.

有时候不存在的东西和空的东西是有区别的。

Let's say we have an app where a user can modify a list of parsed phone numbers and we save said modifications. If no modification has ever occurred the array should be nil. If the user has modified the parsed numbers by deleting them all the array should be empty.

假设我们有一个应用,用户可以修改解析后的电话号码列表,并保存上面的修改。如果没有发生任何修改,则数组应该为nil。如果用户通过删除解析后的数字来修改,那么所有的数组都应该是空的。

When we can't differentiate between a property being empty or not existing empty is the way to go. If a Person were to lose their only child we should simply have to remove that child and have an empty array rather than have to check if the count is 1 then set the entire array to nil.

当我们不能区分一个属性是空的还是不存在的空的时候,就应该这样做。如果一个人失去了他的独子,我们只需删除这个子元素,并拥有一个空数组,而不必检查计数是否为1,然后将整个数组设置为nil。

#6


1  

I always use empty arrays.

我总是使用空数组。

In my humble opinion, the most important purpose of optionals in Swift is to safely wrap some value that may be nil. An array already act as this type of wrapper - you can ask the array if it has anything inside & access its value(s) safely with for loops, mapping, etc. Do we need to put a wrapper within a wrapper? I don't think so.

在我看来,在Swift中,最重要的选择是安全地包装一些可能为零的值。数组已经作为这种类型的包装器—您可以询问数组内部是否有任何内容,并使用for循环、映射等安全地访问其值。是否需要在包装器中放置包装器?我不这么想。