Day - 6
inout 파라미터
- 함수를 통해, 변수를 직접 수정하고 싶은 경우에 사용
- 함수내의 파라미터는 기본적으로 값타입이고(복사되어서 전달) 임시상수이기 때문에 변경 불가 원칙
E.g 1
var num1 = 123 // 전역변수
var num2 = 456 // 전역변수
func swap(a: Int, b: Int) {
var c = a
a = b // Cannot assign to value: 'a' is a 'let' constant
b = c // Also..
}
swap(a: num1, b: num2)
- 함수 내에서 변수를 직접 수정하도록 돕는 inout키워드 (참조로 전달)
- inout 키워드(선언시), &사용(실행시)
num1 = 123 num2 = 456 func swapNumbers(a: inout Int, b: inout Int) { var temp = a a = b b = temp } // 함수 실행시에는 &를 꼭 붙어야함 swapNubers(a: &num1, b: &num2)
- 파라미터의 복사본이 전달되는 것이 아닌, 원본이 전달됨.
- 내부적으로 copy-in copy-out 매커티즘)
- 메모리 주소를 전달한다고 생각하기
- 상수(let), 리터럴 전달하는 것 불가능
- 파라미터의 기본값 선언을 허용하지 않음
- 가변파라미터(여러개의 파라미터)로 선언하는 것 불가능
guard문
- 불만족하는 조건을 사전에 걸러내는 조건문
- if문의 단점
- 여러개의 조건이 있을때 코드의 가독성이 문제됨 → guard 문으로 단점 극복
- 특징
- else문을 먼저 배치 - 먼저 조건을 판별하여 조기 종료(early exit)
- 조건을 만족하는 경우 코드가 다음 줄로 넘어가서 계속 실행
- 가드문에서 선언된 변수를 아래문장에서 사용 가능 (동일한 스코프로 취급) (guard let 바인딩 관련 ) → 이는 추후에 재설명
- 사용 이유
- 가독성을 높임 / if문의 불편함을 해소
- 여러개의 옵셔널타입을 안전하게 벗기기 가능
- code의 영역이 변경되지 않음(범위(Scope)에 대한 원칙에서 벗어나서 자유로운 코드 작성)
- guard 문에서 선언된 변수를 아래문장에서 사용 가능 (동일한 Scope 취급)
- early exit
- 조건을 만족하지 않을 때 실행하고, 종료 시킬 코드가 있어야 함
- 함수에서는 return, throw
- 반복문에서는 break, continue
func checkNumbersOf1(password: String) -> Bool {
if password.count >= 6 {
// 로그인을 처리하는 코드 작성
// 1
// 2
// 3
// 4
// 5
return true
} else {
return false
}
}
func checkNumbersOf2(password: String) -> Bool {
// 감시하다
guard password.count >= 6 else {
return false // 종료 조건 - 함수 내에서는 return / throw
}
// 로그인을 처리하는 코드 작성
// 1
// 2
// 3
// 4
// 5
return true
}
func check(words: String) -> Bool {
guard words.count >= 5 else {
print("5글자 미만입니다.")
return false // 종료 조건 - 함수 내에서는 return / throw
}
print("\(words.count)글자입니다.")
return true
}
check(words: "안녕하세요")
@discardableResult
- 함수의 리턴값 유무
- 리턴값이 있는 함수
// @discardableResult func sayHelloString() -> String { print("하이") return "안녕하세요" } sayHelloString() // 실제 프젝에서 경고창 표시
- 리턴 타입이 없는 함수(Void 타입) vs 리턴타입이 있는 함수의 차이
- 리턴 타입이 없는 함수 → 결과는 Void 타입
- 메모리 공간을 만들지 않음
- (함수 실행시) CPU 제어권만 가짐
- 리턴 타입이 있는 경우
- 함수의 결과로 값을 가짐
- 함수 실행시 (값을 반환하기 위한) 임시 메모리 공간을 별도로 만듦
- (함수 실행시) CPU 제어권 + 리턴값
- 리턴 타입이 없는 함수 → 결과는 Void 타입
@어트리뷰트 키워드
- 추가적인 정보를 제공하는 키워드
- 선언에 추가정보 제공
@available(iOS 10.0) class MyClass { ... }
- 타입에 추가정보 제공
func doSomething(completion: @escaping () -> ()) { ... }
→ 컴파일러에게 추가적인 정보를 알려주는 역할 (2가지 종류)
- @discardableResult
(2) 어트리뷰트의 사용func doSomething() -> String { ... return "Swift" } doSomething() ...'doSomething()' is unsued // 결과값을 사용하지 않는다고 경고로 알려줌 _ = doSomething() // 경고창을 없애려면, 결과값의 사용을 생략한다는 의미에서 _ 사용
@discardableResult func doSomething() -> String { ... return "Swift" } doSomething() // 경고창 X
- → 결과값을 사용하지 않아도 된다고 컴파일러에게 정보를 알려줌
- (1) 일반적인 함수 선언
연습문제
import UIKit
func randPick(from input: String) -> Character? {
guard !input.isEmpty else { return nil }
let randomIndex = Int.random(in: 0..<input.count)
let randomChar = input[input.index(input.startIndex, offsetBy: randomIndex)]
return randomChar
}
let str1 = "Hello, World!"
if let randomChar = randPick(from: str1) {
print("Random chr : \(randomChar)")
} else {
print("Empty string")
}
// Second
func chooseRandomString(_ chars: String) -> String {
return String(chars.randomElement()!)
}
import UIKit
func primeCheck(_ input: Int) -> Bool {
var check: Int = 1
guard input >= 2 else { return false }
for i in 2...input {
if input % i == 0 { check += 1 }
}
if check == 2 { return true }
else { return false }
}
primeCheck(97)
// 재귀함수로 해도 ok..
print 함수
print(_:separator:terminator:)
API
- Application Programming Interface(응용 프로그래밍 인터페이스): 개발자에게 공통 작업을 수행하기 위한 표준 명령을 제공하므로 코드를 청므부터 작성할 필요가 없음.
- : 프로그래머가 외부 시스템과 상호 작용하는 소프트웨어를 만드는데 사용할 수 있는 명령, 함수, 규약 및 객체의 집합.
- 종류
- 애플이 미리 만들어 놓은 API
- 특정한 동작/기능을 실행시키기 위해, 애플이 미리 사용법을 정의(약속)해놓은 그대로 사용
- 특히 앱을 만들때 프레임워크의 API 많이 사용하게 됨
- e.g : print(…), stride(….), UIButton(..)
- 특정한 동작/기능을 실행시키기 위해, 애플이 미리 사용법을 정의(약속)해놓은 그대로 사용
- 서버와 통신하기 위해 만드는 API
- 서버와 데이터를 주고 받기 위해, 어떠한 방식으로 정보를 요청해야하는지에 대한 규격(요청 방법)을 정하는 것
- e.g : 기상청 오픈API를 통해 날씨정보를 앱에 가져와서 보여줄 수 있음
- 서버와 데이터를 주고 받기 위해, 어떠한 방식으로 정보를 요청해야하는지에 대한 규격(요청 방법)을 정하는 것
- 애플이 미리 만들어 놓은 API
옵셔널(Optional)
- 가장 처음 봤을 때 이해안되는 개념이지 않았을까..
타입의 기본 개념에 대한 이해
- 타입이란..?
- 어떤 메모리공간에 어떤 형태로 어떤 자료가 들어있다고 알려주는 개념
- 왜 대입했을까?
- 흔히… 다른 언어에서 이런 상황을 많이 맞이해봤을 거다.
var myValue: Int print(myValue) // Error!!! // 값을 저장하지 않았다면 메모리에 저장된 값이 없는데 접근하므로 에러 발생
- 모든 타입의 뒤에 ? 물음표를 붙이면 옵셔널 타입이 됨.
var num: Int? = 3 var yourAge: Int? = nil var yourGrade: Double? = nil var name1: String? = "Jobs" num = nil name1 = nil // 쌉가능
- nil
- 값이 없음을 나타내는 키워드 (실제 값이 없음이 아님)
- 예를 들어… 어떤 땅이 있는데, 팻말로 “주인 없는 나라 땅” 이다라고 하는 것과 같음. 실제로 주인이 없는 건 아님.(나라가 주인이잖아!)
- 아님 예약 대기 걸어놓은, 찜! 해놓은 상황을 의미하는 거라고 생각할 수도 있겠다. 그럼 다른 사람이 볼 땐 누가 주인인진 모르지만, 소유는 있는 것
- 어라? 그러면 메모리 공간에 “임시적인 타입”을 넣어놓으면 어떨까?
- 따라서, 에러가 나지 않도록 임시적인 타입을 담아두는 개념으로, 포장지로 둘러쌓인 느낌이라고 볼 수 있음.
- 결국, Optional 타입을 Unwrapping해서 ‘실제 값없음’을 한 번 걸러주는 역할을 진행해야 함.
- == nil : 포장지로 둘러쌓여서 값이 없음을 표현
var optionalExample: String? print(optionalExample) // "nil" 값
- 옵셔널 타입 선언의 기본 규칙
- nil 대입 가능
- 자동 초기화 (값을 넣지 않을 시에 nil로 초기화)
var num1: Int? = 2 var num2: Optional<Int> = 0
- 옵셔널 타입 연습
var optionalNum: Int? = nil // 추론할 형식이 없기 때문에 반드시 Type annotation 진행 optionalNum = 10 print(optionalNum) // Optional(10) var a: Int? = 7 var b = a print(b) // Optional(7) var c: Int = 5 b = c // Int를 Int? 타입에 담을 수는 있음. Int -> Int? 가 담긴다. var d = nil // 추론 불가하므로 에러!
옵셔널 타입 사용하기
- 옵셔널 타입: 값이 없을 수도 있는 경우를 포함하고(감싸고) 있는 임시적 타입
var num: Int? var str: String? = "안녕하세요" print(num) print(str)
옵셔널값을 추출하는 4가지 방법
- 강제로 값을 추출(Forced Unwrapping) : 강제, 거의 안씀
- 뒤에 ! 붙임
print(str!)
- if문으로 nil이 아니라는 것을 확인한 후, 강제로 벗기기
if str != nil { print(str!) }
- 옵셔널 바인딩 (if let 바인딩)
- 바인딩이 된다면, { } 중괄호 안의 코드를 실행하면서, 특정 작업을 하겠다는 의미
- 조건문이 아님. optional값이 name에 담긴다면, 즉 새로운 변수에 담아진다면 내부에 있는 걸 실행 (벗겨서 담을 수 있다면)
if let s = str { // s라는 상수에 담긴다면 -> nil 아님, 안 담기면 nil print(s) } var optionalName: String? = "홍길동" if let name = optionalName { print(name) } // 실제 앱을 만들때 guard let 바인딩 많이 사용 -> ㅇㅈ // 네트워크 통신을 하는 과정, 사용자로부터 입력을 받는 과정에서 사용 func becomeHuman(opinion: Bool?) { guard let n = opinion else { return } print(n) } becomeHuman(opinion: False)
- 닐 코얼레싱 (Nil-Coalescing) 연산자를 사용하는 방법
- Coalescing : 영어로 더 큰 덩어리로 합치다는 뜻
- 삼항연산자 개념과 유사
- var “___” = a ?? b // a가 nil이면 b를 대입, a가 nil이 아니면 a를 대입ㅊ
var serverName: String? = "컴공전산실" var userName = serverName ?? "미승인사용자" // String타입 var hello = "해위해위~! " + (str ?? "팔차선 화이팅") print(hello) str = nil print(str ?? "해위다~ 이자식들아")$$
옵셔널 체이닝(Chaining)
- 옵셔널타입에 대해 접근연산자를 사용하는 방법
- 옵셔널타입에 대해 “접근연산자” 사용 시, ?(물음표)를 붙여서, 앞의 타입의 값이 nil일 수도 있음을 표시
- 결과는 항상 옵셔널타입으로 리턴
- 옵셔널 체이닝 과정에서 그 값 중 하나라고 nil을 리턴한다면, 이어지는 표현식을 평가하지 않고 nil 리턴
- 현재까진 ‘.’ 앞에가 Optional 타입이면 ‘?’ 붙여주기
- = 옵셔널 타입으로 선언된 값에 접근해서, 속성, 메서드를 사용할 때 접근연산자(.) 앞에 ?(물음표)를 붙어야 함.
- 옵셔널타입에 대해 “접근연산자” 사용 시, ?(물음표)를 붙여서, 앞의 타입의 값이 nil일 수도 있음을 표시
class MyLog { var name: String? var height: Int var weight: Int init(name: String, weight: Int) { self.name = name self.weight = weight } func introMySelf() { print("제 이름은 \(self.name)입니다!") } func sayHello() { print("안녕하세요~!") } } class Family { var myLog: MyLog? // 뭔가 이상하지만.. ... }
- 함수와 옵셔널타입
- 함수에서 옵셔널 타입 파라미터의 사용
func doExercise(name people: String, age: Int? = nil) { print("\(people): \(age ?? 00)") } doExercise(name: "Foden", age: 25) doExercise(name: "Foden") // age를 안주고 싶을 때 기존 nil 기본값 설정을 안한다면 -> nil로 적어줘야 했었음
반응형
'iOS > Swift' 카테고리의 다른 글
Swift No.9 (0) | 2024.01.10 |
---|---|
Swift No.8 (1) | 2024.01.09 |
Swift Day 5 (1) | 2024.01.09 |
Swift Day 4 (0) | 2024.01.09 |
Swift Day 3 (0) | 2024.01.09 |