این صفحه به‌وسیله ‏Cloud Translation API‏ ترجمه شده است.
Switch to English

یک تور سوئیفت

با تغییراتی از نسخه اصلی A Swift Tour در Swift.org اقتباس شده است. محتوای اصلی توسط اپل تألیف شده است. تحت مجوز Creative Commons Attribution 4.0 International (CC BY 4.0) دارای مجوز است .
مشاهده در TensorFlow.org در Google Colab اجرا کنید مشاهده منبع در GitHub

سنت پیشنهاد می کند که اولین برنامه به زبان جدید باید کلمات "سلام ، جهان!" را چاپ کند. روی صفحه در Swift ، این کار را می توان در یک خط انجام داد:

print("Hello, world!")
Hello, world!

اگر کد را به زبان C یا Objective-C نوشته اید ، این نحو برای شما آشنا به نظر می رسد - در سوئیفت ، این خط کد یک برنامه کامل است. برای کارایی مانند ورودی / خروجی یا کنترل رشته نیازی به وارد کردن کتابخانه جداگانه نیست. کدی که در دامنه جهانی نوشته شده است به عنوان نقطه ورود برنامه استفاده می شود ، بنابراین شما به یک تابع main() نیازی ندارید. همچنین در پایان هر عبارت نیازی به نوشتن نقطه ویرگول نیست.

این تور با نشان دادن نحوه انجام انواع کارهای برنامه نویسی ، به شما اطلاعات کافی برای شروع نوشتن کد در Swift را می دهد. اگر چیزی را نمی فهمید ، نگران نباشید - همه چیزهایی که در این تور معرفی شده است ، در ادامه این کتاب با جزئیات توضیح داده شده است.

مقادیر ساده

برای ایجاد یک ثابت و var برای ایجاد یک متغیر از let استفاده کنید. مقدار یک ثابت لازم نیست در زمان کامپایل مشخص شود ، اما باید دقیقاً یک بار به آن یک مقدار اختصاص دهید. این بدان معناست که شما می توانید از ثابت ها برای نامگذاری مقداری استفاده کنید که یک بار تعیین کنید اما در بسیاری از مکان ها استفاده کنید.

var myVariable = 42
myVariable = 50
let myConstant = 42

یک ثابت یا متغیر باید همان نوع مقداری باشد که می خواهید به آن اختصاص دهید. با این وجود ، لازم نیست همیشه نوع آن را صریح بنویسید. ارائه یک مقدار هنگام ایجاد یک ثابت یا متغیر به شما اجازه می دهد تا کامپایلر نوع خود را استنباط کند. در مثال بالا ، کامپایلر استنباط می کند که myVariable یک عدد صحیح است زیرا مقدار اولیه آن یک عدد صحیح است.

اگر مقدار اولیه اطلاعات کافی را فراهم نمی کند (یا اگر مقدار اولیه وجود ندارد) ، نوع را با نوشتن آن بعد از متغیر ، با دو نقطه از هم جدا کنید ، مشخص کنید. توجه: استفاده از Double به جای Float برای اعداد شناور دقت بیشتری را به شما می دهد و نوع پیش فرض شناور در Swift است.

let implicitInteger = 70
let implicitDouble = 70.0
let explicitDouble: Double = 70
// Experiment:
// Create a constant with an explicit type of `Float` and a value of 4.

مقادیر هرگز به طور ضمنی به نوع دیگری تبدیل نمی شوند. اگر می خواهید مقداری را به نوع دیگری تبدیل کنید ، به صراحت نمونه ای از نوع مورد نظر را ایجاد کنید.

let label = "The width is "
let width = 94
label + String(width)
"The width is 94"

// Experiment:
// Try removing the conversion to `String` from the last line. What error do you get?

حتی یک روش ساده تر برای قرار دادن مقادیر در رشته ها وجود دارد: مقدار را درون پرانتز بنویسید و قبل از پرانتز یک بک اسلش (") بنویسید. مثلا:

let apples = 3
"I have \(apples) apples."
"I have 3 apples."

let oranges = 5
"I have \(apples + oranges) pieces of fruit."
"I have 8 pieces of fruit."

// Experiment:
// Use `\()` to include a floating-point calculation in a string and to include someone's name in a
// greeting.

از سه علامت نقل قول دوگانه ( """ ) برای رشته هایی که چندین خط را اشغال می کنند استفاده کنید. تورفتگی در ابتدای هر خط نقل قول شده حذف می شود ، به شرط آنکه با تورفتگی علامت های نقل قول بسته شده مطابقت داشته باشد. به عنوان مثال:

let quotation = """
    Even though there's whitespace to the left,
    the actual lines aren't indented.
        Except for this line.
    Double quotes (") can appear without being escaped.

    I still have \(apples + oranges) pieces of fruit.
    """
print(quotation)
Even though there's whitespace to the left,
the actual lines aren't indented.
    Except for this line.
Double quotes (") can appear without being escaped.

I still have 8 pieces of fruit.

با استفاده از براکت ( [] ) آرایه ها و دیکشنری ها را ایجاد [] و با نوشتن نمایه یا کلید در پرانتز به عناصر آنها دسترسی پیدا کنید. ویرگول بعد از آخرین عنصر مجاز است.

var shoppingList = ["catfish", "water", "tulips", "blue paint"]
shoppingList[1] = "bottle of water"

var occupations = [
    "Malcolm": "Captain",
    "Kaylee": "Mechanic",
]
occupations["Jayne"] = "Public Relations"
occupations
▿ 3 elements
  ▿ 0 : 2 elements

    - key : "Malcolm"
    - value : "Captain"
  ▿ 1 : 2 elements
    - key : "Kaylee"
    - value : "Mechanic"
  ▿ 2 : 2 elements
    - key : "Jayne"
    - value : "Public Relations"

با افزودن عناصر ، آرایه ها به طور خودکار رشد می کنند.

shoppingList.append("blue paint")
shoppingList
▿ 5 elements

  - 0 : "catfish"
  - 1 : "bottle of water"
  - 2 : "tulips"
  - 3 : "blue paint"
  - 4 : "blue paint"

برای ایجاد یک آرایه یا فرهنگ لغت خالی ، از نحو مقداردهنده اولیه استفاده کنید.

let emptyArray = [String]()
let emptyDictionary = [String: Float]()

اگر می توان اطلاعات نوع را استنباط کرد ، می توانید یک آرایه خالی به عنوان [] و یک فرهنگ لغت خالی به عنوان [:] بنویسید - به عنوان مثال ، وقتی مقدار جدیدی را برای یک متغیر تعیین می کنید یا آرگومان را به یک تابع منتقل می کنید.

shoppingList = []
occupations = [:]

کنترل جریان

استفاده از if و switch به شرطی، و استفاده for - in ، for ، while ، و repeat - while به حلقه. پرانتزهای اطراف متغیر شرط یا حلقه اختیاری هستند. بریس در اطراف بدن لازم است.

let individualScores = [75, 43, 103, 87, 12]
var teamScore = 0
for score in individualScores {
    if score > 50 {
        teamScore += 3
    } else {
        teamScore += 1
    }
}
teamScore
11

در یک عبارت if ، شرط باید یک عبارت بولی باشد - این بدان معنی است که کدی مانند if score { ... } یک خطا است ، نه یک مقایسه ضمنی با صفر.

برای کار با مقادیری که ممکن است از دست نرود ، می توانید از if و let با هم استفاده کنید. این مقادیر به صورت اختیاری نشان داده می شوند. یک مقدار اختیاری یا حاوی مقداری است یا حاوی nil تا نشان دهد که یک مقدار از دست رفته است. علامت سوال ( ? ) را بعد از نوع مقدار بنویسید تا مقدار به عنوان اختیاری علامت گذاری شود.

var optionalString: String? = "Hello"
optionalString == nil
false

var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
    greeting = "Hello, \(name)"
}
greeting
"Hello, John Appleseed"

// Experiment:
// Change `optionalName` to `nil`. What greeting do you get?
// Add an `else` clause that sets a different greeting if `optionalName` is `nil`.

اگر مقدار اختیاری nil ، شرط false و کد موجود در پرانتزها حذف می شود. در غیر این صورت ، مقدار اختیاری باز نشده و به ثابت پس از let اختصاص داده می شود ، که باعث می شود مقدار باز نشده در داخل کد کد موجود باشد.

روش دیگر برای رسیدگی به مقادیر اختیاری ارائه مقدار پیش فرض با استفاده از ?? اپراتور. اگر مقدار اختیاری از بین رفته باشد ، به جای آن از مقدار پیش فرض استفاده می شود.

let nickName: String? = nil
let fullName: String = "John Appleseed"
"Hi \(nickName ?? fullName)"
"Hi John Appleseed"

سوئیچ ها از هر نوع داده و طیف گسترده ای از عملیات مقایسه پشتیبانی می کنند - آنها فقط به اعداد صحیح و آزمایش های برابری محدود نمی شوند.

let vegetable = "red pepper"
switch vegetable {
case "celery":
    print("Add some raisins and make ants on a log.")
case "cucumber", "watercress":
    print("That would make a good tea sandwich.")
case let x where x.hasSuffix("pepper"):
    print("Is it a spicy \(x)?")
default:
    print("Everything tastes good in soup.")
}
Is it a spicy red pepper?

// Experiment:
// Try removing the default case. What error do you get?

توجه کنید که چگونه می توان از let در یک الگو استفاده کرد تا مقداری که با آن قسمت از یک الگو مطابقت دارد را به یک ثابت اختصاص دهیم.

پس از اجرای کدی در داخل کیس سوئیچ که مطابقت دارد ، برنامه از عبارت سوییچ خارج می شود. اعدام به مورد بعدی ادامه نمی یابد ، بنابراین نیازی به قطع صریح سوئیچ در انتهای کد هر پرونده نیست.

شما با استفاده از for - in به تکرار بیش از اقلام در یک فرهنگ لغت با ارائه یک جفت نام برای استفاده برای هر جفت کلید-مقدار. فرهنگ لغت ها مجموعه ای غیر منظم هستند ، بنابراین کلیدها و مقادیر آنها به ترتیب دلخواه تکرار می شوند.

let interestingNumbers = [
    "Prime": [2, 3, 5, 7, 11, 13],
    "Fibonacci": [1, 1, 2, 3, 5, 8],
    "Square": [1, 4, 9, 16, 25],
]
var largest = 0
for (kind, numbers) in interestingNumbers {
    for number in numbers {
        if number > largest {
            largest = number
        }
    }
}
largest
25

// Experiment:
// Add another variable to keep track of which kind of number was the largest, as well as what that
// largest number was.

استفاده از while به تکرار یک بلوک از کد تا زمانی که یک تغییر وضعیت. شرایط یک حلقه می تواند در انتها باشد ، اطمینان حاصل شود که حلقه حداقل یک بار اجرا می شود.

var n = 2
while n < 100 {
    n = n * 2
}

n
128

var m = 2
repeat {
    m = m * 2
} while m < 100

m
128

می توانید یک فهرست را در یک حلقه نگه دارید - یا با استفاده از ..< برای ایجاد دامنه ای از نمایه ها یا با نوشتن یک مقداردهی اولیه ، شرط و افزایش. این دو حلقه همان کار را انجام می دهند:

var total = 0
for i in 0..<4 {
    total += i
}

total
6

برای ایجاد محدوده ای که مقدار بالایی خود را حذف کند از ..< استفاده کنید و برای ایجاد محدوده ای که شامل هر دو مقدار باشد از ... استفاده کنید.

عملکردها و تعطیلات

برای اعلام عملکرد ، از func استفاده کنید. یک تابع را با دنبال کردن نام آن با لیستی از آرگومان ها در پرانتز فراخوانی کنید. از -> برای جدا کردن نام و پارامترهای نوع از نوع بازگشتی تابع استفاده کنید.

func greet(name: String, day: String) -> String {
    return "Hello \(name), today is \(day)."
}
greet(name: "Bob", day: "Tuesday")
"Hello Bob, today is Tuesday."

// Experiment:
// Remove the `day` parameter. Add a parameter to include today’s lunch special in the greeting.

به طور پیش فرض ، توابع از نام پارامترهای خود به عنوان برچسب آرگومان های خود استفاده می کنند. قبل از نام پارامتر ، یک برچسب آرگومان سفارشی بنویسید یا برای استفاده از برچسب آرگومان ، _ بنویسید.

func greet(_ person: String, on day: String) -> String {
    return "Hello \(person), today is \(day)."
}
greet("John", on: "Wednesday")
"Hello John, today is Wednesday."

برای ایجاد مقدار مرکب از tuple استفاده کنید - به عنوان مثال ، برای بازگرداندن چندین مقدار از یک تابع. به عناصر یک تاپل می توان با نام یا تعداد مراجعه کرد.

func calculateStatistics(scores: [Int]) -> (min: Int, max: Int, sum: Int) {
    var min = scores[0]
    var max = scores[0]
    var sum = 0

    for score in scores {
        if score > max {
            max = score
        } else if score < min {
            min = score
        }
        sum += score
    }

    return (min, max, sum)
}
let statistics = calculateStatistics(scores: [5, 3, 100, 3, 9])
print(statistics.sum)
print(statistics.2)
120
120

توابع را می توان تو در تو قرار داد. توابع تو در تو به متغیرهایی که در عملکرد خارجی اعلام شده اند دسترسی دارند. برای سازماندهی کد در تابعی طولانی یا پیچیده می توانید از توابع تو در تو استفاده کنید.

func returnFifteen() -> Int {
    var y = 10
    func add() {
        y += 5
    }
    add()
    return y
}
returnFifteen()
15

توابع از نوع درجه یک هستند. این بدان معنی است که یک تابع می تواند تابع دیگری را به عنوان مقدار خود برگرداند.

func makeIncrementer() -> ((Int) -> Int) {
    func addOne(number: Int) -> Int {
        return 1 + number
    }
    return addOne
}
var increment = makeIncrementer()
increment(7)
8

یک تابع می تواند تابع دیگری را به عنوان یکی از آرگومان های خود در نظر بگیرد.

func hasAnyMatches(list: [Int], condition: (Int) -> Bool) -> Bool {
    for item in list {
        if condition(item) {
            return true
        }
    }
    return false
}
func lessThanTen(number: Int) -> Bool {
    return number < 10
}
var numbers = [20, 19, 7, 12]
hasAnyMatches(list: numbers, condition: lessThanTen)
true

توابع در واقع حالت خاصی از بسته شدن است: بلوک های کد که بعداً می توان فراخوانی کرد. کد موجود در یک بسته می تواند به مواردی مانند متغیرها و توابع دسترسی داشته باشد که در محدوده محل ایجاد بسته وجود دارد ، حتی اگر هنگام اجرا بسته در یک محدوده دیگر باشد - نمونه ای از این موارد را با توابع تو در تو مشاهده کردید. می توانید بسته بندی بدون نام را با کد اطراف با مهاربند ( {} ) بنویسید. برای جدا کردن استدلال ها و بازگشت نوع از بدن ، از in استفاده کنید.

numbers.map({ (number: Int) -> Int in
    let result = 3 * number
    return result
})
▿ 4 elements

  - 0 : 60
  - 1 : 57
  - 2 : 21
  - 3 : 36

// Experiment:
// Rewrite the closure to return zero for all odd numbers.

شما برای نوشتن مختصر بسته گزینه های مختلفی دارید. هنگامی که نوع بسته شدن از قبل شناخته شده است ، مانند پاسخ به تماس برای یک نماینده ، می توانید نوع پارامترهای آن ، نوع بازگشت آن یا هر دو را حذف کنید. بسته شدن بیانیه های منفرد به طور ضمنی مقدار تنها عبارت آنها را برمی گرداند.

let mappedNumbers = numbers.map({ number in 3 * number })
print(mappedNumbers)
[60, 57, 21, 36]

می توانید به جای نام ، به پارامترها از طریق تعداد مراجعه کنید - این رویکرد به ویژه در بسته شدن های بسیار کوتاه بسیار مفید است. یک بسته شدن به عنوان آخرین آرگومان به یک تابع می تواند بلافاصله بعد از پرانتز ظاهر شود. وقتی بسته شدن تنها آرگومان یک تابع باشد ، می توانید پرانتز را به طور کامل حذف کنید.

let sortedNumbers = numbers.sorted { $0 > $1 }
print(sortedNumbers)
[20, 19, 12, 7]

اشیا and و کلاسها

برای ایجاد class از class و به دنبال آن نام کلاس استفاده کنید. اعلامیه ویژگی در یک کلاس به همان صورت اعلامیه ثابت یا متغیر نوشته می شود ، با این تفاوت که در متن یک کلاس است. به همین ترتیب ، اعلامیه های روش و عملکرد نیز به همین ترتیب نوشته می شوند.

class Shape {
    var numberOfSides = 0
    func simpleDescription() -> String {
        return "A shape with \(numberOfSides) sides."
    }
}
// Experiment:
// Add a constant property with `let`, and add another method that takes an argument.

با قرار دادن پرانتز بعد از نام کلاس ، نمونه ای از کلاس را ایجاد کنید. برای دستیابی به خصوصیات و روشهای نمونه از نحو استفاده کنید.

var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()

این نسخه از کلاس Shape مورد مهمی را از دست داده است: یک راه انداز اولیه برای راه اندازی کلاس هنگام ایجاد نمونه. برای ایجاد یکی از init استفاده کنید.

class NamedShape {
    var numberOfSides: Int = 0
    var name: String

    init(name: String) {
        self.name = name
    }

    func simpleDescription() -> String {
        return "A shape with \(numberOfSides) sides."
    }
}

توجه کنید که چگونه از self برای تشخیص ویژگی name از آرگومان name به مقداردهنده اولیه استفاده می شود. هنگام ایجاد نمونه ای از کلاس ، آرگومان ها به مقداردهنده اولیه مانند یک فراخوانی تابع منتقل می شوند. هر خاصیت به مقداری احتیاج دارد - چه در اعلامیه (مانند numberOfSides ) و چه در مقداردهی اولیه (مانند name ).

در صورت نیاز به پاک سازی قبل از جدا شدن محل جسم ، از deinit برای ایجاد deinit استفاده کنید.

کلاسهای فرعی شامل نام فوق کلاس آنها بعد از نام کلاسشان است که با دو نقطه از هم جدا شده اند. کلاسها هیچ کلاس ریشه استانداردی را به زیر کلاس نیاز ندارند ، بنابراین در صورت لزوم می توانید یک ابر کلاس را اضافه یا حذف کنید.

متدهای موجود در زیر کلاس که اجرای superclass را override گیرند ، با override مشخص می شوند - جایگزینی یک روش به طور تصادفی ، بدون override ، توسط کامپایلر به عنوان یک خطا تشخیص داده می شود. کامپایلر همچنین روشهایی را با override تشخیص می دهد که در واقع هیچ روشی را در super کلاس غلبه نمی کند.

class Square: NamedShape {
    var sideLength: Double

    init(sideLength: Double, name: String) {
        self.sideLength = sideLength
        super.init(name: name)
        numberOfSides = 4
    }

    func area() -> Double {
        return sideLength * sideLength
    }

    override func simpleDescription() -> String {
        return "A square with sides of length \(sideLength)."
    }
}
let test = Square(sideLength: 5.2, name: "my test square")
test.area()
test.simpleDescription()
"A square with sides of length 5.2."

// Experiment:
// - Make another subclass of `NamedShape` called `Circle` that takes a radius and a name as
//   arguments to its initializer.
// - Implement an `area()` and a `simpleDescription()` method on the `Circle` class.

علاوه بر ویژگی های ساده ای که ذخیره می شوند ، ویژگی ها می توانند یک گیرنده و یک تنظیم کننده داشته باشند.

class EquilateralTriangle: NamedShape {
    var sideLength: Double = 0.0

    init(sideLength: Double, name: String) {
        self.sideLength = sideLength
        super.init(name: name)
        numberOfSides = 3
    }

    var perimeter: Double {
        get {
            return 3.0 * sideLength
        }
        set {
            sideLength = newValue / 3.0
        }
    }

    override func simpleDescription() -> String {
        return "An equilateral triangle with sides of length \(sideLength)."
    }
}
var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
print(triangle.perimeter)
triangle.perimeter = 9.9
print(triangle.sideLength)
9.3
3.3000000000000003

در تنظیم کننده برای perimeter ، مقدار جدید دارای نام ضمنی newValue . پس از set می توانید نام set را در پرانتز وارد set .

توجه داشته باشید که مقداردهنده اولیه کلاس EquilateralTriangle دارای سه مرحله مختلف است:

  1. تنظیم مقدار خصوصیاتی که زیر کلاس اعلام می کند.

  2. تماس با راه انداز کننده فوق کلاس.

  3. تغییر مقدار خصوصیات تعریف شده توسط ابر کلاس. هر کار راه اندازی اضافی که از روش ها ، گیرنده ها یا تنظیم کننده ها استفاده می کند نیز در این مرحله انجام می شود.

اگر شما لازم نیست برای محاسبه اموال اما هنوز هم نیاز به ارائه کد است که قبل و بعد از تنظیم یک مقدار جدید، استفاده اجرا willSet و didSet . کدی که ارائه می دهید هر زمان که مقدار خارج از مقدار اولیه تغییر کند ، اجرا می شود. به عنوان مثال ، کلاس زیر تضمین می کند که طول ضلع مثلث آن همیشه برابر با ضلع مربع آن است.

class TriangleAndSquare {
    var triangle: EquilateralTriangle {
        willSet {
            square.sideLength = newValue.sideLength
        }
    }
    var square: Square {
        willSet {
            triangle.sideLength = newValue.sideLength
        }
    }
    init(size: Double, name: String) {
        square = Square(sideLength: size, name: name)
        triangle = EquilateralTriangle(sideLength: size, name: name)
    }
}
var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
print(triangleAndSquare.square.sideLength)
print(triangleAndSquare.triangle.sideLength)
triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
print(triangleAndSquare.triangle.sideLength)
10.0
10.0
50.0

هنگام کار با مقادیر اختیاری ، می توانید بنویسید ? قبل از عملیاتی مانند روش ها ، خصوصیات و اشتراک. اگر مقدار قبل از ? است nil ، همه چیز پس از ? نادیده گرفته می شود و مقدار کل عبارت nil . در غیر این صورت ، مقدار اختیاری باز نشده است ، و همه چیز بعد از ? بر روی مقدار باز نشده عمل می کند. در هر دو حالت ، مقدار کل عبارت یک مقدار اختیاری است.

let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
optionalSquare?.sideLength
▿ Optional<Double>

  - some : 2.5

شمارش و ساختارها

برای ایجاد شمارش از enum استفاده کنید. مانند کلاس ها و همه انواع دیگر نامگذاری شده ، شمارش می تواند روش های مرتبط با آنها را داشته باشد.

enum Rank: Int {
    case ace = 1
    case two, three, four, five, six, seven, eight, nine, ten
    case jack, queen, king

    func simpleDescription() -> String {
        switch self {
        case .ace:
            return "ace"
        case .jack:
            return "jack"
        case .queen:
            return "queen"
        case .king:
            return "king"
        default:
            return String(self.rawValue)
        }
    }
}
let ace = Rank.ace
print(ace)
let aceRawValue = ace.rawValue
print(aceRawValue)
ace
1

// Experiment:
// Write a function that compares two `Rank` values by comparing their raw values.

به طور پیش فرض ، سوئیفت مقادیر خام را از صفر شروع می کند و هر بار یک برابر می شود ، اما می توانید با تعیین صریح مقادیر ، این رفتار را تغییر دهید. در مثال بالا ، صراحتاً به Ace مقدار خام 1 و بقیه مقادیر خام به ترتیب تعیین می شوند. همچنین می توانید از رشته ها یا اعداد شناور به عنوان نوع خام شمارش استفاده کنید. از ویژگی rawValue برای دستیابی به مقدار خام یک پرونده شمارش استفاده کنید.

برای تهیه نمونه ای از شمارش از یک مقدار خام ، از مقداردهنده اولیه init?(rawValue:) استفاده کنید. یا حالت شمارش مطابق با مقدار خام را برمی گرداند یا اگر Rank منطبقی وجود نداشته باشد ، nil است.

if let convertedRank = Rank(rawValue: 3) {
    let threeDescription = convertedRank.simpleDescription()
}

مقادیر حروف بزرگ شماری مقادیر واقعی هستند ، نه فقط روش دیگری برای نوشتن مقادیر خام آنها. در حقیقت ، در مواردی که مقدار خام معنی دار وجود ندارد ، نیازی به ارائه آن نیست.

enum Suit {
    case spades, hearts, diamonds, clubs

    func simpleDescription() -> String {
        switch self {
        case .spades:
            return "spades"
        case .hearts:
            return "hearts"
        case .diamonds:
            return "diamonds"
        case .clubs:
            return "clubs"
        }
    }
}
let hearts = Suit.hearts
let heartsDescription = hearts.simpleDescription()
// Experiment:
// Add a `color()` method to `Suit` that returns "black" for spades and clubs, and returns "red" for
// hearts and diamonds.

توجه داشته باشید که دو راه که Hearts مورد شمارش است را به بالا ارجاع: هنگامی که انتساب یک مقدار به hearts ثابت، مورد شمارش Suit.Hearts به با نام کامل خود اشاره به این دلیل که ثابت می کند یک نوع صریح مشخص نشده است. در داخل سوئیچ ، به حالت شمارش با فرم مختصر اشاره می شود. .Hearts زیرا ارزش self از قبل شناخته شده است. هر زمان که نوع مقدار مشخص باشد می توانید از فرم کوتاه شده استفاده کنید.

اگر یک شمارش مقادیر خام داشته باشد ، این مقادیر به عنوان بخشی از اظهارنامه تعیین می شوند ، به این معنی که هر نمونه از موارد خاص شمارش همیشه دارای ارزش خام یکسان است. انتخاب دیگر برای موارد شمارش ، داشتن مقادیر مرتبط با پرونده است - این مقادیر وقتی نمونه را تعیین می کنید ، تعیین می شوند و می توانند برای هر نمونه از موارد شمارش متفاوت باشند. می توانید مقادیر مرتبط را مانند رفتارهای ذخیره شده نمونه مورد برشماری تصور کنید.

به عنوان مثال ، مورد درخواست طلوع و غروب خورشید را از سرور در نظر بگیرید. سرور یا با اطلاعات درخواستی پاسخ می دهد ، یا با شرح آنچه اشتباه رخ داده است پاسخ می دهد.

enum ServerResponse {
    case result(String, String)
    case failure(String)
}

let success = ServerResponse.result("6:00 am", "8:09 pm")
let failure = ServerResponse.failure("Out of cheese.")

switch success {
case let .result(sunrise, sunset):
    print("Sunrise is at \(sunrise) and sunset is at \(sunset).")
case let .failure(message):
    print("Failure...  \(message)")
}
Sunrise is at 6:00 am and sunset is at 8:09 pm.

// Experiment:
// Add a third case to `ServerResponse` and to the switch.

توجه داشته باشید که چگونه زمان طلوع و غروب خورشید از مقدار ServerResponse به عنوان بخشی از تطبیق مقدار با موارد سوئیچ استخراج می شود.

برای ایجاد struct از struct استفاده کنید. ساختارها از بسیاری از رفتارهای مشابه کلاس ها ، از جمله روش ها و آغازگرها پشتیبانی می کنند. یکی از مهمترین تفاوتهای بین ساختارها و کلاسها این است که ساختارها همیشه هنگام انتقال در کد شما کپی می شوند ، اما کلاسها با مرجع منتقل می شوند.

struct Card {
    var rank: Rank
    var suit: Suit
    func simpleDescription() -> String {
        return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
    }
}
let threeOfSpades = Card(rank: .three, suit: .spades)
let threeOfSpadesDescription = threeOfSpades.simpleDescription()
// Experiment:
// Write a function that returns an array containing a full deck of cards, with one card of each
// combination of rank and suit.

پروتکل ها و برنامه های افزودنی

استفاده از protocol به اعلام یک پروتکل.

protocol ExampleProtocol {
    var simpleDescription: String { get }
    mutating func adjust()
}

کلاس ها ، شمارش ها و آمارها همه می توانند پروتکل ها را اتخاذ کنند.

class SimpleClass: ExampleProtocol {
    var simpleDescription: String = "A very simple class."
    var anotherProperty: Int = 69105
    func adjust() {
        simpleDescription += "  Now 100% adjusted."
    }
}
var a = SimpleClass()
a.adjust()
let aDescription = a.simpleDescription

struct SimpleStructure: ExampleProtocol {
    var simpleDescription: String = "A simple structure"
    mutating func adjust() {
        simpleDescription += " (adjusted)"
    }
}
var b = SimpleStructure()
b.adjust()
b.simpleDescription
"A simple structure (adjusted)"

// Experiment:
// Add another requirement to `ExampleProtocol`.
// What changes do you need to make to `SimpleClass` and `SimpleStructure` so that they still
// conform to the protocol?

به استفاده از کلمه کلیدی mutating در اعلامیه SimpleStructure تا روشی را اصلاح کند که ساختار را اصلاح کند. اعلامیه SimpleClass نیازی به هیچ یک از روشهای خود ندارد که به عنوان جهش مشخص شود زیرا متدهای موجود در یک کلاس همیشه می توانند کلاس را اصلاح کنند.

از extension برای افزودن قابلیت به یک نوع موجود مانند روشهای جدید و خصوصیات محاسبه شده استفاده کنید. برای افزودن انطباق پروتکل به نوعی که در جاهای دیگر اعلام شده یا حتی به نوعی که از کتابخانه یا چارچوب وارد کرده اید ، می توانید از پسوند استفاده کنید.

extension Int: ExampleProtocol {
    public var simpleDescription: String {
        return "The number \(self)"
    }
    public mutating func adjust() {
        self += 42
    }
}
7.simpleDescription
"The number 7"

// Experiment:
// Write an extension for the `Double` type that adds an `absoluteValue` property.

می توانید مانند هر نوع نام دیگر از نام پروتکل استفاده کنید - به عنوان مثال ، برای ایجاد مجموعه ای از اشیا types که انواع مختلفی دارند اما همه با یک پروتکل واحد مطابقت دارند. وقتی با مقادیری کار می کنید که نوع آنها از نوع پروتکل باشد ، روشهای خارج از تعریف پروتکل در دسترس نیستند.

let protocolValue: ExampleProtocol = a
protocolValue.simpleDescription
"A very simple class.  Now 100% adjusted."

// Uncomment to see the error.
// protocolValue.anotherProperty

حتی اگر متغیر protocolValue دارای یک نوع زمان اجرا از SimpleClass ، کامپایلر رفتار آن را به عنوان نوع داده ExampleProtocol . این بدان معنی است که شما نمی توانید به طور تصادفی به متدها یا خصوصیاتی که کلاس علاوه بر انطباق پروتکل خود پیاده سازی می کند ، دسترسی پیدا کنید.

مدیریت خطا

شما با استفاده از هر نوع پروتکل Error خطاها را نشان می دهید.

enum PrinterError: Error {
    case outOfPaper
    case noToner
    case onFire
}

استفاده از throw برای پرتاب خطا و throws به علامت یک تابع است که می تواند خطا بزنند. اگر در یک تابع خطا ایجاد کنید ، عملکرد بلافاصله برمی گردد و کدی که تابع را فراخوانی می کند خطا را کنترل می کند.

func send(job: Int, toPrinter printerName: String) throws -> String {
    if printerName == "Never Has Toner" {
        throw PrinterError.noToner
    }
    return "Job sent"
}

روش های مختلفی برای کنترل خطاها وجود دارد. یک راه استفاده از do-catch . در داخل بلوک do ، کدی را علامت گذاری می کنید که می تواند با نوشتن try مقابل آن خطایی ایجاد کند. در داخل بلوک catch ، خطا به طور خودکار error نام error داده می شود ، مگر اینکه نام دیگری به آن بدهید.

do {
    let printerResponse = try send(job: 1040, toPrinter: "Bi Sheng")
    print(printerResponse)
} catch {
    print(error)
}
Job sent

// Experiment:
// Change the printer name to `"Never Has Toner"`, so that the `send(job:toPrinter:)` function
// throws an error.

شما می توانید چند ارائه catch بلوک که مسئولیت رسیدگی به اشتباهات خاص. شما یک الگوی پس از catch می نویسید دقیقاً همانطور که بعد از case در یک سوئیچ می نویسید.

do {
    let printerResponse = try send(job: 1440, toPrinter: "Gutenberg")
    print(printerResponse)
} catch PrinterError.onFire {
    print("I'll just put this over here, with the rest of the fire.")
} catch let printerError as PrinterError {
    print("Printer error: \(printerError).")
} catch {
    print(error)
}
Job sent

// Experiment:
// Add code to throw an error inside the `do` block.
// What kind of error do you need to throw so that the error is handled by the first `catch` block?
// What about the second and third blocks?

روش دیگر برای کنترل خطاها استفاده از try? تا نتیجه را به اختیاری تبدیل کنید. اگر تابع خطایی ایجاد کند ، خطای خاص کنار گذاشته می شود و نتیجه آن nil . در غیر این صورت ، نتیجه یک گزینه اختیاری است که حاوی مقداری است که تابع برگردانده است.

let printerSuccess = try? send(job: 1884, toPrinter: "Mergenthaler")
let printerFailure = try? send(job: 1885, toPrinter: "Never Has Toner")

از defer برای نوشتن بلوکی از کد استفاده کنید که بعد از همه کد های دیگر در عملکرد ، درست قبل از بازگشت تابع اجرا شود. بدون توجه به اینکه تابع خطایی ایجاد کند ، کد اجرا می شود. برای نوشتن کد راه اندازی و پاکسازی در کنار یکدیگر ، می توانید از defer استفاده کنید ، حتی اگر این موارد در زمان های مختلف اجرا شود.

var fridgeIsOpen = false
let fridgeContent = ["milk", "eggs", "leftovers"]

func fridgeContains(_ food: String) -> Bool {
    fridgeIsOpen = true
    defer {
        fridgeIsOpen = false
    }

    let result = fridgeContent.contains(food)
    return result
}
fridgeContains("banana")
fridgeIsOpen
false

ژنیک

داخل براکت های زاویه ای یک نام بنویسید تا یک عملکرد یا نوع عمومی ایجاد کند.

func makeArray<Item>(repeating item: Item, numberOfTimes: Int) -> [Item] {
    var result = [Item]()
    for _ in 0..<numberOfTimes {
        result.append(item)
    }
    return result
}
makeArray(repeating: "knock", numberOfTimes: 4)
▿ 4 elements

  - 0 : "knock"
  - 1 : "knock"
  - 2 : "knock"
  - 3 : "knock"

شما می توانید انواع عمومی توابع و روش ها ، همچنین کلاس ها ، شمارش ها و ساختارها را ایجاد کنید.

// Reimplement the Swift standard library's optional type
enum OptionalValue<Wrapped> {
    case none
    case some(Wrapped)
}
var possibleInteger: OptionalValue<Int> = .none
possibleInteger = .some(100)
print(possibleInteger)
some(100)

برای مشخص کردن لیستی از الزامات از where بعد از نام نوع استفاده می شود - برای مثال برای اجرای پروتکل از نوع ، برای یکسان بودن دو نوع و یا داشتن یک کلاس فوق العاده خاص برای یک کلاس.

func anyCommonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> Bool
    where T.Element: Equatable, T.Element == U.Element
{
    for lhsItem in lhs {
        for rhsItem in rhs {
            if lhsItem == rhsItem {
                return true
            }
        }
    }
    return false
}
anyCommonElements([1, 2, 3], [3])
true

نوشتن <T: Equatable> همان نوشتن است <T> ... where T: Equatable .