Comics: Face the problem

Opublikowano: 21.11.2023 - tagi: Swift Struktura Klasa Protokół Typ Rozszerzenie
Rozszerzenia w Swift służą do dodania funkcjonalności do już istniejącego typu. Może to być typ zarówno wbudowany w język, jak i stworzony przez programistę.
Rozszerzenie można dodać do klasy, struktury, protokołu, enumeracji i innych typów w Swift.
Żeby dodać nowe rozszerzenie, należy użyć słowa kluczowego extension.
Za pomocą rozszerzenie możesz dodać nową funkcjonalność, ale nie możesz nadpisać już istniejącej.
class Calculator {
func add(a: Int, b: Int) -> Int {
return a + b
}
}
extension Calculator {
func subtract(a: Int, b: Int) -> Int {
return a - b
}
}
var calculator = Calculator()
print(calculator.add(a: 7, b: 3)) // 10
print(calculator.subtract(a: 7, b: 3)) // 4
Możesz też dodać rozszerzenie do typu wbudowanego:
extension Int {
func square() -> Int {
return self * self
}
}
var a: Int = 7
print("a * a = \(a.square())") // 49
print("a = \(a)") // 7
Jeśli chcesz zmienić stan użyj słowa kluczowego mutating i nową wartość przypisz do self:
extension Int {
mutating func square() {
self = self * self
}
}
var a: Int = 7
a.square()
print("a = \(a)") // 49
a.square()
print("a = \(a)") // 2401
Za pomocą rozszerzenia nie możesz dodać stored property:
extension SomeType {
var property1: Int // Błąd!
}
ale możesz dodać computed property:
struct User {
var firstName: String = ""
var lastName: String = ""
}
extension User {
var fullName: String {
return firstName + " " + lastName
}
}
var user = User(firstName: "James", lastName: "Bond");
print(user.fullName) // James Bond
Opublikowano: 16.11.2023 - tagi: Swift Klasa Struktura Dostęp
Dzięki kontroli dostępu możesz określić dostępność klas, struktur, enumeracji, protokołach, właściwości, metod itp.
Swift udostępnia kilka poziomów dostępu.
Są to:
Poziom dostępu open pozwala na to, że każdy ma do tego dostęp: zarówno z poziomu modułu jak i z zewnątrz.
Dzięki temu możesz pozwolić sobie także na nadpisywanie.
Jest to najmniej restrykcyjnym poziomem dostępu w Swift.
Jeśli określisz coś jako public to znaczy, że informujesz, że każdy może mieć do tego dostęp.
Podobne do open, ale różnica między nimi jest taka, że masz do tego dostęp wszędzie, ale nie możesz sobie pozwolić na nadpisanie z poziomu innego modułu.
Na przykład:
class MyClass {
public name: String = ""
}
var obj = MyClass()
obj.name = "Public"
print(obj.name) // Public
private sprawia, że dostęp jest ograniczony do danego zakresu.
class User {
private firstName: String = ""
private lastName: String = ""
init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
func getFullName() -> String {
return "\(firstName) \(lastName)"
}
}
var user = User(firstName: "Jan", lastName: "Kowalski")
print(user.getFullName())
user.firstName = "Adam" // Błąd!
Ten poziom dostępu podobny jest do private. Różnica jest taka, że dostęp jest ograniczony w obrębie pliku.
class User {
fileprivate firstName: String = ""
fileprivate lastName: String = ""
init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
func getFullName() -> String {
return "\(firstName) \(lastName)"
}
}
var user = User(firstName: "Jan", lastName: "Kowalski")
print(user.getFullName()) // Jan Kowalski
user.firstName = "Adam"
print(user.getFullName()) // Adam Kowalski
Jeśli spróbujesz się dostać do firstName lub lastName w innym pliku, zostanie zgłoszony błąd.
Inny przykład:
fileprivate class MyClass {
name: String = ""
}
Tak oznaczoną klasę: MyClass możesz używać tylko w pliku, w którym jest stworzona.
internal to domyślny poziom dostępu w Swift.
Czyli zapis:
class MyClass {
...
}
jest równoznaczny z:
internal class MyClass {
...
}
A co to robi? W taki sposób informujemy, że kod jest dostępny w obrębie modułu, ale na zewnątrz już nie.
W Swift protokoły działają w podobny sposób, jak interfejsy w innych językach programowania.
Za pomocą protokołu możesz rozszerzyć swoją klasę, strukturę lub enumerację o nowe właściwości i metody.
W przypadku klasy jest to przydatne, ponieważ klasę w Swift można dziedziczyć, tylko po jednej klasie.
Jeśli chodzi o struktury, tutaj jest większy problem. Struktury nie można rozszerzyć poprzez dziedziczenie. W tym przypadku ten problem rozwiązują protokoły.
Protokół stworzysz za pomocą słowa kluczowego protocol:
protocol Fly {
var maximumSpeed: Int { get }
func fly()
}
Ponieważ protokół jest typem, jego nazwa musi zaczynać się od dużej litery.
W protokole umieszcza tylko się definicje właściwości i metod.
Składnia deklaracji właściwości wygląda tak:
var someProperty: Type { get set }
get i set służą do poinformowania, że dana zmienna jest tylko do: odczytu (get), zapisu (set) lub zarówno do odczytu jak i zapisu (get set).
Jeśli oznaczysz właściwość jako get i set:
protocol Fly {
var maximumSpeed: Int { get set }
}
I zastosujesz ten protokół, w taki sposób, że ta właściwość będzie computed property, zostanie zgłoszony błąd:
struct Bird: Fly {
var energy: Int = 100
var maximumSpeed: Int { // Błąd!
return energy + 50
}
}
Jest tak dlatego, bo maximumSpeed zostało oznaczone jako set, a do computed property nie można przypisać wartości.
Zastosowanie protokołu wygląda tak samo, jak dziedziczenie:
class Bird: Fly {
var maximumSpeed: Int = 50
func fly() {
print("Bird is flying with maximum speed: \(maximumSpeed)")
}
}
class Plane: Fly {
var maximumSpeed: Int = 1000
func fly() {
print("Plane is flying with maximum speed: \(maximumSpeed)")
}
}
var bird = Bird()
bird.fly() // Bird is flying with maximum speed: 50
var plane = Plane()
plane.fly() // Plane is flying with maximum speed: 1000
Swift pozwalana na zastosowanie wielu protokołów:
protocol Fly {
func fly()
}
protocol Eat {
func eat()
}
class Bird: Fly, Eat {
func fly() {
print("Bird is flying")
}
func eat() {
print("Bird is eating")
}
}
var bird = Bird()
bird.fly() // Bird is flying
bird.eat() // Bird is eating
Możesz także dziedziczyć po... innym protokole:
protocol A {
func doA()
}
protocol SomeProtocol: A {
func doSomething()
}
class MyClass: SomeProtocol {
func doA() {
print("Call doA")
}
func doSomething() {
print("Call doSomething")
}
}
Istnieje możliwość dziedziczenia po wielu protokołach:
protocol A {
func doA()
}
protocol SomeProtocol: A {
func doSomething()
}
class MyClass: SomeProtocol {
func doA() {
print("Call doA")
}
func doSomething() {
print("Call doSomething")
}
}
Jeśli zamierzasz zastosować protokół do struktury, a ten posiada metodę, której implementacja w strukturze będzie działać na zasadzie zmiany stanu struktury, musisz użyć dla tej metody słowa kluczowego mutating.
Przykład:
protocol Counter {
var counter: Int { get }
mutating func increment()
mutating func decrement()
}
struct MyCounter: Counter {
var counter = 0
mutating func increment() {
counter += 1
}
mutating func decrement() {
counter -= 1
}
}
var myCounter = MyCounter()
myCounter.increment()
myCounter.increment()
myCounter.decrement()
print("Counter: \(myCounter.counter)")