본문 바로가기
개발(Development)/iOS(아이폰)

[한글 번역_02_작성중] Start Developing iOS Apps (Swift) - Getting Started > Learn the Essentials of Swift

by 카레유 2015. 10. 30.
아직 작성 중이므로 그냥 안 보시길 추천 드립니다.

아래 내용은 Swift의 기본에 대한 설명입니다.
내용이 어려운 것은 아니다, 양이 다소 많고 재미 없어서 아직 작업을 완료하지 못했습니다.

안녕하세요 카레유입니다.

Apple에서 제공해주는 Swift iOS 앱 개발 가이드를 한글 번역해보았습니다.

영어실력도, 개발실력도, 심지어 한국어 실력도 미흡합니다.

부족한 부분이 많지만, 많은 도움 되시길 바라겠습니다.

아직 작성 중인 내용으로 시간을 갖고 개선해 나갈 생각입니다.

감사합니다.






Learn the Essentials of Swift

Your first lesson is presented in the form of a guided Swift playground, a type of file that lets you change and interact with the code directly in Xcode and see the result immediately. Playgrounds are great for learning and experimenting, and this one helps get you up to speed on fundamental Swift concepts.

첫 번째 레슨은 Xcode에서 코딩하면서 즉각적으로 실행결과를 볼 수 있는 Swift playgorund로 진행하겠습니다. playground는 공부하기에 아주 좋은 환경을 제공하며, Swift의 기본 개념을 익히는데 큰 도움이 될 것입니다.


아래 playground 파일을 다운 받아서 Xcode에서 열어보세요

Learning Objectives

At the end of the lesson, you’ll be able to:

  • Differentiate between a constant and a variable - 상수(constant)와 변수(variable)을 구분한다.

  • Know when to use implicit and when to use explicit type declarations - 언제 암시적/명시적(implicit/explicit) 타입으로 선언해야하는지 이해한다.

  • Understand the advantage of using optionals and optional binding - optional과 optional binding을 사용할 줄 안다

  • Differentiate between optionals and implicitly unwrapped optionals - optionals과 implicitly unwrapped optional를 구분한다.

  • Understand the purpose of conditional statements and loops - 조건문과 반복문을 사용할줄 안다.

  • Use switch statements for conditional branching beyond a binary condition - switch 구문을 이용한다.

  • Use where clauses to impose additional constraints in conditional statements - 조건문에서 추자거인 제약을 부여하기 위해 where절을 사용할 줄 안다.

  • Differentiate between functions, methods, and initializers - function, method, initializer의 차이를 이해한다.

  • Differentiate between classes, structures, and enumerations - class, structure, enumeration의 차이를 이해한다.

  • Understand syntax for (and basic concepts behind) inheritance and protocol conformance - 상속(inheritance)과 프로토콜 이행(protocol conformance) 문법을 이해한다.

  • Determine implicit types and find additional information using Xcode’s quick help shortcut (Option-click) - implicit 타입을 사용하고, Xcode의 Quick Help 단축키(Option)을 이용하여 추가적인 정보를 찾을줄 안다.

  • Import and use UIKit - UIKit을 임포트하여 사용할 줄 안다.

Basic Types

constant is a value that stays the same after it’s declared the first time, while a variable is a value that can change. A constant is referred to as immutable, meaning that it can’t be changed, and a variable is mutable. If you know that a value won’t need to be changed in your code, declare it as a constant instead of a variable. - 상수(constant)는 처음에 값을 선언하고 나면, 바뀌지 않는 값입니다. 반면 변수(variable)은 그 값이 계속해서 변경될 수 있습니다. 즉, 상수는 변치 않는 값이며 변수는 변할 수 있는 값입니다. 코드상에서 변경되지 말아야할 값이 있다면 변수가 아니라, 상수로 선언해서 사용하시기 바랍니다.

Use let to make a constant and var to make a variable. - 상수를 선언할 때는 let, 변수를 선언할 때는 var를 사용하세요

  1. var myVariable = 42
  2. myVariable = 50
  3. let myConstant = 42

Every constant and variable in Swift has a type, but you don’t always have to write the type explicitly. Providing a value when you create a constant or variable lets the compiler infer its type. In the example above, the compiler infers that myVariable is an integer because its initial value is an integer. This is called type inference. Once a constant or variable has a type, that type can’t be changed. - Swift에서 모든 상수와 변수는 타입(정수 타입인지, 문자열 타입인지 등)을 갖습니다.  하지만 반드시 어떤 타입인지 명시적으로 작성을 해주어야만 하는 것은 아닙니다. 상수나 변수를 선언하면서 값을 넣으면 컴파일러가 어떤 타입인지를 판단해줍니다. 위에 작성한 코드에서 컴파일러는 myVariable 변수가 integer 타입이라고 판단합니다. 초기값으로 42라고 하는 정수 값을 넣어주었기 때문입니다. 이를 type inference라고 합니다. 한 가지 주의할 점은 일단 어떤 타입인지 결정되고나면, 그 타입은 변경되지 않는 다는 점입니다.

If the initial value doesn’t provide enough information (or if there is no initial value), specify the type by writing it after the variable, separated by a colon. - 처음에 할당한 초기값이 어떤 타입인지 판단하기에 충분하지 않다면(혹은 초기값이 없다면), 변수명 뒤에 콜론을 찍고 어떤 타입인지를 기술해주어야 합니다.

  1. let implicitInteger = 70
  2. let implicitDouble = 70.0
  3. let explicitDouble: Double = 70

Values are never implicitly converted to another type. If you need to convert a value to a different type, explicitly make an instance of the desired type. Here, you convert an Int to a String. - 변수나 상수의 값은 자동으로 다른 타입의 값으로 변환되지 않습니다. 값의 타입을 변한하기 위해서는 명시적으로 원하는 타입의 인스턴스(instance)로 만드는 작업을 해주어야 합니다. 아래의 코드에서는 Int 타입을 String타입으로 변환하는 예입니다.

  1. let label = "The width is "
  2. let width = 94
  3. let widthLabel = label + String(width)

There’s an even simpler way to include values in strings: Write the value in parentheses, and write a backslash (\) before the parentheses. This is known as string interpolation. - string으로 만들어 포함시키기 위한 더 간단한 방법이 있습니다. \(값) 형태로 작성하는 것입니다. 이를 string interpolation기법이라고 합니다.

  1. let apples = 3
  2. let oranges = 5
  3. let appleSummary = "I have \(apples) apples."
  4. let fruitSummary = "I have \(apples + oranges) pieces of fruit."

Use optionals to work with values that might be missing. An optional value either contains a value or contains nil (no value) to indicate that a value is missing. Write a question mark (?) after the type of a value to mark the value as optional. - 혹시 값이 없을 수도 있는 경우엔 optionals를 사용하세요. optional로 선언된 것은 값을 가질수도 있고 안 가질(nil) 수도 있습니다. 해당 값이 optional이라는걸 표시하기 위해서는 값의 타입 바로 뒤에 물음표(?)를 붙이면 됩니다.

  1. let optionalInt: Int? = 9

To get the underlying type from an optional, you unwrap it. You’ll learn unwrapping optionals later, but the most straightforward way to do it involves the force unwrap operator (!). Only use the unwrap operator if you’re sure the underlying value isn’t nil. - optional이 가질 값의 타입을 확인하기 위해서는 unwrap과정을 거쳐야 합니다. optional을 unwrap하는 방식은 나중에 배우겠습니다만, 가장 직관적으로 optional을 unwrap하는 방법은 force unwrap operator(!)를 이용하는 것입니다. 하지만 optional이 가질 값이 nil이 아니라고 확신하는 경우에만 이 unwrap operator를 사용하셔야 합니다.

  1. let actualInt: Int = optionalInt!

Optionals are pervasive in Swift, and are very useful for many situations where a value may or may not be present. They’re especially useful for attempted type conversions. - optional은 Swift 의 전반에 걸쳐 아주 흔하게 사용되며, 어떤 값(변수, 상수 등)이 존재할 때도 있고, 존재하지 않을 때도 있을 때 아주 유용하게 사용할 수 있습니다. 특히 타입 변환이 실패할 수도 있을 때 더욱 유용합니다.

  1. var myString = "7"
  2. var possibleInt = Int(myString)
  3. print(possibleInt)

In this code, the value of possibleInt is 7, because myString contains the value of an integer. But if you change myString to be something that can’t be converted to an integer, possibleInt becomes nil. 위의 코드에서 possibleInt의 값은 7이 됩니다. myString 변수가 integer 값을 갖고 있어 변환이 성공적으로 수행되었기 때문입니다. 하지만 아래의 코드에서 처럼 integer로 변환될 수 없는 myString을 integer로 변환하고자 할 경우, nil이 반환되므로 possibleInt는 nil이 됩니다. 이런 경우에 optional을 사용하는 것이 좋습니다.

  1. myString = "banana"
  2. possibleInt = Int(myString)
  3. print(possibleInt)

 

An array is a data type that keeps track of an ordered collection of items. Create arrays using brackets ([]), and access their elements by writing the index in brackets. Arrays start at index 0. - array는 여러개의 값을 저장할 수 있는 데이터 타입입니다. bracket( [ ] )을 이용하여 array를 생성할 수 있으며, bracket 상에 인덱스 값을 넣어서 array의 각 항목들에 접근할 수 있습니다. array의 index는 0부터 시작한다는 점을 주의해주세요

  1. var ratingList = ["Poor", "Fine", "Good", "Excellent"]
  2. ratingList[1] = "OK"
  3. ratingList

To create an empty array, use the initializer syntax. You’ll learn more about initializers in a little while. - 빈 array를 생성하기 위해서는 아래와 같이 initializer 문법을 사용하면됩니다. initializer에 대해서는 뒤에서 자세히 배우겠습니다.

  1. // Creates an empty array.
  2. let emptyArray = [String]()

You’ll notice that the code above has a comment. A comment is a piece of text in a source code file that doesn’t get compiled as part of the program but provides context or useful information about individual pieces of code. A single-line comment appears after two slashes (//) and a multiline comment appears between a set of slashes and asterisks (/* … */). You’ll see and write both types of comments throughout the source code in the lessons. - 위의 코드에서 주석이라는 것을 작성하였습니다. 주석(comment)는 프로그램으로 컴파일 되지 않는 단순 텍스트입니다. 누구나 이 코드를 보고 이해할 수 있게끔 작성하는 것이라고 보시면 됩니다. 한 줄짜리 주석은 " // " 를 입력하고 작성하면 됩니다. 여러 줄의 주석을 작성할 때는 " /* " 와 " */ " 사이에 작성하면 됩니다. 이 레슨에서 제공하는 소스 코드 전반에 걸쳐 이러한 주석들을 자주 접하게 되실 겁니다.

An implicitly unwrapped optional is an optional that can also be used like a nonoptional value, without the need to unwrap the optional value each time it’s accessed. This is because an implicitly unwrapped optional is assumed to always have a value after that value is initially set, although the value can change. Implicitly unwrapped optional types are indicated with an exclamation mark (!) instead of a question mark (?). - implicitly unwrapped optional은 매번 접근할 때마다 optional value를 unwrap 해주어야 할 필요가 없는 경우에는 non optional 로 사용할 수 있는 optional입니다. implicitly unwrapped optional의 경우,초기값이 설정되었다면 언제나 값을 갖기 때문에(값이 변하더라도) 그냥 이건 값이 있는 것이다라고 가정할 수 있는 것이지요. 이러한 implicitly unwrapped optional의 경우에는 물음표(?)가 아니라 느낌표(!)를 사용하여 지정합니다.

  1. var implicitlyUnwrappedOptionalInt: Int!

You’ll rarely need to create implicitly unwrapped optionals in your own code. More often, you’ll see them used to keep track of outlets between an interface and source code (which you’ll learn about in a later lesson) and in the APIs you’ll see throughout the lessons. - 하지만 우리가 직접 코드상에 implicitly unwrapped optional을 생성하여 사용하는 경우는 거의 없습니다. 나중에 배우겠지만 인터페이스와 소스코드 사이의 연결을 위한 outlet에서 많이 사용하게 되며, 그외 각종 API들에서 좀 보게 될 것입니다.

Control Flow

Swift has two types of control flow statements. Conditional statements, like if and switch, check whether a condition is true—that is, if its value evaluates to the Boolean true—before executing a piece of code. Loops, like for-in and while, execute the same piece of code multiple times.  - Swift에는 크게 두 종류의 제어 구문(control flow statement)가 있습니다. 조건의 참/거짓(Boolean)에 따라 수행여부를 결정하는  조건문(Conditional statements) if와 switch, 그리고 조건에 따라 반복을 수행하는 반복문(loop) for-in과 while 이 있습니다.

An if statement checks whether a certain condition is true, and if it is, the if statement evaluates the code inside the statement. You can add an else clause to an if statement to define more complex behavior. An else clause can be used to chain if statements together, or it can stand on its own, in which case the elseclause is executed if none of the chained if statements evaluate to true. - if구문은 특정 조건이 true인지를 체크합니다. if문의 조건이 true가 아닐 경우 실행해야할 내용은 else 절을 추가하여 만들 수 있습니다. else절은 else if 형식으로 사용하여 추가적인 조건에 대한 검사를 시행할 수도 있습니다. 이 경우 else절은 위의 모든 조건이 false일 경우 실행될 것입니다. 

  1. let number = 23
  2. if number < 10 {
  3. print("The number is small")
  4. } else if number > 100 {
  5. print("The number is pretty big")
  6. } else {
  7. print("The number is between 10 and 100")
  8. }

Statements can be nested to create complex, interesting behavior in a program. Here’s an example of an ifstatement with an else clause nested inside a for-in statement (which iterates through each item in a collection in order, one-by-one). - 위의 구문들은 중첩된 형태로 사용이 가능합니다. 아래의 코드는 for-in 반복문 안에 if-esle 구문을 중첩하여 사용한 예입니다. for 문에 사용된 컬렉션(collection)의 항목을 하나씩 반복하여 수행하게 됩니다.

  1. let individualScores = [75, 43, 103, 87, 12]
  2. var teamScore = 0
  3. for score in individualScores {
  4. if score > 50 {
  5. teamScore += 3
  6. } else {
  7. teamScore += 1
  8. }
  9. }
  10. print(teamScore)

Use optional binding in an if statement to check whether an optional contains a value. - if문의 조건부에 optional binding을 사용해서  optional이 값을 갖고 있는지 여부를 체크해보세요

  1. var optionalName: String? = "John Appleseed"
  2. var greeting = "Hello!"
  3. if let name = optionalName {
  4. greeting = "Hello, \(name)"
  5. }

If the optional value is nil, the conditional is false, and the code in braces is skipped. Otherwise, the optional value is unwrapped and assigned to the constant after let, which makes the unwrapped value available inside the block of code. - optional 의 값이 nil이라면 if절의 조건부는 false가 되고, 괄호 내의 실행코드는 스킵되어 실행되지 않습니다. 만약 nil이 아니고 값을 갖고 있다면, optional value가 unwrap되어 let으로 선언한 상수에 할당되고, 괄호내의 실행 코드 상에서 사용할 수 있게 됩니다.

You can use a single if statement to bind multiple values. A where clause can be added to a case to further scope the conditional statement. In this case, the if statement executes only if the binding is successful for all of these values and all conditions are met. if문 조건부에서 여러개의 값들을 bind 할 수도 있습니다. where절은 조건의 범위를 더 넒히기 위해 사용됩니다. 아래의 코드에서는 optionalHello와 optionalName의 바인딩이 성공하고, hello.hasPrefix("H")가 true여야지만 괄호 안의 실행 코드가 수행됩니다.

  1. var optionalHello: String? = "Hello"
  2. if let hello = optionalHello where hello.hasPrefix("H"), let name = optionalName {
  3. greeting = "\(hello), \(name)"
  4. }

Switches in Swift are quite powerful. A switch statement supports any kind of data and a wide variety of comparison operations—it isn’t limited to integers and tests for equality. In this example, the switch statement switches on the value of the vegetable string, comparing the value to each of its cases and executing the one that matches. - Swift에서 switch 구문은 매우 강력합니다. switch 구문은 모든 종류의 데이터를 지원하며, 다양한 비교 연산도 가능합니다.(단순히 integer 값이 서로 같은지만 체크하는 것으로 한정되지 않습니다) 아래의 코드에서 switch 구문은 string타입의 vegetable 상수의 값을 사용하고 있으며, 각각의 케이스에 다양한 조건을 사용하고 있습니다.

  1. let vegetable = "red pepper"
  2. switch vegetable {
  3. case "celery":
  4. let vegetableComment = "Add some raisins and make ants on a log."
  5. case "cucumber", "watercress":
  6. let vegetableComment = "That would make a good tea sandwich."
  7. case let x where x.hasSuffix("pepper"):
  8. let vegetableComment = "Is it a spicy \(x)?"
  9. default:
  10. let vegetableComment = "Everything tastes good in soup."
  11. }

Notice how let can be used in a pattern to assign the value that matched that part of a pattern to a constant. Just like in an if statement, a where clause can be added to a case to further scope the conditional statement. However, unlike in an if statement, a switch case that has multiple conditions separated by commas executes when any of the conditions are met. - let을 사용하여 각각의 케이스가 매칭되는 경우 상수값을 할동하고 있습니다. 각 케이스의 조건부를 확장하기 위해서 where절도 사용이 가능합니다. 하지만 if구문과는 다르게 switch 케이스 상에 콤마로 구분된 다수의 조건들은 그 중 하나만 만족해도 수행이 됩니다.

After executing the code inside the switch case that matched, the program exits from the switch statement. Execution doesn’t continue to the next case, so you don’t need to explicitly break out of the switch statement at the end of each case’s code. - 매칭되는 switch 의 케이스를 만나면 해당 실행코드를 실행하고 switch 구문을 빠져나옵니다. 따라서 각 코드의 끝에 break 를 명시적으로 작성해줄 필요가 없습니다.

Switch statements must be exhaustive. A default case is required, unless it’s clear from the context that every possible case is satisfied, such as when the switch statement is switching on an enumeration. This requirement ensures that one of the switch cases always executes. - switch 구문은 반드시 실행되고 종료되어야만합니다. 따라서 어떠한 switch 케이스에 매칭되는 값이 없어 실행 되지 않을 수 있다면 반드시 default케이스를 작성해야합니다. 이렇게 하면 모든 케이스가 만족하지 않는 경우, default 케이스에 대한 실행 코드를 수행하고 빠져나오게 됩니다.

You can keep an index in a loop by using a Range. Use the half-open range operator ( ..<) to make a range of indexes. - 반복문에서 Range 개념을 이용하여 반복에 사용될 index를 관리할 수 있습니다.  아래 코드처럼 half-open range 연산자(..<)를 사용해봅시다.

  1. var firstForLoop = 0
  2. for i in 0..<4 {
  3. firstForLoop += i
  4. }
  5. print(firstForLoop)

The half-open range operator (..<) doesn’t include the upper number, so this range goes from 0 to 4 for a total of five loop iterations. Use the closed range operator ( ...) to make a range that includes both values. - half-open range 연산자(..<)는 오른쪽에 있는 수를 포함하지 않습니다. 따라서 4를 제외하고, 0, 1, 2, 3 의 index에 대해서만 반복을 수행합니다. 반면 closed range 연산자(...)를 사용하면 왼쪽, 오른쪽 값 모두를 포함하게 됩니다.

  1. var secondForLoop = 0
  2. for _ in 0...4 {
  3. secondForLoop += 1
  4. }
  5. print(secondForLoop)

The underscore (_) represents a wildcard, which you can use when you don’t need to know which iteration of the loop is currently executing.  - 위의 for-in문에 사용된 언더바(_)는 와일드카드(wildcard)를 나타냅니다. 반복문이 실행되는 동안 어떤 index가 반복되고 있는지 파악할 필요가 없을 때 사용하면 됩니다.

Functions and Methods

function is a reusable, named piece of code that can be referred to from many places in a program.  - 함수(function)은 재사용 가능한 코드 조각들의 이름입니다. 함수는 프로그램의 여러 부분에서 참조되어 사용됩니다.

Use func to declare a function. A function declaration can include zero or more parameters, written as name: Type, which are additional pieces of information that must be passed into the function when it’s called. Optionally, a function can have a return type, written after the ->, which indicates what the function returns as its result. A function’s implementation goes inside of a pair of curly braces ({}). - 함수를 선언하기 위해서는 func 을 사요하면됩니다. 다른 곳에서 함수를 호출할 때 넣어주어야 할 정보가 있는 경우 파라미터를 포함하는 함수로 선언해야합니다. 파라미터는 (파라미터 명: 타입) 형식으로 추가하면 됩니다. 만약 함수가 반환하는 값이 있다면 함수 선언부 뒤에 " -> 반환 타입 "  형식으로 작성하면 됩니다. 함수의 선언부 다음에는 괄호 ( { } ) 사이에 구현부를 작성하면 됩니다.

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

Call a function by following its name with a list of arguments (the values you pass in to satisfy a function’s parameters) in parentheses. When you call a function, you pass in the first argument value without writing its name, and every subsequent value with its name.  - 함수는 "함수명(인자, 파라미터명: 인자)"형식으로 할 수 있습니다. 함수를 호출 할 때 ,첫 번째 파라미터에 대한 인자(argument : 함수의 파라미터에 넣는 실제 값)는 파라미터명을 생략하고 넣으며, 그 다음부터 나오는 파라미터는 모두 "파라미터명: 인자" 형식으로 인자를 넣어주어야 합니다.

  1. greet("Anna", day: "Tuesday")
  2. greet("Bob", day: "Friday")
  3. greet("Charlie", day: "a nice day")

Functions that are defined within a specific type are called methods. Methods are explicitly tied to the type they’re defined in, and can only be called on that type (or one of its subclasses, as you’ll learn about soon). In the earlier switch statement example, you saw a method that’s defined on the String type called hasSuffix(), shown again here: - 함수 중에 메서드(method)라는 것이 있습니다. 메서드란 특정 타입 내에서 정의된 함수에 한정하여 사용하는 용어입니다. 즉, 특정 타입(클래스 등)에 정의된 함수를 메서드라고 하며, 사용할 떄에도 클래스(static인 경우)나 클래스의 객체, 혹은 상속을 받은 subclass를 통해서만 사용할 수 있습니다. 위의 switch 구문 예제에서도 String 타입에 정의된 메서드인 hasSuffix()를 호출한 적이 있습니다. 아래를 참조하세요

  1. let exampleString = "hello"
  2. if exampleString.hasSuffix("lo") {
  3. print("ends in lo")
  4. }

As you see, you call a method using the dot syntax. When you call a method, you pass in the first argument value without writing its name, and every subsequent value with its name. For example, this method on Arraytakes two parameters, and you only pass in the name for the second one: - 위의 예를 보면 알 수 있듯이, 메서드의 호출을 dot문법 ( . 을 사용)을 통해 하고 있습니다. 메서드를 호출할 때에도 함수와 마찬가지로 첫 번째 파라미터에 대한 인자는 파라미터명 없이 넣어줍니다. 그러나 두 번째 파라미터 부터는 반드시 파라미터 명과 함께 인자를 넣어주어야 합니다. 아래의 예에서도 첫번째 파리미터는 인자만 넣었지만, 두번째 파라미터는 파라미터명과 인자를 함께 넣어주고 있습니다.

  1. var array = ["apple", "banana", "dragonfruit"]
  2. array.insert("cherry", atIndex: 2)
  3. array

Classes and Initializers

In object-oriented programming, the behavior of a program is based largely on interactions between objects. An object is an instance of a class, which can be thought of as a blueprint for that object. Classes store additional information about themselves in the form of properties, and define their behavior using methods. - 객체지향프로그래밍(object-oriented programming)에서는 대부분 객체(Object)간의 상호작용을 기반하여 프로그램의 작동이 수행됩니다. 객체(object)란 클래스(class)의 인스턴스(instance)를 의미합니다. 여기서 클래스란 프로퍼티와 메서드로 구성된 일종의 설계도라고 할 수 있으며, 이 설계에 따라 만든 인스턴스인 객체를 통해 실제 작업을 수행합니다.

Use class followed by the class’s name to define a class. A property declaration in a class is written the same way as a constant or variable declaration, except that it’s in the context of a class. Likewise, method and function declarations are written the same way. This example declares a Shape class with a numberOfSidesproperty and a simpleDescription() method. - 클래스를 정의하기 위해서는 "class 클래스명" 형태로 선언을 해주면 됩니다. 클래스의 프로퍼티는 상수(constant)나 변수(variable)을 선언하는 것과 동일한 방식으로 작성하면 됩니다. 단지 상수와 변수 등의 선언을 클래스 내부(in the context of a class)에 해주면 되는 것이지요. 메서드와 함수의 선언도 동일한 방식으로 클래스 내부에 해주면 됩니다. 아래의 예는 Shape클래스를 선언하고 있으며 numberOfSides 프로퍼티와 simpleDescription() 메서드를 포함하고 있습니다.

  1. class Shape {
  2. var numberOfSides = 0
  3. func simpleDescription() -> String {
  4. return "A shape with \(numberOfSides) sides."
  5. }
  6. }

Create an instance of a class—an object—by putting parentheses after the class name. Use dot syntax to access the properties and methods of the instance. Here, shape is an object that’s an instance of the Shapeclass. - 클래스의 인스턴스인 객체(object)를 만들어 봅시다. 클래스 이름 옆에 " () " 라고 작성하면 이 클래스의 객체를 생성할 수 있습니다. 생성한 인스턴스의 프로퍼티와 메서드에 접근하기 위해서는 dot( . ) 문법을 사용하면 됩니다. 아래에서 shape객체는 Shape클래스의 인스턴스입니다.

  1. var shape = Shape()
  2. shape.numberOfSides = 7
  3. var shapeDescription = shape.simpleDescription()

This Shape class is missing something important: an initializer. An initializer is a method that prepares an instance of a class for use, which involves setting an initial value for each property and performing any other setup. Use init to create one. This example defines a new class, NamedShape, that has an initializer which takes in a name. - 하지만 우리의 Shape클래스는 몬가 아주 중요한 것을 빠드렸습니다. 바로 initializer입니다. initializer 은 클래스의 인스턴스를 생성할 때 필요한 준비 작업을 하기 위한 특별한 형태의 메서드입니다. 클래스의 인스턴스를 생성할 때, initializer를 사용하면 프로퍼티의 초기값을 설정하는 등 각종 세팅 작업을 할 수 있습니다. 클래스 상에 init(파라미터명: 타입) 형태로 initializer를 만들 수 있습니다.

  1. class NamedShape {
  2. var numberOfSides = 0
  3. var name: String
  4. init(name: String) {
  5. self.name = name
  6. }
  7. func simpleDescription() -> String {
  8. return "A shape with \(numberOfSides) sides."
  9. }
  10. }

Notice how self is used to distinguish the name property from the name argument to the initializer. Every property needs a value assigned—either in its declaration (as with numberOfSides) or in the initializer (as with name). - 위의 코드에서 self가 사용된 것에 주목해주세요. 클래스 자신의 프로퍼티인 name을 표현하기 위해 self.name형태로 사용되었으며, 이를 통해 initializer 의 인자인 name과 구분할 수 있습니다. 클래스 상의 모든 프로퍼티들은 선언과 동시에 값이 할당되거나(var numberOfSides = 0), initializer ( init(name: String) )를 통해 값이 할당 되어 있어야만 합니다.

You don’t call an initializer by writing init; you call it by putting parentheses with the appropriate arguments after the class name. When you call an initializer, you include all arguments names along with their values.  - initializer는 init(인자) 형태로 호출할 수 없습니다. 클래스의 인스턴스를 생성할 때 괄호한에 인자를 넣어서 호출( "클래스명(인자)" )해야합니다. 아래와 같이 initializer를 호출할 때에는 모든 인자의 파라미터명을 함께 작성해주어야 합니다.

  1. let namedShape = NamedShape(name: "my named shape")

Classes inherit their behavior from their parent class. A class that inherits behavior from another class is called a subclass of that class, and the parent class is called a superclass. Subclasses include their superclass name after their class name, separated by a colon. A class can inherit from only one superclass, although that class can inherit from another superclass, and so on, resulting in a class hierarchy. - 클래스는 부모클래스(superclass)로부터 상속(inherit)을 받을 수도 있습니다. 상속을 받은 클래스를 subclass라고 합니다. 상속을 받기 위해서는 subclass의 이름 뒤에 콜론과 superclass이름을 작성하면 됩니다. 단, 오직 하나의 superclass만 상속 가능하며, 여러개의 클래스를 동시에 상속 받는 것은 불가합니다. 물론 이미 상속받은 subclass를 또다시 상속받는 subclass를 만들 수 는 있습니다.

Methods on a subclass that override the superclass’s implementation are marked with override—overriding a method by accident, without override, is detected by the compiler as an error. The compiler also detects methods with override that don’t actually override any method in the superclass. - 슈퍼클래스의 메서드를 재정의(override)하여 사용하기 위해서는 override 라코 표시를 하고 사용해야합니다. 그렇지 않으면 컴파일러가 에러로 인식합니다. 마찬가지로 슈퍼클래스에 없는 메서드인데 override라고 표시한 경우에도 에러로 인식합니다.

This example defines the Square class, a subclass of NamedShape. - 아래의 예시는 NamedShape 클래스를 상속받는 Square 클래스를 정의하고 있습니다.

  1. class Square: NamedShape {
  2. var sideLength: Double
  3. init(sideLength: Double, name: String) {
  4. self.sideLength = sideLength
  5. super.init(name: name)
  6. numberOfSides = 4
  7. }
  8. func area() -> Double {
  9. return sideLength * sideLength
  10. }
  11. override func simpleDescription() -> String {
  12. return "A square with sides of length \(sideLength)."
  13. }
  14. }
  15. let testSquare = Square(sideLength: 5.2, name: "my test square")
  16. testSquare.area()
  17. testSquare.simpleDescription()

Notice that the initializer for the Square class has three different steps: - 서브클래스인 Square 클래스의 initializer 세 단계의 초기화 과정을 갖고 있다는 것에 주목하세요

  1. Setting the value of properties that the subclass, Square, declares. - 서브클래스인 Square 자체에서 선언한 프로퍼티에 값을 할당합니다.

  2. Calling the initializer of the superclass, NamedShape. - 슈퍼클래스인 NamedShape의 initializer를 호출합니다.

  3. Changing the value of properties defined by the superclass, NamedShape. Any additional setup work that uses methods, getters, or setters can also be done at this point. - 슈퍼클래스인 NamedShape에 정의된 프로퍼티의 값을 변경합니다. 메서드, 게터(getter), 세터(setter)를 이용한 추가적인 세팅 작업도 이 시점에 이루어 질 수 있습니다.

Sometimes, initialization of an object needs to fail, such as when the values supplied as the arguments are outside of a certain range, or when data that’s expected to be there is missing. Initializers that may fail to successfully initialize an object are called failiable initializers. A failable initializer can return nil after initialization. Use init? to declare a failable initializer. - 객체의 초기화 작업이 실패하는 경우가 있습니다. 허용된 범위를 벗어난 인자가 값으로 들어온 경우, 혹은 있어야 할 데이터가 없는 경우에 이런 일이 발생합니다. 이처럼 객체를 조기화 하는데 실패할 수도 있는 initializer를 failiable initializer라고 합니다. failiable initializer는 init? 형태로 선언하며, nil 값을 반환할 수도 있음을 의미합니다.

  1. class Circle: NamedShape {
  2. var radius: Double
  3. init?(radius: Double, name: String) {
  4. self.radius = radius
  5. super.init(name: name)
  6. numberOfSides = 1
  7. if radius <= 0 {
  8. return nil
  9. }
  10. }
  11. override func simpleDescription() -> String {
  12. return "A circle with a radius of \(radius)."
  13. }
  14. }
  15. let successfulCircle = Circle(radius: 4.2, name: "successful circle")
  16. let failedCircle = Circle(radius: -7, name: "failed circle")

Initializers can have quite a few keywords associated with them. A designated initializer indicates that it’s one of the primary initializers for a class; any initializer within a class must ultimately call through to a designated initializer. A convenience initializer is a secondary initializer, which adds additional behavior or customization, but must eventually call through to a designated initializer. Designated and convenience initializers are indicated with the designated and convenience keywords, respectively. - initializer에 적용할 수 있는 키워드가 몇개 있습니다. 대표적인 것이 designated와 convenience입니다. designated 키워드를 initializer 에 사용하면 이 initializer가 이 클래스의 primary initializer(designated initializer)중 하나임을 의미하게 됩니다. convenience 키워드를 initializer에 사용하게 되면 이 initializer 는 커스토마이징 등의 작업을 위해 추가적으로 생성한 initializer임을 의미하게 됩니다. 이렇게 생성한 convenience initializer는 반드시 내부에서 designated initializer를 호출해야만합니다.

required keyword next to an initializer indicates that every subclass of the class that has that initializer must implement its own version of the initializer (if it implements any initializer). - required 키워드도 사용이 가능한데, 이 키워드를 사용하면 이 클래스를 상속받는 서브클래스는 반드시 자신만의 initializer를 구현해야함을 의미합니다.

Type casting is a way to check the type of an instance, and to treat that instance as if it’s a different superclass or subclass from somewhere else in its own class hierarchy. - 형변환(type casting)은 인스턴스의 타입을 체크할 수 있는 방법을 제공하며, 자신이 실제로 속한 클래스 계층구조(class hierarchy)가 아닌 다른 슈퍼클래스나 서브클래스인 것처럼 사용할 수 있게 해줍니다.

A constant or variable of a certain class type may actually refer to an instance of a subclass behind the scenes. Where you believe this is the case, you can try to downcast to the subclass type using a type cast operator. - 특정 클래스 타입의 상수나 변수는 자신의 서브클래스의 인스턴스 타입으로 참조되어야 할 때가 있습니다. 즉, 부모가 자식타입으로 변환되어야 하는 경우입니다. 이럴 때는 형변환 연산자(type case operator)를 이용하여 다운캐스팅(down cast)을 해야합니다.

Because downcasting can fail, the type cast operator comes in two different forms. The optional form, as?, returns an optional value of the type you are trying to downcast to. The forced form, as!, attempts the downcast and force-unwraps the result as a single compound action. - 하지만 다운캐스팅은 종종 실패할 수 있기 때문에 각 상황에 맞는 두가지 형식의 형변환 연산자(type cast operator)를 사용합니다. optional form(as?)는 다운 캐스팅하여 만들 타입을 optional 값으로 반환합니다. forced form(as!)는 다운캐스트 과정에서 강제언래핑(forced-unwrap)을 수행합니다.

Use the optional type cast operator (as?) when you’re not sure if the downcast will succeed. This form of the operator will always return an optional value, and the value will be nil if the downcast was not possible. This lets you check for a successful downcast. - 다운캐스팅이 성공할지 확신할 수 없다면 optional type cast operator(as?)를 사용하세요. 이 연산자는 언제나 optional value를 반환하며, 다운캐스팅이 실패하면 nil을 반환하기 때문에 다운캐스팅이 성공했는지 여부를 체크할 수 있습니다.

Use the forced type cast operator (as!) only when you’re sure that the downcast will always succeed. This form of the operator will trigger a runtime error if you try to downcast to an incorrect class type. - 어떠한 경우에도 다운캐스팅이 성공한다고 확신하는 경우에만 forced type case operator(as!)를 사용하세요. 만약 옳지 않은 클래스 타입으로 다운캐스팅을 수행하려고 한다면 프로그램 실행중(runtime)에 에러를 발생시킵니다.

This example shows the use of the optional type cast operator (as?) to check whether a shape in an array of shapes is a circle or a square. You increment the count of the squares and circles variables by one each time the corresponding shape is found, printing the values at the end. - 아래의 예제는 optional type case operator(as?)를 통해 shapesArray배열에 있는 객체들이 Triangle객체인지 Square 객체인지를 체크합니다. 각 객체가 발견될 때 마다 해당 객체의 갯수를 표시하는 변수의 값을 하나씩 더하는 반복작업을 하고, 각 객체의 수를 출력하고 있습니다.

  1. class Triangle: NamedShape {
  2. init(sideLength: Double, name: String) {
  3. super.init(name: name)
  4. numberOfSides = 3
  5. }
  6. }
  7. let shapesArray = [Triangle(sideLength: 1.5, name: "triangle1"), Triangle(sideLength: 4.2, name: "triangle2"), Square(sideLength: 3.2, name: "square1"), Square(sideLength: 2.7, name: "square1")]
  8. var squares = 0
  9. var triangles = 0
  10. for shape in shapesArray {
  11. if let square = shape as? Square {
  12. squares++
  13. } else if let triangle = shape as? Triangle {
  14. triangles++
  15. }
  16. }
  17. print("\(squares) squares and \(triangles) triangles.")

Enumerations and Structures(Enumeartion 작성 필요)

Classes aren’t the only ways to define data types in Swift. Enumerations and structures have similar capabilities to classes, but can be useful in different contexts. - Swift에서 새로운 데이터 타입을 만들 수 있는 방법은 클래스 뿐만이 아닙니다. Enumeration과 Structure도 클래스와 유사한 기능을 제공합니다. 물론 각각 사용하기에 적합한 상황은 다릅니다.

Enumerations define a common type for a group of related values and enable you to work with those values in a type-safe way within your code. Enumerations can have methods associated with them. - Enumerations은 관련된 값들을 모아 하나의 타입으로 정의합니다. 또한 이 타입에 정의된 값들을 type-safe 방식으로 사용할 수도 있으며 관련 메서드도 갖을 수 있습니다.

Use enum to create an enumeration. - "enum"을 이용해 Enumeration을 만들 수 있습니다.

  1. enum Rank: Int {
  2. case Ace = 1
  3. case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
  4. case Jack, Queen, King
  5. func simpleDescription() -> String {
  6. switch self {
  7. case .Ace:
  8. return "ace"
  9. case .Jack:
  10. return "jack"
  11. case .Queen:
  12. return "queen"
  13. case .King:
  14. return "king"
  15. default:
  16. return String(self.rawValue)
  17. }
  18. }
  19. }
  20. let ace = Rank.Ace
  21. let aceRawValue = ace.rawValue

In the example above, the raw-value type of the enumeration is Int, so you have to specify only the first raw value. The rest of the raw values are assigned in order. You can also use strings or floating-point numbers as the raw type of an enumeration. Use the rawValue property to access the raw value of an enumeration member. 

Use the init?(rawValue:) initializer to make an instance of an enumeration from a raw value.

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

The member values of an enumeration are actual values, not just another way of writing their raw values. In fact, in cases where there isn’t a meaningful raw value, you don’t have to provide one.

  1. enum Suit {
  2. case Spades, Hearts, Diamonds, Clubs
  3. func simpleDescription() -> String {
  4. switch self {
  5. case .Spades:
  6. return "spades"
  7. case .Hearts:
  8. return "hearts"
  9. case .Diamonds:
  10. return "diamonds"
  11. case .Clubs:
  12. return "clubs"
  13. }
  14. }
  15. }
  16. let hearts = Suit.Hearts
  17. let heartsDescription = hearts.simpleDescription()

Notice the two ways that the Hearts member of the enumeration is referred to above: When a value is assigned to the hearts constant, the enumeration member Suit.Hearts is referred to by its full name because the constant doesn’t have an explicit type specified. Inside the switch, the enumeration member is referred to by the abbreviated form .Hearts because the value of self is already known to be a suit. You can use the abbreviated form anytime the value’s type is already known.

Structures support many of the same behaviors as classes, including methods and initializers. One of the most important differences between structures and classes is that structures are always copied when they are passed around in your code, but classes are passed by reference. Structures are great for defining lightweight data types that don’t need to have capabilities like inheritance and type casting. - Structure는 메서드와 initializer 를 포함해 클래스와 유사한 기능들을 지원합니다. 클래스와 structure의 가장큰 차이점은 structure는 코드에서 사용될때 그대로 복제가 되어 사용된다는 점입니다. 클래스는 레퍼런스를 통해 이용됩니다. Structure는 상속이나 타입캐스팅이 필요 없는 간단한 데이터 타입들을 사용할 때 아주 유용합니다.

Use struct to create a structure.  - struct를 통해 structure를 생성할 수 있습니다.

  1. struct Card {
  2. var rank: Rank
  3. var suit: Suit
  4. func simpleDescription() -> String {
  5. return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
  6. }
  7. }
  8. let threeOfSpades = Card(rank: .Three, suit: .Spades)
  9. let threeOfSpadesDescription = threeOfSpades.simpleDescription()

 

Protocols

protocol defines a blueprint of methods, properties, and other requirements that suit a particular task or piece of functionality. The protocol doesn’t actually provide an implementation for any of these requirements—it only describes what an implementation will look like. The protocol can then be adopted by a class, structure, or enumeration to provide an actual implementation of those requirements. Any type that satisfies the requirements of a protocol is said to conform to that protocol. - 프로토콜(protocol)은 메서드, 프로퍼티, 그 외 특정 작업을 위해 필요한 필수요소에 대한 설계도입니다. 하지만 프로토콜은 단순히 프로퍼티와 메서드를 선언하여 어떻게 사용해야하는지만을 정의해 놓았을 뿐, 실제 코드가 구현되어 있지는 않습니다. 프로토콜은 class, structure, enumeration에 의해 채택(adopt)되어 실제로 구현이 됩니다. 프로토콜의 필수요소(requirements)를 구현하는 경우, 프로토콜을 따랐다(conform)고 표현합니다.

Use protocol to declare a protocol. - protocol을 사용하여 프로토콜을 선언합니다.

  1. protocol ExampleProtocol {
  2. var simpleDescription: String { get }
  3. func adjust()
  4. }

Protocols can require that conforming types have specific instance properties, instance methods, type methods, operators, and subscripts. Protocols can require specific instance methods and type methods to be implemented by conforming types. These methods are written as part of the protocol’s definition in exactly the same way as for normal instance and type methods, but without curly braces or a method body.

Classes, structures, and enumerations adopt a protocol by listing its name after their name, separated by a colon. A type can adopt any number of protocols, which appear in a comma-separated list. If a class has a superclass, the superclass’s name must appear first in the list, followed by protocols. You conform to the protocol by implementing all of its requirements. 

Here, SimpleClass adopts the ExampleProtocol protocol, and conforms to the protocol by implementing the simpleDescription property and adjust() method.

  1. class SimpleClass: ExampleProtocol {
  2. var simpleDescription: String = "A very simple class."
  3. var anotherProperty: Int = 69105
  4. func adjust() {
  5. simpleDescription += " Now 100% adjusted."
  6. }
  7. }
  8. var a = SimpleClass()
  9. a.adjust()
  10. let aDescription = a.simpleDescription

Protocols are first-class types, which means they can be treated like other named types. For example, you can create an ExampleProtocol array and call adjust() on each of the instances in it (because any instance in that array would be guaranteed to implement adjust(), one of the protocol’s requirements).

  1. class SimpleClass2: ExampleProtocol {
  2. var simpleDescription: String = "Another very simple class."
  3. func adjust() {
  4. simpleDescription += " Adjusted."
  5. }
  6. }
  7. var protocolArray: [ExampleProtocol] = [SimpleClass(), SimpleClass(), SimpleClass2()]
  8. for instance in protocolArray {
  9. instance.adjust()
  10. }
  11. protocolArray

Swift and Cocoa Touch

Swift is designed to provide seamless interoperability with Cocoa Touch, the set of Apple frameworks you use to develop apps for iOS. As you walk through the rest of the lessons, it helps to have a basic understanding of how Swift interacts with Cocoa Touch.

So far, you’ve been working exclusively with data types from the Swift standard library. The Swift standard library is a set of data types and capabilities designed for Swift and baked into the language. Types like Stringand Array are examples of data types you see in the standard library.

  1. let sampleString: String = "hello"
  2. let sampleArray: Array = [1, 2, 3.1415, 23, 42]

When writing iOS apps, you’ll be using more than the Swift standard library. One of the most frequently used frameworks in iOS app development is UIKit. UIKit contains useful classes for working with the UI (user interface) layer of your app.

To get access to UIKit, simply import it as a module into any Swift file or playground.

  1. import UIKit

After importing UIKit, you can use Swift syntax with UIKit types and with their methods, properties, and so on.

  1. let redSquare = UIView(frame: CGRect(x: 0, y: 0, width: 44, height: 44))
  2. redSquare.backgroundColor = UIColor.redColor()

Many of the classes you’ll be introduced to in the lessons come from UIKit, so you’ll see this import statement often.

With this breadth of knowledge about Swift, you’re about to jump into making a full-fledged app in the next lesson. Although this lesson is the extent of playgrounds you’ll work with for now, remember that they can be a powerful tool in app development for anything from debugging, to visualizing complex code, to rapid prototyping.


댓글