swift 기초 문법 <7>

구찌댕댕이·2022년 7월 9일
0

swift 기초 문법

목록 보기
7/12

//구조체
//구조체/enum의 인스턴스는 값타입, 클래스는 참조타입
// 구조체는 상속 불가
//스위프트의 기본 데이터 타입은 모두 구조체
//public struct Int
//public struct Double
//public struct String
//public struct Array

struct Resolution { //구조체 정의
var width = 1024 // 프로퍼티
var height = 76
}
let myComputer = Resolution() //인스턴스 생성
print(myComputer.width) //프로퍼티 접근

//Memberwise Initializer
// 클래스와 구조체의 큰 차이 중 하나
// 멤버의 initalizer가 자동으로 만들어짐
// 구조체의 장점 중 하나 (초기화 필요가 없다)
struct Resolution {
var width : Int //초기값이 없음
var height : Int
} //init() 메소드도 없음
let myComputer = Resolution(width:1000,height:500) //Memberwise Initializer
print(myComputer.width)

//클래스 내에 구조체
struct Resolution {
var width = 1024
var height = 76
}
class VideoMode {
var resolution = Resolution() //구조체를 프로퍼티로 할당
var framRate = 0.0
}
let myVideo = VideoMode()
print(myVideo.resolution.width)
//1. myVideo에서 VideoMode클래스의 resolution프로퍼티로 접근
//2. resolution프로퍼티에서 Resolution 구조체로 접근
//3. Resolution구조체에서 width로 접근

//클래스 vs 구조체 vs 열거형
// 공통점
//프로퍼티와 메소드를 정의할 수 있다.
//[]를 사용해 첨자(subscript) 문법으로 내부의 값을 액세스 할 수 있는 첨자를 정의
//초기 상태 설정을 위한 init() 정의
//extension을 통해 새로운 기능 추가
//프로토콜 사용 가능
// 클래스만의 특징
//상속이 가능
//타입캐스팅(is,as)을 통해 실행 시점에 클래스 인스턴스의 타입을 해석하고 검사 가능
//deinit함수를 사용해 사용한 자원을 반환할 수 있다.
//참조 카운팅을 통해 한 클래스 인스턴스를 여러 곳에서 참조(사용)할 수 있다.

struct Human {
var age : Int = 1
}
var kim = Human()
var lee = kim //값 타입
print(kim.age, lee.age) // 1, 1
lee.age = 20
print(kim.age, lee.age) // 1, 20
kim.age = 30
print(kim.age, lee.age) // 30, 20
//값타입은 복사할 때 새로운 데이터가 하나 더 생김

class Human {
var age : Int = 1
}
var kim = Human()
var lee = kim //참조(refernce) 타입
print(kim.age, lee.age) // 1, 1
lee.age = 20
print(kim.age, lee.age) // 20, 20
kim.age = 30
print(kim.age, lee.age) // 30, 30
//참조타입은 복사할 때 주소를 복사해서 한 데이터의 refernce가 2개 생김
//kim의 주소를 lee가 가지고 있어 둘의 주소가 같다
//kim의 주소를 변경하면 lee의 주소도 변경된다
//lee의 주소를 변경하면 kim의 주소도 변경된다

struct Resoultin {
var width = 0
var height = 0
}
class VideoMode {
var resolution = Resolution()
var frameRate = 0
var name : String?
}
var hd = Resolution(width: 1920, height: 1080) //구조체타입 할당
var highDef = hd //구조체는 값타입
print(hd.widht, highDef.width)
hd.width = 1024
print(hd.width, highDef.width)

var xMonitor = VideoMode() //클래스타입 할당
xMonitor.resolution = hd
xMonitor.name = "LG"
xMonitor.frameRate = 30
print(xMonitor.frameRate) //30

var yMonitor = xMonitor //클래스는 참조타입(reference type)
yMonitor.frameRate = 25
print(yMonitor.frameRate) //25
print(xMonitor.frameRate) //25

//call by value vs. call by reference
//스위프트에서 제공하는 Int, String, Array, Dictionary 등 기본 자료형들은 구조체로 만들어져 call by value 방식
//enum 도 call by value 방식
//클래스는 call by reference 방식 -> 주소(클래스)가 같으면 값이 바뀔때 같이 바뀐다

// 언제 클래스를 쓰고 언제 구조체를 쓰나?
//클래스는 참조타입, 구조체는 값타입
//구조체는 간단한 데이터 값들을 한데 묶어서 사용하는 경우
//전체 덩어리 크기가 작은 경우, 복사를 통해 전달해도 좋은 경우 구조체
//멀티 쓰레드 환경이라면 구조체가 더 안전
//구조체는 기존타입의 특성을 상속할 필요가 없다. -> 너비, 높이를 표현하는 기하학적 모양을 처리할 경우 / 좌표 시스템의 각 좌표

//옵셔널 체이닝
//옵셔널 변수에 값이 있으면 옵셔널로 '래핑되었다(wrapped)' 라고 한다
//옵셔널에 래핑된 값은 강제 언래핑(forced unwrapping)으로 풀어준다
var x : Int? //옵셔널 정수형 변수 x 선언
x = 10 // 주석처리하면? nil 값
print(x) // Optional(10)
print(x!) // forced unwrapping으로 10이 나옴
//옵셔널 변수가 nil값일때 강제 언래핑 하면 crash나서 nil이 아닐때만 언래핑 해야한다
// 옵셔널을 언래핑 하는 여러가지 방법
var x : String? = "hi"
print(x!) // 강제 언래핑 (비추천)

if let a = x { //옵셔널 바인딩
print(a)
}

let b = x?.count //옵셔널 체이닝
print(b!) // 2 -> 그냥 pirnt(b) 는 optional(2)

let c = x ?? "" //nil coalescing operator
print(c)

//옵셔널 체이닝
//옵셔널형의 프로퍼티나 메소드 호출 뒤에 '?' 사용
//선언할때는 자료형 뒤에 '?' 사용
//옵셔널 타입으로 정의된 값이 프로퍼티나 메소드를 가지고 있을때, 다중 if 를 쓰지 않고 간결하게 코드 작성하기 위해 사용
//옵셔널 타입의 데이터는 연산이 불가능
//연산을 하기위해 옵셔널 언래핑을 해야하는데, 많은 양의 옵셔널 타입의 데이터의 경우 다시한번 옵셔널 타입으로 변경을 하면서 해제를 시켜준다
if let s = p.sns {
if let f = s. fb {
print("(f.account)")
}
}
print("(p.sns!.fb!.account)")
print("(p.sns?.fb?.account)") // 옵셔널체이닝 -> 옵셔널 타입이 되므로 마지막에 해제를 한번 더 해야한다
//중간에 nil 값이 있다면 전체가 nil 값이 된다.

//옵셔널 요소 내부의 프로퍼티로 옵셔널이 연속적으로 연결되는 경우 유용
//클래스나 구조체 안에 또다른 클래스나 구조체 인스턴스가 있을 때 인스턴스를 점으로 연속해서 접근
//프로퍼티가 옵셔널인 경우 nil인지 아닌지 매번 체크를 해야하므로 번거로움
class Person {
var age : Int = 1
var addr : Address?
}
class Address {
var city = "Seoul"
}
let kim = Person() //kim 의 addr 은 nil로 초기화
print(kim.addr!.city) //nil값을 강제 언래핑하여 crash

print(kim.addr?.city) //nil -> 옵셔널 체이닝하면 최종결과는 옵셔널타입으로 나온다

kim.addr = Address()
print(kim.addr!.city) //Seoul -> crash 없음
print(kim.addr?.city) //Optional("Seoul")

print((kim.addr?.city)!) //Seoul -> 최종결과가 옵셔널 타입이므로 마지막에 언래핑 함

if let pcity = kim.addr?.city { //(kim.addr?.cidy)!
print(pcity) // 옵셔널 체이닝 결과를 옵셔널 바인딩으로 언래핑
} else {
print("도시가 지정되지 않았습니다.")
}
/예제
class Person {
var name : String?
var age : Int?
var sns : SNS? = SNS()
}
class SNS {
var fb : FaceBook? = FaceBook()
var tt : Twitter?
}
class FaceBook {
var account : String = "aaa@bbb.com"
}
class Twitter {
var account : String = ""
}

let p = Person()
if let s = p.sns{
if let f = s.fb {
print("1:(f.account)")
}
}
if let account = p.sns?.fb?.account {
print("2:(account)")
}
print("3:((p.sns?.fb?.account)!)")
print("4:(p.sns!.fb!.account)")
print("5:(p.sns?.tt?.account)") //nil
print("6:(p.sns!.tt!.account)") //crash
// 옵셔널 체인의 특성
//옵셔널 체인으로 클래스나 구조체의 프로퍼티를 참조할 경우 클래스의 값이 nil이어도 오류가 발생하지 않음
//옵셔널 체인으로 읽어낸 마지막 값이 일반 타입이라도 모두 옵셔널 타입으로 리턴
//메소드의경우 괄호 다음에 '?' 예) p.getM()?
//리턴값을 옵셔널 체인으로 사용

//print 함수
//3개의 매개변수를 갖는다
func print(_ items: Any..., separator: String = " ", terminator: String = "\n")
//item : 0 이상의 출력할 item
//separator : item사이의 string. default는 공백 (" ")
//terminator : 모든 item 출력후의 string. default는 줄바꿈 ("\n")
print("일 이 삼")
print("1 2 3")
print(1, 2, 3)
print(1.0, 2.0, 3.0)
print(1, 2, 3, separator: "...") //1...2...3
for n in 1...3 { // 1
print(n) // 2
} // 3
for n in 1...3 {
print(n, terminator: " ") // 1 2 3 -> terminator의 줄바꿈 대신 공백
}

//스위프트에서 오류 처리
//예외처리 (exception handing)
//런타임시 오류를 발견하여 응답하고 복구하는 과정
//스위프트에서는 optional을 사용하여 값의 유무를 전달 -> 작업의 성공/실패를 판단
//작업이 실패할 때 코드가 적절히 응답할 수 있도록 하여 오류의 원인을 이해하는데 도움
//디스크상의 파일을 읽어서 처리하는 작업에서 발생할 수 있는 오류는 '존재하지 않는 파일', '읽기 권한 없음', '호환되는 형식이 아님' 등
//오류의 원인에 따라 다양한 대응이 필요한 경우, 오류의 정보를 정확히 전달하여 오류를 복구하는데 도움
//error handling 도입

//throwing function
//매개변수 괄호 다음에 throws라는 키워드가 있는 함수는 그냥 사용할 수 없고 반드시 error handling 해야함
func can() throws
func canThrowErrors() throws -> String

//do~try~catch
//오류 발생 가능 함수의 호출 방식
do {
audioPlayer = try AVAudioPlayer(contentsOf: audioFile)
} catch let error as NSError {
print("Error-initPlay : (error)") //어떤 이유로 에러가 발생했는지 설명
} //AVAudioPlayer(contentsOf: audioFile) -> 이렇게 호출 할 수 없다

do {
try 오류 발생 코드
오류가 발생하지 않으면 실행할 코드
} catch 오류패턴1 {
처리 코드
} catch 오류패턴2 where 조건 {
처리 코드
} catch {
처리 코드
}

//제네릭 < >
//자료형을 나중에 결정. 같은 알고리즘을 다양한 자료형으로 처리.

//기능은 똑같고 자료형만 다른 함수들
func swapInt( a: inout Int, b: inout Int) {
let temp = a
a = b
b = temp
}
var x = 10
var y = 20
swapInt(&x, &y)
print(x,y)
func swapDouble( a: inout Double, b: inout Double) {
let temp = a
a = b
b = temp
}
var xd = 10.3
var yd = 20.7
swapDouble(&xd, &yd)
print(xd,yd)
func swapString( a: inout String, b: inout String) {
let temp = a
a = b
b = temp
}
var xs = "hi"
var ys = "hello"
swapString(&xs, &ys)
print(xs, ys)
//제네릭 함수
func swapAny( a: inout T, b: inout T) { //T는 타입이름
let temp = a
a = b
b = temp
}
var x = 10
var y = 20
swapAny(&x, &y)
print(x,y) //자료형이 Int로 출력
var xd = 10.3
var yd = 20.7
swapAny(&xd, &yd)
print(xd,yd) //자료형이 Double로 출력
var xs = "hi"
var ys = "hello"
swapAny(&xs, &ys)
print(xs, ys) //자료형이 String으로 출력

//Int형 스택 구조체
struct IntStack {
var items = Int
mutating func push( item: Int){
items.append(item) //배열에 정수 추가
}
mutating func pop() -> Int {
return items.removeLast() //배열에서 마지막 정수 빼오기
}
}
//구조체는 value타입이라 메소드 안에서 프로퍼티 값 변경 불가
//mutating 키워드가 이를 가능하게 해준다.
var stackOfInt = IntStack()
print(stackOfInt.items) //[] 빈배열 생성
stackOfInt.push(1) //배열에 1 추가
print(stackOfInt.items) //[1]
stackOfInt.push(2) //배열에 2추가
print(stackOfInt.items) //[1, 2]
stackOfInt.push(3) //배열에 3추가
print(stackOfInt.items) //[1, 2, 3]
print(stackOfInt.pop()) //3 -> 배열의 마지막 정수 빼서 출력
print(stackOfInt.items) //[1, 2]
print(stackOfInt.pop()) //2 -> 배열의 마지막 정수 빼서 출력
print(stackOfInt.items) //[1]
print(stackOfInt.pop()) //1 -> 배열의 마지막 정수 빼서 출력
print(stackOfInt.items) //[]
//제네릭 구조체
struct Stack { //구조체 자료형에 제네릭 넣음
var items = T //자료형을 T(제네릭)로 바꿈
mutating func push(
item: T){ //자료형을 T(제네릭)로 바꿈
items.append(item) //
}
mutating func pop() -> T { //자료형을 T(제네릭)로 바꿈
return items.removeLast()
}
}
var stackOfInt = Stack() //제네릭 구조체를 Int형으로 사용
print(stackOfInt.items) //[] 빈배열 생성
stackOfInt.push(1) //배열에 1 추가
print(stackOfInt.items) //[1]
stackOfInt.push(2) //배열에 2추가
print(stackOfInt.items) //[1, 2]
stackOfInt.push(3) //배열에 3추가
print(stackOfInt.items) //[1, 2, 3]
print(stackOfInt.pop()) //3 -> 배열의 마지막 정수 빼서 출력
print(stackOfInt.items) //[1, 2]
print(stackOfInt.pop()) //2 -> 배열의 마지막 정수 빼서 출력
print(stackOfInt.items) //[1]
print(stackOfInt.pop()) //1 -> 배열의 마지막 정수 빼서 출력
print(stackOfInt.items) //[]

var stackOfString = Stack() //제네릭 구조체를 String형으로 사용
print(stackOfString.items) //[] 빈배열 생성
stackOfString.push("일") //배열에 일 추가
print(stackOfString.items) //[일]
stackOfString.push("이") //배열에 이 추가
print(stackOfString.items) //[일, 이]
stackOfString.push("삼") //배열에 삼 추가
print(stackOfString.items) //[일, 이, 삼]
print(stackOfString.pop()) //삼 -> 배열의 마지막 String 빼서 출력
print(stackOfString.items) //[일, 이]
print(stackOfString.pop()) //이 -> 배열의 마지막 정수 빼서 출력
print(stackOfString.items) //[일]
print(stackOfString.pop()) //일 -> 배열의 마지막 정수 빼서 출력
print(stackOfString.items) //[]

//스위프트 배열은 제네릭 구조체
var x : [Int] = [] // 빈배열 생성
var y = Int // 빈배열 생성
var z : Array = [] // 빈배열 생성

var a : [Int] = [1, 2, 3, 4] // 배열에 초기값 부여
var b : Array = [1, 2, 3, 4] // 배열에 초기값 부여
var c : Array = [1.2, 2.3, 3.4, 4.5] // 배열에 초기값 부여

@frozen struct Array
//@frozen은 구조체에 저장프로퍼티 추가, 삭제 불가능

출처 : https://www.youtube.com/channel/UCM8wseo6DkA-D7yGlCrcrwA

profile
개발자를 꿈꾸는 사람 입니당

0개의 댓글