Swift Language Guide: Basics

J.Noma·2021년 11월 14일
0

Swift : 문법

목록 보기
6/11

The Basics

서문

  • 기존 개발에 사용되던 C와 Obj-C을 고려하여 유사점을 가지고 개발되었다
  • 기존 언어C에 비해 Constant의 활용도가 높다 - for safer and clearer code
  • 기존에 없던 튜플도 쓴다 (예시. 함수에서 여러 개 return 하기)
  • Optional type이 등장.
    1. value가 없을 수 있음을 표현 (nil과 유사)
    2. Swift의 핵심 기능
  • type-safe 언어이다 = 언어 자체가 개발자가 type을 명확히 파악하는데 도움을 준다 optional과 non-optional도 엄격히 구분

상수와 변수

  1. 선언
  • 상수는 unchangeable, 변수는 changeable
  • 값이 바뀌지 않으면 항상 상수로 선언할 것
  • 한번에 여러 개를 선언할 수도 있다
    var x = 0.0, y = 0.0, z = 0.0
  1. Type 명시
var welcomeMessage: String

var red, green, blue: Double
  • 실제로는 Type 명시를 안해도 Swift가 initial value를 보고 적절히 부여한다
  • 하지만, 선언시점에 initial value를 안 줄거면 Type 명시를 해야함
  1. 이름짓기
  • any character가 가능하다. (대부분의 Unicode포함. 심지어 이모티콘같은 것도)
  • 안되는 것 List
    1. 공백
    2. 수학기호 (+, *, - )
    3. 이상한 Unicode (private-use Unicode scalar values, or box-drawing)
    4. line-
    5. 숫자로 시작하면 안됨
  • Swift의 reserved keyword를 사용하려면 백틱 ( ` ) 사용
    var `class`: String = "aa";
    print(`class`)

주석

  • C랑 똑같다. 다만, nested로 써도 된다는 점이 다르긴 하다
// This is a comment.

/* This is also a comment
but is written over multiple lines. */

/* This is the start of the first multiline comment.
 /* This is the second, nested multiline comment. */
This is the end of the first multiline comment. */

세미콜론

  • Swift는 세미콜론이 필요없다
  • 물론 한 줄에 여러 statement를 쓰려면 구분을 위해 필요하긴 하다
    let cat = "aaa"; print(cat)

정수

  • 다른 언어와 같이 unsigned / signed가 있다 특별한 경우가 아니면, 심지어 non-negative더라도 signed로 통일하는걸 권장한다 Type 변환을 최대한 피하기 위함. 상호운용성(?)에 좋기 때문
  • 8, 16, 32, 64 bit을 지원한다
    let minValue = UInt8.min  // minValue is equal to 0, and is of type UInt8
    let maxValue = UInt8.max  // maxValue is equal to 255, and is of type UInt8
  • 그냥 Int라고 사용하면 코드가 돌아가는 platform에 따라 Int32 혹은 Int64로 동작한다

Type safety / Type 추론

  • 개발자가 사용하는 value의 Type들을 명확히 파악할 것을 요구하는 언어
  • 그렇다고 모든 상수/변수에 type을 명시할 것을 요구하진 않음 (Swift 컴파일러가 코드 flow를 기반으로 Type 추론을 하기 때문)
  • 비록 type-safe 언어이긴 하나 type이 명확한 경우 생략할 수 있다는 것도 Swift의 장점 중 하나임
  • Type추론은 initial value가 있는 경우 특히 유용하다
  • 가끔은 아래와 같이 무슨 타입인지 딱 짚진 않는(?) 리터럴(literal value)로 주어 행해진다
    • 리터럴 : 소스코드에 무슨 값인지 보이는거

      let meaningOfLife = 42
  • 정수와 소수를 섞어 버리면 소수에 맞춰 Double로 추론
    let anotherPi = 3 + 0.14159
    // anotherPi is also inferred to be of type Double

숫자 표현 (Numeric Literals)

  • 진법 표현
    let decimalInteger = 17
    let binaryInteger = 0b10001       // 17 in binary notation
    let octalInteger = 0o21           // 17 in octal notation
    let hexadecimalInteger = 0x11     // 17 in hexadecimal notation
    없음 → 10진법 0b ~ → 2진법 0o ~ → 8진법 0x ~ → 16진법
  • Floating-Point

    • 10진법 or 16진법만 사용가능
  • Exponential

    1. 10진법
      • 1.25e2 == 1.25 x 10^2
      • 1.25e-22 == 1.25 x 10^-2
    2. 16진법
      • 0xFp2== 15 x 2^2
      • 0xFp-22 == 15 x 2^-2
  • 기타 가독성을 위한 특수문법

    • zero padding / 언더바

      let paddedDouble = 000123.456
      let oneMillion = 1_000_000
      let justOverOneMillion = 1_000_000.000_000_1

Numeric 변환

  • Integer를 표현할 땐 특별한 경우가 아니라면 상호운용성이 좋은 "Int"를 써라 (특별한 경우 : 성능, 메모리, 최적화, 코드에 명시적으로 표현하고 싶을 때 등)
  1. 정수 변환
  • 변환예시
    • 이런 변환 작업없이 바로 연산은 불가하다
      let twoThousand: UInt16 = 2_000
      let one: UInt8 = 1
      let twoThousandAndOne = twoThousand + UInt16(one)
    • SomeType(ofInitialValue)를 사용하면 "넣어주는 value"로 해당 Type의 initializer가 수행되며 새로운 인스턴스를 생성한다
    • "넣어주는 value"가 해당 Type의 initializer에서 수용가능한 Type이어야 한다
    • 기존에 initializer가 구현되어 있지않은 Type을 변환하게 하려면 extension을 추가하는 방식으로 가능하다
  1. 정수 ←→ 실수 간 변환
  • 변환예시
    let three = 3
    let pointOneFourOneFiveNine = 0.14159
    let pi = Double(three) + pointOneFourOneFiveNine
    let integerPi = Int(pi)
    • 4.75 → 4
    • -3.9 → -3.
  • 참고사항
    • Numeric 상/변수를 더하는 것과 리터럴을 더하는 것은 다름
    • 상/변수로 이미 만들어진 것은 더할 때 Type check가 들어가지만 리터럴로 할 때는 아직 명시적인 type이 없기 때문에 먼저 더하고 Type 추론이 이루어진다
  1. Type 별명 설정
  • 기존에 있던 Type의 이름을 달리 쓰고 싶다
typealias AudioSample = UInt16

var maxAmplitudeFound = AudioSample.min
  • 경우에 따라 이런 표현이 가독성에 도움된다

Boolean

  • 다른 언어의 bool과 동일하여 기본적인 설명은 skip
  • if 문에 정수를 사용할 수 없다
    let i = 1
    if i {
        // 컴파일 에러
    }

튜플

  • 여러 value를 하나의 compound로 묶는 것
  • 아무 type이나 사용가능하며 튜플 내 type을 통일할 필요도 없다
  • 간단한 Data 묶음일 때 유용하고 complex하다면 class나 struct 사용을 권장
  1. 사용예시 - 선언

    let http404Error = (404, "Not Found")
    1. 여러 Type을 섞어 써도 된다
  2. 사용예시 - 값 추출

    //값 추출 #1
    let (statusCode, statusMessage) = http404Error
    print("The status code is \(statusCode)")
    print("The status message is \(statusMessage)")
    
    //값 추출 #2
    let (justTheStatusCode, _) = http404Error
    print("The status code is \(justTheStatusCode)")
    
    //값 추출 #3
    print("The status code is \(http404Error.0)")
    print("The status message is \(http404Error.1)")
    1. 굳이 뽑아내고 싶지 않은 값은 언더바 처리
    2. 바로 사용하려면 .0 혹은 .1 같은 숫자로 접근
  3. 사용예시 - Name을 부여할 수도 있다

    let http200Status = (statusCode: 200, description: "OK")
    
    print("The status code is \(http200Status.statusCode)")
    print("The status message is \(http200Status.description)")

Optionals

  • value가 존재하지 않을 수 있음을 인지시키기 위한 개념
  • Objective-C에 "valid Object가 없음"을 뜻하는 nil return이 있지만 Obj에서 대해서만 쓸 수 있고 class, struct 등은 사용불가 (그래서 NSNotFound라는걸 사용하는데 일단 skip)
  • 예시
    • 정수 initializer 중 String → Int 변환자는 실패할 수 있으므로 optional을 return하도록 선언되어 있다

  1. nil
  • value가 없는 상태를 표현하는 방법
  • optional로 선언한 상/변수에만 사용할 수 있다
  • optional로 선언한 상/변수에 초기값을 주지 않으면 nil로 초기화된다
  • 참고로, Objective-C의 nil과는 다른 개념이다. (Obj-C에서는 존재하지 않는 오브젝트를 가리키는 '포인터'를 의미) (굳이 따지자면 C언어의 null과 유사한 듯하다. 단순히 값이 없음을 의미)
  1. 강제 unwrapping (with if문)
  • if로 nil 검사 후 느낌표(!)로 과감히 까서 사용하는 기법이 있다
  • nil 검사 안 했다가 nil을 느낌표로 까면 런타임 에러 발생
if convertedNumber != nil {
    print("convertedNumber has an integer value of \(convertedNumber!).")
}
  1. Optional Binding (한국말로 뭘까)
  • 느낌표처럼 unwrapping syntax가 명시적으로 존재하진 않지만 암묵적으로 수행된다
  • 참고로, if let이든 if var이든 무관하다
  • if 혹은 while 문에 아래 예시와 같이 작성하면 동작한다
    if let actualNumber = Int(possibleNumber) {
        print("The string \"\(possibleNumber)\" has an integer value of \(actualNumber)")
    } else {
        print("The string \"\(possibleNumber)\" couldn't be converted to an integer")
    }

if let 구문의 의미는,

"Int(possibleNumber)에서 return되는 Optional Int 타입의 value가 존재하면 이를 unwrapping하여 actualNumber에 저장한다"

  1. 기타
  • 한번에 여러개 Binding할 수도 있다
    if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber < secondNumber && secondNumber < 100 {
        print("\(firstNumber) < \(secondNumber) < 100")
    }
  • if let 구문에서 선언한 상/변수는 if 중괄호 내에서만 사용가능하지만 guard let으로 선언하면 중괄호 밖에서도 (부모 함수 내에서) 사용 가능하다
  1. 암시적 Unwrapped Optionals
  • 값이 코드끝까지 항상 존재함(=nil이 아님)을 이미 알고 있을 때 unwrap없이 바로 사용이 가능하도록 선언하는 방법
  • class initialization에서 유용하다는데 일단 지금은 skip
  • 선언 시점에 unwrap을 하는게 아니다. 사용 시점에 Optional로 사용될 수 있는 구문인지 먼저 check한 후에 불가하면 그제서야 unwrapping하는 구조로, 사용 시점에 그때그때 unwrapping이 발생한다
  • 예시#1. unwrapping되는 경우
    //일반 Optional
    let possibleString: String? = "An optional string."
    let forcedString: String = possibleString! // requires an exclamation point
    
    //암시적 Unwrapped Optionals
    let assumedString: String! = "An implicitly unwrapped optional string."
    let implicitString: String = assumedString // no need for an exclamation point
  • 예시#2. Optional인 그대로 사용되는 경우
    let assumedString: String! = "An implicitly unwrapped optional string."
    
    //optionalString의 타입은 "String?"가 된다
    let optionalString = assumedString
    
    //기본은 일반 Optional로 간주되므로 if let구문에도 사용가능
    if let definiteString = assumedString {
        print(definiteString)
    }

Error Handling

  • 예기치 않은 Error가 발생했을 때 그 내용을 파악하고 필요시 코드의 다른 부분으로 전달한다
  1. Error를 던지는 함수임을 알림 - throws

  2. Error가 발생할 수 있는 코드영역 - do

  3. Error를 던지는(throw) 함수 - try

  4. Error 발생 시 수행할 코드 - catch

    func canThrowAnError() throws {
        // this function may or may not throw an error
    }
    
    do {
        try makeASandwich()
        eatASandwich()
    } catch SandwichError.outOfCleanDishes {
        washDishes()
    } catch SandwichError.missingIngredients(let ingredients) {
        buyGroceries(ingredients)
    }
    //parameter(ex. ingredients)를 넣어주면 error의 내용이 담긴다

Assertions and Preconditions

  • 디버깅 목적으로 runtime 중에 어떤 확인 작업을 넣는 것

  • 어떤 코드를 실행하기 전에 반드시 만족해야 하는 조건에 "예기치 않은" 문제가 있는지 확인절차를 넣을 수 있다.

  • 명백히 일어나면 안되는 조건을 넣는 것이므로 복구같은 것 없이 app을 종료시킨다. 에러에 대한 예방책으로 동작하는게 아니다

  • 이런 구문은 documentation 기능을 할 수도 있다

  • Assertions과 Preconditions의 차이는

    1. Assertion은 Debug build에서만 동작

    2. Precondition은 Debug + Product build 모두에서 동작

      그러므로, 개발 중엔 Assertion을 막 써도 된다

  • Assertion 예시
    // assert(_:_:file:line:)
    let age = -3
    assert(age >= 0, "A person's age can't be less than zero.")
    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 {
    		//이미 fail인게 확인되었으면 조건문없이 바로 assert
        assertionFailure("A person's age can't be less than zero.")
    }
  • Precondition 예시
    • App을 종료시켜버리기 때문에 명백히 잘못일때만 사용할 것

      // precondition(_:_:file:line:)
      precondition(index > 0, "Index must be greater than zero.")
      
      //이미 fail인게 확인되었으면 조건문없이 바로 precondition
      preconditionFailure("Index must be greater than zero.")
  • compile 시 최적화 모드 중 unchecked mode (-Ounchecked) 사용 시 precondition이 동작하지 않는다 대신, fatalError(_:file:line:)를 사용하면 최적화와 무관하게 무조건 App 종료시키는데 미완성일 때 테스트용 정도로만 쓸만하다
profile
노션으로 이사갑니다 https://tungsten-run-778.notion.site/Study-Archive-98e51c3793684d428070695d5722d1fe

0개의 댓글