Swift 자료형과 연산자

Swift

  • 애플에서 개발한 프로그래밍 언어
  • 오픈소스 언어
  • 컴파일 언어
  • REPL(레플) 사이트가 있다 - swiftfiddle.com
  • 안전하고 신속하다. 포인터 쓰는 데 안 쓰는 것처럼 한댄다.
  • 실행 속도 최적화와 컴파일러 개선을 통한 신속한 실행이 가능하다.
  • 객체 지향 언어이기 때문에 class에 대해 숙지하고 있는 게 좋다.
  • 함수형 언어, 프로토콜 지향 언어

Swift 실행 환경 - Xcode

애플 공식 통합 개발 환경이다.

Playground로 프로젝트 생성 없이 소스코드를 실행할 수 있다.

실행 버튼 눌러도 아무 일도 안 일어나는데 그냥 느려서 그런 거다. Xcode 위에 지금 뭐하고 있는지 상태가 나온다.

Quick look 기능으로 변수 값의 변화를 확인할 수 있다.

도움말은 Control 키를 누른 상태에서 해당 부분 클릭 - Show Quick Help 혹은 Jump to Definition

실행했을 때 속도는 레플 사이트가 좀 더 빠른 것 같다.

명명법

유니코드에서 지원하는 문자는 사용 가능하지만 주석까지도 전부 영어로 작성하는 게 좋다.

*사용 불가능한 것들

  • 스위프트에서 미리 정한 예약어 및 키워드
  • 연사자 기호
  • 숫자로 시작하는 이름
  • 공백이 포함된 이름

세미콜론(;) 안 붙여도 된다.

자료형, 열거형, 클래스, 구조체, 익스텐션, 프로토콜의 이름은 첫 글자를 대문자로 시작하는 카멜 케이스를 사용한다. (예: UpperCamelCase)

변수, 상수 함수, 메서드, 객체(인스턴스)의 이름은 첫 글자를 소문자로 시작하고 다음 단어의 첫 글자는 대문자로 표기하는 소문자 카멜 케이스 사용한다. (예: lowerCamelCase)

코딩 환경에 따라 스네이크 표기법, 파스칼 표기법 등을 사용할 수 있다.

관련 내용은 Swift API Design Guidelines를 참고한다.

주석

// 한줄 주석
/* 여러 줄 주석 */
/* 주석 내용
//중첩 주석
주석 내용 */

변수와 상수

  • 변수(variable): 선언 후 데이터 값 변경 가능
  • 상수(constant): 선언 후 데이터 값 변경 불가능
var 변수명: 자료형 = 값
var 변수명 = 값

let 상수명: 자료형 = 값
let 상수명 = 값

선언할 때 자료형을 명시하는 것을 권장한다. 자료형을 명시하지 않으면 컴파일할 때 컴파일러가 선언에 사용된 값을 통해 자료형이 추론될 수 있어 컴파일 시간이 오래 걸릴 수 있다. 값에 따라 변수 또는 상수의 타입 추론이 불가능할 수 있다.

변수 이름은 소문자로, 자료형은 대문자로 시작하니까 구분해서 유추할 수 있다.

var intVar: Int = 3
var dblVar: Double = 3.1
let strLet: String = "my name is"

자료형

타입 추론(type inference)

Swift는 컴파일 언어이므로 변수 또는 상수를 선언할 때 자료형을 명시해야 한다. 이를 명시하지 않으면 컴파일할 때 컴파일러가 선언된 값을 통해 자료형을 추론한다. 값을 통해 타입 추론이 불가능할 경우 에러가 발생한다.

배열이나 딕셔너리를 만들 때 값을 비운 상태에서 자료형을 명시하지 않으면 에러가 발생하므로 반드시 값을 넣거나 자료형을 명시한다.

변수의 자료형을 확인하는 함수: type

print(type(of: 변수이름))

타입 별칭(type alias)

자료형에 대해 별칭을 생성할 수 있다. C/C++의 typedef와 유사하다.

typealias MyInt = Int
typealias TupIntDbl = (String, Int, Double) typealias ArrStr = Array<String>

var int: MyInt = 3
var personG: PersonTuple = ("kang", 11, 160.25)

정수형(Int, UInt)

  • Int: -, + 부호를 포함하는 정수
  • UInt(unsigned Int): -부호를 포함하지 않는 정수 표현. 큰 수치의 데이터를 사용해야 할 때 활용할 수 있다.
자료형 설명 표현 범위
최소값 최대값
Int 32 bit CPU(32 bit 아키텍처)에서는 Int32와 동일, 4 byte −231 231 − 1
64 bit CPU(64 bit 아키텍처)에서는 Int64와 동일, 8 byte −263 263 − 1
Int8 1 byte (8 bit)로 정수표현 (28개의 정수 표현) −27 27 − 1
Int16 2 byte (16 bit)로 정수표현 (216개의 정수 표현) −215 215 − 1
Int32 4 byte (32 bit)로 정수표현 (232개의 정수 표현) −231 231-1
Int64 8 byte (64 bit)로 정수표현 (264개의 정수 표현) −263 263 − 1
UInt 32 bit CPU(32 bit 아키텍처)에서는 UInt32와 동일 0 232 − 1
64 bit CPU(64 bit 아키텍처)에서는 UInt64와 동일 0 264 − 1
UInt8 1 byte (8 bit)로 부호 없는 정수표현 (28개의 부호 없는 정수표현) 0 28 − 1
UInt16 2 byte (16 bit)로 부호 없는 정수표현 (216개의 부호 없는 정수표현) 0 216 − 1
UInt32 4 byte (32 bit)로 부호 없는 정수표현 (232개의 부호 없는 정수표현) 0 232 − 1
UInt64 8 byte (64 bit)로 부호 없는 정수표현 (264개의 부호 없는 정수표현) 0 264 − 1
//자료형의 최소값과 최대값 확인
print("Int.min: \(Int.min), Int.max: \(Int.max)")

//형 변환
var intNum: Int = -100
var uintNum: UInt = 50

intNum = Int(uintNum)

컴파일러가 자동으로 해주지만 좋은 성능을 위해 형 변환을 확실하게 적어주자!!

*진수에 따른 정수 표현

  • 2진수(binary): 접두어 0b
  • 8진수(octal): 접두어 0o
  • 16진수(hexadecimal): 접두어 0x

실수형(Float, Double) 또는 부동소수점형(floating-point type)

  • Float: 4 byte 실수 표현. e로 표현된 실수에서 6자리까지 정확히 표현한다. 표현 범위가 넓지 않아 빠른 계산이 필요할 때 사용한다. 속도가 빠르다.
  • Double: 8 byte 실수 표현. e로 표현된 실수에 15자리까지 정확히 표현한다. 표현 범위가 넓어서 float보다 속도가 2~4배 정도 느릴 수 있다.

자료형을 명시하지 않으면 컴파일러에 의해 Double형으로 타입 추론이 된다.

실수형만으로 1시간 동안 강의하실 수 있다는데 대체 무슨 이야기를 할 수 있는 걸까.

*e(exponent)를 이용한 실수 표현

e는 10이라고 생각하면 된다. 실수를 간단하게 표현할 수 있다.

1230 = 1.23e3 = 1.23e+3 = 1.23 × 103

문자열(String)

  • String: 하나 이상의 문자로 구성된 문자열을 큰따옴표(")를 이용해서 표현한다.
var str1: String = "aaa"
var str2: String
str2 = "bbb"

//빈 문자열 생성
var str3: String = String()    // ""

//문자열 뒤에 문자열 추가
str2.append("ccc")        // str2 == "bbbccc"

//문자열 결합
str3 = str1 + " " + str2    // str3 == "aaa bbbccc"

//빈 문자열 여부, 문자열 내 문자 수 확인
print(str1.count)        // 3
print(str1.isEmpty)    // false

//문자열 접두어와 접미어 확인
var result: Bool
result = str3.hasPrefix("aa")    //문자열 str3이 "aa"로 시작하는가?
result = str3.hasSuffix("cc")    //문자열 str3이 "cc"로 끝나는가?

//대소문자 변환
str4: String
str4 = str1.uppercased()
str4 = str1.lowercased()

//문자열 보간법(String Interpolation): 문자열 내에 변수 또는 상수 값을 넣고 싶을 때
str4 = "Hello \(str1)"

//여러 줄 표현: 큰따옴표 3개 사용. 실제로 줄바꿈을 하지 않지만 에디터에서 줄바꿈을 하게 될 경우 백슬래시(\) 사용
var str5: String = """
Hello \
Goodbye
"""
print(str5)    //Hello Goodbye

문자(Character)

Character: 하나의 문자를 큰따옴표(")를 이용해서 표현한다.

*탈출문자(escape character): C랑 똑같다.

탈출 문자 설명
\n 줄 바꿈(개행)
\\ 백슬래시(\)
\" 큰따옴표(")
\t 탭(tab)
\0 문자열의 끝(null 문자)

Any, AnyObject

  • Any: 모든 자료형을 표현한다. 
  • AnyObject: 모든 클래스를 표현한다. 어떤 클래스의 객체도 할당 받을 수 있다.
  • nil: 어떠한 메모리 주소도 가리키지 않는다는 의미이다. C/C++의 NULL과 유사하다. 변수 초기화를 위해 사용한다.

C에서는 변수를 초기화하지 않으면 쓰레기값이 들어가는데 Swift에서는 에러가 발생하므로 반드시 초기화를 해주어야 한다.

var value1: Int
print(value1)    // value1을 초기화하지 않아 에러 발생

var value2: Int?    // 옵셔널(optional) 변수 value2는, 값을 가지거나 갖지 않음
var value3: Int? = nil

print(value2)    // 실행결과: nil
print(value3)

*와일드 카드 식별자(_)는 어떠한 수도 허용한다는 의미.

튜플(Tuple)

  • C/C++의 구조체처럼 여러 자료형으로 구성된다.
  • 파이썬의 튜플과 달리 튜플 내의 각 element를 수정할 수 있다.
  • 자료형의 이름이 따로 없는데 이름을 붙여도 된다. 변수의 이름을 명시적으로 확실하게 표현해주는게 좋다.

var personA: (String, Int, Double) // unnamed
var personB: (name: String, age: Int, height: Double) // named
personA = ("joe", 10, 150.23)
personB = (name: "joe", age: 10, height: 150.23)
personB = ("joe", 10, 150.23)

print("이름: \(personA.0), 나이: \(personA.1), 키: \(personA.2)")
print("이름: \(personB.name), 나이: \(personB.age), 키: \(personB.height)")

personA.0 = "jane"
personB.name = "lee"

배열(Array)

  • element가 순서대로 저장된다.
  • index는 0부터 시작된다. 점(.) 찍지 말고 C처럼 [index]으로 접근하면 된다.

var arrayA: Array<String>
var arrayB: [String]

arrayA = ["aa", "bb", "cc", "dd"]
arrayB = ["aa", "bb", "cc", "bb"]

//여러 자료형일 경우 타입 추론이 안 되므로 반드시 Any형을 명시해야 한다.
var arrayAnyA: Array<Any> = [1, 2.3, "c", "string"]

//빈 배열을 생성하려면 반드시 자료형을 명시해야 한다.
var emptyArrayA: Array<String> = Array<String>()
var emptyArrayB: [String] = [String]()
var emptyArrayC: [String] = [] // element 접근 및 수정 arrayA[0] = "ee" print(arrayA.first)    //첫 번째 값 print(arrayA.last)    //마지막 값 print(arrayB.firstIndex(of: "bb"))    //해당 원소가 앞에서 몇 번째 인덱스인지 탐색 print(arrayB.lastIndex(of: "bb"))    //해당 원소가 뒤에서 몇 번째 인덱스인지 탐색 print(arrayB)        //배열 전체 출력 print(arrayB[1...3])    //인덱스 1부터 3까지 출력 //element 추가 arrayC.append("ee") arrayC.append(contensOf: ["ff", "gg"]) arrayC.insert("hh", at: 2)    //인덱스 2에 삽입

딕셔너리(dictionary)

  • element가 키(key)와 값(value)으로 구성된다. 같은 키를 중복해서 사용할 수 없다. 키 값은 Any를 사용할 수 없다.
  • element가 순서없이 저장된다.
  • 여러 자료형으로 구성된 딕셔너리를 생성할 때는 반드시 Any형을 명시해야 한다.

var dictA: Dictionary<String, Int>

var dictB: [String: Int]

var emptyDictC: [String: Int] = [:]


dictA = ["aa": 1, "bb": 2, "cc": 3]

dictB = ["aa": 1, "bb": 2, "cc": 3]


typealias DicStrInt = Dictionary<String, Int>


dictA["aa"] = dictA["bb"]! + dictA["cc"]!   //키에 대한 값은 없을 수도 있으므로 키에 대한 값은 옵셔널 변수이다. 따라서 강제 추출 연산자(!)를 사용하여 옵셔널 변수의 값을 추출한다.

print(dictA["aa"]!)


//element 추가 삭제

dictA["dd"] = 4

dictA.removeValue(forKey: "aa")

집합(Set)

배열처럼 같은 자료형(Any를 허용하지 않음)의 element들로 구성되어 있지만 순서 없이 저장되며 같은 element가 존재하지 않는다.

var setA: Set<String>

setA = ["aa", "bb", "cc"]

var setB: Set<String> = ["aa", "bb", "cc"]


//빈 집합 생성

var emptySetA: Set<String> = Set<String>()

var emptySetB: Set<String> = []


typealias SetStr = Set<String>

var emptySetC: SetStr = SetStr()


//빈 집합인지 확인 및 element 수 확인

print(setA.isEmpty)

print(setA.count)


//element 추가 삭제

setA.insert("dd")

setA.remove("aa")


//집합 연산

let classA: Set<String> = ["aa", "bb", "cc"]

let classB: Set<String> = ["bb", "cc", "dd", "ee", "ff"]

var result: Set<String>


result = classA.union(classB)           //합집합

result = classA.subtracting(classB)     //차집합

result = classA.intersection(classB)    //교집합

result = classA.symmetricDifference(classB) //여집합의 합(배타적 논리합)


//정렬 - 배열을 반환한다.

var sortedResult: Array<String>

sortedResult = result.sorted()


//포함관계 연산

print(A.isDisjoint(with: B))    //서로 배타적인지

print(A.isSubset(of: Bl))        //A가 B의 부분집합인지

print(A.isSuperset(of: B))    //A가 B의 전체집합인지

열거형(enum)

연관된 항목을 하나로 묶어서 표현한다. 열거형으로 선언된 변수는 열거형 내의 항목만 값으로 가질 수 있다. 각 항목 자체가 고유의 값이다.

enum Week: String {

    case sunday = "Sun."

    case monday         //원시값이 monday가 된다.

    case tuesday = "Tue."

    case wednesday = "Wed."

    case thursday = "Thu."

    case friday = "Fri."

    case saturday = "Sat."

}


//원시값 얻는 법

var day: Week = Week.wednesday

print("\(day.rawValue)")


var dayA: Week = Week.sunday

var dayB: Week = .monday

C와 달리 switch case문처럼 case가 있어 각 항목은 원시값을 가질 수 있다. 원시값을 넣으려면 원시값에 대한 자료형을 넣어주어야 한다. 원시값이 없을 경우 원시값의 자료형이 String이면 항목 이름이 원시값이 된다. 자료형이 Int이면 원시값은 첫 항목을 기준으로 0부터 1씩 증가한다.

연관값(튜플)을 가진 항목들로 구성시킬 수 있다. named 튜플이면 named 튜플로 사용해야 한다. 연관값을 상수로 받아서 사용할 수도 있다.

enum Device {

    case iPhone(model: String, storage: Int)

    case iMac(size: Int, model: String, price: Int)

    case macBook(Int, String, Int)

}

var giftA: Device = Device.iPhone(model: "Y", storage: 256)

var giftB: Device = Device.iPhone("Y", 256) //에러 발생

var giftC: Device = .macBook(12, "X", 200)


switch giftA {

    case .iPhone(model: "X", storage: 256):

        print("iPhone X and 256GB")

    case .iPhone(model: "Y", _):    //와일드 카드

        print("iPhone Y")

    case .iPhone:

        print("iPhone")

    default:

        print("default")

}

//출력 결과: iPhone Y


//연관값을 상수로 받아 사용할 때

switch giftA {

    case.iPhone(let mdl, let stg):

        print("iPhone \(mdl) and \(stg)GB")

    case let .iMac(sz, mdl, prc):

        print("iMac \(sz), \(mdl), \(prc)")

    default:

        print("default")

}

//출력 결과: iPhone Y and 256GB

switch giftB {

    case .iPhone(let mdl, let stg):

        print("iPhone \(mdl) and \(stg)GB")

    case let .iMac(sz, mdl, prc):

        print("iMac \(sz), \(mdl), \(prc)")

    default:

        print("default")

}

// 출력 결과: iMac 12, X, 100

연산자

C와 매우 유사하다. 거의 똑같은 듯??

연산자 분류

  • 피연산자 수에 따라: 단항, 이항, 삼항
  • 피연산자 앞/사이/뒤에 있는 연산자: 전위, 중위, 후위

산술 연산자

  • 더하기 A+B
  • 빼기 A-B
  • 곱하기 A*B
  • 나누기 A/B
  • 나머지 A%B    (둘 다 정수일 경우에만 가능)

*실수형 나누기, 나머지 연산

var c: Double = 5.0
var d: Double = 3.0
var dblQ: Double = c / d
var dblR: Double = c.truncatingRemainder(dividingBy: d)
print("dblQ: \(dblQ), dblR: \(dblR)") // dblQ: 1.6666666666666667, dblR: 2.0

비교 연산자

연산자 설명
A == B 같은 지 확인
A != B 다른 지 확인
A >= B 크거나 같은 지 비교
A <= B 작거나 같은 지 비교
A > B 큰 지 비교
A < B 작은 지 비교
A === B 같은 클래스의 인스턴스를 가리키는지 확인
A !== B 다른 클래스의 인스턴스를 가리키는지 확인
A ~= 패턴이 매치되는지 확인

===와 !==는 참조가 같은지 같지 않은 지를 확인하는 것이다.

삼항 연산자(ternary operator)

Qustion ? A:B

→ Question이 참이면 A를, 거짓이면 B를 반환한다.

범위 연산자(range operator)

분류 연산자 설명
폐쇄 범위 연산자 A...B A이상 B이하의 수
반폐쇄 범위 연산자 A..<B A이상 B미만의 수
단방향 범위 연산자 A... A이상의 수
...A A이하의 수
..<A A미만의 수

논리 연산자

논리 연산자 표현
NOT !A
AND A && B
OR A || B

비트 연산자

비트 연산자 표현 설명
NOT ~A A의 비트를 반전
AND A & B A와 B 비트 AND 논리 연산
OR A | B A와 B 비트 OR 논리 연산
XOR A ^ B A와 B 비트 XOR 논리 연산
비트 이동(shift) 연산자 A >> B A의 비트를 B만큼 비트를 오른쪽으로 이동
A << B A의 비트를 B만큼 비트를 왼쪽으로 이동

복합 할당 연산자

A += B 처럼 연산자를 간소하게 표현할 수 있다. C랑 똑같음.

오버플로 연산자

값의 범위를 넘어가서 오버플로가 발생했을 때 자동으로 대처하는 연산자. 예외 처리를 어떻게 할 것인가.

연산자 표현 설명
오버플로 더하기 연산 &+ 오버플로에 대비한 덧셈 연산
오버플로 빼기 연산 &- 오버플로에 대비한 뺄셈 연산
오버플로 곱하기 연산 &* 오버플로에 대비한 곱셈 연산

기타 연산자

연산자 표현 설명
nil 병합 연산자 A ?? B A가 nil이 아니면 A를, nil이면 B를 반환
부호 변경 연산자 -A A의 부호 변경
옵셔널 강제 추출 연산자 O! O(옵셔널 개체)의 값을 강제로 추출(가능한 사용하지 않는 것이 좋음)
옵셔널 연산자 V? V(옵셔널 값)을 안전하게 추출하거나 V(데이터 타입)가 옵셔널임을 표현

연산자 우선순위

연산자 우선순위 그룹 연산자
Bitwise shift >>, <<
Multiplicative %, *, /
Additive |, -, +, ^
Range ..<, ...
Casting is as
Nil-Coalescing ??
Comparison !=, >, <, >=, <=, ===, ==
Logical AND &&
Logical OR ||
Ternary Operator ? :
Assignment Precedence |=, %=, /=, *=, >>=, <<=, ^=, +=, -=

참고

2023-2 모바일 프로그래밍 성광제 교수님 수업