CHAPTER 5. 집단 자료형 : 연관된 데이터를 손쉽게 다루기
1. 배열
- 저장할 아이템의 타입에 제약이 없음. 하나의 배열에 저장하는 아이템 타입은 모두 같아야 함
- 선언 시 배열에 저장할 아이템 타입을 명확히 정의해야 함
- 배열의 크기는 동적으로 확장할 수 있음
순회 방법
1 2 3 4 5 6 | var cities = ["서울", "인천", "부산"] let length = cities.count for i in 0..<length { print("\(i)번째 인덱스 : \(cities[i])") } | cs |
< 결과화면 >
1 2 3 | 0번째 인덱스 : 서울 1번째 인덱스 : 인천 2번째 인덱스 : 부산 | cs |
배열 크기를 상수에 할당한 이유 :
for ~ in 구문에 직접 넣으면, 매 반복문 실행 시 매번 배열의 크기를 다시 계산하므로 전체적인 실행 시간을 떨어트리는 원인이 된다. 배열의 크기를 한 번만 읽어 상수에 저장한 다음 쓰는 편이 좋다.
2. 배열값의 순회 특성을 사용하여 탐색
1 2 3 4 5 | var cities = ["서울", "인천", "부산"] for row in cities { print("배월 원소는 \(row)입니다") } | cs |
1 2 3 | 0번째 인덱스 : 서울 1번째 인덱스 : 인천 2번째 인덱스 : 부산 | cs |
아이템을 통해 인덱스 값을 역으로 찾는 배열 메소드
배열 아이템 동적 추가
- append(_:) : 입력된 값을 배열의 맨 뒤에 추가
- insert(_:at:) : 원하는 인덱스 위치에 넣고, 이를 기준으로 뒤 인덱스는 하나씩 다음으로 밀려난다.
- append(contentsOf:) : 배열의 맨 마지막에 '배열'을 추가한다. 메소드의 인자값은 배열이다.
공간 미리 할당하기
1 2 3 | extension Array : RangeReplaceableCollection { public int(repeating repeatedValue: Element, count: Int) } | cs |
코코아 터치 프레임워크에서, 배열을 생성하는 여러 가지 방법 중 초기화할 때 배열의 크기를 지정할 수 있는 구문이다.
1 2 3 | var cities = Array(repeating: "None", count: 3) var cities = [String](repeating: "None", count: 3) | cs |
범위 연사자를 이용한 인덱스 참조
1 2 3 4 5 | var alphabet = ["a", "b", "c", "d", "e"] alphabet[0...2] // ["a", "b", "c"] alphabet[2...3] // ["c", "d"] alphabet[1..<3] // ["b", "c"] | cs |
2. 집합
집합의 정의
1 | var genres : Set = ["클래식", "락", "발라드"] | cs |
Set<데이터 타입>() 으로 데이터 타입을 명시적으로 지정해 줄 수 있다.
- insert(_:) : 집합에 데이터 추가. 이미 있는 경우에는 아무 처리도 하지 않는다.
- remove(_:) : 아이템을 삭제하고, 그 아이템을 반환한다. 없다면 nil을 반환한다.
- removeAll() : 집합의 모든 아이템을 삭제한다.
- contains(_:) : 해당 집합 내에 아이템이 있으면 true, 없다면 false를 반환한다.
집합 연산
부분집합 포함관계 판단 연산
- isSubset(of_:) : 주어진 집합의 값 전체가 특정 집합에 포함되는지를 판단
- isSuperset(of:) : 주어진 집합이 특정 집합의 모든 값을 포함하는지를 판단
- isStrictSubset(of:), isStrictSuperset(of:) : 위 두 메소드오 차이점은 서로 일치하는 집합은 true로 반환 안함
- isDisjoint(with:) : 공통 값이 없을 때 판단
3. 튜플
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | let tupleValue = ("a", "b", 1, 2.5, true) tupleValue.0 tupleValue.1 var tpl01 : (Int, Int) = (100, 200) let (a, b, c, d, e) = tupleValue print(a) print(b) func getTupleValue() -> (String, String, Int) { return ("t", "v", 100) } let (var1, var2, var3) = getTupleValue() print(var1) print(var2) print(var3) let (var4, var5, _) = getTupleValue() print(var4) print(var5) | cs |
점(.) 연산자로 인덱스로 접근이 가능하다. 바인딩(binding) 기능으로 튜플 값을 각각의 변수로 넣어줄 수 있다.
이 기능은 함수가 여러 타입의 데이터들을 반환하고자 할 때 진가를 발휘한다.
순회 처리를 지원하지 않는다.
4. 딕셔너리
- 하나의 키는 하나의 데이터에만 연결된다.
- 키는 중복될 수 없다. 중복 선언 시 기존 키에 연결된 데이터가 제거된다.
- 저장할 수 있는 데이터 타입에는 제한이 없지만, 하나의 딕셔너리에 저장하는 데이터 타입은 모두 일치해야 한다.
- 아이템에는 순서가 없지만 키에는 내부적으로 순서가 있으므로 for~in 구문으로 순회가 가능하다
- 키의 타입은 거의 제한이 없으나 해시(Hash) 연산이 가능한 타입이어야 한다.
딕셔너리 선언
1 2 3 4 | var capital1 = ["KR":"Seoul", "EN":"London", "FR":"Paris"] var capital2 : [String : String] = ["KR":"Seoul", "EN":"London", "FR":"Paris"] var captial3 : Dictionary<String, String> = ["KR":"Seoul", "EN":"London", "FR":"Paris"] | cs |
1. updateValue(<저장할 데이터>, forKey: <키>) : 해당 키에 데이터를 수정함. 또는 없다면 추가함
2. removeValue(forKey:) : 해당 키 데이터를 삭제하고 그 데이터를 반환. 없다면 nil을 반환
순회 탐색
1 2 3 4 5 6 7 8 9 10 11 | var capital1 = ["KR":"Seoul", "EN":"London", "FR":"Paris"] for row in capital1 { let (key, value) = row print("현재 데이터는 \(key) : \(value)입니다.") } for (key, value) in capital1 { print("현재 데이터는 \(key) : \(value)입니다.") } | cs |
CHAPTER 6. 옵셔널 : 스위프트가 잠재적 오류를 다루는 방법
옵셔널(Optional)은 스위프트에서 도입한 개념으로 프로그램의 안정성을 높이고자 사용하는 개념.
값을 처리하는 과정에서 오류가 발생할 가능성이 있느 값을 옵셔널 타입이라는 객체로 감싼 후 반환하는 개념. 이를 옵셔널 래핑(Optional Wrapping)이라고 함.
1 2 3 | let num = Int("123") => 123 let num2 = Int("swift") => nil | cs |
문자열을 숫자로 바꾸는 경우이다. 첫 번째의 경우는 Int(문자열)로 성공적으로 반환하지만,
두 번째의 경우는 nil을 반환한다.
보통 다른 언어에서는 두 번째의 경우 예외를 발생시키거나, 자바스크립트의 경우 NaN을 반환한다. 그러나 스위프트는 언어의 안정성을 위해 가급적 오류를 발생시키지 않으려 한다. 따라서 '값이 없음'을 뜻하는 nil을 반환한다. ( 오브젝티브-C에서는 빈 메모리 주소를 가리켰다. )
스위프트에서 또 다른 안정성을 위해 nil 사용을 제약했다. 일반 데이터 타입에는 nil 값을 가질 수 없다.
nil 값을 저장하기 위해서 옵셔널 타입을 사용해야 한다. 오류가 발생할 가능성이 있다면, 타입을 옵셔널 타입으로 설정해야 한다.
옵셔널 타입이 실제로 가질 수 있는 값은 nil이 아닌 값과 nil 이다. 그리고 Optional() 으로 둘러싼 결과값이 나오게 된다.
Int("123") => Optional(123)
Int("swift") => nil
옵셔널 타입을 사용할 때 매 번 nil인지 아닌지 체크해줘야 한다.
선언
1 2 3 4 5 6 7 | var optInt : Int? var optStr : String? var optDouble : Double? var optrr : [String]? var optDic : Dictionary<String, String>? var optDic2: [String:String]? var optClass : AnyObject? | cs |
옵셔널 값 처리
옵셔널 강제 해제 (Forced Unwrapping)
1 2 3 4 5 6 7 | var optInt : Int? = 3 print("옵셔널 자체의 값 : \(optInt)") print("!로 강제 해제한 값 : \(optInt)") 옵셔널 자체의 값 : Optional(3) !로 강제해제한 값 : 3 | cs |
그러나 nil 타입에 강제 연산자를 붙여버리면 오류가 발생한다. 옵셔널 변수나 상수를 안전하게 사용하려면 조건문으로 꼭 확인을 해주어야 한다.
1 2 3 4 5 6 7 8 | var str = "123" var intFromStr = Int(str) if intFromStr != nil { print("값이 변환됨. 변환 값 : \(intFromStr!)") } else { print("값 변환 실퍠") } | cs |
intFromStr 과 != 연산자 사이에 문법의 오류를 막기 위해 꼭 공백이 있어야한다.
(intFromStr)! = (nil)
(intFromStr!) = (nil)
으로 모호하므로, 컴파일러는 구문 분석 오류를 발생한다.
옵셔널 바인딩
1 2 3 4 5 6 | var str = "Swift" if let intFromStr = Int(str) { print("값이 변환됨. 변환 값 : \(intFromStr)") } else { print("값 변환 실패") } | cs |
1 2 3 4 5 6 7 8 9 | func intStr(str:String) { guard let intFromStr = Int(str) else { print("값 변환 실패") return } print("값 변환 됨. 변환 값 \(intFromStr)") } | cs |
guard 구문을 이용해 옵셔널 바인딩을 구현했다. guard 구문 특성상 함수나 메소드에만 사용한다. 앱을 만드는 과정에서 거의 대부분 함수로 이루어지기 때문에 guard 구문을 사용할 기회가 많다.
guard 구문은 함수를 종료시키는 특성이 있어, 옵셔널 값이 반드시 해제되어야 할 때 사용한다.
옵셔널 타이비지만, 절대 nil 값이 들어오지 않을 것이라는 보장이 있을 때는, 강제 해제 연산자 ! 를 사용하는 것이 더 효율적이다.
1 2 3 4 | var capital = ["KR": "서울", "EN": "런던", "FR": "파리"] print(capital["KR"]) // Optionl("서울") print(capital["KR"]!) // 서울 | cs |
컴파일러에 의한 옵셔널 자동 해제
옵셔널의 묵시적 해제
1 2 3 4 5 | var strOpt : String? = "Swift Optional" var str : String! = "Swift Optional" print(strOpt) => Optional("Swift Optional") print(str) => Swift Optional | cs |
! 선언에 nil 을 대입해도 아무런 문제가 없다. print() 함수 시 nil 이 할당된 변수를 출력하려고 시도해서 발생하는 오류일 뿐이다.
묵시적 옵셔널은 컴파일러에 의해 자동으로 옵셔널이 해제된다. 따라서 nil 이 대입되어 있다면 오류를 피할 수가 없다. 이는 옵셔널 타입을 사용하는 메리트가 없어진다. 형식상 옵셔널로 정의해야 하지만, 실제로 사요할 때 절대 nil 값이 대입될 가능성이 없는 변수일 때 사용하면 편리하다.
var value : Int! = Int("123")
Int(문자열)이 반환하는 값이 옵셔널 타입이기 때문에, 어쩔 수 없이 value 변수를 옵셔널 타입으로 선언한다. Int("123")은 누가봐도 정수로 반환될 것이기 때문에, 묵시적 해제를 써준다.
또 다른 경우에는 클래스 또는 구조체 내에서다. 주로 멤버 변수를 정의할 때, 선언과 초기화를 분리시켜줘야 하는 경우다.
옵셔널의 강점은 안전성뿐 아니라 안전성을 담보하는 과정에서 표현되는 코드의 간결성이다.
옵셔널 체인(Optional Chain)으로, 코드를 간결하게 만들 수 있다.
1 2 3 4 5 | if (myDelegate != nil) { if ([myDelegate respondsToSelector:@selector(scrollViewDidScroll:)]) { [myDelegate scrollViewDidScroll:myScrollView]; } } | cs |
1 | myDelegate?.scrollViewDidScroll?(myScrollView) | cs |
CHAPTER 7 : 함수 : 함수가 갑입니다
함수의 장점
- 동일한 코드들을 함수 호출만으로 재사용
- 기능 단위로 함수화 시 가독성이 조항지고, 코드와 로직을 이해하기 쉬움
- 비즈니스 로직을 변경할 때, 함수 내부만 수정하면 되므로 유지보수가 용이
함수 정의 방법
1 2 3 4 | func 함수 이름(매개변수1 : 타입, 매개변수2 : 타입, ...) -> 반환 타입 { 실행 내용 return 반환값 } | cs |
함수 네이밍 시, 숫자는 자제한다.
함수 호출 방법
1 2 3 4 5 6 7 | func times(x:Int, y:Int) -> Int { return x*y } times(x: 5, y: 10) // 함수의 이름만으로 호출한 구문 times(x:y:)(5, 10) // 함수의 식별자를 사용하여 호출한 구문 | cs |
함수의 반환값과 튜플
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | typealias infoResult = (Int, Character, String) func getUserInfo() -> infoResult { let gender : Character = "M" let height = 180 let name = "다람쥐" return (height, gender, name) } let info = getUserInfo() info.0 info.1 info.2 | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | typealias infoResult = (h: Int, g: Character, n: String) func getUserInfo() -> infoResult { let gender : Character = "M" let height = 180 let name = "다람쥐" return (height, gender, name) } let info = getUserInfo() info.0 info.1 info.2 info.h info.g info.n | cs |
매개 변수
- 내부 매개변수 : 함수가 내부적으로 인자값을 참조하기 위해 사용하는 변수.
- 외부 매개변수 : 함수 외부에서 함수나 인자값을 구분하기 위해 사용하는 변수.
1 2 3 4 | func 함수이름(<외부 매개변수명> <내부 매개변수명> : <타입>, <외부 매개변수명> <내부 매개변수명> : <타입> ...) { // 함수 내용 } | cs |
1 2 3 4 5 6 | func printHello(to name : String, welcomeMessage msg : String) { print("\(name)님, \(msg)") } printHello(to: "홍길동", welcomeMessage: "안녕하세요.") | cs |
1 2 3 4 5 6 | func printHello(_ name : String, _ msg : String) { print("\(name)님, \(msg)") } printHello("홍길동", "안녕하세요.") | cs |
외부 매개변수 자리에 언더바(_)를 넣어주면 함수 호출 시 매개변수를 사용하지 않아도 된다.
가변 인자
1 2 3 4 5 6 7 8 9 10 11 | 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)) | cs |
< 실행 결과 >
1 2 | 25.0 | cs |
기본값을 갖는 매개변수
1 2 3 4 5 6 7 8 9 10 | func echo(message : String, newline : Bool = true) { if newline { print(message, true) } else { print(message, false) } } echo(message: "안녕하세요.") echo(message: "안녕하세요.", newline: false) | cs |
< 실행 결과 >
1 2 | 안녕하세요. true 안녕하세요. false | cs |
매개변수의 수정
InOut 매개변수
1 2 3 4 5 6 7 8 9 10 11 | var cnt = 30 func autoIncrement(value : Int) -> Int { var value = value value += 1 return value } print(autoIncrement(value: cnt)) // 31 print(cnt) // 30 | cs |
inout 키워드로 함수 외부에 영향을 미칠 수 있다. 값 자체를 전달하는 것이 아니라, 값이 저장된 메모리 주소를 전달한다는 의미다. 인자값에는 주소 추출 연산자 &를 붙여주어야 정상적으로 전달이 가능하다.
& 연산자는 C언어와 유사하고, inout은 포인터 개념과 유사하다.
매개변수 뒤에 클론(:)과 'inout' 키워드를 붙인다.
1 2 3 4 5 6 7 8 | func foo(paramCount:inout Int) -> Int { paramCount += 1 return paramCount } var count = 30 print(foo(paramCount: &count)) // 31 print(count) // 31 | cs |
이처럼 주소를 전달하는 것을 프로그래밍 용어로 '참조(Reference)에 의한 전달'이라고 한다. 기존처럼 값을 복사하여 전달하는 것을 '값(Value)에 의한 전달'이라고 한다.
값에 의한 전달과 참조에 의한 전달
변수의 생존 범위와 생명 주기
- 함수 내부에서 정의된 변수를 찾음
- 하수 외부에서 정의된 변수를 찾음
- 글로벌 범위에서 정의된 변수를 찾음
- import 된 라이브러리 범위
일급 함수
- 객체가 런타임에도 생성이 가능
- 인자값으로 객체를 전달 가능
- 반환값으로 객체를 사용 가능
- 변수나 데이터 구조 안에 저장 가능
- 할당에 사용된 이름과 관계없이 고유한 구별이 가능
1. 변수나 상수에 함수를 대입할 수 있음
1 2 3 4 5 6 | func foo(base:Int) -> String { return "결과값은 \(base + 1)입니다" } let fn1 = foo print(fn1(5)) | cs |
변수나 상수에 함수 대입시, 함수 타입(Function Type)을 가진다.
함수 타입은 일반적으로 함수의 형태를 축약한 형태로 사용
( 인자타입1, 인자타입2, ...) -> 반환 타입
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | func boo(age : Int) -> String { return "\(age)" } func boo(age : Int, name : String) -> String { return "\(name)의 나이는 \(age)세 입니다." } let s : (Int, String) -> String = boo // 함수의 이름 let s2 : (Int, String) -> String = boo(age:name:) // 함수의 식별자 // let t = boo (오류) let t1 : (Int, String) -> String = boo let t2 = boo(age:name:) let t3 : (Int) -> String = boo | cs |
함수 타입을 표시할 때, 인자값이 없거나 반환값이 없는 경우 빈 괄호를 사용하는 대신 Void를 사용해 명시적으로 "값이 없음"을 표시한다. Void는 빈 튜플 값을 나타내는 값으로 타입 알리어스로 정의된 단어다.
public typealias Void = ()
() -> String ==> (Void) -> String
Int -> () ==> (Int) -> Void
() -> () ==> (Void) -> Void
2. 함수의 반환 타입으로 함수를 사용할 수 있음
1 2 3 4 5 6 7 8 9 10 | func desc() -> String { return "this is desc()" } func pass() -> () -> String { return desc } let p = pass() p() | cs |
< 실행 결과 >
1 | "this is desc()" | cs |
3. 함수의 인자값으로 함수를 사용할 수 있음
1 2 3 4 5 6 7 8 9 | func incr(param : Int) -> Int { return param + 1 } func broker(base : Int, function fn : (Int) -> Int) -> Int { return fn(base) } broker(base:3, function: incr) // 4 | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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) | cs |
defer 블록은 함수나 메소드에서 코드의 흐름과 상관없이 가장 마지막에 실행되는 블록이다.
지연 블록이라고 부르기도 한다. 작성된 위치에 상관없이 항상 함수의 종료 직전에 실행된다. 종료 시점에 맞추어 처리해야 할 구문이 있다면 어디에 작성해야 할지 고민하지 않고 defer 블록에 넣어두면 된다.
사용된 각종 리소스의 처리나 해제, 연결 종료 등의 구문을 처리하는 용도로 유용하게 사용된다.
다음은 defer 블록의 특성이다.
- 작성된 위치와 순서에 상관없이 함수가 종료되기 직전에 실행된다.
- 블록을 읽기 전에 함수의 실행이 종료될 경우 defer 블록은 실행되지 않는다.
- defer 블록을 여러번 사용할 수 있다. 가장 마지막에 작성된 defer 블록부터 역순으로 실행된다.
- defer 블록을 중첩해서 사용할 수 있다. 바깥쪽 defer 부터 실행되며 가장 안쪽에 있는 defer 블록은 가장 마지막에 실행된다.
1 2 3 4 5 6 7 8 9 10 | divide(base: 30, success: { () -> Void in print("연산 처리 성공!") }, fail: { () -> Void in print("연산 처리 실패..") } ) | cs |
자바(Java) 같은 경우, 일급 함수를 다루지 않으므로, 함수나 메소드를 직접 전달하지 못한다. 이 때문에 Invoke라는 번거로운 방식을 채택했었고 최근에는 Java 8 을 기준으로 람다 기능이 추가되어 편해지긴 했다. 그러나 스위프트에 비해 번거로운건 마찬가지다.
함수의 중첩
1 2 3 4 5 6 7 8 9 10 11 12 | // 외부 함수 func outer(base : Int) -> String { // 내부 함수 func inner(inc : Int) -> String { return "\(inc)를 반환" } let result = inner(inc: base + 1) return result } outer(base: 3) | cs |
< 결과 값 >
1 | "4를 반환" | cs |
일반적으로 함수는 자신을 참조하는 곳이 있으면 생성되었다가 참조하는 곳이 사라지면 제거되는 생명 주기를 가진다. 참조 카운트와 관련이 있다. 참조 카운트가 0에서 1이 되는 순간 생성되어, 1 이상인 동안 유지되어 0이 되면 소멸한다. 내부 함수는 참조할 수 있는 곳이 외부 함수이므로, 외부 함수에 의존한다.
내부 함수를 반환값으로 제공할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | // 외부 함수 func outer(param : Int) -> (Int) -> String { // 내부 함수 func inner(inc : Int) -> String { return "\(inc)를 리턴" } return inner } let fn1 = outer(param: 3) // outer() 실행, 그 결과로 inner let fn2 = fn1(30) // inner(inc: 30) | cs |
이제 내부 함수를 외부에서도 접근할 수 있게 됐다. outer(param: 3) 구문이 실행한 후 이제 외부 함수의 참조 카운트가 0이 되었기 때문에 소멸한다. 그러나, 내부 함수인 inner은 상수 fn1에 참조되었으므로 참조 카운트가 존재한다. 내부 함수 inner 혼자만 살아 남은 셈이다.
만약 내부 함수에 외부 함수의 지역 상수, 또는 지역 변수가 참조되면 어떤 일이 일어날까
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | func basic(param: Int) -> (Int) -> Int { let value = param + 20 func append(add : Int) -> Int { return value + add } return append } let result = basic(param: 10) // 1 result(10) // 2 // 40 | cs |
basic 외부 함수가 사라져도, 그 안에 있는 지역 변수 value 는, 여전히 내부 함수 append 에서 사용하고 있다! 이는 클로저(Closure) 때문이다. append 함수가 클로저를 갖기 떄문이다.
- 클로저는 두 가지로 이루어진 객체다. 하나는 내부 함수이며 또 다른 하나는 내부 함수가 만들어진 주변 환경이다.
- 클로저는 외부 함수 내에서 내부 함수를 반환하고, 내부 함수가 외부 함수의 지역 변수나 상수를 참조할 때 만들어진다.
내부 함수를 둘러싼 주변 환경 객체가 값으로 바뀌어 저장된다. 클로저에 의해 내부 함수 주변의 지역 변수나 상수도 함께 값이 저장된다. 이를 캡처(Capture) 되었다라고 한다. 변수나 상수의 타입이 기본 자료형이나 구조체 자료형일 때 발생한다.
1 2 3 4 5 6 7 8 9 10 11 12 | let result = basic(param: 10) // 1 let result2 = basic(param: 15) // 2 // result1에 할당된 클로저 정의 func append(add : Int) -> Int { return 30 + add } // result2에 할당된 클로저 정의 func append(add : Int) -> Int { return 35 + add } | cs |
클로저
스위프트에서 클로저는 다음 세 가지 경우 중 하나에 대부분 해당된다.
1. 전역 함수 : 이름이 있으며, 주변 환경에서 캡처할 어떤 값도 없는 클로저
2. 중첩 함수 : 이름이 있으며 자신을 둘러싼 함수로부터 값을 캡처할 수 있는 클로저
3. 클로저 표현식 : 이름이 없으며 주변 환경으로부터 값을 캡처할 수 있는 경량 문법으로 작성된 클로저
클로저 표현식
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | { (매개변수) -> 반환 타입 in 실행할 구문 } let f = { () -> Void in print("클로저 실행") } f() let c = { (s1:Int, s2:String) -> Void in print("s1:\(s1), s2:\(s2)") } c(1, "closure") ({ (s1:Int, s2:String) -> Void in print("s1:\(s1), s2:\(s2)") })(1, "closure") | cs |
클로저 표현식과 경량 문법
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | var value = [1, 9, 5, 7, 3, 2] func order(s1: Int, s2: Int) -> Bool { if s1 > s2 { return true } return false } value.sort(by: order) // [9, 7, 5, 3, 2, 1] value.sort(by: { (s1:Int, s2:Int) -> Bool in if s1 > 2 { return true } return false }) value.sort(by: {(s1:Int, s2:Int) -> Bool in return s1 > s2}) value.sort(by: {(s1:Int, s2:Int) in return s1 > s2 }) // 반환값 추론 value.sort(by: { s1, s2 in return s1 > s2 }) // 인자값도 추론 value.sort(by: { $0 > $1 }) // 매개변수 생략, in 키워드 생략, return 구문 생략 value.sort(by: >) // 연산자 함수(Operator Functions) | cs |
'iOS > iOS 자료실' 카테고리의 다른 글
[오류] Could not insert new outlet connection 해결법 (2) | 2018.08.03 |
---|---|
Swift(iOS) 스터디. 5번째 SQLite (0) | 2018.06.03 |
Swift 스터디. 4번째 (0) | 2018.05.19 |
Swift 스터디. 3번째 (0) | 2018.05.19 |
Swift 스터디. 4월 첫째주 (0) | 2018.04.08 |
댓글