Swift 기본 문법

11 분 소요

상수와 변수

상수(값이 변하지 않는 수): let 상수명: 데이터 타입 = 값
변수(값이 변하는 수): var 변수명: 데이터 타입 = 값


기본 데이터 타입

Int: 64bit 정수형
Uint: 부호가 없는 64bit 정수형
Float: 32bit 부동 소수점
Double: 64bit 부동 소수점
Bool: true, false 값
Character: 문자
Ex) var someCharacter: Character = “가”
String: 문자열
Any: 모든 타입을 지칭하는 키워드

  • 타입 추론: 데이터 타입을 명시하지 않아도 알아서 값을 보고 데이터 타입을 추론
    ex) var number = 10

컬렉션 타입(collection type)

: 데이터들의 집합 묶음

  1. Array: 데이터 타입의 값들을 순서대로 저장
    Ex)
     var numbers: Array<Int> = Array<Int>()
     = var numbers: [Int]()
     = var numbers: [Int] = [] 
    

    데이터 추가: append((data)) ex) numbers.append(1)
    중간에 데이터 추가: insert((data), at: (location)) ex) numbers.insert(4, at: 2)
    데이터 제거: remove(at: (location)) ex) numbers.remove(at: 0)

  2. Dictionary: 순서없이 키(key)와 값(value) 한 쌍으로 데이터를 저장
    Ex)
      var dic: Dictionary<String, Int> = Dictionary<String, Int>()
     = var dic: [String: Int] = [:]
    

    데이터 추가: dic[(key)] = (value)
    데이터 제거: removeValue(forKey: (key)) ex) dic.removeValue(forKey: “김경록”)

  3. Set: 같은 데이터 타입의 값을 순서없이 저장
    Ex)
    var set: Set = Set<Int>()
    

    데이터 추가: insert ex) set.insert(10)
    데이터 제거: remove ex) set.remove(20)


함수 사용법


func 함수명(파라미터 이름: 데이터 타입) -> 반환 타입 {
	return 반환 값
}

! 반환타입이 void일 경우 생략가능 !

전달인자 레이블(from, to) 을 사용한 함수
-> 매개변수 역할을 명확히 하기 위해, 코드 가독성 높아진다


func 함수명(전달인자 레이블 매개변수 이름: 매개변수 타입, 
	   전달인자 레이블 매개변수 이름: 매개변수 타입…) -> 반환 타입 {
	return 반환 값
}

Ex)

func sendMessage(from myName: String, to name: String){
	return “Hello \(name)! I’m \(myName)”
}
sendMessage(from: “Gunter”, to: “Json”)
  • 와일드카드 식별자(_)
    매개변수 이름을 적어주지 않아도 값으로만 함수의 매개변수 전달 가능
    Ex)
    func sendMessage(_ name: String) -> String {
      return “Hello \(name)”
    }
    sendMessage(“Gunter”)
    
  • 가변 매개변수(…)
    매개변수로 몇개의 값이 들어올지 모를때, 0개 이상 값을 받아올 수 있다, 배열처럼 사용 함수마다 1개만 사용가능
    Ex)
    func sendMessage(me: String, friends: String…) -> String {
      return “Hello \(friends)! I’m \(me)”
    }
    sendMessage(me: “Gunter”, friends: “Json”, “Albert”, “Stella”)
    

    ! 함수는 1급 객체이기 때문에 변수, 상수로 할당 및 매개변수로 전달 가능 !


조건문

  • if:

 	if 조건식 {
    		조건식1을 만족할 때 실행할 구문
	} else if 조건식2 {
    		조건식2을 만족할 때 실행할 구문
	} else {
     		아무 조건식도 만족하지 않을 때 실행할 구문
	}

  • switch:

	switch 비교대상 {
  	    case 패턴1:
		패턴1 일치할 때 실행되는 구문
  	    case 패턴2, 패턴3:
		패턴2, 3이 일치할 때 실행되는 구문
  	    default:
		어느 비교 패턴과도 일치하지 않을 때 실행되는 구문
	}

  • guard
! 사잇값(…) 15…25: 15이상 25이하 !

반복문

  • for-in: 일정횟수만큼 특정구문을 반복하고자 할 때

	for 루프상수 in 순회대상(문자열, 컬렉션 타입, 사잇값) {
		실행할 구문
	}

  • while: 주어진 조건식이 false가 될 때까지 반복

	while 조건식 {
		실행할 구문
	}

  • repeat-while: 적어도 한번은 구문을 실행 /c언어의 do-while

	repeat {
		실행할 구문
	} while 조건식


옵셔널(optional)

값이 있을 수도 있고 없을 수(nil)도 있다.
변수에 nil을 지정하기 위해서는 type뒤에 ‘?’를 붙인다.
Ex) var name: String? = “”(초기화도 가능)
옵셔널이 아닌 변수에 옵셔널 변수를 대입할 수 없다. -> 옵셔널 바인딩


옵셔널바인딩(optional binding)

  • 명시적 해제
    • 강제 해제 (!)
      값이 nil인 변수를 강제 해제하게 될 경우 프로그램이 강제종료될 수 있으므로 위험한 방법
      Ex)
      var number: Int? = 3
      print(number!)
      
    • 비강제 해제(옵셔널 바인딩)
      1. if문
        Ex)
          if let result = number {
         print(result)
          } else {
         // 값 추출 실패시
          }
        
      2. guard문
        Ex) guard let result = number else { return }

    if문 - 추출된 변수를 if문 내에서 사용, guard문 - 함수 전체에서 사용

  • 묵시적 해제
    • 컴파일러에 의한 자동 해제
      비교 연산자를 사용하면 컴파일러가 자동적으로 옵셔널값 해제

      Ex)

      if number == 6 {
            print(“number = 6”)
      } else {
            printf(“number != 6”)
      }
      
    • 옵셔널의 묵시적 해제
      타입 뒤에 “!”가 붙어있는 옵셔널 변수는 사용할 때만 묵시적으로 해제

      Ex)

      let string = “12”
      var stringToInt: Int! = Int(string)
      

구조체(struct)

  • 값 타입
  • 구조체 변수를 새로운 변수에 할당할 때마다 새로운 구조체가 할당됩니다.
  • 즉 같은 구조체를 여러 개의 변수에 할당한 뒤 값을 변경시키더라도 다른 변수에 영향을 주지 않음
    (값 자체를 복사)

  • 선언:

	struct 구조체 이름 {
    	   프로퍼티(멤버변수)와 메소드(멤버함수)
	}

  • 호출:

	var 변수 = 구조체(프로퍼티) // 호출 시 프로퍼티 초기화

  • 프로퍼티 접근:

	변수.프로퍼티


클래스(class)

  • 참조 타입
  • ARC로 메모리를 관리
  • 상속이 가능
  • 타입 캐스팅을 통해 런타임에서 클래스 인스턴스의 타입을 확인할 수 있음
  • deinit을 사용하여 클래스 인스턴스의 메모리 할당을 해제할 수 있음
  • 같은 클래스 인스턴스를 여러 개의 변수에 할당한 뒤 값을 변경 시키면 모든 변수에 영향을 줌(메모리가 복사 됨)

  • 선언:

	class 클래스 이름 {
	   프로퍼티와 메소드
	}

  • 호출:

	var 변수 = 클래스() // **선언시 클래스 내에서 초기화**


클래스와 구조체의 공통점

  • 값을 저장할 프로퍼티를 선언할 수 있습니다
  • 함수적 기능을 하는 메서드를 선언할 수 있습니다
  • 내부 값에 . 을 사용하여 접근할 수 있습니다
  • 생성자를 사용해 초기 상태를 설정할 수 있습니다
  • extension을 사용하여 기능을 확장할 수 있습니다.
  • protocol을 채택하여 기능을 설정할 수 있습니다.

클래스와 구조체의 차이점

struct a { var p1, let p2 } => 값 타입
var instance = a() -> p1 값 변경 가능, p2 값 변경x
let instance = a() - > p1 값 변경x, p2 값 변경x

class b { var p1, let p2 init(){} } => 참조 타입
let instance = a() - > p1 값 변경 가능, p2 값 변경x


Init(initialization, 초기화)

클래스 구조체 또는 열거형의 인스턴스를 사용하기 위한 준비과정
인스턴스가 생성되면 호출


init(매개변수: 타입, …) {
	// 프로퍼티 초기화
	// 인스턴스 생성시 필요한 설정을 해주는 코드 작성
}

Ex)

Class User {
	var nickname: String
	var age: Int

	init(nickname: String, age: Int) { // 기본 정의 생성자
		self.nickname = nickname
		self.age = age
	}
	init(age: Int) { // 사용자 설정 생성자
		self.nickname = “Albert”
		self.age = age
	}
	deinit { // 클래스가 더이상 필요하지 않으면(nil 대입) 자동으로 메모리에서 소멸 -> deinit 발생
		print(“deinit”)
	}
}

프로퍼티(Property)

  • 저장 프로퍼티, Stored Property(구조체, 클래스)
    인스턴스에 프로퍼티의 값이 저장
    Ex)
      Class User {
          var nickname: String
          var age: Int
      }
    
  • 연산 프로퍼티, Computed Property(구조체, 클래스, 열거형)
    getter, setter을 이용해서 값을 연산하고 프로퍼티에 전달
    Ex)
      struct Stock {
          var averagePrice: Int
          var quantity: Int
          var purchasePrice: Int {
              get { // get만 구현 가능 (읽기전용, 값 변경x)
                  return averagePrice * quantity
              }
              set(newValue) { // purchasePrice 값이 변할 때 호출
                  averagePrice = newValue / quantity
              }
          }
      } 
    
    • 프로퍼티 옵저버, Property Observer
      (저장 프로퍼티, overriding된 저장프로퍼티, overriding된 계산 프로퍼티)
      • 프로퍼티 값의 변화를 관찰하고 반응(set 될때마다)
      • willSet: 값이 저장되기 직전에 호출
      • didSet: 값이 저장된 직후에 호출
        Ex)
          class Account {
          var credit: Int = 0 {
          willSet {
          print(“잔액이 \(credit)원에서 \(newValue)원으로 변경될 예정”)
          }
          didSet {
          print(“잔액이 \(oldValue)원에서 \(credit)원으로 변경”)
          }
          }
          credit = 1000 // set 되기전 willSet, 된 후 didSet 호출
        
  • 타입 프로퍼티, Type Property(저장, 연산 프로퍼티)
    인스턴스 생성없이 객체내의 프로퍼티에 접근을 가능하게 함
    Ex)
    Struct S {
      static var a = “”
      static var b: Int {
          return 1
      }
    }
    S.a
    S.b
    

상속


class 클래스 이름: 부모클래스 이름 {
   //하위 클래스 정의
}

  • base 클래스: 상속받지 않은 클래스
  • super 클래스: 부모 클래스, 상위 클래스
  • sub 클래스: 자식 클래스, 하위 클래스
  • 자식클래스는 부모클래스의 모든 프로퍼티와 메소드를 상속받아 사용가능

  • Overriding
    서브클래스는 super클래스의 프로퍼티, 메소드, subscript을 자신만의 기능으로 바꿔 사용
    Ex) override func a() {}

    • super 클래스의 메소드를 사용하기 위해서는 ‘super’ 키워드를 사용
      Ex) override func a() { super.a() }

    • 프로퍼티 override
      프로퍼티 자체가 아니라 프로퍼티의 getter, setter, 옵저버를 재정의
      Ex)

        override var description: String {
        return super.description + “ in gear \(gear)”
        }
      
      > 계산프로퍼티, 저장프로퍼티를 override한 프로퍼티는 getter, setter를 가질 수 있음
      > 자식 클래스에서 재정의하려는 프로퍼티는 슈퍼클래스 프로퍼티명과 일치
      > super클래스에서 read write -> sub클래스 read only X
      > super클래스에서 read only -> sub클래스 read write O
      > 상속된 override 프로퍼티에 옵저버도 추가 가능

      Ex)

        override var currentSpeed: Double {
            didSet {
                gear = Int(currentSpeed / 10) + 1
            }
        }
      

      상수 저장 프로퍼티, read only 연산프로퍼티에는 프로퍼티 옵저버 추가x -> 값을 설정할 수 없기 때문


Final

메소드, 프로퍼티, type프로퍼티가 override되는 것을 방지
Ex)

final var currentSpeed = 0.0
final class AutomaticCar: Car { }  

타입 캐스팅(type casting)

  • 인스턴스의 타입을 확인
  • 어떠한 클래스의 인스턴스를 해당 클래스 계층 구조의 슈퍼 클래스나 서브 클래스로 취급
  • is
    Ex)
      for item in library {
          if item is Movie {
          movieCount += 1
          } else if item is Song {
          songCount += 1
          }
      }
    
  • as 형변환(다운캐스팅)
    as? : 조건부형식, 다운캐스팅하려는 타입의 옵셔널 값 반환
    as! : 강제형식, 강제로 언래핑하여 값 반환, 성공 확신이 있을때만 사용(runtime error)
    Ex)
      for item in library {
          if let movie = item as? Movie {
          print(“Movie: \(movie.name), dir. \(movie.director)”)
          // 다운 캐스팅이 된 타입에 접근이 가능
          } else if let song = item as? Song {
          print(“Song: \(song.name), by \(song.artist)”)
          }
      }
    

assert

  • 특정 조건을 체크하고, 조건이 성립되지 않으면 컴파일 에러 메세지를 출력하게 할 수 있는 함수
  • assert 함수는 디버깅 모드에서만 동작하고 주로 디버깅 중 조건의 검증을 위하여 사용
    Ex)
      var value = 2
      assert(value == 0, “값이 0이 아닙니다”)
    

guard

  • 뭔가를 검사하여 그 다음에 오는 코드를 실행하지 말지 결정 하는 것
  • guard 문에 주어진 조건문이 거짓일 때 구문이 실행됨(early exit - 방어코드)
    Ex)
      guard value == 0 else { return }
    
  • guard 문을 이용한 옵셔널 바인딩
    Ex)
      var value: Int? 
      guard let value = value else { return } // 값이 nil일 경우 return
      print(value)
    

프로토콜(Protocol)

특정 역할을 하기 위한 Method, Property, 기타 요구사항 등의 청사진
Ex)

protocol SomeProtocol { }
protocol SomeProtocol2 { }
struct SomeStructure: SomeProtocol, SomeProtocol2 { }
class SomeClass: SomeSuperclass, SomeProtocol, SomeProtocol2 {}
// 상속받았다면 super class는 맨앞에 위치
  • Type Property(Static)
    Ex)

     protocol SomeProtocol3 {
         static var someTypeProperty: Int { get set }
     }
    
  • function :매개변수 지정, 구현x
    Ex)
      protocol SomeProtocol4 {
          func someTypeMethod(int a)
      }
    
  • initializer
    Ex)
      protocol SomeProtocol5 {
          init(someParameter: Int)
      }
    
    • 구조체
        struct someStructure: SomeProtocol5 {
        init(someParameter: Int) { }
        }
      
    • 클래스
        class someClass: SomeProtocol5 {
        required init(someParameter: Int) { }
        }
      
    • 상속받을 수 없는 final class일 경우
        final class someClass: SomeProtocol5 {
        init(someParameter: Int) { }
        }
      

extension

기존의 클래스, 구조체, 열거형, 프로토콜에 새로운 기능을 추가하는 기능
기존 기능을 override할 수 없다.

  • Computed Type Property / Computed Instance Property
    • Not Stored Property
    • Can’t add a Property Observer to the existing Property

    Ex)

      extension Int {
          var isEven: Bool {
              return self % 2 == 0
          }
      }
      var number = 3
      number.isEven // false
    
  • Type Method / Instance Method
    Ex)
      extension String {
          func convertToInt() -> Int? {
              return Int(self)
          }
      }
      var string = “0”
      string.convertToInt() // 0 
    
  • Initaializer
  • Subscript
  • Nested Type
  • Functions for a specific Protocol

열거형(enum)

연관성이 있는 값을 모아 놓은 것
switch구문에서 활용

enum Number { // 하나의 새로운 type처럼 사용 <swift 이름규칙> 이름은 대문자로 시작
	case one, two
	case three
	case four
}
var num = Number.two
num = .one
  • 원시값을 가지는 열거형
    특정타입으로 지정된 값을 가짐
    Ex)
    enum CompassPoint: String {
      case north = “북”
      case south = “남”
      case east = “동”
      case west = “서”
    }
    var direction = .north
    
    • 원시값 반환 direction.rawValue // “북”
    • 열거형 반환 direction = CompassPoint(rawValue: “남”) // south
  • 연관값을 가지는 열거형
    열거형내 항목이 자신과 연관된 값을 가짐
    Ex)
    enum PhoneError {
      case unknown
      case batteryLow(String)
    }
    let error = PhoneError.batteryLow(“배터리가 곧 방전됩니다.”)
    
    • 연관값 추출 (if or switch)
        switch error {
        case .batteryLow(let message):
        print(message)
        }
        if error == .batteryLow(let message) { 
        print(message)
        }
      

옵셔널 체이닝(Optional Chaining)

옵셔널에 속해 있는 nil 일지도 모르는 프로퍼티, 메소드, 서브스크립션 등을 가져오거나 호출할 때 사용할 수 있는 일련의 과정
옵셔널 바인딩보다 쉽게 접근
Ex)

print(company.developer?.name) // Optional(“name”) -> 옵셔널 바인딩
print(company.developer!.name) // name

try-catch

프로그램 내에서 에러가 발생한 상황에 대해 대응하고 이를 복구하는 과정
swift 런타임 에러 처리

  • 발생 (throwing)
  • 감지 (catching)
  • 전파 (propagating)
  • 조작 (manipulating)

swift의 열거형은 오류 원인을 나누고 해당 오류의 특성에 대한 추가정보를 전달하는 모델을 만드는 데 적합
Ex)

enum PhoneError: Error { // Error 프로토콜 채택
	case unknown
	case batteryLow(batteryLevel: Int)
}

오류 발생: throw PhoneError.batteryLow(batteryLevel: 20)

swift 오류 처리 방법

  1. throws
    함수에서 발생한 오류를 해당 함수를 호출한 코드에 전파
    func checkPhoneBatteryStatus(batteryLevel: Int) throws -> String { }
  2. do-catch
    do {
         try checkPhoneBatteryStatus(batteryLevel: -1)
     } catch PhoneError.unknown {
         print(“알 수 없는 에러입니다.”)
     } catch  PhoneError.unknwon(let batteryLabel) {
         print(“배터리 전원 부족 남은 배터리 : \(batteryLabel)%”)
     } catch {
         print(“그 외 오류 발생: \(error)”)
     }
    
  3. try?
    에러를 던져주지 않을 경우 옵셔널 값, 에러 발생시 nil 반환
    let status = try? checkPhoneBatteryStatus(batteryLevel: 30)
  4. try!
    오류가 발생하지 않을 것이란 확신, 에러 발생시 강제종료
    let status = try! checkPhoneBatteryStatus(batteryLevel: 30)

클로저(익명함수 unnamed closure)

코드에서 전달 및 사용할 수 있는 독립 기능 블록이며, 일급 객체의 역할을 할 수 있음

일급객체란 전달 인자로 보낼 수 있고, 변수/상수 등으로 저장하거나 전달할 수 있으며, 함수의 반환 값이 될 수 도 있다.


{ (매개 변수) -> 리턴 타입 //헤드 in
	실행 구문 //body
} 

Ex)

let hello = { () -> () in
	print(“Hello”)
}
hello()

let hello2 = { (name: String) -> String in
	return “Hello, \(name)”
}
hello2(“abc”) //전달인자 레이블 사용x
  • 함수의 파라미터로 클로저를 전달
    func doSomething(closure: () -> ()) {
      closure()
    }
    doSomething(closure: { () -> () in
      print(“hello”)
    })
    
  • 함수의 반환타입으로 클로저 사용
    func doSomething2() -> () -> () {
      return { () -> () in
          print(“hello4”)
      }
    }
    doSomething2()()
    
  • 후행클로저
    • 마지막 매개변수로 전달되는 경우
        doSomething() {
        print(“hello2”)
        }
      
    • 하나의 매개변수만 전달되는 경우
        doSomething {
        print(“hello3”)
        }
      
  • 다중 후행클로저
    매개 변수에 클로저가 여러 개있는 경우
    func doSomething2(success: () -> (), fail: () -> ()) { }
    —> doSomething2 { 
      //code
     } fail: { 
      //code
     }
    
  • 클로저 표현 간소화 문법 최소화
    func doSomething3(closure: (Int, Int, Int) -> Int) {
      closure(1,2,3)
    }
    doSomething3(closure: { (a, b, c) in
      return a+b+c
    })
    doSomething3(closure: {
      return $0+$1+$2
    })
    doSomething3(closure: {
      $0+$1+$2 //단일 리턴문
    }
    doSomething3() {
      $0+$1+$2
    }
    doSomething3 {
      $0+$1+$2
    }
    

고차함수

다른 함수를 전달 인자로 받거나 함수 실해의 결과를 함수로 반환하는 함수
map, filter reduce -> collection type

  • map
    컨테이너 내부에 기존 데이터를 변형하여 새로운 컨테이너를 생성
let number = [0, 1, 2, 3]
let mapArray = numbers.map { (number) -> Int in
	return number * 2
}
  • filter
    컨테이너 내부의 값을 걸러서 새로운 컨테이너로 추출
let intArray = [10, 5, 20, 13, 4]
let filterArray = intArray.filter { $0 > 5 } // 간소화
  • reduce
    컨테이너 내부의 요소를 하나로 통합
let someArray = [1, 2, 3, 4, 5]
let reduceResult = someArray.reduce(0) {
	(result: Int, element: Int) -> Int in
	return result + element
}

카테고리:

업데이트: