Ответ 1
Дженерики безопасны по типу, то есть если вы передаете строку как общий и попытаетесь использовать в качестве целого числа, компилятор будет жаловаться, и вы не сможете скомпилировать ваш (что хорошо). (Это происходит из-за того, что Swift использует Static typing и может дать вам ошибку компилятора)
Если вы используете AnyObject, компилятор понятия не имеет, может ли объект обрабатываться как String или Integer. Это позволит вам делать то, что вы хотите с ним (что плохо).
например. если вы попытаетесь передать String, когда это ранее использовавшееся Integer, приложение будет аварийно завершено. (Это происходит из-за того, что Swift использует Динамическое типирование и только даст вам сбой во время выполнения)
Generics в основном сообщает компилятору:
"Я дам вам тип позже, и я хочу, чтобы вы применяли это тип везде, где я указываю."
AnyObject в основном сообщает компилятору:
"Не беспокойтесь об этой переменной , не нужно применять какой-либо тип здесь, чтобы я сделал все, что захочу.
Ответ 2
Примечание: ответ Икаро все равно будет принятым ответом, я просто расширяю его объяснение.
TL; DR: проверьте ответ Icaro.
Об использовании AnyObject
Икаро справедливо ставит:
Не беспокойтесь об этой переменной, не нужно принуждать любого типа здесь я делаю все, что хочу.
Что это значит? Возьмем пример кода в вопросе (я сделал шаг вверх и изменил 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
также может быть НИЧЕГО. Если бы мы назовем эти "НИЧЕГО" - скажем НИЧЕГО 1 и НИЧЕГО2 - этот подход не требует, чтобы НИЧЕГО 1 было равно НИЧЕГО2.
Пусть сидят сложа руки и размышляют над последствиями этого...
Скажем, мы хотим отфильтровать evens из массива целых чисел и использовать наш фильтр 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)
, потому что типы predicate
и source
не совпадают. Ошибка времени компиляции.
Generics является таким общим: в этом конкретном примере - в то время как в точке написания функции myFilter
вам не нужно указывать тип source
или аргумент, который predicate
принимает, это Т, это НИЧЕГО. Но как только я скажу, что source
является массивом НИЧЕГО, вам нужно убедиться, что аргумент, который принимает predicate
, тот же НИЧЕГО. На фоне нашей предыдущей номенклатуры ANYTHING1, ANYTHING2 мы можем сказать, что generics заставляет ANYTHING1 быть равно ANYTHING2
Ответ 3
Учтите, что в первой функции T не является типом, как и AnyObject, но переменной типа; это означает, что в первой функции вы можете передать массив значений любого типа в качестве первого параметра, но предикат, который работает только с значениями этого конкретного типа в качестве второго параметра. То есть вы можете передать массив строк и предикат для строк или массив целых чисел и предикат для целых чисел, в то время как вы не можете передать массив целых чисел и предикат на строки. Таким образом, тело функции гарантировано будет правильным для того, что касается типов.
Вместо этого во втором примере вы можете передать значение любого типа и предиката, который работает на любом (возможно, другом!) типе, так что, если предикат будет вызываться в теле функции со значением первый параметр, тогда может произойти ошибка динамического типа. К счастью, программа Swith typechecker отмечает вызов предиката как ошибку типа, чтобы предотвратить эту возможность.