1주차 과제
Xcode 설치 완료
https://yagom.net/courses/swift-basic/ 야곰의 Swift 기초 문법 강의를 듣고 1주차 과제에 정리한다. ( 기초개념, 데이터 타입, 함수 3가지 듣기)
Lower Camel Case : function, method, variable, constant
소문자로 시작하기
Upper Camel Case: type(class, struct, enum, extension 등)
대문자로 시작하기
Swift는 대소문자를 구분함.
print : 단순 문자열 출력
dump : 인스턴스의 자세한 설명(description 프로퍼티)까지 출력
String Interpolation
프로그램 실행 중 문자열 내에 변수 또는 상수의 실질적인 값을 표현하기 위해 사용
() 괄호 안에 값을 넣어주면 값 치환
상수 선언 키워드 let 상수 선언 후 차후에 값을 변경할 수 없음
변수 선언 키워드 var 변수 선언 후 차후에 변경이 가능함
선언하는 방법 (띄어쓰기 신경 쓰기)
let 이름: 타입 = 값
var 이름: 타입 = 값
값의 타입이 명확한 경우 타입은 생략할 수 있음.
나중에 할당하려고 하는 상수나 변수는 타입을 꼭 명시해주어야 함.
할당하지 않은 상수나 변수를 출력하려고 하면 에러 발생.
[예시]
let sum: Int
let inputA: Int = 100
let inputB: Int = 200
sum = inputA + inputB // sum 선언 후 첫 할당
sum = 1 // sum은 let으로 선언하여 값을 바꿀 수 없음. 에러 발생!
var nuckName: String
nickName = “은지”
nickName = “bcsd” // var 선언 했으므로 변수 값을 다른 걸로 할당 가능 -> 에러 발생 안함
기본적인 데이터 타입은 Bool, Int, UInt, Float, Double, Character, String이 있음
Swift는 데이터 타입에 매우 엄격함
var name: Bool = true
true나 false만을 값으로 가지는 타입
0, 1 -> 에러 발생
var name: Int = 100
name = -100
name = 100.1 // 오류 발생. 100.1 값은 Float 또는 Double형
현재는 기본적으로 64비트 정수형
var name: UInt = 100
name = -100 // 오류 발생
양의 정수 타입. 음수 값을 할당할 시 오류 발생
현재는 기본적으로 64비트 양의 정수형
var nameFloat: Float = 3.14
name = 10 // 가능
실수 타입. 32비트 부동소수형
var nameDouble: Double = 3.14
name = 3
nameDouble = nameFloat // 오류 발생. Double 타입 != Float 타입
실수 타입. 64비트 부동소수형
var nameCharacter: Character = “🇰🇷”
name = “가"
name = “가나다라" // 오류 발생. “가나다라"는 문자열
문자 타입. 큰따옴표("”) 사용
유니코드를 사용한 모든 문자를 값으로 넣어줄 수 있음.
var nameString: String = “가나다라"
name = name + “마바사"
print(name) // 가나다라마바사 출력
nameString = nameCharacter // 오류 발생. 서로 다른 타입.
여러 줄의 문자열은 큰따옴표 세 개 사용
someString = “””
여러 줄의 문자열을 사용하려면 첫 줄에 큰따옴표 세 개,
마지막 줄에 큰따옴표 세 개를 사용하면 됨.
큰따옴표 세 개인 줄(첫 줄, 끝 줄)에서
줄 바꿈을 하지 않으면 오류가 발생함.
“””
someString = “””이런경우
오류가 발생해요!“””
var someAny: Any = 100
someAny =”가나다라마바사”
someAny = 3.141592
let someDouble: Double = 3.1234
someDouble = someAny // 오류 발생. 타입이 다름.
someAny = someDouble // 가능! Any 타입이므로 할당할 수 있음.
모든 타입을 지칭하는 키워드
class SomeClass {}
var someAnyObject: AnyObjecct = SomeClass()
모든 클래스 타입을 지칭함
AnyObject는 클래스의 인스턴스만 수용 가능하기 때문에, 클래스가 아니면 할당할 수 없음.
someAnyObject = 123.12 // 오류 발생. 클래스의 인스턴스만 수용 가능.
타 언어의 null과 같은 의미
존재하지 않음을 나타내는 키워드. 타입이 아님!!
someAny = nil // 오류 발생
someAnyObject = nil // 오류 발생
nil을 다루는 방법은 옵셔널 파트에서 배움.
함수 선언의 기본 형태
[ 반환 값이 있는 함수 ]
func 함수이름(매개변수1이름: 매개변수1타입, 매개변수2이름: 매개변수2타입 …) -> 반환타입 {
/ 함수 구현부 /
return 반환값
}
[ 반환 값이 없는 함수 ]
func 함수이름(매개변수1이름: 매개변수1타입, 매개변수2이름: 매개변수2타입 …) -> Void {
/ 함수 구현부 /
return
}
// 반환 값이 없는 경우, 반환 타임(Void)을 생략해 줄 수 있음.
func printHello(name: String) {
print(name)
}
매개변수가 없는 함수
func 함수이름() -> 반환타입 {
/ 함수 구현부 /
return 반환값
}
매개변수와 반환값이 없는 함수
func 함수이름() -> Void {
/ 함수 구현부 /
return
}
함수 구현이 짧은 경우, 가독성을 해치지 않는 범위에서 줄바꿈을 하지 않고 한 줄에 표현해도 무관하다.
func hello() -> Void {print(“hello”)}
반환 값이 없는 경우, 반환 타입(Void)을 생략해 줄 수 있습니다.
func 함수이름() {
/ 함수 구현부 /
return
}
import UIKit
var name: String = ""
func sum(a: Int, b: Int) -> Int {
return a + b
}
sum(a: 3, b: 5) // 8 출력
func printMyName(name: String) -> Void {
print(name)
}
printMyName(name: "eunji") // eunji
func printYourName(name: String) {
print(name)
}
printYourName(name: "bcsd") // bcsd
func maximumIntegerValue() -> Int {
return Int.max
}
print(maximumIntegerValue()) // Int의 최댓값
func hello() -> Void { print("hello") }
hello()
func bye() { print("bye") }
bye()
매개변수 기본 값
매개변수에 기본적으로 전달될 값을 미리 지정할 수 있음.
기본값을 갖는 매개변수는 매개변수 목록 중에 뒤쪽에 위치하는 것이 좋음.
func 함수이름(매개변수1이름: 매개변수1타입, 매개변수2이름: 매개변수2타입 = 매개변수 기본값 …) -> 반환타입 {
/ 함수 구현부 /
return 반환값
}
[예시]
매개변수 기본 값을 가지는 매개변수는 호출 시 생략해도 출력함.
UIKit : 프레임워크
UI의 모든 요소가 object로써 기능함. 화면의 오브젝트를 뷰컨트롤러에 끌어다 놓으면 연결할 수 있다!(실습 때 했다.)
전달인자 레이블
함수를 호출할 때 함수 사용자의 입장에서 매개변수의 역할을 명확하게 표현하고자 할 때 사용함.
func 함수이름(전달인자 레이블 매개변수1이름: 매개변수1타입, 전달인자 레이블 매개변수2이름: 매개변수2타입 …) -> 반환타입 {
/함수 구현부/
return
}
함수 내부에서 전달 인자를 사용할 때에는 매개변수 이름을 사용함.
함수를 호출할 때에는 전달인자 레이블을 사용해야 함.
가변 매개변수
전달 받을 값의 개수를 알기 어려울 때 사용할 수 있음.
가변 매개변수는 함수당 하나만 가질 수 있음.
func 함수이름(매개변수1이름: 매개변수1타입, 전달인자 레이블 매개변수2이름: 매개변수2타입…) -> {
/ 함수 구현부 /
return
}
스위프트는 함수형 프로그래밍 패러다임을 포함하는 다중 패러다임 언어이므로,
스위프트의 함수는 일급 객체임.
반환 타입은 생략할 수 없음.
(매개변수1타입, 매개변수2타입 …) -> 반환타입
타입이 다른 함수는 할당할 수 없음.
옵셔널? <- 얘
swift 언어의 안전성에 해당함
정의
nil을 사용할 수 있는 타입과 없는 타입을 구분하기 위함
Optional Type : nil을 사용할 수 있는 타입
nil이라는 값을 가질 수 있으면 옵셔널 타입
옵셔널 타입 선언하고 싶으면 타입 옆에 ?(물음표)를 붙임
let name: String?
nil
값이 없음을 나타냄. null과 유사함.
오류가 있는데 앱은 중단시키기 싫다
-> 안정성을 위해서 오류는 안 발생시킨다
-> nil을 돌려주어서 컴퓨터가 오류인 걸 알게 함
찾는 값이 없는데 Crash를 발생시키는 게 아니라 nil을 발생시키는 것
Non-Optional Type vs Optional Type
nil은 특수한 값이라서 일반적으로 쓰이는 자료형은 nil을 반환하고 저장할 수 없음
그러면 nil을 저장할 수 있는 거는??
Optional로 선언된 자료형만 사용할 수 있음
Non-Optional Type
var name = “Lee”
var name: String
name = “Lee”
이것처럼 앞서 공부했던 변수(상수)타입을 논옵셔널타입이라고 부름
Non-Optional Type은 무조건 값을 할당해야 함. 할당하지 않고 출력한다면 에러 발생!
Non-Optional Type은 무조건 값이 있다는 전제가 깔려있음.
var name: String
name = nil // 에러 발생!!! nil은 오직 Optional Type만 저장 가능
Optional Type 선언하는 법
Type Annotation 이용하기
var name: String?
name = nil // 에러 안발생
?로 선언된 변수에는 nil 값을 저장할 수 있음
Type Inference(타입 추론) 이용하기
기존처럼 선언과 동시에 값을 초기화함. 옵셔널로 선언하고 싶으면 초기화되는 값이 무조건 옵셔널 자료형이어야 함.
let a: Int? = nil
let b = a // a, b 모두 옵셔널 타입
하지만 nil은 “없는 값"일 뿐이지 자료형을 유추할 수는 없음.
var age = nil // 에러 발생!!
Optional Unwrapping
Optional Type의 값을 Non-Optional Type의 값으로 변환할 때 사용.
옵셔널로 선언된 자료형은 일반 자료형이 아닌 옵셔널로 한 번 포장된 옵셔널 자료형임
예시 사진은 Optional String Type
let a: Int? = nil
print(type(of: a)) // Optional 타입
옵셔널 자료형의 값을 사용하기 위해서 옵셔널이란 포장지를 까줘야 함 -> Optional Unwrapping!
옵셔널 언래핑을 이용하면 옵셔널 타입을 논옵셔널 타입으로 변환할 수 있음
Optional Unwrapping을 하기 위해선 절대 Unwrapping 하고자 하는 변수(상수)가 nil이면 안 된다!!
nil은 값이 아님! 값이 없음을 나타내는 것일 뿐임.
따라서 만약 값이 없으면 nil인 것이지 Optional이 아니라는 것.
Optional은 값을 감싸는 것이지 값이 아닌 nil을 감싸는 것이 아님.
let a: Int? = nil
print(“a == \(a)”) // a == nil
let b: Int? = 4
print(“b == \(b)”) // b == Optional(4)
nil은 값이 없어서 Optional 포장지도 없음 -> Optional Unwrapping도 하면 안됨
nil이 지정된 변수(상수)를 Optional Unwrapping 하려고 하면 치명적인 에러 발생!!!
강제 추출(Forced Unwrapping)
Optional Unwrapping하는 여러 가지 방법 중 하나
강제로 옵셔널을 Unwrapping하는 것
!(느낌표)
Optional로 선언된 변수(상수)를 사용할 때 뒤에 ! 붙임
var myInfo = “내 나이는 (a!)살입니다.”
느낌표 없었을 때는 Optional(22)살이었지만, 느낌표를 붙이면 22살로 알맞게 출력됨.
Optional이 해제된 값을 얻을 수 있음.
하지만 값이 없어서 nil일 경우에도 옵셔널을 강제로 해제하는 특징을 주의해야됨!!
let a: Int? = nil
print(a!) // 이런 경우를 주의해야 함.
값이 있는지 없는지를 확인하는 방법도 있지만, 옵셔널 바인딩을 사용하면 좋음
Optional Binding
안정적으로 옵셔널을 언래핑하는 방법
세 가지 Syntax
if let name: Type = OptionalExpression {
}
while let name: Type = OptionalExpression { // 거의 사용하지 않음
}
guard let name: Type = OptionalExpression else {
}
세 가지 방식 공통점
옵셔널 표현식을 먼저 평가하기
값이 있는 경우(nil이 아닌 경우), 정의된 상수(name)에 옵셔널이 해제된 값을 저장하고 true를 반환함
값이 없는 경우(nil) false를 반환함
타입 추론이 되므로 Type Annotation은 대부분 생략함
if let
let optionalNum: Int? = 4
if let을 이용하여 옵셔널 바인딩을 하면 다음과 같이 사용 가능
if let nonOptionalNum = optionalNum {
print(nonOptionalNum)
} else {
print(optionalNum)
}
let optionalNum: Int? = 4 // 결과값은 4. 옵셔널 해제된 값.
let optionalNum: Int? = nil // 결과값은 nil.
매커니즘
표현식이 nil인지 아닌지 판별
위 코드로 예시를 들면 optionalNum 변수가 nil인지 아닌지 체크
표현식이 nil이 아닌 경우
optionalNum의 값이 nil이 아닌 경우, optionalNum을 Unwrapping한 값을 nonOptionalNum에 대입함.
이때 주의할 점!
2-1) nonOptionalNum이라고 if let을 통해 선언된 상수의 scope는 if 구문임
if문 밖에서는 nonOptionalNum 상수에 접근 불가
2-2) nonOptionalNum은 optionalNum을 Unwrapping한 값을 대입한 것일 뿐. optionalNum의 값은 여전히 Optional Type이다.
if let nonOptionalNum = optionalNum {
print(optionalNum) // Optional(4)
print(nonOptionalNum) // 4 출력
}
표현식이 null인 경우
nil인 경우에는 if 구문을 거치지 않고 else 구문으로 빠짐
nonOptionalNum 상수는 사용 불가!
Tip
바인딩 될 논 옵셔널 타입의 이름을 짓는 게 귀찮은데, 그럴 때는 옵셔널 타입의 이름을 그대로 똑같이 써도 됨,
한번에 여러 개의 옵셔널 타입을 바인딩 할 수도 있음. 변수가 nil이 아니면 됨.
guard let
guard문은 함수(메서드)에서만 쓰임.
guard 구문의 조건을 만족하지 못하면 else문으로 빠져서 함수의 실행을 종료 시킬 때 사용함(return)
guard 구문은 else와 항상 같이 다님
[예시]
let optionalNum: Int? = nil
guard let nonOptionalNum = optionalNum else {
// optionalNum is nil
return
}
// optionalNum is not nil
매커니즘
표현식이 nil인지 아닌지 판별
optionalNum이 nil인지 아닌지 체크한다.
표현식이 nil이 아닌 경우
guard let nonOptionalNum = optionalNum else {
return
}
print(nonOptionalNum)
guard문에서는 표현식이 nil이 아닐 경우 else문으로 빠지지 않고, 원래 가드문을 실행시키려던 scope로 돌아옴. -> NonOptionalNum이라는 옵셔널이 해제된 값을 가드 구문 밖에서 사용할 수 있음.
주의할 점!
옵셔널 바인딩된 nonOptionalNum의 Scope는 guard 구문 밖이기 때문에 else문에서는 nonOptionalNum을 사용할 수 없음.
표현식이 nil인 경우
표현식이 nil인 경우에는 else구문으로 빠져서 return을 하던가 실행을 종료시킴.
그래서 nonOptionalNum 값 사용 불가
Tip
guard let 구문도 if let과 마찬가지로 Optional Binding될 NonOptional Type의 상수 이름을 Optional Type 이름과 같게 사용하려고 함. 이건 경우에 따라서 가능, 불가능 나뉨.
가드문은 함수(메서드)의 매개변수로 들어온 표현식에 한해서만 가능함.
함수(메서드)로 넘어온 파라미터 값(임시 상수 혹은 변수)를 바인딩 하는 경우.
이럴 경우에는 바인딩 되는 표현식의 이름을 같게 해도 됨.
함수(메서드) 내에서 선언한 상수 혹은 변수를 바인딩 하는 경우
이 경우에는 이름 같게 못함. scope의 문제!
옵셔널 바인딩을 하나의 가드문에서 연속으로 할 수도 있고, 컨디션을 넣을 수도 있음.
if let vs guard let
if let과 guard let을 쓰는 경우
if let
단순히 옵셔널 처리 값에 대한 피드백만 주고 받을 때
값 있으면 이렇게 처리하고 nil이면 저렇게 해!
guard let
옵셔널 처리 값이 nil인 경우 무조건 함수의 실행을 종료시킬 때
값 없으면 return
IUO(Implicitly Unwrapped Optional)
옵셔널 묵시적(암묵적) 추출
별도의 추출하는 과정을 거치지 않아도 자동으로 옵셔널이 해제되는 것.
let name: String!
이처럼 변수나 상수를 선언할 때 기존의 Optional Type을 선언할 때처럼 Type 뒤에 ?가 아니라 !를 붙이는 것.
옵셔널을 선언하는 방식이 다름.
IUO도 Optional Type으로 선언하는 방법 중 하나.
하지만, Non-Optional Type으로 처리되어야 할 때 값을 자동으로 추출해 줌.
특정 조건일 때만 자동 추출해줌!!!
원하는 대에 자동으로 전부다 값이 Unwrapping 되는 것이 아니라, Optional Type을 Non-Optional Type에 대입할 때 별도의 추출 과정 없이 대입이 가능함.
IUO도 강제 추출이다
값이 nil인 경우에 묵시적 추출을 하려고 하면 에러 발생.
IUO를 언제, 왜 사용하냐면 …
프로퍼티 지연 초기화를 위해 -> 나중에 배움
IBOutlet
API에서 IUO를 return한 경우
두 가지 경우 외에는 안전하게 옵셔널 바인딩을 쓰는 것이 나음.
Nil-Coalescing Operator
닐 코어레이싱을 사용하면 옵셔널 타입 표현식에 값이 저장되어있는지 확인하고 꺼낼 필요가 없어짐.
Optional Expression ?? Non-Optional Expression
[예제]
let name: String? = “Eunji” // 옵셔널 스트링 타입인 변수 name
이때 name이 nil이 아닐 경우 -> hello, (name)
name이 nil일 경우 -> hello, what’s your name?
라고 출력하고 싶음.
닐 코어레이싱을 이용해서 만들면 다음처럼 간단하게 만들 수 있음.
print(“hello, “ + (name ?? “what’s your name?))
매커니즘은 name에 값이 있을 경우 name의 언래핑된 값을 리턴하고, 값이 없을 경우에는 논 옵셔널 타입인 오른쪽 피연산자 값을 리턴함.
C++의 삼항연산자 같은 느낌…?
옵셔널 타입과 논옵셔널 타입은 옵셔널을 제외하면 동일한 타입이어야 함!
옵셔널 체이닝
옵셔널을 연쇄적으로 사용하는 것
person.contacts?.address
person?.contacts?.address
이런 식으로 사용한다
.(점)을 통해 내부 프로퍼티나 메서드에 연속적으로 접근할 때 옵셔널 값이 하나라도 껴 있으면 옵셔널 체이닝임.
하나도 없으면 옵셔널 체이닝이 아님.
옵셔널 표현식의 멤버에 접근할 때, 표현식이 nil일 수 있으니 ? 사용해야 함.
값이 nil일 경우에는? nil 반환.
!과 다른 이유는 !는 옵셔널 강제 해제 연산이기 때문임.(웬만하면 쓰지 않는 게 좋다)
옵셔널 체이닝의 특징
옵셔널 체이닝의 결과값의 타입은 마지막 표현식의 옵셔널 타입이다.
이런 경우 email 타입에 옵셔널을 씌운 Optional 이메일의 타입이 반환된다.
어떤 조건이든 옵셔널 체이닝의 결과값은 무조건 마지막 표현식의 옵셔널 타입이다.
옵셔널 체이닝의 마지막 표현식은 옵셔널이더라도 ?를 생략한다.
email의 변수를 옵셔널 스트링으로 바꿔줬음.
옵셔널 체이닝의 마지막 표현식은 옵셔널이든 아니든 ? 생략함.
let email = sodeul?.contacts.email 처럼 해주면 에러 안 난다.
옵셔널 체이닝의 표현식 중 하나라도 nil이라면, 이어지는 표현식은 평가하지 않고 nil을 리턴한다.
옵셔널 체이닝 첫 표현식 sodeul이 nil이라면
다음에 오는 contacts, email 표현식을 평가하지 않고 바로 nil을 리턴하며 종료함.
함수가 껴있는 옵셔널 체이닝
()?
Person 구조체에 getContacts라는 함수를 만듦. 이 함수의 리턴값으로 얻은 contacts의 email 값에 접근하려고 함.
sodeul?.getContacts()?.email
함수의 리턴 값이 Optional인 경우에는 함수를 호출하는 () 뒤에 ? 붙임
() 뒤에 ?가 붙는 경우는 함수의 리턴 값의 속성에 접근할 때.
?()
함수 자체가 옵셔널인 경우.
리턴 타입이 논 옵셔널 타입이며 함수를 담은 function이란 상수는 Optional Type인 경우
function?().email
이렇게 사용함
?()?
함수 자체도 옵셔널이고 리턴 값도 옵셔널일 때
3-1) ?() function이란 함수가 먼저 옵셔널인지 아닌지 체크
3-2) ()? function 함수가 옵셔널이 아니라면 리턴값이 옵셔널인지 아닌지 체크
딕셔너리가 껴있는 옵셔널 체이닝
[]?
딕셔너리의 키 값으로 얻은 value 값의 속성에 접근 할 때
value 값의 속성에 접근할 때에는 무조건 옵셔널 체이닝을 해줘야 함
?[]?
딕셔너리 타입 자체가 옵셔널인 경우
2-1) dic이 nil인지 아닌지 체크
2-2) key “name”으로 얻은 value 값이 nil 인지 아닌지 체크
class, struct, enum
열거형과 구조체는 값 타입이지만 클래스는 참조타입.
클래스는 상속이 가능하지만, 구조체와 열거형은 상속이 불가능.
struct ValueType {
var property = 1
}
class ReferenceType {
var property = 1
}
// 첫 번째 구조체 인스턴스
let firstStructInstance = ValueType()
// 두 번째 구조체 인스턴스에 첫 번째 인스턴스 값 복사
var secondStructInstance = firstStructInstance
// 두 번째 구조체 인스턴스 프로퍼티 값 수정
secondStructInstance.property = 2
// 두 번째 구조체 인스턴스는 첫 번째 구조체를 똑같이 복사한
// 별도의 인스턴스이기 때문에
// 두 번째 구조체 인스턴스의 프로퍼티 값을 변경해도
// 첫 번째 구조체 인스턴스의 프로퍼티 값에는 영향이 없음
print("first struct instance property : (firstStructInstance.property)") // 1
print("second struct instance property : (secondStructInstance.property)") // 2
// 클래스 인스턴스 생성 후 첫 번째 참조 생성
let firstClassReference = ReferenceType()
// 두 번째 참조 변수에 첫 번째 참조 할당
let secondClassReference = firstClassReference
secondClassReference.property = 2
// 두 번째 클래스 참조는 첫 번째 클래스 인스턴스를 참조하기 때문에
// 두 번째 참조를 통해 인스턴스의 프로퍼티 값을 변경하면
// 첫 번째 클래스 인스턴스의 프로퍼티 값을 변경하게 됨
print("first class reference property : (firstClassReference.property)") // 2
print("second class reference property : (secondClassReference.property)") // 2
접근 제어(Access Control)
접근 제어는 다른 소스 파일과 모듈에서 코드 접근에 대해 제한함.
코드의 구현 세부 사항을 숨김
해당 코드에 접근하고 사용될 수 있는 기본 인터페이스 지정 가능
스위프트는 일반적인 시나리오에 대한 기본 접근 수준을 제공하여 명시적으로 접근 제어 수준을 지정할 필요성을 줄여줌.
엔티티
: 프로퍼티, 타입, 함수 등에 접근 제어를 적용할 수 있는 코드의 다양한 측면
모듈, 소스 파일, 패키지
모듈
코드 배포의 단일 단위
스위프트의 import 키워드로 다른 모듈에서 가져올 수 있는 framework, application도 모듈임
Xcode에서 각 빌드 타겟은 스위프트에서 별도의 모듈로 처리됨.
여러 애플리케이션에서 해당 코드를 캡슐화 하고 재사용하기 위해 코드의 여러 측면을 독립형 프레임워크로 그룹화함 -> 프레임워크 내에 정의한 모든 것은 앱 내에서 가져오고 사용되거나 다른 프레임워크 내에서 사용될 때 별도의 모듈에 속하게 됨.
소스 파일
모듈 내 하나의 스위프트 소스 코드 파일(앱 또는 프레임워크 내의 단일 파일)
하나의 소스 파일에 여러 타입, 함수 등 포함 가능
패키지
하나의 단위로 개발한 모듈의 그룹
사용 중인 빌드 시스템 구성의 부분으로 정의
Xcode를 사용하면 Package Access Identifier 빌드 설정에서 패키지 이름을 지정함.
접근 수준(Access Levels)
Swift는 코드 내의 엔티티에 대해 5개의 다른 접근 수준을 제공함.
엔티티가 정의된 소스 파일과 소스 파일이 속한 모듈과 관련.
Open 접근, public 접근
정의한 모듈의 모든 소스 파일과 정의한 모듈을 import함
다른 모듈의 소스 파일에서 엔티티 사용 가능
일반적으로 프레임워크를 공개 인터페이스로 지정하려면 Open, public 사용함.
Internal 접근
정의한 모듈의 모든 소스 파일 내에서 사용할 수 있지만, 해당 모듈의 외부 소스 파일에서는 사용 불가
일반적으로 앱 또는 프레임워크의 내부 구조체를 정의할 때 internal 접근 사용
File-private 접근
자체 정의한 소스파일. 엔티티 사용 제한.
세부 내용은 파일 전체에서 사용
기능의 특정 부분의 구현 세부 정보를 가리기 위해 사용함
Private 접근
둘러싸인 선언과 같은 파일에 있는 해당 선언의 확장
엔티티 사용 제한
세부 내용은 단일 선언 내에서만 사용됨
기능의 특정 부분의 구현 세부 정보를 가리기 위해서 사용함
Open 접근은 가장 높은(가장 제약이 적은) 접근 수준 <-> private 접근은 가장 적은(가장 제약이 많은) 접근 수준
Open 접근과 public 접근의 차이
Open 접근은 클래스와 슬래스 멤버에만 적용. 모듈 외부의 코드를 하위 클래스와 재정의 할 수 있음.
접근 수준의 기본 원칙
public 변수는 internal, file-private, private 타입으로 정의될 수 없음. 왜? public 변수는 어디서나 사용될 수 있지만 세 타입은 아님.
함수는 주변 코드에서 구성 타입을 사용할 수 없는 상황에도 함수를 사용할 수 있기 때문에 파라미터 타입과 반환 타입보다 더 높은 접근 수준을 가질 수 없음.
기본 접근 수준(Default Access Levels)
코드의 모든 엔티티는 명시적으로 접근 수준을 지정하지 않으면 internal의 기본 접근 수준을 가짐.
많은 경우에 코드에서 명시적으로 접근 수준을 지정할 필요가 없음.
단일 타겟 앱에 대한 접근 수준
간단한 단일 타겟 앱을 작성할 때에는 앱의 모듈 외부에서 사용할 수 있도록 만들 필요가 없음.
-> 사용자 정의 접근 수준을 지정할 필요가 없음.
하지만 접근 수준을 지정하고 싶으면 해도 됨
프레임워크에 대한 접근 수준
프레임워크를 가져오는 앱과 같은 다른 모듈에서 보고 접근할 수 있게 공용 인터페이스를 open 또는 public으로 표시함.
이 공용 인터페이스는 프레임워크용 애플리케이션 프로그래밍 인터페이스 또는 API임.
프레임워크의 내부 구현 세부 정보는 여전히 internal의 기본 접근 수준을 사용할 수 있거나, 프레임워크의 내부 코드의 다른 부분에서 가리기 위해 private 또는 file private으로 표기될 수 있음.
프레임워크 API의 부분이 되길 원하면 엔티티를 open이나 public으로 표시해야 함.
유닛 테스트 타겟에 대한 접근 수준
유닛 테스트 타겟과 함께 앱을 작성할 때 앱의 코드는 테스트를 위해 해당 모듈에서 사용할 수 있어야 함.
기본적으로 open 또는 public으로 표기된 엔티티만 다른 모듈에서 접근할 수 있음.
그러나 모듈에 대한 가져오기 선언을 @testable 속성으로 표시하고 테스트를 활성하여 해당 모듈을 컴파일하면 유닛 테스트 타겟은 모든 내부 엔티티에 접근할 수 있음.
접근 제어 구문
엔티티 선언 시작에 open, public, internal, fileprivate, private 수식어 중 하나를 앞에 붙임.
open class SomeOpenClass {}
public class SomePublicClass {}
internal class SomeInternalClass {}
fileprivate class SomeFilePrivateClass {}
private class SomePrivateClass {}
open var someOpenVariable = 0
public var somePublicVariable = 0
internal let someInternalConstant = 0
fileprivate func someFilePrivateFunction() {}
private func somePrivateFunction() {}
지정하지 않으면 접근 수준은 internal이다.
사용자 정의 타입
서브 클래싱
프로토콜
프로토콜 상속
기존 프로토콜 상속하는 새로운 프로토콜 정의 -> 새로운 프로토콜은 상속된 프로토콜과 최대 동일한 접근 수준을 가질 수 있음.
프로토콜 준수
제너릭 (Generics)
제너릭 타입, 제너릭 함수에 대한 접근 수준
: 모든 타입 제약 조건의 접근 수준의 최소.
초기화 메서드
https://zeromin-code.tistory.com/143?category=1069346
https://zeromin-code.tistory.com/144?category=1069346
초기화 = 이니셜라이저(Initializer)
초기화란?
클래스, 구조체, 열거형의 인스턴스를 생성하고 초기 상태를 설정하는 역할을 하는 메소드.
인스턴스가 생성될 때 자동으로 호출
인스턴스의 모든 속성에 초기값을 할당
이니셜라이저는 인스턴스의 모든 속성이 올바른 초기 상태에 있게 하는 역할.
모든 프로퍼티를 기본값으로 초기화 함.
이니셜라이저 장점
데이터 무결성 보장
코드의 명확성 향상
다양성 제공
오류 처리
이니셜라이저는 init 키워드를 사용하여 정의
클래스나 구조체 정의 내부에 위치함
init(name: String, age: Int) {
self.name = name
self.age = age
}
이니셜라이저 내부에서는 self 키워드를 사용하여 인스턴스 속성에 접근 가능
규칙
모든 저장 속성이 초기화되기 전에는 인스턴스 메소드를 호출할 수 없음
자식 클래스는 부모 클래스의 이니셜라이저를 자동으로 상속받지 않음. 사용하려면 자식 클래스에서 명시적으로 구현.
편의 이니셜라이저는 반드시 같은 클래스의 다른 이니셜라이저 호출해야 함.
기본 이니셜라이저는 반드시 직접 또는 간접적으로 부모 클래스의 이니셜라이저를 호출해야 함.
extension(확장)
https://babbab2.tistory.com/124
정의
기존 클래스, 구조체, 열거형 타입에 새로운 Property, Method, Initializer 등을 추가하는 것으로, 원본 타입(소스 코드)에 접근하지 못하는 타입들도 확장해서 사용할 수 있음.
extension SomeType {
}
extension SomeType: SomeProtocol, AnotherProtocol {
}
extension 뒤에 추가로 채택하고 싶은 Protocol 붙이기
스위프트의 익스텐션이 타입에 추가할 수 있는 기능
연산 타임 프로퍼티 / 연산 인스턴스 프로퍼티
타입 메서드 / 인스턴스 메서드
이니셜라이저
서브스크립트
중첩 타입
특정 프로토콜을 준수할 수 있도록 기능 추가
타입에 새로운 기능을 추가할 수는 있지만, 기존 새로운 기능을 재정의(override) 할 수는 없음.
상속과 익스텐션 비교
프로퍼티, 메서드
인스턴스
클래스 / 구조체 / 열거형에서 생성된 객체
var Class: Person = Person();
프로퍼티
클래스, 구조체, 열거형과 연관되어 있는 정보 또는 값
즉, 클래스, 구조체, 열거형 안에서 만들어진 변수
struct Person {
var name: String
var height: Int
}
메서드
클래스, 구조체, 열거형과 관련된 함수
class Calculate {
func sum(a: Int, b: Int) -> Int {
return a + b
}
}
comment