Generic
-
Generic을 이용해 코드를 구현하면 어떤 형식에도 유연하게 대응할 수 있음.
-
제네릭으로 구현한 기능과 타입은 재사용도 쉽고, 코드의 중복을 줄일 수 있음.
-
compile타임에 자료형을 결정하지않고, runtime때 자료형을 결정
Generic Function
func swapInteger(lhs: inout Int, rhs: inout) {
let tmp = lhs
lhs = rhs
rhs = tmp
}
swapInteger(lhs: &a, rhs: &b)
위에 함수는 Int형식만 받음. 다른 형식이 필요하면 개별적으로 함수를 구현해야 해서 코드 중복이 발생함.
------------------------------------------------------------------------------------------
문법:
func name<T> (parameters) -> Type {
code
}
여기서 중요한 것은 타입파라미터임. 타입파라미터는 함수선언부분에서 파라미터형식이나 리턴형으로 사용됨. 함수바디에서 사용도 가능함. 이름으로 T를 사용했으나, 다른 이름도 괜찮음.
<T> 는 타입파라미터임. 형식이름이므로 대문자로 표기해야 함. 플레이스홀더의 개념임. 2개 이상의 타입파라미터도 허용함.
func swapValue<T>(lhs: inout T, rhs: inout T) {
let tmp = lhs
lhs = rhs
rhs = tmp
}
UpperCamelCase를 사용.
위와같이 코드를 작성하면 형식에 구애받지 않고 함수를 사용할수있음.
a = 1
b = 2
swapValue(lhs: &a, rhs: &b)
a
b
var c = 1.2
var d = 3.4
swapValue(lhs: &c, rhs: &d)
c
d
func printAllTypes<T>(items:[T]) {
for item in items {
print(item)
}
}
func printAllTypes<General>(items:[General])
import UIKit
var str = "Hello, playground"
"""
형식제약
<TypeParameter: ClassName>
<TypeParameter: ProtocolName>
"""
타입파라미터가 대체하는 형식에는 제한이 없음. 제네릭함수로 전달하는 파라미터형식에는 제한이 없음. int로 저장된 값을 두 값을 비교하는 연산자가 구현되어 있기 때문. 우리가 직접 형식을 선언하면 비교기능도 직접 구현해야 함. 타입파라미터가 대체할 수 있는 형식을 equtable을 채용한 형식으로만 제한하면 문제가 해결됨. 파라미터가 대체할 수 있는 형식이 Equatable로 제한됨. 프로토콜을 채용한 형식으로만 제한됨. equal to연산자를 통해 비교할 수 있는 값만 전달할 수 있게 됨.
func swapValue<T: Equatable>(lhs: inout T, rhs: inout T) {
print("generic version")
if lhs == rhs { return }
let tmp = lhs
lhs = rhs
rhs = tmp
}
제네릭함수는 기본적으로 형식에 관계없이 동일한 코드를 실행함. 특수화를 통해서 특정 형식을 위한 함수를 구현함.
스트링형식을 위한 함수를 구현함 . 함수 이름은 동일하게 선언하고 타입파라미터는 선언하지 않음.
func swapValue(lhs: inout String, rhs: inout String) {
print(“Specialized version”)
if lhs.caseInsensitiveCompare(rhs) == .orderedSame {
return
}
let tmp = lhs
lhs = rhs
rhs = tmp
}
//함수를 호출하고 정수를 전달해 보기// SwapValue라는 두가지 함수가 있음. 실제로 실행되는 함수는 전달되는 값을 통해서 결정됨. string이 아닌 나머지 값이 전달되면 첫번째 함수가 호출됨. 문자열을 전달해 보면 두번째 함수가 호출됨. 특수화로 구현한 두번째 함수는 제네릭함수를 오버라이딩한 함수로 이ㄴ식하고 제네릭 함수보다 우선순위가 높음.
var a = 1
var b = 2
swapValue(lhs: &a, rhs: &b)
a
b
"""
형식제약
<TypeParameter: ClassName>
<TypeParameter: ProtocolName>
"""
func swapValue(lhs: inout String, rhs: inout String) {
print("specialized version")
if lhs.caseInsensitiveCompare(rhs) == .orderedSame {
return
let tmp = lhs
lhs = rhs
rhs = tmp
}
}
a = 1
b = 2
swapValue(lhs: &c, rhs: &d)
var c = "ggg"
var d = "kkk"
실제 자료형으로 대체되는 place holder임.
범용자료형으로 사용 가능.
generic types , 제네릭 형식
제네릭 타입: 스위프트 컬렉션은 구조체이면 제네릭 타입임. 컬렉션에는 동일한 타입의 값만 저장할 수 있음.
선언 문법 : 기존형식이름 뒤에 타입파라미터를 붙임.
class Name<T> {
code
}
struct Name<T> {
code
}
enum Name<T> {
code
}
새로운 제네릭 타입 선언
struct Color<T> {
var red: T // 속성형식은 타입파라미터로 선언
var green: T
var blue: T
}
//새로운 인스턴스를 생성. 인스턴스를 생성하는 방식은 일반 형식과 동일.
var c = Color(red: 128, green; 80, blue: 200)
여기서 c의 자료형은 Color<Int> 타입파라미터를 대체하는 형식임.
왜 INT임? 생성자를 호출하면서 파라미터로 INT형식의 값을 전달함. 타입 파라미터가 INT로 추론되었음.
모든 속성도 INT임.
c 의 타입파라미터는 INT로 추론됨.
속성도 INT임.
//다시 새로운 인스턴스를 생성하고 파라미터로 실수(double)을 전달함.
c = Color(red: 128.0, green: 80.0, blue: 200.0)
// error 발생. color<Int> 형식에 color<double>형식을 저장할 수 없음.
let d: Color = Color(red: 128.0, green: 80.0, blue: 200.0) // ok
이 경우에는 이전 코드처럼 타입파라미터가 자동으로 추론 됨.
let d: Color 만 놓으면 추론이 불가능 함.
let d: Color<Int>
let arr: [Int] //단축 문법임.
let arr: Array<Int> // 정식 문법임. 배열을 처리하는 다양한 타입이 있음. INT로 선언하면 컴파일러가 INT를 처리하는 함수를 자동으로 생성함.
let dict: Dictionary<String: Double> // 스트링키와 더블값을 처리하는 딕셔러니가 생성됨.
extension을 통해 제네릭 타입을 확장
extension Color {
func getComponents() -> [T] {
return [red, green, blue]
}
}
extension Color<T> { //compile error 발생.
func getComponents() -> [T] {
return [red, green, blue]
}
}
let arr: Array<Int>
어레이형식에는 배열을 처리하는 코드가 있음. 다양한 코드를 처리할 수 있도록 제네릭타입으로 선언되어 있음. 컴파일러가 array를 처리하는 코드를 자동으로 생성함.
"""
Generic
형식파라미터 <T>
작성해야 하는 코드가 줄어듬.
"""
var a: Float = 12.34
var b: Float = 34.56
func add(lhs: Int , rhs: Int) -> Int {
return lhs + rhs
}
func add(lhs: Double, rhs: Double) -> Double {
return lhs + rhs
}
func add(lhs: Float, rhs: Float) -> Float {
return lhs + rhs
}
func add<T: Numeric>(lhs: T, rhs: T) -> T {
return lhs + rhs
}
let r: Float = add(lhs: a, rhs: b)
// 스택 - 자료구조
// 스택은 LIFO (Last In First Out) . 택시에 있는 동전 //
class IntStack {
var buffer = [Int]()
func push(data: Int) {
buffer.append(data)
}
func pop() -> Int? {
buffer.popLast()
}
func printBuffer() {
for data in buffer.reversed() {
print(data)
}
}
}
let c = IntStack()
c.push(data: 1)
c.push(data: 2)
c.push(data: 3)
c.printBuffer()
class Stack<T> {
var buffer = [T]()
func push(data: T) {
buffer.append(data)
}
func pop() -> T? {
buffer.popLast()
}
func printBuffer() {
for data in buffer.reversed() {
print(data)
}
}
}
let dblStack = Stack<Double>()
dblStack.push(data: 1.0)
dblStack.push(data: 2.2)
dblStack.push(data: 3.3)
dblStack.printBuffer()
dblStack.pop()
dblStack.pop()
dblStack.pop()
dblStack.pop()
let strStack = Stack<String>()
strStack.push(data: "Apple")
strStack.push(data: "Google")
strStack.printBuffer()
FILO = First In Last Out
'Swift' 카테고리의 다른 글
Swift - Comparison Operator (비교연산자) (0) | 2020.03.23 |
---|---|
Swift - In Out Parameters (입출력 파라미터) (0) | 2020.03.22 |
Swift - Self & Super (0) | 2020.03.22 |
Swift - 고차 함수 (0) | 2020.03.21 |
Swift - Optional(옵셔널) (0) | 2020.03.20 |