Swift 기본문법

이재원·2024년 6월 23일
0

Swift

목록 보기
1/4

기본(The basics)

타입 세이프티와 타입 추론

  • 스위프트는 타입 세이프(type-safe)언어입니다.
  • 코드 컴파일시 타입 검사 통해 일치하지 않은 타입을 오류로 표시합니다.
  • 타입 추론도 가능하기 때무에 모든 상수와 변수에게 타입을 지정해줄 필요는 없습니다.
  • 정수와 부동소수 리터럴을 합치면 Double 타입으로 추론합니다.

타입 별칭(Type Aliases)

타입 별칭은 typealias 키워드를 이용하여 이미 존재하는 타입을 다른 이름으로 정의합니다.

typealias AudioSample = String

var audioName: AudioSample = "audio"

var audioName: String = "audio"

이런 식으로 AudioSample을 String이라는 타입으로 별칭을 주어 String 타입처럼 사용할 수 있도록 할 수 있습니다.

튜플(Tuples)

튜플은 여러값을 단일 복합 값으로 그룹화 합니다. 튜플안에 값은 어떠한 타입도 가능하며 서로 같은 타입일 필요는 없습니다.

let http404Error = (404, "Not Found")

이 예제는 HTTP 상태 코드를 나타내는 튜플입니다. 이 예제에서는 상태 코드인 Int와 설명이 담긴 String을 함께 그룹화 하여 (Int, String) 튜플 타입으로 제공합니다.

이 외에도 어떤 타입으로든 가능하며, (Int, Int, Int) 또는 (String, Bool)과 같이 만들 수 있습니다.

let (statusCode, statusMessage) = http404Error

print("The status code is \\(statusCode)")
print("The status message is \\(statusMessage)")

이처럼 튜플의 내용을 별도의 상수 또는 변수로 분해하여 접근할 수 있습니다.

let (justTheStatusCode, _) = http404Error

print("The status code is \\(justTheStatusCode)")
print("The status code is \\(http404Error.0)")
print("The status message is \\(http404Error.1)")

튜플을 정의할 때 튜플 요소에 이름을 통해 접근이 가능합니다.

let http200Status = (statusCode: 200, description: "OK")

print("The status code is \\(http200Status.statusCode)")

print("The status message is \\(http200Status.description)")

추가로, 튜플은 관련된 값의 간단한 그룹화를 시키는데는 유용하지만, 복잡한 데이터 구조를 생성하는데는 맞지 않습니다. 이 경우에는 클래스나 구조체를 사용하는 것이 더 적합합니다.

옵셔널(Optionals)

값이 없는 경우에 옵셔널을 사용합니다. 옵셔널은 값이 있을 수도 있고 없을 수도 있다는 것을 의미하며, 지정된 타입의 값이 있고 옵셔널을 풀어서 접근할 수 있습니다.

let possibleNumber: String = "123"
let convertedNumber: Int? = Int(possibleNumber)

코드에서 타입 뒤에 (?)가 붙은 것을 볼 수 있는데 이것이 옵셔널을 표현하는 방식입니다. 만약 possibleNumber의 값이 123이 아닌 Hello라면 이를 Int로 변환하는 것을 실패할 것입니다. 이럴 경우 값이 없음(nil)을 반환하여 오류를 방지할 수 있습니다.

nil

옵셔널 변수에 특수한 값 nil로 지정하여 값이 없는 상태를 나타낼 수 있습니다. 즉, 옵셔널이 아닌 상수 또는 변수에는 nil을 사용할 수 없습니다.

var serverResponseCode: Int? = 404
// serverResponseCode에 정수 404를 포함
serverResponseCode = nil
// serverResponseCode에는 값이 없음을 뜻함

이와 같이 옵셔널 변수를 정의하면 값을 넣을 수도 있고, nil을 지정하여 값이 없음을 뜻할 수도 있습니다.

var surveyAnswer: String?

이렇게 변수를 선언하게 될 경우에는 자동적으로 nil로 설정됩니다.

옵셔널 바인딩(Optional Binding)

옵셔널 바인딩은 옵셔널 값을 포함하고 있는지 확인하고 값이 있는 경우 해당 값을 임시 상수 또는 변수로 사용할 수 있게 해줍니다. 옵셔널 바인딩은 if, gaurd, while 구문에서 값을 체크하고 임수 상수나 변수로 추출할 수 있습니다.

if let actualNumber = convertedNumber {
    print("The string \\"\\(convertedNumber)\\" has an integer value of \\(actualNumber)")
} else {
    print("The string \\"\\(convertedNumber)\\" could not be converted to an integer")
}

이 코드는 convertedNumber에 값이 있을 경우와 없을 경우(nil)를 조건문을 통해 구분하고, 이를 actualNumber라는 임시 상수에 추출하는 코드입니다. 이 경우 임시 상수actualNumber는 Int타입이 되고, 이 과정을 옵셔널 바인딩이라고 합니다.

if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber < secondNumber && secondNumber < 100 {
    print("\\(firstNumber) < \\(secondNumber) < 100")
}
// Prints "4 < 42 < 100"

if let firstNumber = Int("4") {
    if let secondNumber = Int("42") {
        if firstNumber < secondNumber && secondNumber < 100 {
            print("\\(firstNumber) < \\(secondNumber) < 100")
        }
    }
}
// Prints "4 < 42 < 100"

필요한 경우 if구문에 쉽표로 구분하여 옵셔널 바인딩 및 부울 조건을 여러개 포함할 수 있습니다.

if구문에서 옵셔널 바인딩으로 생성된 상수와 변수는 if구문의 본문내에서만 사용 가능합니다.

대체값 제공(Providing a Fallback Value)

누락된 값을 처리하는 다른 방법은 nil-결합 연산자(??)를 사용하여 기본값을 제공하는 방법입니다.

let name: String? = nil
let greeting = "Hello, " + (name ?? "friend") + "!"
print(greeting)
// Prints "Hello, friend!"

옵셔널에서 name의 값이 nil이 아니라면 name이 사용되고, nil이라면 대체값인 friend가 사용됩니다.

강제 언래핑(Force Unwrapping)

강제 언래핑은 다른 언래핑 방법에 비해 간단하지만 주의가 필요한 방법입니다. 강제 언래핑은 변수 뒤에 느낌표(!)를 붙여주면 됩니다.

let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)

let number = convertedNumber!
print("number: \\(number)")
// Prints number: 123

강제 언래핑은 nil값이 아닌 경우에는 정상적으로 작동하지만 nil이 들어 있는 경우에는 에러가 발생합니다.

암시적으로 언래핑된 옵셔널(Implicitly Unwrapped Optionals)

옵셔널은 상수 또는 변수에 값이 없다는 것을 의미하는데, 이를 옵셔널 바인딩을 통해 접근할 수 있습니다. 하지만 때로는 옵셔널 값을 처음 설정한 후 항상 값을 갖는 경우, 값에 접근할 때마다 언래핑 할 필요가 없습니다. 이러한 옵셔널을 언래핑된 옵셔널이라고 합니다.

암시적으로 언래핑된 옵셔널은 내부적으로 옵셔널이지만 옵셔널에 접근할 때마다 옵셔널 값을 풀 필요없어 옵셔널이 아닌 값처럼 사용할 수 있습니다. 하지만 중간에 nil이 될 가능성이 있다면 에러가 발생하기 때문에 언래핑하여 사용하면 안됩니다.

let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // Requires explicit unwrapping

let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // Unwrapped automatically

이 코드에서 assumedString은 implicitString이 명시적으로 옵셔널이 아닌 String 타입이기 때문에 implicitString에 값을 할당하기 전에 강제로 언래핑 됩니다.

아래의 코드에서 optionalString은 명시적 타입이 없으므로 기본적으로 옵셔널입니다.

let optionalString = assumedString
// The type of optionalString is "String?" and assumedString isn't force-unwrapped.

암시적으로 언래핑 된 옵셔널이 nil이고 래핑된 값에 접근하려고 하면 런타임 에러가 발생합니다. 결과는 값을 포함하지 않는 일반적인 옵셔널을 강제 언래핑을 하기 위해 느낌표를 작성한 것과 같습니다.

if assumedString != nil {
    print(assumedString!)
}
// Prints "An implicitly unwrapped optional string."

또한 언래핑된 옵셔널은 옵셔널 바인딩을 단일 구문으로 사용하여 해당 값을 확인하고 언래핑할 수 있습니다.

if let definiteString = assumedString {
    print(definiteString)
}
// Prints "An implicitly unwrapped optional string."

에러 처리(Error Handling)

에러 발생시 에러 핸들링을 사용하여 처리할 수 있습니다.

func makeASandwich() throws {
    // this function may or may not throw an error
}

함수는 선언에 throws 키워드를 포함시켜 에러가 발생할 수 있음을 나타냅니다. 에러가 발생할 수 있는 함수를 호출할 때는 do try catch를 사용하여 호출할 수 있습니다.

func makeASandwich() throws {
    // ...
}

do {
    try makeASandwich()
    eatASandwich() // 에러가 없을 경우 실행
} catch SandwichError.outOfCleanDishes {
    washDishes()
} catch SandwichError.missingIngredients(let ingredients) {
    buyGroceries(ingredients)
	}
}

makeASandwich() 함수는 에러를 발생할 수 있으므로 try표현식으로 래핑됩니다. 함수 호출을 do 구문으로 래핑하면 에러 발생시 catch절로 전달됩니다.

역설과 전제조건(Assertions and Preconditions)

Assertions and Preconditions은 런타임시 발생하는 조건입니다. 이를 사용하여 추가 코드를 실행하기 전에 필수조건이 충족되는지 확인할 수 있습니다. 이 값이 true인 경우에는 정상적으로 작동하지만 false인 경우에는 앱이 종료됩니다.

Assertions은 개발과정에서 실수와 잘못된 가정을 찾는데 도움이 되고, Preconditions은 프로덕션 문제를 감지하는데 도움이 됩니다. 에러 처리와 다르게 역설과 전제조건은 복구 가능하거나 예상되는 에러에 사용되는 것이 아닙니다. 역설이 false라는 것은 프로그램의 데이터 중 하나가 유효하지 않다는 것이고, 이는 유효하지 않은 프로그램 상태를 나타내기 때문에 복구하는 것이 불가능합니다.

이를 사용하는 이유는 유효하지 않은 상태가 발생하면 앱이 종료되기 때문에 더 쉽게 디버깅 할 수 있습니다. 이를 확인하지 않고 계속 코드를 작성하면 다른 코드가 작동하지 않게 되고, 사용자 데이터가 손상된 후에 문제를 알 수 있습니다.

역설과 전제조건의 차이점은 언제 체크되는지에 있습니다. 역설은 디버그 빌드에서만 체크되지만 전제조건은 디버그와 프로덕션 빌드에서 체크됩니다. 따라서 프로덕션 빌드일 때는 역셜 내부의 조건은 실행되지 않습니다. 즉, 이는 프로덕션에서 성능의 영향이 없이 개발 단계에서 많은 양의 역설을 사용할 수 있다는 뜻입니다.

역설을 통한 디버깅(Debugging with Assertions)

swift 표준 라이브러리에 있는 '''assert()'''함수를 이용합니다.

let age = -3
assert(age >= 0, "A person's age can't be less than zero.")
// This assertion fails because -3 is not >= 0.

이렇게 사용될 수 있습니다.

assert(age >= 0)

메세지를 생략하고 싶은 경우에는 조건만 넣어주면 됩니다.

if age > 10 {
    print("You can ride the roller-coaster or the ferris wheel.")
} else if age >= 0 {
    print("You can ride the ferris wheel.")
} else {
    assertionFailure("A person's age can't be less than zero.")
}

코드가 이미 조건이 체크되었다면 '''assertionFailure'''함수를 사용합니다.

if age > 10 {
    print("You can ride the roller-coaster or the ferris wheel.")
} else if age >= 0 {
    print("You can ride the ferris wheel.")
} else {
    assertionFailure("A person's age can't be less than zero.")
}

강제 전제조건(Enforcing Preconditions)

'''precondition(

:

:file:line:) 함수로 전제조건을 작성할 수 있습니다.

// In the implementation of a subscript...
precondition(index > 0, "Index must be greater than zero.")

출처
이 글은 swift 공식문서를 읽고 정리한 내용입니다.
https://bbiguduk.gitbook.io/swift/language-guide-1/the-basicshttps://swiftdoc.org/

profile
20학번 새내기^^(였음..)

0개의 댓글