swift 기본문법 - 값 타입과 참조 타입?

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.
인프런, 야곰의 스위프트 기본문법 강좌를 듣고 정리하였습니다.


Class

  • 전통적인 OOP관점에서의 클래스
  • 단일 상속
  • (인스턴스/타입) 메서드
  • (인스턴스/타입) 프로퍼티
  • 참조타입
  • Apple 프레임워크의 대부분의 큰 뼈대는 모두 클래스로 구성

Struct

  • C언어 등의 구조체보다 다양한 기능
  • 상속 불가
  • (인스턴스/타입) 메서드
  • (인스턴스/타입) 프로퍼티
  • 값 타입
  • Swift의 대부분의 큰 뼈대는 모두 구조체로 구성

Enum (Enumeration)

  • 다른 언어의 열거형과는 많이 다른 존재
  • 상속 불가
  • (인스턴스/타입) 메서드
  • (인스턴스/타입) 연산 프로퍼티
  • 값 타입
  • 유사한 종류의 여러 값을 유의미한 이름으로 한 곳에 모아 정의 (요일, 상태값 월 등..)
  • 열거형 자체가 하나의 데이터타입으로 열거형의 case 하나하나 전부 하나의 유의미한 값으로 취급
  • 선언 키워드: enum

구조체는 언제사용하나?

  • 연관된 몇몇의 값들을 모아서 하나의 데이터타이으로 표현하고 싶을때
  • 다른 객체 또는 함수 등으로 전달될 때 참조가 아닌 복사를 원할때
  • 자신을 상속할 필요가 없거나, 자신이 다른 타입을 상속받을 필요가 없을 때
  • Apple 프레임워크에서 프로그래밍을 할 때에는 주로 클래스를 많이 사용

Value vs Reference?

Value: 데이터를 전달할 때 값을 복사하여 전달
Reference: 데이터를 전달할 때 값의 메모리 위치를 전달 > 단순히 참조값을 전달

code1

struct ValueType {
  var property = 1
}

class ReferenceType {
  var property = 1
}


let firstStructInstance = ValueType()
var secondStructInstance = firstStructInstance
secondStructInstance.property = 2

print("first struct instance property: \(first struct instance property)")  // 1
print("second struct instance property: \(second struct instance property)")  // 2


let firstStructInstance = Referencetype()
var secondStructInstance = firstStructInstance
secondStructInstance.property = 2

print("first struct instance property: \(first struct instance property)")  // 2
print("second struct instance property: \(second struct instance property)")  // 2

code2

struct SomeStruct {
  var someProperty: String = "Property"
}

var SomeStructInstance: SomeStruct = SomeStruct()

func someFunction(structInstance: SomeStruct) {
  var localVar: SomeStruct = structInstance
  localVar.someProperty = "ABC"
}

someFunction(SomeStructInstance)
print(SomeStructInstance.someProperty)  // Property

code3

class SomeClass {
  var someProperty: String = "Property"
}

var someClassInstace: SomeClass = SomeClass()

func someFunction(classInstance: SomeClass) {
  var localVar: SomeClass = classInstance
  locarVar.someProperty = "ABC"
}

someFunction(SomeStructInstance)
print(SomeStructInstance.someProperty)  // ABC

-> 스위프트는 구조체, 열거형 사용을 선호
-> Apple 프레임워크는 대부분 클래스 사용
-> Apple 프레임워크 사용시 그조체/클래스 선택은 각자의 몫이다

swift 기본문법 - 열거형이란?

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.
인프런, 야곰의 스위프트 기본문법 강좌를 듣고 정리하였습니다.


열거형(enum)

열거형은 타입이므로 대문자 카멜케이스를 사용하여 이름을 정의한다.

기본적으로 식별자들을 한 타입으로 사용하고 싶을때 열거형을 선언한다.

  • 각 case는 소문자 카멜케이스로 정의한다.
  • 각 case는 그 자체가 고유의 값이다.
enum 이름 {
  case 이름1
  case 이름2
  case 이름3, 이름4, 이름5
  ...
}

예시는 아래와 같다.

enum Weekday {
  case mon, tue, wed, thu, fri, sat, sun
}

// 열거형의 case를 나타내는 문법: 열거형 타입이름.case이름
var day: Weekday = Weekday.mon
day = .tru
print(day)  // tue

switch day {
case .mon, .tue, .wed, .thu:
  print("평일입니다")

case Weekday.fri:
  print("불금!")

case .sat, .sun:
  print("신나는 주말!!")
}

원시값

C언어의 enum처럼 정수값을 가지게 만들 수 있다.

  • rawValue 를 사용하면 된다.
  • rawValue는 case별로 각각 다른 값을 가져야 한다.
  • 자동으로 1이 증가된 값이 할당된다.
  • rawValue를 반드시 지닐 필요가 없다면 굳이 만들지 않아도됨
enum Fruit: Int {
  case apple = 0
  case grape = 1
  case peach
}

print("Fruit.peach.rawValue == \(Fruit.peach.rawValue)")  // 2

정수타입 뿐만 아니라 Hashable프로토콜을 따르는 모든 타입을 원시값의 타입으로 지정할 수 있다.

enum School: String {
  case elementary = "초등"
  case middle = "중등"
  case high = "고등"
  case university
}

// 열거형의 원시값 타입이 String일때, 원시값이 지정되지 않는다면 case의 이름을 원시값으로 사용
print("School.middle.rawValue == \(School.middle.rawValue)")  // 중등
print("School.university.rawValue == \(School.university.rawValue)")  // university

원시값을 통한 초기화

  • rawValue를 통해 초기화 할 수 있다.
  • rawValue가 case에 해당하지 않을 수 있으므로(3이상의 값) rawValue를 통해 초기화 한 인스턴스는 옵셔널 타입 이다.
let apple: Fruit? = Fruit(rawValue: 0)

if let orange: Fruit = Fruit(rawValue: 5) {
  print("rawValue 5에 해당하는 케이스는 \(orange)")
} else {
  print("rawValue 5에 해당하는 케이스가 없습니다")
}  // rawValue 5에 해당하는 케이스가 없습니다

열거형의 메서드

enum Month {
  case dec, jan, feb
  case mar, apr, may
  case jun, jul, aug
  case sep, act, nov

  func printMessage() {
    switch self {
      case .mar, .apr, .may {
        print("봄")
      }
      case .jun, .jul, .aug {
        print("여름")
      }
      case .sep, .oct, .nov {
        print("가을")
      }
      case .dec, .jan, .feb {
        print("겨울")
      }
    }
  }
}

Month.mar.printMessage()  // 봄

swift 기본문법 - 구조체와 클래스

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.
인프런, 야곰의 스위프트 기본문법 강좌를 듣고 정리하였습니다.


구조체(struct)

대부분의 타입이 구조체로 이루어져있음 > 타입을 정의 > 값 타입

struct 이름 {
  구현부
}

예시는 아래와 같다.

struct Sample {
  // 인스턴스 프로퍼티
  var mutableProperty: Int = 100  // 가변 프로퍼티
  let immutableProperty: Int = 100  // 불변 프로퍼티

  // 타입 프로퍼티
  static var typeProperty: Int = 100  

  // 인스턴스 메서드
  func instanceMethod() {
    print("instance method")
  }

  // 타입 메서드
  static func typeMethod() {
    print("type method")
  }
}

프로퍼티: 구조체 안에 들어가는 인스턴스 변수
메서드: 구조체 안에 들어가는 함수

구조체 사용

// 가변 인스턴스 > 인스턴스에서 사용하는 프로퍼티
// Sample이라는 구조체가 타입이 된다.
var mutable: Sample = Sample()

mutable.mutableProperty = 200
mutable.immutableProperty = 200  // 프로퍼티 선언 자체에서 불변으로 선언한 프로퍼티의 값은 변경 불가능하다.

// 불변 인스턴스 > 인스턴스에서 사용하는 프로퍼티
let immutable: Sample = Sample()

immutable.mutableProperty = 100  // 가변 프로퍼티로 설정했다고 하더라도 불변 인스턴스의 갑은 변경 불가능하다.

// 타입 프로퍼티 및 메서드 > Sample이라는 구조체 타입 자체가 사용할 수 있는 프로퍼티, 메서드
Sample.typeProperty = 300
Sample.typeMethod()  // type method

// 인스턴스에서 타입 프로퍼티를 사용하는 것은 불가능하다.
mutable.typeProperty = 400
mutable.typeMethod()

예시는 아래와 같다.

struct Student {
   var name: String = "unknown"
   var `class`: String = "Swift"

   // 타입메서드
   static func selfIntroduce() {
     print("학생 타입입니다...")
   }

   // 인스턴스 메서드
   func selfIntroduce() {
     print("저는 \(self.class)\(name)입니다...")
   }
}

Student.selfIntroduce()  // 학생 타입입니다...

var zehye: Student = Student()
zehye.name = "zehye"
zehye.class = "스위프트"
zehye.selfIntroduce()  // 저는 스위프트반 zehye입니다...

let kina: Student = Student()  // 불변 인스턴스로 프로퍼티값 변경 불가능
kina.name = kina
kina.selfIntroduce()  // 저는 스위프트반 unknown입니다...

클래스(class)

구조체와 거의 비슷하지만 값타입인 구조체와는 다르게 클래스는 참조타입 이다.
더 나아가, 다중상속이 불가능하다.

class 이름 {
  구현부
}

예시는 아래와 같다.

class Sample {
  var mutableProperty: Int = 100
  let immutableProperty: Int = 100

  static var typeProperty: Int = 100

  func instanceMethod() {
    print("instance method")
  }

  // 타입 메서드
  // 상속을 받았을 때, 재정의 불가 타입 메서드 - static
  static func instanceMethod() {
    print("type method - static")
  }

  // 상속을 받았을 때, 재정의 가능 타입 메서드 - class
  class func classMethod() {
    print("type method - class")
  }
}

클래스

클래스는 구조체와 다르게 let, var로 인스턴스 설정하였다고 하더라도 가변 프로퍼티를 통해 값 변경이 가능하다.

var mutableReference: Sample = Sample()
let immutableReference: Sample = Sample()

mutableReference.mutableProperty = 300
immutableReference.mutableProperty = 300

// 불변 프로퍼티를 통한 값변경은 당연히 불가능하다.
mutableReference.immutableProperty = 200
immutableReference.immutableProperty = 200

// 타입 프로퍼티 및 메서드
Sample.typeProperty = 300
Sample.typeMethod()

예시는 아래와 같다.

class Student {
  var name: String = "unknown"
  var `class`: String = "Swift"

  class func selfIntroduce() {
    print("학생 타입입니다...")
  }

  func selfIntroduce() {
    print("저는 \(self.class)\(name)입니다...")
  }
}


Student.selfIntroduce()  // 학생 타입입니다...

var zehye: Student = Student()
zehye.name = "지혜"
zehye.class = "스위프트"

zehye.selfIntroduce()  // 저는 스위프트반 지혜입니다...


// 구조체와 다르게 가변프로퍼티를 let으로 선언한 인스턴스의 값도 변경이 가능하다.
let kina: Student = Student()
kina.name = "키나"
kina.class = "스위프트"
kina.selfIntroduce()  // 저는 스위프트반 키나입니다...

swift 기본문법 - 옵셔널(Optional)

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.
인프런, 야곰의 스위프트 기본문법 강좌를 듣고 정리하였습니다.


Optional

값이 ‘있을 수도, 없을 수도 있음’

let optionalConstant: Int? = nil  //혹은
let optionalConstant: Optional<Int> = nil

let someConstant: Int = nil  // 컴파일 에러 발생

옵셔널이 아닌 상수에 nil값을 할당하려고 하면 컴파일 오류가 발생한다.

Optional은 왜 필요한가?

nil의 가능성을 명시적으로 표현해주는 것

  • nil의 가능성을 문서화하지 안아도 코드만으로 충분히 표현 가능
    • 문서/주석 작성 시간을 절약
  • 전달받은 값이 옵셔널이 아니라면 nil체크를 하지 않더라도 안심하고 사용 가능
    • 효율적인 코딩이 가능해짐
    • 예외 상황을 최소화하는 안정된 코딩 가능
func someFunction(someOptionalParam: Int?) {
  // ...
}

func someFunction(someOptionalParam: Int) {
  // ...
}

someFunction(someOptionalParam: nil)
someFunction(someOptionalParam: nil)  // 컴파일 에러 발생

컴파일 단계에서도 안전하게 Optional값을 처리 가능하다.

Optional의 표현방식 > ?, !

Implicitly Unwrapped Optional, 암시적 추출 옵셔널 > !

타입 뒤에 !를 표현하는 것

옵셔널 타입 자체는 열거형이기 때문에 switch구문으로 표현이 가능하다.

var optionalValue: Int! = 100

switch optionalValue {
case .none:  // 값이 없다
  print("This Optional variable is nil!!")

case .some(let value):  // 값이 들어왔다
  print("Value is \(value)")
}

// 기존 변수처럼 사용 가능
optionalValue = optionalValue + 1

// nil 할당 가능
optionalValue = nil

// 잘못된 접근으로 인한 런타임 오류 발생, 이미 nil값을 할당했기 때문
optionalValue = optionalValue + 1

일반적인 옵셔널 > ?

var optionalValue: Int? = 100

switch optionalValue {
case .none:  // 값이 없다
  print("This Optional variable is nil!!")

case .some(let value):  // 값이 들어왔다
  print("Value is \(value)")
}

// nil 할당 가능
optionalValue = nil

// 기존 변수처럼 사용불가 - 옵셔널과 일반 값은 다른 타입이기 때문에 연산불가
optionalValue = optionalValue + 1

Optional Unwrapping, 옵셔널 값 추출

옵셔널 값을 어떻게 꺼내서 쓰고 활용하는가?

  • 옵셔널 바인딩(Optional Binding) > if let
  • 강제 추출(Force Unwrapping) > !

옵셔널 바인딩(Optional Binding)

옵셔널 값을 꺼내오는 방법 중 하나로 nil체크 + 안전한 값 추출하다는 특징을 가진다.

값이 있는지 확인해보고 값이 있다면 꺼내오고 없다면 지나간다.

func printName(_ name: String) {
  print(name)
}

var myName: String? = nil

// 전달되는 값의 타입이 다르기 때문에 컴파일 오류 발생
printName(myName)

전달되는 값의 타입이 다를때마다 컴파일 오류가 발생하기 때문에 if-let을 통해 안전하게 옵셔널 값을 추출하도록 한다.

func printName(_ name: String) {
  print(name)
}

var myName: String! = nil

if let name: String = myName {
  printName(name)
} else {
  print("myname == nil")
}

// name이라는 상수는 if-let 구문안에서만 사용이 가능하다.
// 구문 바깥에서 사용했기 때문에 컴파일 오류가 발생한다.
printName(myName)

그리고 여러 옵셔널 변수들을 바인딩할 수 있다. 그러나 해당 변수들에 모두 값이 들어가있어야 한다.

var myName: String? = "zehye"
var youtName: String? = nil

if let name = myName, let friend = youtName {
  print("\(name) and \(friend)")  // youtName이 nil이기 때문에 실행이 안됨
}

yourName = "hana"
if let name = myName, let friend = youtName {
  print("\(name) and \(friend)")  // 실행됨
}

강제 추출(Force Unwrapping)

옵셔널의 값을 강제로 추출

func printName(_ name: String) {
  print(name)
}

var myName: String? = "zehye"
printName(myName!)  // zehye !를 사용함으로써 값을 강제로 추출

myName = nil
print(myName!)  // 강제추출 시 값이 없으므로 런타임 오류 발생

var yourName: String! = nil  // 처음 선언시 사용 가능
printName(yourName)  // 여기서 ! 사용하지 않았어도 강제추출 가능

swift 기본문법 - 조건문과 반복문

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.
인프런, 야곰의 스위프트 기본문법 강좌를 듣고 정리하였습니다.


조건문

if-else

if condition {
  statements
} else if condition {
  statements
} else {
  statements
}

예시는 아래와 같다.

if someInteger < 100 {
  print("100 미만")
} else if someInteger > 100 {
  print("100 이상")
} else {
  print("100이다!")
}

스위프트의 조건에는 항상 bool타입이 들어와야 한다. someInteger는 bool타입이 아닌 Int 타입이기 때문에 컴파일 오류가 발생한다.

Switch

if는 bool 값으로 조건을 확인하기 위해, 양자 택일의 형태밖에 처리하지 못한다.

값을 확인하고 다수의 분기를 할 수 있도록 하는것이 switch문이다. 체크하는 값을 준비하고 그 값에 따라 처리하는 부분에 점프를 한다.

  • swift 이후에 체크하는 값을 작성한다. » 숫자, 텍스트, 논리값 다 쓸 수 있다.
  • case: 값을 작성한다.
  • default: 마지막에는 default를 준비해야한다.
switch value {
case pattern:
  code
default:
  code
}

스위프트에서는 범위연산자 라는게 등장하는데 이를 활용하면 더욱 쉽고 유용하게 사용가능하다.

switch someInteger {
case 0:
  print("zero")
case 1..<100:
  print("1~99")
case 100:
  print("100")
case 101...Int.max:
  print("over 100")
default:
  print("unknown")
}

그리고 정수 외의 대부분의 기본 타입을 사용할 수 있다.
문자열을 가지고 비교값에 넣어주고 case구문을 작성할 수 있다.

switch "zehye" {
case "jake", "mina":
  print("jake")
case "zehye":
  print("zehye")
default:
  print("unknown")
}

그런데 스위프트의 switch 구문에서는 명확히 case가 다 명시되지 않는 한 default 구문을 반드시 작성해주어야 한다. 그리고 case구문 다음에 break이 없는데, 명시적으로 break를 하지 않아도 break가 걸린다.

혹은 break가 걸리지 않게 하고 싶다면 fallthrough를 사용해주면 된다.

switch "zehye" {
case "jake":
  print("jake")
  fallthrough
case "mina":
  print("mina")
case "zehye":
  print("zehye")
  fallthrough
default:
  print("unknown")
}

>>>zehye
   unknown

반복문

반복문은 컬렉션 타입과 많이 사용되므로 미리 변수와 상수를 만들어놓도록 한다.

var integers = [1,2,3]
let people = ["zehye": 10, "eric": 15, "mike": 12]

for-in

for item in items {
  code
}

예시는 아래와 같다.

for integer in integers {
  print(integer)  // [1,2,3]
}

// Dictionary의 item은 key와 value로 구성된 튜플타입이다.
for (name, age) in people {
  print("\(name): \(age)")
}

while

while condition {
  code
}

예시는 아래와 같다.
조건문과 같이 bool값이 들어와야 한다.

while integers.count > 1 {
  integers.removeLast()
}

repeat-while

다른 언어의 do~while과 같다.
while문과 비슷하지만 반복문 내의 코드를 우선 한번은 실행 한 뒤 조건문을 테스트하는 점이 차이점이다.

repeat {
  code
} while condition

예시는 아래와 같다.

repeat {
  integers.removeLast()
} while integers.count > 0