Why do Swift Class-Only Protocols need AnyObject Inheritance?
When you write a protocol, you define an interface for the types which adopt, or conform to the protocol:
// Every Greeter has a greeting message
// and a way for it to present that message.
protocol Greeter {
var greeting: String { get }
func greet()
}
extension Greeter {
// Default implementation for the `greet` method.
func greet() {
print(self.greeting)
}
}
This gives consistent behavior to every type which conforms and lets you treat them all in the same way regardless of what they are. For instance, a type can store a value bound to a protocol type:
struct Store {
// The name of the store.
var name: String
// The address of the store.
var address: String
// An entity that greets customers when they walk in.
var greeter: Greeter
}
In this case, Store.greeter
can be set to any value or object which conforms to the Greeter
protocol:
struct Bell: Greeter {
let greeting = "Ding!"
}
class Person: Greeter {
let greeting: String
init(name: String) {
greeting = "Welcome! My name is \(name)."
}
}
var store = Store(name: "My Shop", address: "123 Main St.", greeter: Bell())
store.greeter.greet() // => "Ding!"
store.greeter = Person(name: "Itai")
store.greeter.greet() // => "Welcome! My name is Itai."
By default, a protocol can be adopted by both value types and object types, i.e., both struct
s and class
es. This is a good default because in many cases, there's no reason to restrict who can conform to a protocol.
However, there is one case where this matters. By default, Store
owns its greeter
property, i.e., when you assign an object to store.greeter
, it is retained. This is normally fine, except when you have a potentially circular relationship (for example, if Person
has a reference to the Store
they work at).
Normally, to break a potential circular chain like this up, you would make the variable weak
:
struct Store {
// ...
weak var greeter: Greeter?
// ^ error: 'weak' must not be applied to non-class-bound 'Greeter'; consider adding a protocol conformance that has a class bound
}
If you try this, though, you'll get the above error. This is because weak
only makes sense for objects, not for struct
s (struct
s can only be owned because they are value types — every owner just makes a copy of a struct
, so there is no one central value to retain or release). In this case, to ensure a weak
reference, you must ensure that Greeter
an only be an object type.
The way to do this is to constrain the protocol to only allow classes to conform by using AnyObject
as a constraint:
protocol Greeter: AnyObject {
// ...
}
Now, Bell
can't conform to Greeter
any more (error: non-class type 'Bell' cannot conform to class protocol 'Greeter'
) but you are allowed to have weak var greeter: Greeter?
as a variable.
So, when should you restrict your protocol to be "class-bound" (using AnyObject
)? Well, only when you really have to. The one benefit for class-bound protocols is to allow weak
references to them, and the main use-case for that is with delegate references to prevent circular retains.
For instance, UITableView
has a weak
delegate
property because the delegate
object is usually the view controller which owns the UITableView
object itself. If the view controller retains the table view, and the table view retains the controller, neither can be released automatically.
So, in the general case, you don't need to worry about the AnyObject
constraint unless you really do only want classes to conform to the protocol.
Why use class only protocols in Swift?
One use case:
- You have a "delegate" protocol and someone wants to have a
weak
property of that protocol type.weak
can only be used on reference types; therefore, the protocol must be class-only.
Class-Only Protocols in Swift
Swift 4 allows you to combine types, so you can have your protocol and then create, for example, a type alias to combine it with a specific class requirement.
For (a contrived) example:
typealias PresentableVC = UIViewController & Presentable
For the presented code:
The problem is that you're trying to limit to specific classes and Swift can't do that (at the moment anyway). You can only limit to classes and inherit from other protocols. Your syntax is for protocol inheritance but you're trying to use it as a class limitation.
Note that the purpose of class protocols is:
Use a class-only protocol when the behavior defined by that protocol’s requirements assumes or requires that a conforming type has reference semantics rather than value semantics.
When to use `protocol` and `protocol: class` in Swift?
Swift 4 version
AnyObject
added to a protocol definition like this
protocol FilterViewControllerDelegate: AnyObject {
func didSearch(parameters:[String: String]?)
}
means that only a class will be able to conform to that protocol.
So given this
protocol FilterViewControllerDelegate: AnyObject {
func didSearch(parameters:[String: String]?)
}
You will be able to write this
class Foo: FilterViewControllerDelegate {
func didSearch(parameters:[String: String]?) { }
}
but NOT this
struct Foo: FilterViewControllerDelegate {
func didSearch(parameters:[String: String]?) { }
}
Swift 3 version
:class
added to a protocol definition like this
protocol FilterViewControllerDelegate: class {
func didSearch(Parameters:[String: String]?)
}
means that only a class will be able to conform to that protocol.
So given this
protocol FilterViewControllerDelegate: class {
func didSearch(Parameters:[String: String]?)
}
You will be able to write this
class Foo: FilterViewControllerDelegate {
func didSearch(Parameters:[String: String]?) { }
}
but NOT this
struct Foo: FilterViewControllerDelegate {
func didSearch(Parameters:[String: String]?) { }
}
Why protocol is better than class in swift?
Lets take a downloading example.
You have a Base class FileDownloadModel, and have 3 subclasses AudioFileDownloadModel, VideoFileDownloadModel, and ImageDownloadModel.
You have a DownloadManager that takes a FileDownloadModel input and uses this model's urlToDownload property to download the file.
Later down the line you are told that there is one more model coming but it's of type UserDownloadModel which is a subclass of User, and not FileDownloadModel.
See now it becomes difficult to handle this scenario where you will have to change a lot of code to incorporate downloading methods.
How protocol oriented programming will help you here:
- Create a protocol named DownloadingFileProtocol and add
methods and properties that you need for downloading a file. eg. urlToDownload,
pathToSave, extension etc. - Implement the same protocol in FileDownloadModel and
UserDownloadModel. The benefit here is that you don't have to change a
lot of code in UserDownloadModel. You will just implement the
methods from the DownloadingFileProtocol. - If a new entity comes down the line again, you will not
change any code. Rather, you'll just implement the protocol methods. - And now your DownloadManager can take a
DownloadingFileProtocol as input instead of a specific model. As well, you can now make any model "downloadable" by having it adopt this protocol.
Why Swift protocol conforming values are treated as Value types by default?
Your protocol SampleProtocol
could be adopted by a class
or a struct
. Swift is using the behavior of the value type which is the more restrictive type until you tell it that the protocol will only be used by a class
reference type.
Add conformance to AnyObject
to your protocol to get reference type behavior:
protocol SampleProtocol: AnyObject {
var message: String? { get set }
}
See The Swift 5.1 Programming Guide - Class-Only Protocols for more details.
The guide notes:
Use a class-only protocol when the behavior defined by that protocol’s
requirements assumes or requires that a conforming type has reference
semantics rather than value semantics.
Historical note: Using class
keyword:
Prior to Swift 4.0, this was written using the class
keyword:
protocol SampleProtocol: class {
var message: String? { get set }
}
This still works for the time being, but it is currently just a type alias for AnyObject
and will likely be removed in a later version of Swift.
what is the difference protocol's `: AnyObject` between `none`?
AnyObject
requires that conforming types be classes, rather than structs or enums. See Class-Only Protocols for details.
Classes provide reference semantics, rather than value semantics, which you may require for your protocol. For example, if you want to observe an object, that only makes sense for a reference type (you can't observe changes to a value type, since value types are copied when modified). For more details on that see Structures and Classes, and particularly the sections explaining value and reference types.
There is no value type equivalent of AnyObject
. You can't require value semantics for a protocol. This can lead to some headaches. As an example of the difference, consider the following:
protocol Incrementing {
var value: Int { get set }
}
extension Incrementing {
func incremented() -> Self {
var new = self // Value types copy, reference types reference
new.value += 1
return new
}
}
struct S: Incrementing { var value: Int = 0 }
class C: Incrementing { var value: Int = 0 }
var s = S()
s.incremented().value // 1
s.value // 0
var c = C()
c.incremented().value // 1
c.value // 1 (!!!)
This code makes sense for a struct, but is buggy for a class. There's no real fix for that in Swift today.
When to use protocol in Swift
Protocols have more than one use case, but think of it this way:
For classes, protocols provide a lightweight inheritance hierarchy separate from the class hierarchy. Given a class Animal and its subclasses Cat, Dog, Bird, and Insect, how would you specify that only Bird and Insect share the
fly
method?For structs, protocols provide an inheritance hierarchy that would otherwise be missing entirely! A struct has no superstruct. So, given a struct Bird and a struct Insect, how would you specify that they share a
fly
method?
Now, you could answer that Bird and Insect just happen to have fly
methods and that's the end of the story. But that won't do when you need to speak of the set "all types that have a fly
method". And you do need to speak of that set when you want the compiler to be able to send the fly
method to an object on the grounds that it has a fly
method.
The solution is a protocol:
protocol Flier {
func fly()
}
Now Flier is a type. An instance of any type that conforms to Flier can be used where a Flier is expected, and the compiler will let you tell any Flier to fly
, so the problem is solved:
var myFlier : Flier = Bird() // if Bird conforms to Flier
myFlier.fly() // legal
Related Topics
How to Cancel Firebase Setvalue While Pending for Completion (When Offline)
Resulting Mtltexture Lighter Than Cgimage
Changing The Color of a Button in Swiftui on Tvos
Play Sound with a Little Delay
Could Not Find a Storyboard Named 'Maintabcontroller' in Bundle Nsbundle
Swift 3 - Pass Struct by Reference via Unsafemutablerawpointer
Spritekit Skphysicsjointfixed Odd Behaviour
Use of Nspathcontrol to Represent Virtual Path
How to Pass a Class and Method to Create an Instance of a Class
How to Set iOS 13 Glyphs Programmatically
How to Load Nsview from Xib with Swift 3
Swift 3 Closure Overload Resolution
Fetching Child Sum from Core Data
Xcode 8 Swift 3 Pitch-Altering Sounds
Iwatch: Wkinterfacelabel How to Stop Text from Being Cut Off with "..." at The End of a Label
Firebase and Reading Nested Data Using Swift