옵셔널은 안전성을 높이기 위해 사용되는 개념이다.(가급적 오류를 발생시키지 않으려는) 즉 nil을 사용할수 있는 타입을 옵셔널타입이라고 부른다.
중요한 점은 오류가 발생할 가능성이다. 가능성이 있다면 무조건 옵셔널타입으로 정의해야한다.
또한 반환된 값은 언래핑을 통해 사용해야한다.
// 옵셔널 Int 타입
var optInt: Int?
// 옵셔널 String 타입
var optStr: String?
// 옵셔널 Double 타입
var optDouble: Double?
// 옵셔널 Array 타입
var optArray: [String]?
// 옵셔널 Dictionary 타입
var optDic: Dictionary<String, String>?
var optDic2: [String: String]?
// 옵셔널 Class 타입
var optClass: AnyObject?
타입뒤에 ?을 붙여주기만 하면 되겠다.
옵셔널타입끼리는 사실 쓸모가 없다. 연산을 지원하지도 않는다. +-*/조차도
앞에서 말했듯이 언래핑(옵셔널해제)를 해야한다.
옵셔널해제
강제 하는 방법은 옵셔널 타입뒤에 !만 붙여주면 된다.
단 nil을 반환한다면 오류를 출력한다.
때문에 강제 해제 연산자를 사용한다면 반드시 if조건절을 사용해야한다.
var str = "123"
var intFromStr = Int(str)
if intFromStr != nil {
print("변환된 값은 \(intFromStr!)입니다")
} else {
print("값 변환에 실패하였습니다")
}
참고로 Int()는 옵셔널을 반환한다. String()도? 아니 여기선 오류가 일어날 일이 없기때문에 문자열 그대로 반환한다.
var str = "Swift"
if let intFromStr = Int(str) {
print("변환된 값은 \(intFromStr)입니다")
} else {
print("값 변환에 실패하였습니다")
}
위와 비슷해보이지만, 상수 선언이 되면 옵셔널이 아닌, 일반타입이다. 마찬가지로 nil이면 else구문이 실행된다.
이번엔 guard구문을 써보자
func intStr(str: String) {
guard let intFromStr = Int(str) else {
print("값 변환에 실패하였습니다")
return
}
print("변환된 값은 \(intFromStr)입니다")
}
옵셔널타입변수를 사용하려면 !을 사용해야한다고 했는데 그렇디 않는 경우도 있다. 바로 비교연산자를 사용할때다. 그때는 자연스럽게 언래핑이 된다.
let optInt = Int("123")
if (optInt == 123) {
print("optInt == 123")
} else {
print("optInt != 123")
}
?대신 !을 사용한다. 그래서 일반 변수마냥 쓸수 있다. 주의해야할점은, nil이 될 가능성이 있다면, 이 테크를 쓰지 않는 것이 좋다.
// 명시적 옵셔널
var str: String? = "Swift"
print(str)
// 실행 결과
Optional("Swift")
// 묵시적 옵셔널
var str: String! = "Swift"
print(str)
// 실행 결과
Swift
함수의 정의는 생략하고, 스위프트가 함수형 프로그래밍 패러다임을 채택하고 있는 언어이기 때문에 함수형 프로그래밍의 특성을 이해하는 것은 매우 중요하다.
파이썬과 달리 파라미터의 레이블을 항상 작성해야한다.
func times(x: Int, y: Int) -> Int {
return (x * y)
}
times(x: 5, y: 10)
typealias infoResult = (Int, Character, String)
func getUserInfo() -> infoResult {
let gender: Character = "M"
let height = 100
let name = "Peter"
return (height, gender, name)
}
타입 알리어스는 이름이 길거나 사용하기 복잡한 타입표현을 새로운 타입명으로 정의해주는 문법
typealias infoResult = (h: Int, g: Character, n: String)
func getUserInfo() -> infoResult {
let gender: Character = "M"
let height = 100
let name = "Peter"
return (height, gender, name)
}
이렇게도 쓸수 있다.
함수 선언시 언더바를 넣으면 레이블을 생략할수 있다.
func printHello(_ name: String, _ msg: String) {
print("\(name)님, \(msg)")
}
printHello("홍길동", "안녕하세요")
매개변수가 여러개일경우도 있다.
func avg(score: Int...) -> Double {
var total = 0 // 점수 합계
for r in score { // 배열로 입력된 값들을 순회 탐색하면서 점수를 합산
total += r
}
return (Double(total) / Double(score.count)) // 평균 구해서 반환
}
print(avg(score: 10,20,30,40)) // 결과 : 25.0
일급객체 조건
1. 객체가 런타임에도 생성이 가능하다
2. 인자값으로 객체를 전달할 수 있다.
3. 반환값으로 객체를 사용할 수 있다.
4. 변수나 데이터 구조안에 저장할 수 있다.
5. 할당에 사용된 이름에 관계없이 고유한 구별이 가능해야한다.
위 조건을 만족하면 일급함수가 되며, 그 언어를 함수형 언어로 분류한다.
func successThrough() {
print("연산 처리가 성공했습니다")
}
func failThrough() {
print("처리 과정에 오류가 발생했습니다")
}
func divide(base: Int, success sCallBack: () -> Void, fail fCallBack: () -> Void) -> Int {
guard base != 0 else {
fCallBack() // 실패 함수를 실행한다
return 0
}
defer {
sCallBack() // 성공 함수를 실행한다
}
return 100 / base
}
divide(base: 30, success: successThrough, fail: failThrough)
// 실행 결과
연산 처리가 성공했습니다
defer블록은 코드에 상관없이 가장 마지막에 실행된다.
쉽게말해, 람다, 화살표 함수같은거라고 생각하면 되겠다.
트레일링 클로저는 함수의 마지막 인자값(1개)이 클로저일때, 이를 인자값형식으로 작성하는 대신 함수의 뒤에 꼬리처럼 붙이는것.
value.sort() { (s1, s2) in
reutnr s1 > s2
}
{}안의 코드들은 원래 ()안에 있어야하지만, 스위프트에선 꺼내서 작성할수도 있다.
value.sort { (s1, s2) in
reutnr s1 > s2
}
만약 파라미터가 한개라면 ()도 생략할수 있다.
클로저를 위와같이 인자값으로 사용할때 부여할수 있는 속성이다.
이스케이핑은 인자값으로 전달된 클로저를 전달해 두었다가 나중에 다른 곳에서도 실행할 수 있도록 허용해주는 속성이다.
func callback(fn: () -> Void) {
let f = fn // 클로저를 상수 f에 대입
f() // 대입된 클로저를 실행
}
위코드의 일반적인클로저는 다른곳에 대입될 수 없다.
func callback(fn: @escaping () -> Void) {
let f = fn // 클로저를 상수 f에 대입
f() // 대입된 클로저를 실행
}
속성을 부여해줌으로써 가능해졌다.
오토클로저속성은 인자값으로 전달된 구문과 함수를 클로저로 래핑한다. 예제로 보자.
// 함수 정의
func condition(stmt: () -> Bool) {
if stmt() == true {
print("결과가 참입니다")
} else {
print("결과가 거짓입니다")
}
}
그리고 사용시에는 보통 이렇게 사용할 것이다.
// 1. 일반 구문
condition(stmt: {
4 > 2
})
// 2. 클로저 구문
condition {
4 > 2
}
그러나 오토클로저속성을 부여하면 인자값을 넣으면 알아서 클로저로 만들어준다. 추가적으로 위 두가지 사용은 할수없다.
func condition(stmt: @autoclosure () -> Bool) {
if stmt() == true {
print("결과가 참입니다")
} else {
print("결과가 거짓입니다")
}
}
// 실행 방법
condition(stmt: (4 > 2))
guard는 단독으로 사용이 불가한가? while func안에 있어야만 하는가