Swift中泛型和AnyObject之间的区别

时间:2015-06-25 04:33:39

标签: ios swift functional-programming

考虑这个myFilter函数,它接受泛型参数并根据谓词过滤数组。这与Swift提供的filter()函数相同。

func myFilter<T>(source: [T], predicate:(T) -> Bool) -> [T] {
  var result = [T]()
  for i in source {
    if predicate(i) {
      result.append(i)
    }
  }
  return result
}

这有什么不同,

func myFilter(source: [AnyObject], predicate:(AnyObject) -> Bool) -> [AnyObject] {
  var result = [AnyObject]()
  for i in source {
    if predicate(i) {
      result.append(i)
    }
  }
  return result
}

即使在后一个例子中,我们也不是达到了泛型的地步吗?

3 个答案:

答案 0 :(得分:77)

泛型是类型安全的,这意味着如果你将字符串作为泛型传递并尝试使用整数,编译器会抱怨并且你将无法编译你的(这很好)。 (这是因为Swift正在使用静态类型,并且能够为您提供编译器错误

如果使用AnyObject,编译器不知道该对象是被视为String还是Integer。它可以让你随心所欲地做任何事情(这很糟糕)。

e.g。如果你尝试传递一个String 它之前使用过的Integer,应用程序将崩溃。 (这是因为Swift使用动态类型并且

泛型基本上告诉编译器:

  

“我稍后会给你一个类型,我希望你强制执行   键入我指定的任何地方。“

AnyObject基本上告诉编译器:

  

“不要担心这个变量不需要强制执行任何类型让我做任何我想做的事。”

答案 1 :(得分:14)

注意:Icaro的答案仍然是接受的答案,我只是在扩展他的解释。

TL; DR :检查Icaro的答案。

关于AnyObject Icaro的用法正确地说:

  

不要担心这个变量不需要在这里强制执行任何类型   我做任何我想做的事。

这是什么意思?我们来看一下问题中的代码示例(我已经上升并将AnyObject更改为Any而不更改问题的含义):

func myFilter(source: [Any], predicate:(Any) -> Bool) -> [Any] {
  var result = [Any]()
  for i in source {
    if predicate(i) {
      result.append(i)
    }
  }
  return result
}

这意味着,myFilter函数接受两个参数source和一个闭包predicate。如果仔细观察,源数组的内容可以是任何内容。封闭predicate的论点也可以是任何东西。如果我们要命名这些“ANYTHING” - 比如ANYTHING1和ANYTHING2 - 这种方法不要求ANYTHING1等于ANYTHING2。

让我们坐下来思考这个问题......

说,我们希望从整数数组中过滤出均衡,让我们使用我们的Any过滤器

var ints = [1,2,3,4,5] as [Any]
var predicate = { (a : Any) -> Bool in
    return (a as! Int) % 2 == 0
}

let evens = myFilter(source: ints, predicate:predicate)
哇,那有效,不是吗?满脸笑容?没有。

注意如何:

return (a as! Int) % 2 == 0

我强行向下a。如果a不是Int,则此行会崩溃。但它的用法是合理的;毕竟,我们只想过滤掉Int,我很聪明,只使用Int s的数组。

但是,因为说,我是一个天真的程序员,我这样做:

var ints = [1,2,3,4,5,"6"] as [Any]
var predicate = { (a : Any) -> Bool in
    return (a as! Int) % 2 == 0
}

let evens = myFilter(source: ints, predicate:predicate)

这很愉快地编译,但在运行时崩溃了。如果只有一种方法,编译器会告诉我这一行......

var ints = [1,2,3,4,5,"6"]

......有问题,我们不会发生车祸。我马上就修好了!

原来,有。泛型。再次引用Icaro,

  

我稍后会给你一个类型,我希望你强制执行   在我指定的任何地方打字。

func myFilter<T>(source: [T], predicate:(T) -> Bool) -> [T] {
    var result = [T]()
    for i in source {
        if predicate(i) {
            result.append(i)
        }
    }
    return result
}

var ints = [1,2,3,4,5,6]
var predicate = { (a : Int) -> Bool in
    return a % 2 == 0
}

let evens = myFilter(source: ints, predicate:predicate)

这个新过滤器很棒。它不会让我这样做:

let evens = myFilter(source: ints, predicate:predicate)因为predicatesource的类型不匹配。编译时间错误。

泛型是通用的:在这个特定的例子中 - 在编写myFilter函数时,你不需要给出source的类型或predicate的参数。 {1}}需要,它是T,它是任何东西。但是,一旦我说source是一个任意数组,你必须确保predicate接受的论点是相同的。在我们之前的ANYTHING1,ANYTHING2命名法的背景下,我们可以说泛型强迫ANYTHING1等于ANYTHING2

答案 2 :(得分:1)

考虑在第一个函数T中是一个类型,就像AnyObject一样,但是类型变量;这意味着在第一个函数中,您可以将任何类型的值数组作为第一个参数传递,但谓词仅对 特定类型的值作为第二个参数进行操作。也就是说,您可以在字符串上传递字符串和谓词数组,或者在整数上传递整数数组和谓词,而不能在字符串上传递整数数组和谓词。因此,函数的主体保证对于类型的关注是正确的。

相反,在第二个示例中,您可以传递任何类型的值和一个对任何(可能不同的!)类型进行操作的谓词,这样,如果谓词将在函数体中调用,其值为第一个参数,然后可能发生动态类型错误。幸运的是,Swith类型检查器将谓词的调用标记为类型错误,以防止这种可能性。

相关问题