Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference between Generics and AnyObject in Swift

Consider this myFilter function that takes in a generic argument and filters the array based on the predicate. This is same as the filter() function provided by Swift.

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

How is this different from,

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

Aren't we achieving the point of generics even in the latter example?

like image 928
avismara Avatar asked Jun 25 '15 04:06

avismara


People also ask

What is the difference between any and AnyObject in Swift?

Swift provides two special types for working with nonspecific types: Any can represent an instance of any type at all, including function types. AnyObject can represent an instance of any class type.

What can AnyObject represent in Swift?

AnyObject can also be used as the concrete type for an instance of a type that bridges to an Objective-C class. Many value types in Swift bridge to Objective-C counterparts, like String and Int . The flexible behavior of the AnyObject protocol is similar to Objective-C's id type.

What is the use of generics in Swift?

Swift Generics allows us to create a single function and class (or any other types) that can be used with different data types. This helps us to reuse our code.


2 Answers

Generics are type safe, meaning if you pass a string as a generic and try to use as a integer the compiler will complain and you will not be able to compile your (which is good). (This happens because Swift is using Static typing, and is able to give you a compiler error)

If you use AnyObject the compiler has no idea if the object can be treated as a String or as an Integer. It will allow you to do whatever you want with it (which is bad).

e.g. if you try to pass a String when it the your previously used Integer the application will crash. (This happens because Swift is using Dynamic typing and will only give you a runtime crash)

Generics basically tells the compiler:

"I am going to give you a type later and I want you to enforce that type everywhere I specify."

AnyObject basically tells the compiler:

"Don't worry about this variable no need to enforce any type here let me do whatever I want to."

like image 196
Icaro Avatar answered Sep 24 '22 02:09

Icaro


Note: Icaro's answer would still be the accepted answer, I am just extending his explanation.

TL;DR : Check Icaro's answer.

About the usage of AnyObject Icaro rightly puts:

Don't worry about this variable no need to enforce any type here let me do whatever I want to.

What does this mean? Let's take the code example in the question (I've gone a step up and changed AnyObject to Any without changing the meaning of the question):

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

This means, the myFilter function takes in two arguments one source and a closure predicate. If you look closely, the contents of the source array can be ANYTHING. And the argument of the closure predicate can be ANYTHING as well. If we were to name these "ANYTHING"s -- say ANYTHING1 and ANYTHING2 -- this approach doesn't require ANYTHING1 to be equal to ANYTHING2.

Let's sit back and ponder over the implications of this...

Say, we want to filter out evens from an array of integers and let's use our Any filter for this

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) 

Wow, that worked, didn't it? All smiles? No.

Notice how in the line :

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

I'm forcefully down-casting a. This line would crash if a was anything other than an Int. But its usage is justified; after all, we want to just filter out the Ints and I am smart enough to use just an array of Ints.

But, because say, I am a naive programmer, I do this :

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) 

This happily compiles, but crashes in the runtime. If only there was a way, where the compiler would tell me that this line...

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

... was faulty, we would not have had a crash. I would have fixed it right away!

Turns out, there is. Generics. To quote Icaro again,

I am going to give you a type later and I want you to enforce that type everywhere I specify.

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) 

This new filter is awesome. It won't let me do :

let evens = myFilter(source: ints, predicate:predicate) because, the types of the predicate and source don't match. Compile time error.

Generics is generic in this way : in this specific example -- while at the point of writing the myFilter function, you do not need to give a type of the source or the argument that predicate takes, it's T, it's ANYTHING. But once I say that source is an array of ANYTHING, you HAVE to make sure the argument that the predicate accepts is the same ANYTHING. With the background of our previous ANYTHING1, ANYTHING2 nomenclature, we can say that generics forces ANYTHING1 to be equal to ANYTHING2

like image 27
avismara Avatar answered Sep 20 '22 02:09

avismara