Swift – Metody

Properties u struktur, výčtů, … A jak jsou na tom metody? Stejně. Poměrně dost novinek a možností.

Metody jsou funkce, které jsou součástí tříd. Swift přináší možnost definovat metody pro struktury a výčty, což v Objective-C nebylo možné. Metody také mohou být součástí typu (class methods v Objective-C). A mají stejnou syntax jako funkce.

class Counter {
  var count = 0

  func inc() {
    self.count++
  }

  func incBy( amount: Int ) {
    self.count += amount
  }

  func reset() {
    self.count = 0
  }
}

var c = Counter()  // c.count == 0
c.inc()            // c.count == 1
c.incBy( 10 )      // c.count == 11
c.reset()          // c.count == 0
c.inc()            // c.count == 1

Lokální a externí názvy parametrů

Objective-C má jednu velkou výhodu – je to sice dost ukecaný jazyk, ale na druhou stranu dost self documenting. Pěkně se čte a hned víte co daná funkce dělá (pokud nejste prase). Ve Swiftu se dá dosáhnout něčeho podobného pomocí externích a lokálních názvů parametrů. Lokální název parametru se používá uvnitř metody. Externí název parametru se používá při volání metody. Mohou být stejné a mohou se i lišit. A rozdíly jsou také u prvního parametru. Naši třídu Counter  rozšíříme o další metodu.

func incBy( amount: Int, numberOfTimes: Int ) {
  self.count += amount * numberOfTimes
}

Volá se takto.

c.incBy( 10, numberOfTimes: 3 )

Co funkce dělá je zřejmé z názvu a z toho je i zřejmý první parametr. Má název amount  a tento název je pouze lokální. Nelze ho použít při volání metody. Následující příklad nepůjde přeložit.

c.incBy( amount: 10, numberOfTimes: 3 )

Pokud chci změnit defaultní chování, mám možnost přidat externí název i pro první parametr a to takto.

func incBy( by amount: Int, numberOfTimes: Int ) {
  self.count += amount * numberOfTimes
}

c.incBy( by: 10, numberOfTimes: 3 )  // prelozitelne
c.incBy( 10, numberOfTimes: 3 )      // neprelozitelne, chybi by

Jak vidíte, je to blbost, protože to tam je 2x. Jednou je By  v názvu metody a podruhé je to jako název prvního parametru. To jenom na vysvětlenou, že to lze, ale nedělejte to.

Od druhého parametru (včetně) je chování odlišné v tom, že námi zvolený název je použit jako externí i lokální název. Vynutit toto chování lze pomocí # .

func incBy( amount: Int, #numberOfTimes: Int ) {
  self.count += amount * numberOfTimes
}

Je to ekvivalent tohoto.

func incBy( amount: Int, numberOfTimes: Int ) {
  self.count += amount * numberOfTimes
}

Pokud tedy chceme rozdílný externí a lokální název, můžeme tak učinit takto.

func incBy( amount: Int, numberOfTimes times: Int ) {
  self.count += amount * times
}

c.incBy( 10, numberOfTimes: 3 )

numberOfTimes  je externí název (používá se při volání metody) a times  je interní název (používá se uvnitř metody). Kód se tak lépe čte, jeho funkce je zřejmá na první pohled a zároveň nejsme nuceni interně pracovat s dlouhým názvem parametru.

self

Uvnitř metody je možné se odkázat na instanci pomocí self . By default není nutné používat self  pro přístup k properties, k volání metod, apod. Ale nejsem toho zastáncem, lépe se to s self  čte.

Modifikace struktur, výčtů z instančních metod

Platí pro všechny value types. By default, z instančních metod není možné modifikovat property struktury. Níže uvedený příklad nelze přeložit.

struct Point {
  let moveStep: Double = 10.0

  var x: Double = 0
  var y: Double = 0

  func moveRight() {
    self.x += self.moveStep
  }
}

Pokud chceme modifikovat property, musíme metodu označit klíčovým slovem mutating . Toto už lze přeložit.

struct Point {
  let moveStep: Double = 10.0

  var x: Double = 0
  var y: Double = 0

  mutating func moveRight() {
    self.x += self.moveStep
  }
}

Metody označené klíčovým slovem mutating  mohou modifikovat nejenom property, ale i sebe, tj. je možné změnit self . Výše uvedený příklad změníme tak, že metoda moveRight()  bude modifikovat celou instanci.

struct Point {
  let moveStep: Double = 10.0

  var x: Double = 0
  var y: Double = 0

  mutating func moveRight() {
    self = Point( x: self.x + self.moveStep, y: self.y )
  }
}

A protože to jde u všech value types, můžeme to dělat i u výčtů.

enum Toggler {
  case On, Off

  mutating func toggle() {
    switch self {
      case On:
        self = Off
      case Off:
        self = On
    }
  }
}

var t = Toggler.On // On
t.toggle()         // On -> Off
t                  // Off

Type methods

Stejně jako properties, můžou být i metody definovány pro typ. Ale narozdíl od Objective-C je to možné i u struktur a výčtů. U tříd se používá klíčové slovo class  (před func ) a u struktur & výčtů to je klíčové slovo static .

class Greeting {
  class func greet() {
    println( "Hallo" )
  }
}

Greeting.greet()

U těchto metod self  neodkazuje na instanci, nemá na jakou, ale přímo na typ.