Mutating function inside class - swift

Consider this class in Swift:
class Zombie: Monster {
var walksWithLimp = true
final override func terrorizeTown()
{
town?.changePopulation(-10)
super.terrorizeTown()
}
func changeName(name: String, walksWithLimp: Bool)
{
self.name = name
self.walksWithLimp = walksWithLimp
}
}
Zombie inherits the name field from the Monster class.
var name = "Monster"
Why does
fredTheZombie.changeName("Tom", walksWithLimp: true)
work even if there is no mutating keyword before the function header?

From The Language Guide - Methods:
Modifying Value Types from Within Instance Methods
Structures and enumerations are value types. By default, the
properties of a value type cannot be modified from within its instance
methods.
However, if you need to modify the properties of your structure or
enumeration within a particular method, you can opt in to mutating
behavior for that method. The method can then mutate (that is, change)
its properties from within the method, and any changes that it makes
are written back to the original structure when the method ends. The
method can also assign a completely new instance to its implicit self
property, and this new instance will replace the existing one when the
method ends.
You can opt in to this behavior by placing the mutating keyword before
the func keyword for that method ...
Hence, we need to include the keyword mutating to allow a member (e.g. a function†) of a value type to mutate its members (e.g. the member properties of a struct). Mutating a member of a value type instance means mutating the value type instance itself (self), whereas mutating a member of a reference type instance will not mean the reference of the reference type instance (which is considered self) is mutated.
Hence, since a class is a reference type in Swift, we need not include the mutating keyword in any of the instance methods of your Zombie class, even if they mutate the instance members or the class. If we were to speak of mutating the actual class instance fredTheZombie, we would refer to mutating its actual reference (e.g. to point at another Zombie instance).
[†]: As another example, we may use e.g. mutating getters (get); in which case we need to mark this explicitly as these are nonmutating by default. Setters (set), on the other hand, are mutating by default, and hence need no mutating keyword even if they mutate members of a value type.

mutating is not relevant for classes, it is only for value types, such as struct and enum

Without mutating func
struct Counter {
let count: Int
init(count: Int = 0) {
self.count = count
}
// the functional approach
func counterByIncrementing() -> Counter {
let newCount = count + 1
return Counter(count: newCount)
}
}
var counter = Counter()
counter = counter.counterByIncrementing()
Mutating func
struct Counter {
// this now has to be a var :/
var count: Int
init(count: Int = 0) {
self.count = count
}
// the mutating keyword approach
mutating func increment() {
count += 1
}
}
var counter = Counter()
counter.increment()
In class, all func are mutating. But for struct and enum we need to specify.

Another easy to understand example, verified in Swift 3 & 4
struct City
{
var population : Int
func changePopulation(newpopulation : Int) {
population = newpopulation //error: cannot modify property "population"
}
}
var mycity = City(population : 500)
mycity.changePopulation(newpopulation : 2000) //error: cannot modify property "population"
Solution
mutating func changePopulation(newpopulation : Int)

Related

Cannot assign to a property: 'class' is a 'let' constant

I'm trying to update a property on a service that is defined as let subscriber: SubscriberContextProviding in the WatchlistViewModel by simply setting it directly like so:
subscriber.watchlist = watchlist
relevant subscriber definition:
final class Subscriber: SubscriberContextProviding {
var watchlist = [String]()
}
however I get an error saying: Cannot assign to property: 'subscriber' is a 'let' constant
. The subscriber service is declared as a let and is initialised in the client init.
here is the relevant protocol part & init.
protocol SubscriberContextProviding {
var watchlist: [String] { get set }
}
class WatchlistViewModel: NSObject {
let subscriber: SubscriberContextProviding
init(subscriber: SubscriberContextProviding){
self.subscriber = subscriber
super.init()
}
}
however If I change the protocol from the one above to
protocol SubscriberContextProviding {
func set(watchlist: [String])
}
and I simply define the function in the subscriber as
func set(watchlist: [String]){
self.watchlist = watchlist
}
and instead of setting the property directly now using the function like so
subscriber.set(watchlist: watchlist)
works no problem. Why the first approach doesn't work while the latter one does as the result is the same of both approaches?
The "issue" (though it's not an issue, really) is that you haven't restricted SubscriberContextProviding to being a class-bound protocol (with : AnyObject).
let subscriber: SubscriberContextProviding is declaring that your subscriber variable with contain an existential of any object whose type conforms to SubscriberContextProviding. Since that protocol isn't class-bound, it's possible that the concrete value you're dealing with is a value type (tuple, struct or enum), for which mutations are only allowed on mutable var variables. This existential is itself a value type, and abides the same rules of only allowing mutations on var variables.
Thus, you must either:
Declare SubscriberContextProviding as class-bound:
protocol SubscriberContextProviding: AnyObject { ... }
or
Keep your protocol as-is, but make your subscriber variable mutable, to account for the possibility that it contains a value type.
Add this to your protocol:
protocol SubscriberContextProviding: AnyObject{
var watchlist: [String] { get set }
}

Is it possible to write mutating function in swift class?

I am able to write mutating functions in structure but not in class.
struct Stack {
public private(set) var items = [Int]() // Empty items array
mutating func push(_ item: Int) {
items.append(item)
}
mutating func pop() -> Int? {
if !items.isEmpty {
return items.removeLast()
}
return nil
}
}
In swift, classes are reference type whereas structures and enumerations are value types. The properties of value types cannot be modified within its instance methods by default. In order to modify the properties of a value type, you have to use the mutating keyword in the instance method. With this keyword, your method can then have the ability to mutate the values of the properties and write it back to the original structure when the method implementation ends.
If you change the struct to a class, just delete the keyword mutating wherever it appears.
That's because classes are reference types, and structures are value types.
struct TestValue {
var a : Int = 42
mutating func change() { a = 1975 }
}
let val = TestValue()
val.a = 1710 // Forbidden because `val` is a `let` of a value type, so you can't mutate it
val.change() // Also forbidden for the same reason
class TestRef {
var a : Int = 42
func change() { a = 1975 }
}
let ref = TestRef()
ref.a = 1710 // Allowed because `ref` is a reference type, even if it's a `let`
ref.change() // Also allowed for the same reason
So on classes, you don't need to specify if a function is mutating or not, because, even when defined with let variables, you can modify instance...
That's why the mutating key word has no meaning on classes.

How to reference an attribute as a method default in a Swift class? [duplicate]

In a Swift class, I want to use a property as a default parameter value for a method of the same class.
Here is my code :
class animal {
var niceAnimal:Bool
var numberOfLegs:Int
init(numberOfLegs:Int,animalIsNice:Bool) {
self.numberOfLegs = numberOfLegs
self.niceAnimal = animalIsNice
}
func description(animalIsNice:Bool = niceAnimal,numberOfLegs:Int) {
// I'll write my code here
}
}
The problem is that I can't use my niceAnimal property as a default function value, because it triggers me a compile-time error :
'animal.Type' does not have a member named 'niceAnimal'
Am I doing something wrong ? Or is it impossible in Swift ? If that's impossible, do you know why ?
I don't think you're doing anything wrong.
The language specification only says that a default parameter should come before non-default parameters (p169), and that the default value is defined by an expression (p637).
It does not say what that expression is allowed to reference. It seems like it is not allowed to reference the instance on which you are calling the method, i.e., self, which seems like it would be necessary to reference self.niceAnimal.
As a workaround, you could define the default parameter as an optional with a default value of nil, and then set the actual value with an "if let" that references the member variable in the default case, like so:
class animal {
var niceAnimal: Bool
var numberOfLegs: Int
init(numberOfLegs: Int, animalIsNice: Bool) {
self.numberOfLegs = numberOfLegs
self.niceAnimal = animalIsNice
}
func description(numberOfLegs: Int, animalIsNice: Bool? = nil) {
if let animalIsNice = animalIsNice ?? self.niceAnimal {
// print
}
}
}
I think for now you can only use literals and type properties as default arguments.
The best option would be to overload the method, and you can implement the shorter version by calling the full one. I only used a struct here to omit the initializer.
struct Animal {
var niceAnimal: Bool
var numberOfLegs: Int
func description(#numberOfLegs: Int) {
description(niceAnimal, numberOfLegs: numberOfLegs)
}
func description(animalIsNice: Bool, numberOfLegs: Int) {
// do something
}
}

Specify a settable property/variable in a protocol

I would like my protocol to declare that there is a read/write property available. I have attempted it, but this does not work:
protocol EdibleThing {
var eaten: Bool { get set }
}
class Pickle: EdibleThing { var eaten = false }
class RusticGrapefruit: EdibleThing { var eaten = false }
class Jar {
let contents: [EdibleThing] = [Pickle(), RusticGrapefruit()]
var nextItem: EdibleThing {
return contents.last ?? Pickle() // Lazy pickle generation technology
}
func eat() {
let food = nextItem
food.eaten = true // (!) ERROR: Cannot assign to result of this expression
}
}
What am I doing wrong? I think I've declared that the protocol has a get/set var called eaten, so why can't I set it?
The protocol might be implemented by either classes and structs - that prevents you from changing the internal status of an instance of a class or struct implementing that protocol using an immutable variable.
To fix the problem you have to either declare the food variable as mutable:
func eat() {
var food = nextItem
food.eaten = true // (!) ERROR: Cannot assign to result of this expression
}
or declare the EdibleThing protocol to be implementable by classes only:
protocol EdibleThing : class {
var eaten: Bool { get set }
}
Note that this happens because food is a variable of EdibleThing type - the compiler doesn't know if the actual instance is a value or reference type, so it raises an error. If you make it a variable of a class type, like this:
let food: Pickle = nextItem as! Pickle
the compiler knows without any ambiguity that it's a reference type, and in that case it allows the assignment. But I guess that breaks your app logic... so consider it just as an example
You're mutating food.
Replace let food = nextItem with var food = nextItem
The problem is that you can't mutate a property on a value type defined by let.
Even though both of RusticGrapefruit and Pickle are class implementations (reference types), the protocol could be assigned to a value type like a struct. The compiler detects a potential problem and stops us.
Two solutions:
Change let to var (in my case, this would mean changing a lot of code that refers to objects of this type. Also, I like the semantic value and possible compiler optimizations from let)
Declare the protocol as only valid for classes: protocol EdibleThing: class { }

Swift: How to add a class method in 'String" extension

I want to add a class function into extension:
extension String {
class func test () {
}
}
I get the error: Class methods are only allowed within classes; use 'static' to declare a static method
Or how should i call " String.test()"
But for NSString
extension NSString {
class func aaa () {
}
}
no errors.
If i add static keyword:
extension String {
static func aaa () {
self.stringByAppendingString("Hello")
}
}
Got: Expression resolves to an unused function,
So how should i add a class function also want to use self. method.
EDIT: This works!
extension String {
static func aaa (path:String) -> String {
return path.stringByAppendingString("Hello")
}
}
but about #lan's answer:
mutating func bbb(path: String) {
self += "world"
}
When i type it appears like this:
String.bbb(&<#String#>)
String.bbb(&"nihao")
Cannot invoke 'bbb' with an argument list of type '(String)'
Class and static functions are not called on an instance of a class/struct, but on the class/struct itself, so you can't just append a string to a class.
Apple Documentation:
Within the body of a type method, the implicit self property refers to
the type itself, rather than an instance of that type.
You can, however, append a string to a variable instance of a String using the mutating keyword:
extension String {
mutating func aaa() {
self += "hello"
}
}
let foo = "a"
foo.aaa() // ERROR: Immutable value of type 'String' only has mutating members named 'aaa'
var bar = "b"
bar.aaa() // "bhello"
If you are trying to use a pointer to a string as a parameter, you can use the inout keyword to alter the inputed string:
extension String {
static func aaa(inout path: String) {
path += "Hello"
}
}
var foo = "someText"
String.aaa(&foo)
foo //someTextHello
While correct, it's somewhat atypical to see a mutating member added to a String extension as shown in Ian's answer. Strings (and value types in general) are meant to be immutable so the only way to use a mutating method is to declare instances var at the call site. Most of the time in your code you should be using let constants.
As such, it is much more common to extend structs to return new instances. So this is typical:
extension String {
func appending(_ string: String) -> String {
return self + string
}
}
and then at the call site:
let hello = "Hello, "
let helloWorld = hello.appending("World!")
You'll note of course that I'm not using static at all. That's because appending(_:) needs to use the current instance value of the String we're appending to, and class/static do not refer to instances and therefore do not have values.
"Within the body of a type method, the implicit self property refers to the type itself, rather than an instance of that type."
Thus when you extend a type by adding a type method you can only call other type methods through self. If you want to call an instance method you need to create an instance and call a method on that.