generics和Swift中的AnyObject之间的区别
考虑这个myFilter
函数,它接受一个generics参数,并根据谓词过滤数组。 这与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 }
即使在后一个例子中,我们是不是也能达到generics的地步呢?
generics是types安全的,这意味着如果你传递一个string作为一个通用的,并尝试使用整数编译器会抱怨,你将无法编译你的(这是好的)。 (这是因为Swift使用静态types ,并能够给你一个编译器错误 )
如果使用AnyObject,编译器不知道该对象是否可以被当作String或Integer。 它将允许你做任何你想要的(这是不好的)。
例如,如果您尝试传递一个string时,它以前使用整数应用程序将崩溃。 (这是因为Swift使用dynamictypes , 只会给你一个运行时崩溃 )
generics基本上告诉编译器:
“我将在稍后给你一个types,我希望你在我指定的任何地方执行这种types 。”
AnyObject基本上告诉编译器:
“不要担心这个variables, 不需要在这里执行任何types的操作,让我做任何我想做的事情。”
注 :Icaro的答案仍然是被接受的答案,我只是扩大了他的解释。
TL; DR :检查Icaro的答案。
关于AnyObject
Icaro的用法AnyObject
得好:
不要担心这个variables,不需要在这里强制执行任何types的任何操作。
这是什么意思? 我们来看问题中的代码示例(我已经进行了一些改动,将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
。 如果仔细观察,源数组的内容可以是任何东西。 closurespredicate
的参数也可以是任意的。 如果我们要命名这些“任何东西” – 比如ANYTHING1和ANTHING2–这种方法不需要ANTHING1等于ANYTHING2。
让我们坐下来思考这个问题的意义
比方说,我们想从一个整数数组中筛选出evens,让我们使用我们的Any
filter来实现这个
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
。 如果除了Int
以外的其他任何东西,这行就会崩溃。 但是它的用法是合理的。 毕竟,我们只想过滤出Int
而且我足够聪明,只需要使用Int
数组。
但是,因为我是一个天真的程序员,我这样做:
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"]
…是错误的,我们不会有一个崩溃。 我会马上解决它!
原来,有。 generics。 再次引用Icaro,
我将在稍后给你一个types,我希望你在我指定的任何地方执行这个types。
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)
这个新的filter真棒。 它不会让我这样做:
let evens = myFilter(source: ints, predicate:predicate)
因为predicate
和source
的types不匹配。 编译时间错误。
generics是通用的:在这个特定的例子中 – 在编写myFilter
函数的时候,你不需要给predicate
的types或predicate
所需的参数,它是T,它是任何东西。 但是,一旦我说source
是一个任意数组,你必须确保predicate
接受的参数是相同的。 在我们以前的ANYTHING1 ANYTHING2术语的背景下,我们可以说generics使得ANYTHING1等于ANYTHING2
考虑在第一个函数T 不是一个types,就像是AnyObject,而是一个typesvariables ; 这意味着在第一个函数中,您可以传递任何types的值的数组作为第一个参数,而谓词仅在该特定types的值上作为第二个参数。 也就是说,你可以在string上传递一个string和一个谓词数组,或者一个整数数组和一个整数的谓词,而不能在string上传递一个整数数组和一个谓词。 所以,函数的主体是保证正确的types有关。
相反,在第二个例子中,可以传递任何types的值和一个操作任何(可能不同的!)types的谓词,这样,如果在函数体中用第一个参数的值调用谓词,那么可能会发生dynamictypes错误。 幸运的是,Swithtypes检查器将谓词的调用标记为types错误,以防止这种可能性。