서브스크립트

- 클래스, 구조체, 열거형에는 컬렉션, 리스트, 시퀀스 등에서 접근할 수 있는 서브스크립트를 정의할 수 있다

- 별도의 설정자, 접근자 등의 메서드를 구현하지 않아도 인덱스를 통해 값을 설정하거나 가져올 수 있다

ex) Dictionary에서 dictionary[key] -> 이것이 서브스크립트 다

 

문법

subscript(index: Int) -> Int{
	get{
    	//getter
    }
    set(newValue){
    	//setter
    }
}

 

구현

struct Student{
	//...
}

class School{
	var students: [Student] = []
    
    func addStudent()...
    ...
    
    //School Class 내부에 있는 값들 혹은 그 값들의 조합으로도 반환 가능
    subscript(index: Int) -> Student?{
    	if index < self.number{
        	return self.students[index]
        }
        return nil
    }
}

 

복수개의 서브스크립트

- 하나의 타입이 여러개의 서브스크립트를 가징 수 있다

- 매개변수 타입과 개수, 반환 타입이 다르면 가능하다

- 서브스크립트에도 다형성이 가능

 

타입 서브스크립트

- subscript 앞에 static/class 키워드를 붙여준다

 

 

모나드

- 스위프트는 함수형 프로그래밍 패러다임에서 파생된 기능이나 개념이 종종 등장한다.

- 단순히 고차함수를 사용, 함수를 일급 객체로 사용, 재귀함수를 사용한 로직을 구현하는 등의 특정 기능에 국한되는 것은 아니지만, 모나드를 익혀두면 더 깊이 있는 함수형 프로그래밍을 이해할 수 있다

 

모나드의 조건

  • 타입을 인자로 받는 타입 (특정 타입의 값을 포장)
  • 특정 타입의 값을 포장한 것을 반환하는 함수(메서드)가 존재
  • 포장된 값을 변환하여 같은 형태로 포장하는 함수(메서드)가 존재
  • 옵셔널 = 기본적인 모나드

FlatMap vs Map

let optionals: [Int?] = [1,2,nil,5]

let mapped: [Int?] = optionals.map{ $0 } // [Optional(1),Optional(2),nil,Optional(5)]
let compactMapped: [Int] = optionals.compactMap{ $0 } // compactMap = flatMap
//[1,2,5]

//compactMap 은 컨테이너 내의 컨테이너까지 벗기는? 것을 볼 수 있다.

맵, 필터, 리듀스

 

Map

  • 자신을 호출할 때, 매개변수로 전달된 함수를 실행하여 그 결과를 반환
  • Sequence, Collection 프로토콜을 따르거나 옵셔널은 모두 사용 가능
  • 사용 시, 컨테이너가 담고 있던 각각의 값을 매개변수를 통해 적용 후, 다시 컨테이너로 포장하여 반환
  • 이전에 알아본 for - in 구문과 크게 다르지 않다
  • B.U.T. 코드 재사용 측면, 컴파일러 최적화 측면에서 성능 차이 있다
  • 다중 쓰레드 환경에서도 예측지 못한 결과의 부작용을 방지 가능 -> (Thread-Safe ? )
let numbers = [1,2,3,4,5]
var doubledNum = []
var stringNum = []

//for - in
for number in numbers{
	doubledNum.append(number * 2)
    stringNum.append("\(number)")
}

//map
//클로저 간소화한 문법
doubledNum = numbers.map{ $0 * 2 }
stringNum = numbers.map{ "\($0)" }

 

Filter

  • 내부의 값을 걸러서 추출하는 고차함수
  • 맵과 마찬가지로 새로운 컨테이너에 값을 담아 반환
  • Map = 변형, Filter = 거르기
  • 반환 타입 = Bool
let numbers = [1,2,3,4,5]

//짝수 걸러내기
ler evenNumbers = numbers.filter{ $0 % 2 == 0 } // [2,4]

 

Reduce

  • 컨테이너 내부의 콘텐츠를 하나로 합하는 기능을 실행하는 고차함수
  • 두가지 형태로 구현됨
    • 1. 클로저가 각 요소를 전달받아 연산한 후 값을 다음 클로저 실행을 위해 반환하며 컨테이너를 순환
    • 2. 컨테이너를 순환하며 클로저가 실행되지만 클로저가 따로 결괏값을 반환하지 않은 형태
      • 대신 inout 매개변수를 사용하여 초깃값에 직접 연산을 실행함
let numbers = [1,2,3]

// 1번 형태 
//초깃값이 0이고 모든 값을 더한다
var sum: Int = numbers.reduce(0,{ (result: Int, next: Int) -> Int in
	print("\(result) + \(next)")
    // 0 + 1
    // 1 + 2
    // 3 + 3
    return result + next
})

// 2번 형태
//초깃값이 0이고 모든 값을 더한다
sum = numbers.reduce(into: 0,{ (result: inout Int, next: Int) in
	print("\(result) + \(next)")
    // 0 + 1
    // 1 + 2
    // 3 + 3
	result += next
})

옵셔널 체이닝과 빠른 종료

- 옵셔널 바인딩을 하는 것인데 여러 값이 중첩된 형태를 띄우는 형태를 말한다

- 옵셔널을 이해하지 못하고 Swift를 사용한다면 절반도 이해하지 못한다고 할 정도로 중요한 개념이 옵셔널이다

 

옵셔널 체이닝

- 옵셔널에 속해 있는 nil일지도 모르는 프로퍼티, 메서드, 서브스크립트 등을 가져오거나 호출할 때 사용할 수 있는 일련의 과정

  - 옵셔널이 nil이면 모두 nil을 반환한다.

  - 당연히 nil이 아니면 제 값을 반환/호출한다

- 옵셔널을 반복 사용하여 옵셔널이 체인처럼 꼬리를 물고 있는 모양을 말한다

 

느낌표(!)의 사용

  • 값을 강제로 추출하기 때문에 옵셔널에 값이 없다면 런타임 오류가 발생한다
  • 또한 반환값은 옵셔널이 아니라는 점이다
  • 따라서 옵셔널이 아닌 타입에 nil 이 들어오는? 오류가 발생한다
  • 무조건 100% nil이 아니라고 확신하더라도 사용을 지양하는 것이 좋다

 

let optionalChaining: Int? = person.address?.building?.room?.number // nil

let oprionalUnwrapping: Int = person.address?.building?.room?.number // 런타임 오류

 

빠른 종료

- 옵셔널을 추출할 때 사용되는 guard가 핵심이다

- if 문과 유사하게 Bool 타입으로 동작한다

- B.U.T. guard 뒤의 구문이 false 라면 else 블록 내부 코드를 실행하게 되는데, 여기서 블록 내부에는 자신보다 상위의 코드 블록을 종료하는 코드가 들어가야합니다

- guard 구문을 사용해서 함수나 블록에서 쓸데없이 모두 진행할 필요없이, 앞에서 옵셔널을 추출한 후에 nil이라면 바로 블록을 나올 수 있다 -> 이게 바로 빠른 종료를 이용하는 것이다

 

guard Bool타입 else{
	예외상황 실행문
    상위 코드 블록을 종료하는 코드
}

 

func enterClub(name: String?, age: Int?){
	guard let name: String = name, let age: Int = age, age > 19, name.isEmpty == false else{
    	print("넌 너무 어려서 못 들어간다!")
        return // 반환 타입이 Void기 때문에 어떠한 값도 반환하지 않는다
    }
    
    print("어서오세요 \(name)")
}

//대충 예가 기억이 안나서 이 사람이 클럽에 들어갈 수 있는가?라는 함수를 작성해보자

func canEnterClub(name: String?, age: Int?) -> Bool{
	guard let name: String = name, let age: Int = age, age > 19, name.isEmpty == false else{
    	print("넌 너무 어려서 못 들어간다!")
        return false // 반환 타입이 Bool이기 때문에 Bool 타입의 값을 반환하는데, 이 사람은 들어갈 수 없으므로 false를 반환
    }
    
    print("어서오세요 \(name)")
}

 

+ Recent posts