-
Swift4 : 프로토콜 2 : #델리게이트 패턴 : #델리게이션 (2 / 2)스위프트: Swift/스위프트: 언어자체: 문법 2018. 9. 7. 22:00
안녕하세요 ! 씩이 입니다!
저는 Swift 와 iOS 를 공부하고 연구하는 학생입니다.
같은 분야를 공부하는 분들에게 조금이라도 도움이 주고 싶어서 공부하는 것들을 공유합니다.
제 3자가 있다고 가정하고 설명하기 때문에 존대를 하지 않는점 이해 부탁드립니다.
공유가 미래 라고 생각합니다.
한국의 모든 개발자분들 존경합니다!
- Swift version : Swift 4.2 Swift 언어
- 참고한 것들
- 씩이 Github
- 자료구조 소스파일 있습니다.
- iOS 관련 자료들, 정보들 정리해 두었습니다.
- 스위프트로 구현한 자료구조 : DataStructures in Swift4
- Swift4 : 연결리스트 (1 / 3) : #LinkedList : #DataStructrue : #자료구조
- Swift4 : 연결리스트 (2 / 3) : #LinkedList : #값 추가하기, push, append : #값 삽입하기,insert
- Swift4 : 연결리스트 (3 / 3) : #LinkedList : #값 제거하기, pop, removeLast, remove(at: )
- [스위프트 : 자료구조] 스택: Stack: 자료구조: DataStructure: 쌓기
- [스위프트 : 자료구조] 스택 : Stack : 프로토콜 지향 스택 구현하기
- [스위프트 : 자료구조] 큐 (1 / 4): Queue: #자료구조: #배열로 구현한 큐: #배열의원리
- [스위프트 : 자료구조] 큐 (2 / 4): Queue: #자료구조: #연결리스트: #더블연결리스트: #DoublyLinkedList
- [스위프트 : 자료구조] 큐 (3 / 4): Queue: #자료구조: #Stack으로 구현: #더블스택: #DoubleStack: #제일좋음
- [스위프트 : 자료구조] 큐 (4 / 4): Queue: #자료구조: #RingBuffer: #링버퍼로 구현한 큐: #고정된배열: #마지막!!
- [스위프트 : 자료구조] 큐: Queue: 프로토콜 지향 큐 구현하기
- 스위프트: 트리: Tree: #자료구조: #깊이우선탐색: #레벨정렬탐색: #검색알고리즘: Swift4
- 스위프트: 이진 탐색 트리(1 / 2): #BinarySearchTree: #자료구조: #배열과 비교: #트리: #탐색: #삽입: #삭제
- 스위프트: 이진 탐색 트리(2 / 2): #BinarySearchTree: #자료구조: #배열과 비교: #트리: #탐색: #삽입: #삭제
- [스위프트 : 자료구조] AVL Tree: 자가 균형 트리: #balance: #트리의 높이: #rotation메소드: #성능오짐
- [스위프트:자료구조] 트라이: Trie: 문자열 찾기: 단어 찾기
- [스위프트:자료구조] Heap: 힙 자료구조 (1 / 2) : Heap 이란?
- [스위프트:자료구조] Heap: 힙 자료구조 (2 / 2) : Heap 구현하기
- 스위프트로 구현한 알고리즘 : Algorithms in Swfit4
- [스위프트 : 알고리즘] 재귀호출 (1 / 6) : recursive: 재귀호출 : 재귀함수: 반복문: 팩토리얼: 거듭제곱: 피보나치: 하노이의 탑: 최대공약수
- [스위프트 : 알고리즘] 재귀 : 팩토리얼 (2 / 6) : factorial: 재귀호출 : 재귀함수: 반복문: 팩토리얼: 거듭제곱: 피보나치: 하노이의 탑: 최대공약수
- [스위프트 : 알고리즘] 재귀 : 거듭제곱 (3 / 6) : Power: 재귀호출 : 재귀함수: 반복문: 팩토리얼: 거듭제곱: 피보나치: 하노이의 탑: 최대공약수
- [스위프트 : 알고리즘] 재귀 : 피보나치 수열(4 / 6) : Fibonacci: 재귀호출 : 재귀함수: 반복문: 팩토리얼: 거듭제곱: 피보나치: 하노이의 탑: 최대공약수
- [스위프트 : 알고리즘] 재귀 : 하노이의 탑 (5 / 6) : Hanoi: 재귀호출: 재귀함수: 반복문: 팩토리얼: 거듭제곱: 피보나치: 하노이의 탑: 최대공약수
- [스위프트 : 알고리즘] 재귀 : 최대공약수 (6 / 6) : GCD: 재귀호출: 재귀함수: 반복문: 팩토리얼: 거듭제곱: 피보나치: 하노이의 탑: 최대공약수
- [스위프트:알고리즘] 이진 탐색[1 / 3]: Binary Search: 이진 탐색이 뭐야?
- [스위프트:알고리즘] 이진 탐색[2 / 3]: Binary Search: 이진 탐색: 반복문, 재귀호출로 구현하기
- [스위프트:알고리즘] 이진 탐색[3 / 3]: Binary Search: 이진 탐색: 프로토콜 지향으로 구현하기
- Swift 주제별 분류
- Swift4 : 제어 전달 명령문( Control Transfer Statement ) : #continue, #break, #return 키워드
- Swift4 : 클래스와 구조체 : #값을 대하는 방식 : #참조타입, 값 타입 : #===
- Swift4 : 프로퍼티 : #Property : #get, set : #willSet, didSet
- Swift4 : 메소드 : #Method : #영향력 범위 : #self : #mutating : #값타입 수정
- Swift4 : 프로토콜 1 : #Protocol : #설계 : #요구사항 : #델리게이트 패턴 전처리 (1 / 2)
- Swift4 : 프로토콜 2 : #델리게이트 패턴 : #델리게이션 (2 / 2)
- Swift4 : 제네릭 : #Generics : #왜필요해? : #where키워드 : #제약사항걸기
- Swift4 : 자동 참조 카운팅 : #Automatic Referece Counting : #ARC :#강한참조 : #Strong Reference Cycle : #메모리 누수
- Swift4 : 클로저: Closure: #표현방식: #왜필요해?: #효율적: #간결성: #생략
- [스위프트 : 기초] 서브스크립트 : Subscript : 지름길
프로토콜 2 : #델리게이트 패턴 : #델리게이션 (2 / 2)- 델리게이션 : 위임
- What is delegation ?
- 델리게이션( 위임 )은 클래스나 구조체가 따로 혹은 일부 수행해야 하는 일을 다른 타입( 보통 클래스 )의 인스턴스에게 위임하는 디자인 패턴입니다.
- 이 패턴은 프로토콜을 정의하면서 수행됩니다. 그래서 delegate 라고 알려진 프로토콜이 그 위임된 기능을 제공하도록 보장합니다.
- 특정 액션에 반응하거나 외부 소스의 기본 타입을 알 필요 없이 외부 소스로부터 데이터를 가져오는데 사용될 수 있습니다.
- 이전 편에 있었던 DiceGameDelegate 프로토콜은 주사위 게임의 진행과정을 추적하는데 채택될 수 있습니다. 보통 delegate 프로토콜은 '진행과정을 추적' 하는 일을 위임합니다 ( 아래에도 DiceGameDelegate 나오니 다시 넘어가지 마시길 )
- '강한 참조 사이클'을 예방하기 위해 위임자( delegate )는 약한참조로 선언됩니다. '강한 참조 사이클'은 후에 ARC 에서 다루게 됩니다. 아직은 몰라도^^..
- 위의 설명에서 아무것도 이해 못하겠죠? 이해하면 아인슈타인 입니다. 예를 들어 설명하겠습니다.
- 예제에는 이러한 내용이 포함 되어 있습니다.
- #Protocol : #delegate : #위임 : #추적 : #class-only 프로토콜 : #강한참조 : #약한참조 : #weak키워드
- 위의 예제를 이어서 작성해서 앞 부분 생략하려다가 복사해서 직접 테스트해보실 분들 있을 것 같아 같이 첨부합니다. 생략하실 분은 주석으로 구분해 놓았으니 밑 부분만 보셔도 무방합니다.
protocol DiceGame { // 주사위 게임이 채택해야 하는 프로토콜
var dice: Dice { get } // 주사위
func play() // 게임 플레이 메소드
}
protocol DiceGameDelegate: AnyObject {
// 주사위 게임 추적할 델리게이트 프로토콜
// AnyObject를 붙이면 클래스만 이 프로토콜을 채택할 수 있는 class-only 속성이 적용된다.
func gameDidStart(_ game: DiceGame)
func game(_ game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int)
func gameDidEnd(_ game: DiceGame)
}
protocol RandomNumberGenerator { // 랜덤의 수 생성하는 것들이 채택하는 프로토콜.
func random() -> Double
}
class LinearCongruentialGenerator: RandomNumberGenerator {
// RandomNumberGenerator 프로토콜을 채택한 의사 난수를 생성하는 클래스
var lastRandom = 42.0
let m = 139968.0
let a = 3877.0
let c = 29573.0
func random() -> Double { // 이 메소드 호출하면 반환값으로 난수 반환 하는 듯.
lastRandom = ((lastRandom * a + c).truncatingRemainder(dividingBy:m))
return lastRandom / m
}
}
class Dice { //주사위를 표현하는 Dice 클래스.
let sides: Int //주사위의 면.
let generator: RandomNumberGenerator
// 랜덤 수 생성기 프로토콜을 타입으로 가진다 함은?
// RandomNumberGenerator 프로토콜을 채택하는 모든 타입(클래스나 구조체나..)의 인스턴스를 설정할 수 있는 것.
// 인스턴스가 RandomNumberGenerator 프로토콜을 채택해야 한다는 것 외에는 별다른 제약사항은 없습니다.
// 이 말은 이 프로토콜을 채택한 (예를들어) 클래스의 인스턴스만 generator 상수에 저장될 수 있다. 이 말.
init(sides: Int, generator: RandomNumberGenerator) { // 초기 설정을 위한 초기화 함수.
self.sides = sides
self.generator = generator
}
func roll() -> Int { //주사위 굴리는 행위를 메소드로 표현한 것, 주사위에서 나온 랜덤한 수.
return Int(generator.random() * Double(sides)) + 1 // 이 수식의 의미에 빠지지 않아도 됩니다. 확률론적 지식이 있어야 해요. 이것이 주사위 던저서 나온 랜덤한 수 라는 것만 알면 될듯.
}
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
class SnakesAndLadders: DiceGame { // 주사위 게임들이 채택하는 DiceGame 프로토콜을 채택한 뱀과 사다리게임.
let finalSquare = 25 // 스코어는 25점 까지네요.
let dice = Dice(sides: 6, generator: LinearCongruentialGenerator())
// 6면을 가지고 있고 난수생성기는 LinearCongruentialGenerator 방식을 쓰려고 그의 인스턴스를 넣어주었네요.
var square = 0 // 칸을 표현하는 변수입니다. ( 정사각형의 칸이 있는 보드게임 이거든요. )
var board: [Int] // 보드게임 이기 때문에 보드를 표현하는 배열입니다.
init() {
board = Array(repeating: 0, count: finalSquare + 1) // 배열의 값은 0, 그 값으로 26개의 배열을 생성한다는 의미
board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
//특정 칸에 사다리 혹은 뱀이 있는 게임이기 때문에
//사다리가 있는 칸은 위로 올라갈 수 있게 양의 정수, 뱀이 있는 쪽은 아래로 내려가게 음수 설정해주기.
}
weak var delegate: DiceGameDelegate?
// DiceGameDelegate를 타입으로 가지는 delegate 프로퍼티
// 옵셔널인 이유 : 게임 플레이에 지장이 없어. 인스턴스 받는 곳에서 선택할 수 있게 해 주려고.
// DiceGameDelegate 프로토콜 class-only 이기 때문에 '강한 참조 사이클'을 예방하려면 weak 붙여주어야 함.
// weak 키워드는 약한참조를 하라는 의미 ( 강한 참조 사이클 이라는 문제때문에 이 키워드를 붙이는데 아직 학습할 단계가 아닙니다.)
// class-only 인데 프로퍼티가 프로토콜을 타입으로 가질 수 있냐는 의문이 생길 수 있겠죠?
// class-only 는 이 프로토콜을 타입으로 가지는 것에서 범위를 제한하는 것이 아니라, 이 프로토콜을 채택하는 타입이 클래스로 제한된다는 뜻입니다.
func play() {
// SnakesAndLadders 이라는 주사위 게임의 플레이를 메소드로 표현했네요.
// 이 메소드를 호출하면 게임을 플레이 하는 것이겠죠?
square = 0 // 0 번째 칸에서 시작하는 보드게임.
delegate?.gameDidStart(self) // 게임 시작했다는 메소드를 호출해 줍니다.
gameLoop: while square != finalSquare { // 점수가 마지막 점수까지 도달해야 하니까!
let diceRoll = dice.roll() // 주사위 떤지기! (1~6 의 수가 나오겠죠?)
delegate?.game(self, didStartNewTurnWithDiceRoll: diceRoll) // 주사위 턴이 시작될 때.
switch square + diceRoll { //게임 끝낼지 더 갈지 체크합니다.
case finalSquare:
break gameLoop // 이전 점수와 주사위 값을 더한 값인 현재 내 점수가 최종 점수랑 같다면 끝.(승리)
case let newSquare where newSquare > finalSquare:
continue gameLoop // 현재 점수가 최종 점수보다 크면 계속 진행 (일치해야 이기는 게임임.)
default:
square += diceRoll // 점수 높아지고
square += board[square] //뱀 만나서 점수 낮아지게 해야겠지뭐.
}
}
delegate?.gameDidEnd(self) // 게임 끝낫다
}
}
class DiceGameTracker: DiceGameDelegate {
// DiceGameDelegate 프로토콜(클래스만 채택할 수 있는 프로퍼티임.)을 채택한 DiceGameTracker 클래스
// 역할을 다시 알려주자면 게임의 진행과정 추적 에 있다. 추적!
var numberOfTurns = 0 // 턴 수.
func gameDidStart(_ game: DiceGame) { //시작할 때 추적해서 아래 내용을 수행.
numberOfTurns = 0
if game is SnakesAndLadders { // SnakesAndLadders 클래스의 인스턴스가 매개변수로 들어오면 (인스턴스 체크.)
print("Started a new game of Snakes and Ladders")
}
print("The game is using a \(game.dice.sides)-sided dice")
}
func game(_ game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int) { //새로운 턴일 때 추적해서 아래 내용 수행
numberOfTurns += 1 // 턴 수 체크.
print("Rolled a \(diceRoll)")
}
func gameDidEnd(_ game: DiceGame) { // 게임 오버 일때 추적해서 아래꺼 수행.
print("The game lasted for \(numberOfTurns) turns") // 몇 턴으로 게임 오버됬는지 출력하면서 엔딩.
}
}
let tracker = DiceGameTracker() // DiceGameDelegate를 채택한 DiceGameTracker 클래스의 인스턴스
let game = SnakesAndLadders() // DiceGame 프로토콜을 채택한 SnakesAndLadders 클래스 인스턴스
game.delegate = tracker
// SnakesAndLadders 클래스의 delegate 프로퍼티에 DiceGameDelegate를 채택한 클래스의 인스턴스인 tracker 대입.
game.play()
// Started a new game of Snakes and Ladders
// The game is using a 6-sided dice
// Rolled a 3
// Rolled a 5
// Rolled a 4
// Rolled a 5
// The game lasted for 4 turns 이 출력됩니다.
정리하겠습니다.
- 이렇게 어떠한 과정을 추적해서 그 시점에 어떤 일을 수행하는 함수를 따로 작성할 수 있게 해주는 패턴을 델리게이트 패턴이라고 합니다.
- 소프트웨어에서 코드를 설계하는 여러가지 패턴이 있는데 그 중 하나인 델리게이트 패턴인 것이죠.
- iOS 프로그래밍 에서는 이 패턴을 이용해서 기기의 특정 시점에 특정 기능을 수행하게 함으로써 기기를 더 효과적으로 제어할 수 있게 해줍니다.
- 예를들면 아래와 갔습니다.
- 휴대폰 잠금해제 전 -> 아무것도 접근할 수 없게 하는 로직 코딩
- 휴대폰 잠금해제 후 -> 접근 가능하게 하는 로직 코딩.
- ( 알람 앱을 누름 )
- 알람앱 켜지기 전 -> "시간 설정할 준비 됬나요?" 알림표시
- 알람앱 켜진 상태 -> "시간을 설정하세요" 알림 표시
- 알람앱 종료 전 -> "시간을 정확히 설정했는지 확인하세요" 알림 표시
- ( 알람앱 종료 )
- 휴대폰 잠금 해제 -> 아무것도 접근할 수 없게 로직 코딩
'스위프트: Swift > 스위프트: 언어자체: 문법' 카테고리의 다른 글
댓글