1. Advanced Operators πŸ‘©β€πŸ’»

Swift λŠ” Cλ‚˜ Objective-C와 μœ μ‚¬ν•œ Bitwise Operatorsλ₯Ό 포함해 μ—¬λŸ¬ κ³ κΈ‰ μ—°μ‚°μžλ₯Ό μ œκ³΅ν•œλ‹€. Swift λŠ” C 의 Arithmetic Operators 와 달리 기본적으둜 Overflow λ˜μ§€ μ•ŠλŠ”λ‹€. Overflow λŠ” trappedλ˜μ–΄ μ—λŸ¬λ‘œ λ³΄κ³ λœλ‹€.
Swift μ—μ„œ Overflow 행동을 ν•˜λ„λ‘ ν•˜λ €λ©΄ Overflow Addition Operator($+)와 같은 μ—°μ‚°μžλ₯Ό μ‚¬μš©ν•΄μ•Όν•œλ‹€ (λͺ¨λ“  Overflow OperatorsλŠ” &둜 μ‹œμž‘ν•œλ‹€).

Custom Classes, Structures, Enumerations λ₯Ό μ •μ˜ν•  λ•Œ, Custom Types 에 λŒ€ν•΄ Standard Swift Operators 의 κ΅¬ν˜„μ„ μ œκ³΅ν•˜λŠ” 것이 μœ μš©ν•  수 μžˆλ‹€. Swift λŠ” Custom Types 에 λŒ€ν•΄ Custom Operators λ₯Ό μ†μ‰½κ²Œ μ œκ³΅ν•  수 μžˆλ„λ‘ ν•˜λ©°, 각 Types 에 λŒ€ν•œ 행동이 μ •ν™•νžˆ 무엇인지 κ²°μ •ν•  수 μžˆλ‹€.

Custom Operators λŠ” 사전에 μ •μ˜λœ Operators 둜 μ œν•œλ˜μ§€ μ•ŠμœΌλ©°, Swift λŠ” μžμ‹ λ§Œμ˜ Infix, Prefix, Assignment Operatorsλ₯Ό μ •μ˜ν•¨μ€ λ¬Όλ‘ , μžμ‹ λ§Œμ˜ μš°μ„ μˆœμœ„λ₯Ό 자유둭게 μ •μ˜ν•  수 μžˆλ‹€. μ΄λŸ¬ν•œ Custom Operators λŠ” μ½”λ“œμ—μ„œ Swift κ°€ 기본적으둜 μ œκ³΅ν•˜λŠ” Predefined Operators 처럼 μ‚¬μš©λ˜λ©°, Custom Operators λ₯Ό μ±„νƒν•˜λ„λ‘ 기쑴의 Types λ₯Ό ν™•μž₯ν•  수 μžˆλ‹€.


2. Bitwise Operators πŸ‘©β€πŸ’»

1. Bitwise Operators

Bitwise Operators λŠ” Data Structure λ‚΄μ—μ„œ κ°œλ³„ Raw Bitsλ₯Ό μ‘°μž‘ν•  수 있게 ν•΄μ€€λ‹€. 이것은 Graphics Programming μ΄λ‚˜ λ””λ°”μ΄μŠ€ λ“œλΌμ΄λ²„ 생성 같은 Low-Level Programming μ—μ„œ 주둜 μ‚¬μš©λœλ‹€. λ˜ν•œ μ™ΈλΆ€ μ†ŒμŠ€λ‘œλΆ€ν„° Custom Protocol 을 μ‚¬μš©ν•΄ ν†΅μ‹ ν•˜λŠ” 데이터 Encoding/Decoding μž‘μ—…μ— μ‚¬μš©ν•˜κΈ°λ„ ν•œλ‹€. Swift λŠ” C κ°€ κ°–κ³  μžˆλŠ” λͺ¨λ“  Bitwise Operators λ₯Ό μ§€μ›ν•œλ‹€.

func printToBinary(number: UInt8) {
    print(toBinary(number))

    func toBinary(_ number: UInt8) -> String {
        let binary = String(number, radix: 2)
        if binary.count < number.bitWidth {
            return String(repeating: "0", count: 8 - binary.count) + binary
        } else {
            return binary
        }
    }
}

μœ„ ν•¨μˆ˜λ₯Ό λ§Œλ“€κ³  λΉ„νŠΈ μ—°μ‚° κ²°κ³Όλ₯Ό ν™•μΈν•΄λ³΄μž.

2. Bitwise NOT Operator ~

Bitwise NOT Operator ~λŠ” Prefix Operator둜 곡백 없이 κ°’ λ°”λ‘œ μ•žμ— μœ„μΉ˜ν•΄ 숫자의 λͺ¨λ“  λΉ„νŠΈλ₯Ό λ°˜μ „μ‹œν‚¨λ‹€.

Bitwise NOT Operator

let initialBits: UInt8 = 0b00001111
let invertedBits = ~initialBits
printToBinary(number: invertedBits) // 11110000

UInt8 μ •μˆ˜λŠ” 8λΉ„νŠΈλ₯Ό 가지며 0 ~ 255 μ‚¬μ΄μ˜ 숫자λ₯Ό μ €μž₯ν•  수 있으며, 2μ§„μˆ˜ 00001111둜 이루어진 8λΉ„νŠΈ 데이터 (10μ§„μˆ˜λ‘œ 15와 κ°™μŒ)에 ~ Operator λ₯Ό μ μš©ν•΄ 2μ§„μˆ˜ 11110000(10μ§„μˆ˜λ‘œ 240κ³Ό κ°™μŒ)이 λ˜μ—ˆλ‹€.

3. Bitwise AND Operator &

Bitwise AND Operator &λŠ” 두 κ°’ 사이에 μœ„μΉ˜ν•΄ μ—°μ‚°λœ 값을 λ°˜ν™˜ν•œλ‹€. λΉ„νŠΈμ˜ 각 μžλ¦Ώμˆ˜κ°€ λͺ¨λ‘ 1이면 1을, κ·Έ μ™Έμ—λŠ” 0을 λ°˜ν™˜ν•œλ‹€.

Bitwise AND Operator

let firstSixBits: UInt8 = 0b11111100
let lastSixBits: UInt8 = 0b00111111
let middleFourBits = firstSixBits & lastSixBits
printToBinary(number: middleFourBits)   // 00111100

2μ§„μˆ˜ 11111100κ³Ό 00111111에 & Operator λ₯Ό μ μš©ν•΄ 2μ§„μˆ˜ 00111100이 λ˜μ—ˆλ‹€.

4. Bitwise OR Operator |

Bitwise OR Operator |λŠ” 두 κ°’ 사이에 μœ„μΉ˜ν•΄ μ—°μ‚°λœ 값을 λ°˜ν™˜ν•œλ‹€. λΉ„νŠΈμ˜ 각 μžλ¦Ώμˆ˜κ°€ λͺ¨λ‘ 0이면 0을, κ·Έ μ™Έμ—λŠ” 1을 λ°˜ν™˜ν•œλ‹€.

Bitwise OR Operator

let someBits: UInt8 = 0b10110010
let moreBits: UInt8 = 0b01011110
let combinedBits = someBits | moreBits
printToBinary(number: combinedBits) // 11111110

2μ§„μˆ˜ 10110010κ³Ό 01011110에 | Operator λ₯Ό μ μš©ν•΄ 2μ§„μˆ˜ 11111110이 λ˜μ—ˆλ‹€.

5. Bitwise XOR Operator ^

Bitwise XOR Operator(=Exclusive OR Operator) ^λŠ” 두 κ°’ 사이에 μœ„μΉ˜ν•΄ μ—°μ‚°λœ 값을 λ°˜ν™˜ν•œλ‹€. λΉ„νŠΈμ˜ 각 μžλ¦Ώμˆ˜κ°€ μ„œλ‘œ κ°™μœΌλ©΄ 0을, λ‹€λ₯΄λ©΄ 1을 λ°˜ν™˜ν•œλ‹€.

Bitwise XOR Operator

let firstBits: UInt8 = 0b00010100
let otherBits: UInt8 = 0b00000101
let outputBits = firstBits ^ otherBits
printToBinary(number: outputBits)   // 00010001

2μ§„μˆ˜ 00010100κ³Ό 00000101에 ^ Operator λ₯Ό μ μš©ν•΄ 2μ§„μˆ˜ 00010001이 λ˜μ—ˆλ‹€.

6. Bitwise Left and Right Shift Operators << >>

Bitwise Left Shift Operator <<λŠ” λͺ¨λ“  λΉ„νŠΈλ₯Ό μ™Όμͺ½μœΌλ‘œ μ΄λ™μ‹œν‚€λ©° μ •μˆ˜λ₯Ό 2배둜 κ³±ν•˜λŠ” νš¨κ³Όκ°€ 있고, Bitwise Right Shift Operator >>λŠ” λͺ¨λ“  λΉ„νŠΈλ₯Ό 였λ₯Έμͺ½μœΌλ‘œ μ΄λ™μ‹œν‚€λ©° μ •μˆ˜λ₯Ό 반으둜 λ‚˜λˆ„λŠ” νš¨κ³Όκ°€ μžˆλ‹€.


1 ) Shifting Behavior for Unsigned Integers

λΆ€ν˜Έ μ—†λŠ” μ •μˆ˜μ˜ Bit-Shifting 행동은 λ‹€μŒκ³Ό κ°™λ‹€.

  • 기쑴의 λΉ„νŠΈλ₯Ό μš”μ²­λœ 숫자만큼 μ™Όμͺ½ λ˜λŠ” 였λ₯Έμͺ½μœΌλ‘œ μ΄λ™μ‹œν‚¨λ‹€.
  • μ •μˆ˜μ˜ μ €μž₯ λ²”μœ„(UInt8 μ •μˆ˜λŠ” 8λΉ„νŠΈλ₯Ό 가지며 0 ~ 255 μ‚¬μ΄μ˜ 숫자λ₯Ό μ €μž₯)λ₯Ό λ„˜λŠ” λΉ„νŠΈλŠ” μ œκ±°λœλ‹€.
  • λΉ„νŠΈ μ΄λ™μœΌλ‘œ 빈 곡간에 0이 μ‚½μž…λœλ‹€.

Bit-Shift Unsigned

let shiftBits: UInt8 = 4
printToBinary(number: shiftBits)        // 00000100
printToBinary(number: shiftBits << 1)   // 00001000
printToBinary(number: shiftBits << 2)   // 00010000
printToBinary(number: shiftBits << 5)   // 10000000
printToBinary(number: shiftBits << 6)   // 00000000
printToBinary(number: shiftBits >> 2)   // 00000001


λ‹€μŒ μ˜ˆμ œλŠ” 16μ§„μˆ˜ Cascading Style Sheets 색상값을 각각 RGB 둜 λΆ„λ¦¬ν•˜λŠ” 연산을 μˆ˜ν–‰ν•œλ‹€.

let pink: UInt32 = 0xCC6699
let redComponent = (pink & 0xFF0000) >> 16    // redComponent is 0xCC, or 204
let greenComponent = (pink & 0x00FF00) >> 8   // greenComponent is 0x66, or 102
let blueComponent = pink & 0x0000FF           // blueComponent is 0x99, or 153

16μ§„μˆ˜ Cascading Style Sheets 색상값을 μ €μž₯ν•˜κΈ° μœ„ν•΄ UInt32 μƒμˆ˜λ₯Ό μ‚¬μš©ν–ˆκ³  μ €μž₯된 색상은 뢄홍색이닀.

  • 빨간색을 λΆ„λ¦¬ν•˜κΈ° μœ„ν•΄ 뢄홍색에 λΉ¨κ°•μƒ‰μ˜ μžλ¦Ώκ°’ 0xFF0000을 & μ—°μ‚°ν•œ λ‹€μŒ 였λ₯Έμͺ½μœΌλ‘œ 16λΉ„νŠΈλ₯Ό μ΄λ™μ‹œν‚¨λ‹€.
  • 녹색을 λΆ„λ¦¬ν•˜κΈ° μœ„ν•΄ 뢄홍색에 λ…Ήμƒ‰μ˜ μžλ¦Ώκ°’ 0x00FF00을 & μ—°μ‚°ν•œ λ‹€μŒ 였λ₯Έμͺ½μœΌλ‘œ 8λΉ„νŠΈλ₯Ό μ΄λ™μ‹œν‚¨λ‹€.
  • νŒŒλž€μƒ‰μ„ λΆ„λ¦¬ν•˜κΈ° μœ„ν•΄ νŒŒλž‘μƒ‰μ˜ μžλ¦Ώκ°’ 0x0000FF을 & μ—°μ‚°ν–ˆκ³  μžλ¦Ώκ°’ 이동이 ν•„μš” μ—†μ–΄ κ·ΈλŒ€λ‘œ μ’…λ£Œν–ˆλ‹€.


2 ) Shifting Behavior for Signed Integers

λΆ€ν˜Έ μžˆλŠ” μ •μˆ˜μ˜ Bit-Shifting 행동은 μ΄μ§„μœΌλ‘œ ν‘œν˜„λ˜λŠ” 방법 λ•Œλ¬Έμ— λΆ€ν˜Έ μ—†λŠ” μ •μˆ˜λ³΄λ‹€ 더 λ³΅μž‘ν•˜λ‹€(λ‹€μŒ μ˜ˆμ œλŠ” λ‹¨μˆœν™”λ₯Ό μœ„ν•΄ 8λΉ„νŠΈμ˜ λΆ€ν˜Έ μžˆλŠ” μ •μˆ˜λ₯Ό μ‚¬μš©ν•˜μ§€λ§Œ λ™μΌν•œ 원칙이 λͺ¨λ“  λΆ€ν˜Έ μžˆλŠ” μ •μˆ˜μ— μ μš©λœλ‹€).

λΆ€ν˜Έ μžˆλŠ” μ •μˆ˜λŠ” 첫 번째 λΉ„νŠΈλ₯Ό λΆ€ν˜Έλ‘œ μ‚¬μš©ν•œλ‹€. 이λ₯Ό Sign Bit둜 0은 μ–‘μˆ˜λ₯Ό, 1은 음수λ₯Ό ν‘œν˜„ν•œλ‹€. 그리고 λ‚˜λ¨Έμ§€ λΉ„νŠΈλŠ” Value Bits둜 μ‹€μ œ 값을 μ €μž₯ν•œλ‹€. μ–‘μˆ˜μΌ λ•ŒλŠ” λΆ€ν˜Έ μ—†λŠ” μ •μˆ˜μ™€ λ™μΌν•œ 방식을 μ‚¬μš©ν•œλ‹€.

Bit Shift Signed Four

λΆ€ν˜Έ μžˆλŠ” μ •μˆ˜μ˜ +4


ν•˜μ§€λ§Œ 음수의 경우 μš°λ¦¬κ°€ μ§κ΄€μ μœΌλ‘œ μ‚¬μš©ν•˜λŠ” λΆ€ν˜Έ + μ ˆλŒ€κ°’ 숫자의 ν˜•νƒœλ₯Ό 띄지 μ•ŠλŠ”λ‹€. +4, -4 이런 μ‹μ˜ ν‘œν˜„μ€ μ‚¬λžŒμ—κ²Œ 쉽고 μ΅μˆ™ν•œ 것이지 컴퓨터 μΉœν™”μ μ΄μ§€ μ•ŠκΈ° λ•Œλ¬Έμ΄λ‹€. μ»΄ν“¨ν„°λŠ” Binary 둜 데이터λ₯Ό 닀루기 λ•Œλ¬Έμ— 2의 보수λ₯Ό μ‚¬μš©ν•΄ ν‘œν˜„ν•œλ‹€.

Bit Shift Signed Minus Four

λΆ€ν˜Έ μžˆλŠ” μ •μˆ˜μ˜ -4

2μ§„μˆ˜κ°€ κ°€μ§ˆ 수 μžˆλŠ” λ³΄μˆ˜λŠ” 2의 λ³΄μˆ˜μ™€ 1의 λ³΄μˆ˜λ‹€.

  1. 2μ§„μˆ˜ μ–‘μˆ˜ +4λŠ” 00000100이닀.
  2. +4의 1의 λ³΄μˆ˜λŠ” 11111111 - 00000100 = 111110110이닀.
  3. +4의 2의 λ³΄μˆ˜λŠ” 1의 λ³΄μˆ˜μ— 1을 더해 111110110 + 00000001 = 11111100이 λœλ‹€.

λΆ€ν˜Έ μžˆλŠ” μ •μˆ˜μ˜ -4λŠ” Sign Bit 1κ³Ό Value Bits 1111100으둜 이루어진닀. 10μ§„μˆ˜μ—μ„œ 이 값은 124λ₯Ό κ°–λŠ”λ‹€. λ”°λΌμ„œ, λΆ€ν˜Έ μžˆλŠ” μ •μˆ˜μ˜ 음수 ν‘œν˜„μ€ 2의 보수λ₯Ό μ‚¬μš©ν•΄ 음수λ₯Ό ν‘œν˜„ν•˜λŠ” Sign Bit와 2의 보수둜 ν‘œν˜„λ˜λŠ” Value Bits 128 - 4λ₯Ό ν‘œν˜„ λ°©μ‹μœΌλ‘œ μ‚¬μš©ν•˜κ³  μžˆμŒμ„ 확인할 수 μžˆλ‹€. 이λ₯Ό Two's Complement Representation(2의 보수 ν‘œν˜„)이라 λΆ€λ₯Έλ‹€.

2의 보수 ν‘œν˜„μ„ μ‚¬μš©ν•˜λ©΄ 컴퓨터 연산에 μ—¬λŸ¬ μž₯점을 κ°€μ§ˆ 수 μžˆλ‹€.

  • -1 + -4와 같은 연산을 λ‹¨μˆœνžˆ ν‘œμ€€ 이진 λ§μ…ˆμœΌλ‘œ λ‹€λ£° 수 μžˆλ‹€.

Bit Shift Signed Addition

2의 보수둜 ν‘œν˜„λœ -4와 -1을 ν‘œμ€€ 이진 λ§μ…ˆ 연산을 ν•œ ν›„ μ •μˆ˜μ˜ μ €μž₯μ†Œ λ²”μœ„λ₯Ό λ„˜μ–΄ μ΄λ™λœ λͺ¨λ“  λΉ„νŠΈλ₯Ό μ‚­μ œν•˜λ©΄ μ†μ‰½κ²Œ -5의 2의 보수 ν‘œν˜„μ„ μ–»λŠ”λ‹€.


  • Bitwise Shift Operators λ₯Ό Unsigned Integers 와 μœ μ‚¬ν•˜κ²Œ λ‹€λ£° 수 μžˆλ‹€.

Bit Shift Signed

λΆ€ν˜Έ μžˆλŠ” μ •μˆ˜μ˜ Bitwise Left Shift Operator λŠ” λΆ€ν˜Έ μ—†λŠ” μ •μˆ˜μ™€ λ™μΌν•˜κ²Œ ν–‰λ™ν•˜λ©° 값을 2배둜 λŠ˜λ¦°λ‹€.
λΆ€ν˜Έ μžˆλŠ” μ •μˆ˜μ˜ Bitwise Right Shift Operator λŠ” λΆ€ν˜Έ μ—†λŠ” μ •μˆ˜μ™€ μœ μ‚¬ν•˜λ‚˜, λΉ„νŠΈ μ΄λ™μœΌλ‘œ 빈 곡간을 0으둜 μ±„μš°λŠ” 것이 μ•„λ‹Œ Sign Bit 둜 빈 자리λ₯Ό μ±„μš΄λ‹€. 이것을 Arithmetic Shift라 ν•œλ‹€.


3. Overflow Operators πŸ‘©β€πŸ’»

1. Overflow Operators

Swift λŠ” μ •μˆ˜ μƒμˆ˜ λ˜λŠ” λ³€μˆ˜μ— μ €μž₯ν•  수 μ—†λŠ” 값을 μ‚½μž…ν•˜λ €κ³  ν•˜λ©΄, μœ νš¨ν•˜μ§€ μ•Šμ€ 값을 생성을 ν—ˆμš©ν•˜μ§€ μ•ŠμœΌλ©° μ—λŸ¬λ₯Ό λ°œμƒμ‹œν‚¨λ‹€. μ΄λŸ¬ν•œ 행동은 λ„ˆλ¬΄ ν¬κ±°λ‚˜ μž‘μ€ 값을 λ‹€λ£° λ•Œ 좔가적인 Safety λ₯Ό μ œκ³΅ν•œλ‹€.

예λ₯Ό λ“€μ–΄ Int16 μ •μˆ˜λŠ” 2^16 = 65,536 개의 값을 0을 κΈ°μ€€μœΌλ‘œ μ €μž₯ν•˜λ―€λ‘œ -32,768 ~ 32,767 의 값을 μ €μž₯ν•  수 μžˆμœΌλ―€λ‘œ 이 λ²”μœ„λ₯Ό μ΄ˆκ³Όν•˜λŠ” 숫자λ₯Ό μ €μž₯ν•˜λ €κ³  ν•˜λ©΄ μ—λŸ¬λ₯Ό λ°œμƒμ‹œν‚¨λ‹€.

var potentialOverflow = Int16.max   // 32,767
potentialOverflow += 1  //  error, Swift runtime failure: arithmetic overflow

λ”°λΌμ„œ 경계값 쑰건을 μ½”λ”©ν•  λ•Œ μ—λŸ¬ 처리λ₯Ό μ œκ³΅ν•΄ μœ μ—°μ„±μ„ 높일 수 μžˆλ‹€. ν•˜μ§€λ§Œ μ—λŸ¬λ₯Ό λ°œμƒμ‹œν‚€λŠ” λŒ€μ‹  &λ₯Ό λΆ™μ—¬ Overflow Operatorsλ₯Ό μ‚¬μš©ν•  μˆ˜λ„ μžˆλ‹€. Swift λŠ” 3가지 Arithmetic Overflow Operators λ₯Ό μ œκ³΅ν•œλ‹€.

  • Overflow addition &+
  • Overflow subtraction &-
  • Overflow multplication &*
var potentialOverflow = Int16.max   // 32,767

print(potentialOverflow &+ 1)   // -32768
print(potentialOverflow &+ 2)   // -32767
print(potentialOverflow &+ 3)   // -32766

print(potentialOverflow &- 1)   // 32766

print(potentialOverflow &* 2)   // -2

2. Value Overflow

μˆ«μžλŠ” Positive, Negative μ–‘ λ°©ν–₯으둜 μ˜€λ²„ν”Œλ‘œμš° 될 수 μžˆλ‹€.

μ•žμ—μ„œ μ •μ˜ν•œ printToBinary(number:) ν•¨μˆ˜λ₯Ό λ‹€μŒκ³Ό 같이 고치고 Overflow Operators 의 λ™μž‘μ„ μ‚΄νŽ΄λ³΄μž.

func printToBinary<T: BinaryInteger>(number: T) {
    print("Binary: \(toBinary(number)), Decimal: \(number)")

    func toBinary(_ number: T) -> String {
        let absoluteNumber = abs(Int(number))
        let binary = String(absoluteNumber, radix: 2)
        if binary.count < 8 {
            return String(repeating: "0", count: 8 - binary.count) + binary
        } else {
            return binary
        }
    }
}

λ‹€μŒμ€ λΆ€ν˜Έ μ—†λŠ” μ •μˆ˜μ˜ Positive λ°©ν–₯으둜의 μ˜€λ²„ν”Œλ‘œμš° λ°œμƒμ— λŒ€ν•œ μ˜ˆμ œλ‹€.

Overflow Unsigned Addition

var unsignedOverflow = UInt8.max
printToBinary(number: unsignedOverflow)
// Binary: 11111111, Decimal: 255

unsignedOverflow = unsignedOverflow &+ 1
printToBinary(number: unsignedOverflow)
// Binary: 00000000, Decimal: 0
  • λ³€μˆ˜ unsignedOverflow λŠ” UInt8의 μ΅œλŒ“κ°’ 11111111을 μ΄ˆκΉƒκ°’μœΌλ‘œ μ €μž₯ν•œλ‹€.
  • Overflow Addition Operator &+λ₯Ό μ‚¬μš©ν•΄ 값을 1 μ¦κ°€μ‹œν‚¨λ‹€.
  • μ •μˆ˜μ˜ μ €μž₯ λ²”μœ„λ₯Ό λ„˜λŠ” λΉ„νŠΈλŠ” 제거되고 00000000이 λ‚¨κ²Œ λœλ‹€.


μ΄λ²ˆμ—λŠ” λΆ€ν˜Έ μ—†λŠ” μ •μˆ˜μ˜ Negative λ°©ν–₯으둜의 μ˜€λ²„ν”Œλ‘œμš° λ°œμƒμ— λŒ€ν•œ 예제λ₯Ό μ•Œμ•„λ³΄μž.

Overflow Unsigned Subtraction

var anotherUnsignedOverflow = UInt8.min
printToBinary(number: anotherUnsignedOverflow)
// Binary: 00000000, Decimal: 0

anotherUnsignedOverflow = anotherUnsignedOverflow &- 1
printToBinary(number: anotherUnsignedOverflow)
// Binary: 11111111, Decimal: 255


μ˜€λ²„ν”Œλ‘œμš°λŠ” Signed Integers μ—μ„œλ„ λ°œμƒν•œλ‹€. λΆ€ν˜Έ μžˆλŠ” μ •μˆ˜μ˜ λͺ¨λ“  λ§μ…ˆ, λΊ„μ…ˆμ€ λΉ„νŠΈ λ°©μ‹μœΌλ‘œ μˆ˜ν–‰λœλ‹€.

Overflow Signed Subtraction

var signedOverflow = Int8.min
printToBinary(number: signedOverflow)
// Binary: 10000000, Decimal: -128

signedOverflow = signedOverflow &- 1
printToBinary(number: signedOverflow)
// Binary: 01111111, Decimal: 127
  • λ³€μˆ˜ signedOverflow λŠ” Int8의 μ΅œμ†Ÿκ°’ 10000000을 μ΄ˆκΉƒκ°’μœΌλ‘œ μ €μž₯ν•œλ‹€.
  • Overflow Subtraction Operator &-λ₯Ό μ‚¬μš©ν•΄ 값을 1 κ°μ†Œμ‹œν‚¨λ‹€.
  • 결과값은 λΆ€ν˜Έ λΉ„νŠΈκ°€ ν† κΈ€λ˜μ–΄ μ–‘μˆ˜κ°€ λ˜μ–΄ 01111111을 μ €μž₯ν•œλ‹€.

Signed Intergers, Unsigned Integers λŠ” λ™μΌν•˜κ²Œ μ΅œλŒ“κ°’μ„ λ„˜μ–΄μ„œλ©΄ μ΅œμ†Ÿκ°’μœΌλ‘œ, μ΅œμ†Ÿκ°’μ„ λ„˜μ–΄μ„œλ©΄ μ΅œλŒ“κ°’μœΌλ‘œ μˆœν™˜λœλ‹€.


4. Precedence and Associativity πŸ‘©β€πŸ’»

μ—°μ‚°μž μš°μ„ μˆœμœ„(precedence)λŠ” λ‹€λ₯Έ μ—°μ‚°μžλ³΄λ‹€ 높은 μš°μ„ μˆœμœ„λ₯Ό 갖도둝 ν•΄ λ¨Όμ € 적용되게 ν•œλ‹€. μ—°μ‚°μž μ—°κ΄€μ„±(associativity)은 λ™μΌν•œ μš°μ„ μˆœμœ„λ₯Ό κ°–λŠ” μ—°μ‚°μžλ“€μ΄ μ™Όμͺ½κ³Ό κ·Έλ£Ήν™” 될지, 였λ₯Έμͺ½κ³Ό κ·Έλ£Ήν™” 될지λ₯Ό μ •μ˜ν•œλ‹€.

Swift λŠ” C 처럼 Multiplication Operator *, Division Operator /, Remainder Operator % 같은 것듀은 Addition Operator +, Subtraction Operator - 같은 것듀보닀 더 높은 μš°μ„ μˆœμœ„λ₯Ό κ°–λŠ”λ‹€. λ™μΌν•œ μš°μ„ μˆœμœ„ μ‚¬μ΄μ—μ„œλŠ” μ™Όμͺ½μœΌλ‘œ κ·Έλ£Ήν™” λœλ‹€. 즉, μˆ˜ν•™μ  사칙연산 μš°μ„ μˆœμœ„λ₯Ό κ·ΈλŒ€λ‘œ λ”°λ₯Έλ‹€.

2 + 3 % 4 * 5

λ”°λΌμ„œ μœ„ 연산은 κ΄„ν˜Έλ₯Ό μ‚¬μš©ν•΄ μš°μ„ μˆœμœ„λ₯Ό λͺ…μ‹œμ μœΌλ‘œ ν‘œν˜„ν•˜λ©΄ λ‹€μŒκ³Ό κ°™λ‹€.

2 + ((3 % 4) * 5)

(3 % 4)λŠ” 3 μ΄λ―€λ‘œ λ‹€μŒ 연산은 2 + (3 * 5)κ°€ 되고, 또 λ‹€μ‹œ (3 * 5)λŠ” 15 μ΄λ―€λ‘œ λ‹€μŒ 연산은 2 + 15κ°€ λ˜μ–΄ μ—°μ‚° κ²°κ³ΌλŠ” 17 이 λœλ‹€.

Swift 의 Operator Precedences와 Operator Associativity RulesλŠ” C λ‚˜ Objective-C 보닀 더 κ°„λ‹¨ν•˜κ³  예츑 κ°€λŠ₯ν•˜λ‹€. 이것은 C-based 언어와 μ™„μ „νžˆ μΌμΉ˜ν•˜μ§€ μ•ŠμŒμ„ μ˜λ―Έν•˜λ―€λ‘œ, κΈ°μ‘΄ μ½”λ“œλ₯Ό Swift 둜 μ „ν™˜ν•  λ•Œ μ—°μ‚°μž μƒν˜Έμž‘μš©μ΄ μ˜λ„ν•œλŒ€λ‘œ μž‘λ™ν•˜λŠ”μ§€ ν™•μΈν•΄μ•Όν•œλ‹€. Swift Standard Library κ°€ μ œκ³΅ν•˜λŠ” Operators λŠ” Operator Declarations μ—μ„œ 확인할 수 μžˆλ‹€.


5. Operator Methods πŸ‘©β€πŸ’»

1. Operator Methods

Classes 와 Structures λŠ” κΈ°μ‘΄ μ—°μ‚°μžλ₯Ό Overloading μ‹œμΌœ 자체 κ΅¬ν˜„μ„ μ œκ³΅ν•  수 μžˆλ‹€.

Arithmetic Addition Operator λŠ” 두 νƒ€κ²Ÿμ— μž‘λ™ν•˜λ―€λ‘œ Binary Operator이며, 두 νƒ€κ²Ÿ 사이에 μœ„μΉ˜ν•˜λ―€λ‘œ Infix Operatorλ‹€. μ•„λž˜ μ˜ˆμ œλŠ” Custom Structure μ—μ„œ Overloading 을 톡해 Arithmetic Addition Operator +κ°€ μ–΄λ–»κ²Œ κ΅¬ν˜„λ˜λŠ”μ§€λ₯Ό 보여쀀닀.

struct Vector2D {
    var x = 0.0, y = 0.0
}

extension Vector2D {
    static func + (lhs: Vector2D, rhs: Vector2D) -> Vector2D {
        Vector2D(x: lhs.x + rhs.x, y: lhs.y + rhs.y)
    }
}

Vector2D의 Type Method 둜 μ •μ˜λœ μ—°μ‚°μž +λŠ” 이름이 Arithmetic Addition Operator 와 μΌμΉ˜ν•˜κΈ° λ•Œλ¬Έμ— Overloading λœλ‹€. Arithmetic Addition Operator κ°€ Binary Operator 이며, Infix Operator μ΄λ―€λ‘œ 이 μ—°μ‚°μž μ—­μ‹œ λ™μΌν•œ ν˜•νƒœλ‘œ μž‘μ„±λ˜μ—ˆλ‹€. λ˜ν•œ λ§μ…ˆ 연산은 λ²‘ν„°μ˜ ν•„μˆ˜ λ™μž‘μ΄ μ•„λ‹ˆλ―€λ‘œ Structures μ •μ˜ μžμ²΄μ— ν¬ν•¨μ‹œν‚€μ§€ μ•Šκ³  Extensions λ₯Ό μ΄μš©ν•΄ λΆ„λ¦¬μ‹œμΌœ μ •μ˜ν–ˆλ‹€.

let vector = Vector2D(x: 3.0, y: 1.0)
let anotherVector = Vector2D(x: 2.0, y: 4.0)
let combinedVector = vector + anotherVector
print("Combined Vector is (\(combinedVector.x), \(combinedVector.y)).")
// Combined Vector is (5.0, 5.0).

Vector Addition

2. Prefix and Postfix Operators

μœ„ μ˜ˆμ œλŠ” Binary Infix Operator의 Custom Implementation 을 λ³΄μ—¬μ£Όμ—ˆλ‹€. Classes 와 Structures λŠ” Standard Unary Operators 와 같은 것듀도 κ΅¬ν˜„ν•  수 μžˆλ‹€.

Unary Operators

  • Single Target 을 λŒ€μƒμœΌλ‘œ μž‘λ™ν•œλ‹€.
  • Operator κ°€ νƒ€κ²Ÿ μ•žμ— μœ„μΉ˜ν•˜λŠ” Prefix Operators, νƒ€κ²Ÿ 뒀에 μœ„μΉ˜ν•˜λŠ” Postfix Operators 2κ°€μ§€λ‘œ λ‚˜λ‰œλ‹€.

Binary Operators

  • Two Target 을 λŒ€μƒμœΌλ‘œ μž‘λ™ν•œλ‹€.
  • Operator κ°€ 두 νƒ€κ²Ÿ 사이에 μœ„μΉ˜ν•œλ‹€.


Unary OperatorsλŠ” func keyword μ•žμ— prefix λ˜λŠ” posfix modifier λ₯Ό μž‘μ„±ν•΄ μ •μ˜ν•œλ‹€. λ‹€μŒ Operator λŠ” Unary Minus Operator 둜 Prefix Operator 둜 μ •μ˜λ˜μ—ˆλ‹€.

extension Vector2D {
    static prefix func - (vector: Vector2D) -> Vector2D {
        Vector2D(x: -vector.x, y: -vector.y)
    }
}
let positive = Vector2D(x: 3.0, y: 4.0)
let negative = -positive
print("Negative Vector is (\(negative.x), \(negative.y)).")
// Negative Vector is (-3.0, -4.0).
let alsoPosotive = -negative
print("Also Positive Vector is (\(alsoPosotive.x), \(alsoPosotive.y)).")
// Also Positive Vector is (3.0, 4.0).

3. Compound Assignment Operators

Compound Assignment OperatorsλŠ” μ—°μ‚°μžμ™€ Combine Assignment =λ₯Ό κ²°ν•©ν•΄ λ§Œλ“ λ‹€. 예λ₯Ό λ“€μ–΄ Addition Assignment Operator +=λŠ” 단일 μ—°μ‚°μœΌλ‘œ λ§μ…ˆκ³Ό 할당을 κ²°ν•©ν•œλ‹€.

Compound Assignment Operators의 left input parameter λŠ” Operator Method λ‘œλΆ€ν„° 값이 직접 μˆ˜μ •λ˜λ―€λ‘œ inout이 λ˜μ–΄μ•Ό ν•œλ‹€.

λ‹€μŒμ€ Vector2D 의 Addition Assignment Operator 의 κ΅¬ν˜„μ΄λ‹€. μ—¬κΈ°μ„œ Arithmetic Addition Operator λŠ” Operator Methodsμ—μ„œ μ •μ˜λœ 것을 μ‚¬μš©ν•œλ‹€.

extension Vector2D {
    static func += (lhs: inout Vector2D, rhs: Vector2D) {
        lhs = lhs + rhs
    }
}
var original = Vector2D(x: 1.0, y: 2.0)
let vectorToAdd = Vector2D(x: 3.0, y: 4.0)
original += vectorToAdd
print("Original Vector is (\(original.x), \(original.y)) now.")
// Original Vector is (4.0, 6.0) now.

4. Equivalence Operators

기본적으둜 Custom Classes 와 Structures λŠ” Equivalence Operators ==와 !=λ₯Ό κ΅¬ν˜„μ„ 갖지 μ•ŠλŠ”λ‹€. λ”°λΌμ„œ 이λ₯Ό κ΅¬ν˜„ν•  λ•ŒλŠ” 일반적으둜 == μ—°μ‚°μžλ₯Ό κ΅¬ν˜„ν•˜κ³ , !=λŠ” Swift Standard Library 의 κΈ°λ³Έ κ΅¬ν˜„μ΄ ==의 λΆ€μ •μž„μ„ μ΄μš©ν•œλ‹€.

μœ„ Vector2D 에 Custom Equal to Operator ==λ₯Ό κ΅¬ν˜„ν•˜λŠ” 방법은 두 가지가 μžˆλ‹€.


1 ) Infix Operator λ₯Ό 직접 κ΅¬ν˜„ν•˜κΈ°

extension Vector2D: Equatable {
    static func == (lhs: Vector2D, rhs: Vector2D) -> Bool {
        lhs.x == rhs.x && lhs.y == rhs.y
    }
}


2 ) Protocol μ±„νƒμœΌλ‘œ Swift κ°€ κ΅¬ν˜„μ„ μžλ™μœΌλ‘œ ν•©μ„±ν•˜λ„λ‘ ν•˜κΈ°

extension Vector2D: Equatable {}

μš°λ¦¬λŠ” Swift Protocols 의 Adopting a Protocol Using a Synthesized Implementation μ—μ„œ λ‹¨μˆœνžˆ Protocol 을 μ±„νƒν•˜λŠ” 것 만으둜 Protocols κ°€ μ œκ³΅ν•˜λŠ” Default Implementations λ₯Ό Swift κ°€ μžλ™μœΌλ‘œ ν•©μ„±ν•΄ κ΅¬ν˜„ν•˜λ„λ‘ ν•  수 μžˆμŒμ„ ν™•μΈν–ˆλ‹€.

let alpha = Vector2D(x: 2.0, y: 3.0)
let beta = Vector2D(x: 2.0, y: 3.0)
if alpha == beta {
    print("These two vectors are equivalent.")
}
These two vectors are equivalent.

5. Impossible Operators to Overload

Classes 와 Structures λ₯Ό κ΅¬ν˜„ν•  λ•Œ λͺ¨λ“  Operators κ°€ Overloading κ°€λŠ₯ν•œ 것은 μ•„λ‹ˆλ‹€. Default Assignment Operator = λ˜λŠ” Ternary Conditional Operator a ? b : c와 같이 Overloading 이 ν—ˆμš©λ˜μ§€ μ•ŠλŠ” μ—°μ‚°μžκ°€ μ‘΄μž¬ν•œλ‹€. Overloading 이 λΆˆκ°€λŠ₯ν•œ λͺ¨λ“  μ—°μ‚°μž λͺ©λ‘μ€ λ‹€μŒ μ„Ήμ…˜μ˜ Custom Operators 둜 μ‚¬μš©ν•  수 μ—†λŠ” μ—°μ‚°μž μ—μ„œ 확인할 수 μžˆλ‹€.


6. Custom Operators πŸ‘©β€πŸ’»

1. Custom Operators

Swift κ°€ μ œκ³΅ν•˜λŠ” Standard Operators 외에 Custom Operators λ₯Ό μ„ μ–Έν•˜κ³  κ΅¬ν˜„ν•  수 μžˆλ‹€. Custom Operators λŠ” operator keyword λ₯Ό μ‚¬μš©ν•˜λ©° prefix, infix, postfix modifiers λ₯Ό 가지며 Global Level둜 μ •μ˜λœλ‹€. λ‹€μŒ μ˜ˆμ œλŠ” +++λΌλŠ” μƒˆλ‘œμš΄ Prefix Operator λ₯Ό μ •μ˜ν•œλ‹€.

prefix operator +++

이 +++ μ—°μ‚°μžλŠ” Swift 에 μ‘΄μž¬ν•˜λŠ” Operators κ°€ μ•„λ‹ˆλ―€λ‘œ Protocols λ₯Ό μ±„νƒν•˜λ„λ‘ ν•΄ κ΅¬ν˜„μ„ ν•©μ„±ν•˜λ„λ‘ ν•  수 μ—†λ‹€. 이 μƒˆ Operators λ₯Ό μ‚¬μš©ν•΄ μ •μ˜ν•˜λ €λŠ” μž‘μ—…μ„ μ‚¬μš©μžκ°€ 직접 κ΅¬ν˜„ν•΄μ•Όν•˜λ©°, κ·Έ κ΅¬ν˜„μ€ μ‚¬μš©μžκ°€ μ •μ˜ν•œ νŠΉμ • context 내에 μ˜λ―Έκ°€ λΆ€μ—¬λœλ‹€.

prefix operator +++

extension Vector2D {
    static prefix func +++ (vector: inout Vector2D) -> Vector2D {
        vector += vector
        return vector
    }
}

이제 Vector2D λŠ” 기쑴재 μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” μ‚¬μš©μž μ •μ˜ μ—°μ‚°μž +++λ₯Ό μ‚¬μš©ν•΄ 값을 2배둜 λ§Œλ“œλŠ” 연산을 μˆ˜ν–‰ν•  수 μžˆλ‹€.

var toBeDoubled = Vector2D(x: 1.0, y: 4.0)
let afterDoubling = +++toBeDoubled
print("After Doubling Vector is (\(afterDoubling.x), \(afterDoubling.y)).")
// After Doubling Vector is (2.0, 8.0).


1 ) Custom Operators 둜 μ‚¬μš©ν•  수 μžˆλŠ” μ—°μ‚°μž

  • ASCII 문자 /, =, -, +, !, *, %, <, >, &, |, ^, ?
  • λ‹€μŒ 문법과 μΌμΉ˜ν•˜λŠ” μ—°μ‚°μž

Grammar of operators

operator β†’ operator-head operator-characters?
operator β†’ dot-operator-head dot-operator-characters
operator-head β†’ / | = | - | + | ! | * | % | < | > | & | | | ^ | ~ | ?
operator-head β†’ U+00A1–U+00A7
operator-head β†’ U+00A9 or U+00AB
operator-head β†’ U+00AC or U+00AE
operator-head β†’ U+00B0–U+00B1
operator-head β†’ U+00B6, U+00BB, U+00BF, U+00D7, or U+00F7
operator-head β†’ U+2016–U+2017
operator-head β†’ U+2020–U+2027
operator-head β†’ U+2030–U+203E
operator-head β†’ U+2041–U+2053
operator-head β†’ U+2055–U+205E
operator-head β†’ U+2190–U+23FF
operator-head β†’ U+2500–U+2775
operator-head β†’ U+2794–U+2BFF
operator-head β†’ U+2E00–U+2E7F
operator-head β†’ U+3001–U+3003
operator-head β†’ U+3008–U+3020
operator-head β†’ U+3030
operator-character β†’ operator-head
operator-character β†’ U+0300–U+036F
operator-character β†’ U+1DC0–U+1DFF
operator-character β†’ U+20D0–U+20FF
operator-character β†’ U+FE00–U+FE0F
operator-character β†’ U+FE20–U+FE2F
operator-character β†’ U+E0100–U+E01EF
operator-characters β†’ operator-character operator-characters?
dot-operator-head β†’ .
dot-operator-character β†’ . | operator-character
dot-operator-characters β†’ dot-operator-character dot-operator-characters?
infix-operator β†’ operator
prefix-operator β†’ operator
postfix-operator β†’ operator


2 ) Custom Operators 둜 μ‚¬μš©ν•  수 μ—†λŠ” μ—°μ‚°μž

λ‹€μŒ μ—°μ‚°μžλ“€μ€ μ˜ˆμ•½λ˜μ–΄μžˆμœΌλ©°, Overloading ν•˜κ±°λ‚˜ Custom Operators 둜 μ‚¬μš©ν•  수 μ—†λ‹€.

  • Tokens 둜 μ‚¬μš©ν•  수 μ—†λŠ” μ—°μ‚°μž: =, ->, //, /*, */, .
  • Prefix Operators 둜 μ‚¬μš©ν•  수 μ—†λŠ” μ—°μ‚°μž: <, &, ?
  • Infix Operators 둜 μ‚¬μš©ν•  수 μ—†λŠ” μ—°μ‚°μž: ?
  • Postfix Operators 둜 μ‚¬μš©ν•  수 μ—†λŠ” μ—°μ‚°μž: >, !, ?

2. Precedence for Custom Infix Operators

λͺ¨λ“  Custom Infix OperatorsλŠ” κΈ°λ³Έ Infix Operators 와 λ§ˆμ°¬κ°€μ§€λ‘œ νŠΉμ • μš°μ„ μˆœμœ„ 그룹에 μ†ν•˜κ²Œ λœλ‹€. μ„ μ–Έν•  λ•Œ μš°μ„ μˆœμœ„ 그룹을 λͺ…μ‹œν•  수 있으며, λͺ…μ‹œλ˜μ§€ μ•Šμ€ μ—°μ‚°μžλŠ” Default Precedence Group 에 μ†ν•˜κ²Œ λ˜λŠ”λ° 이것은 Ternary Conditional Operator 의 λ°”λ‘œ μœ„μ— μœ„μΉ˜ν•˜κ²Œλœλ‹€.

λ‹€μŒ μ˜ˆμ œλŠ” New Custom Infix Operator +-λ₯Ό μ„ μ–Έ 및 μ •μ˜ν•œλ‹€. 이 μ—°μ‚°μžλŠ” μ‚°μˆ μ—°μ‚°μ„ ν•˜λ―€λ‘œ Addition Precednece 그룹에 μ†ν•˜λ„λ‘ μ„ μ–Έλ˜μ—ˆλ‹€.

infix operator +-: AdditionPrecedence

extension Vector2D {
    static func +- (lhs: Vector2D, rhs: Vector2D) -> Vector2D {
        Vector2D(x: lhs.x + rhs.x, y: lhs.y - rhs.y)
    }
}
let firstVector = Vector2D(x: 1.0, y: 2.0)
let secondVector = Vector2D(x: 3.0, y: 4.0)
let plusMinusVector = firstVector +- secondVector
print("Plus Minus Vector is (\(plusMinusVector.x), \(plusMinusVector.y)).")
// Plus Minus Vector is (4.0, -2.0).

Prefix Operators λ˜λŠ” Postfix Operatorsλ₯Ό μ •μ˜ν•  λ•ŒλŠ” μš°μ„ μˆœμœ„λ₯Ό μ§€μ •ν•˜μ§€ μ•ŠλŠ”λ‹€. λ§Œμ•½ ν”Όμ—°μ‚°μž(operand)에 λ‘˜μ„ λͺ¨λ‘ μ μš©ν•  경우 Postfix Operatorsκ°€ 더 높은 μš°μ„ μˆœμœ„λ₯Ό κ°€μ Έ λ¨Όμ € μ μš©λœλ‹€.


7. Result Builders πŸ‘©β€πŸ’»

1. The Problem That Result Builders Solve

κ²°κ³Ό λΉŒλ” (result builder) λŠ” 리슀트 (list) λ‚˜ 트리 (tree) 와 같은 μ€‘μ²©λœ 데이터λ₯Ό μžμ—°μŠ€λŸ½κ³  선언적인 λ°©μ‹μœΌλ‘œ μƒμ„±ν•˜κΈ° μœ„ν•œ ꡬ문을 μΆ”κ°€ν•˜λŠ” νƒ€μž…μž…λ‹ˆλ‹€. κ²°κ³Ό λΉŒλ”λ₯Ό μ‚¬μš©ν•˜λŠ” μ½”λ“œλŠ” μ‘°κ±΄μ μ΄κ±°λ‚˜ λ°˜λ³΅λ˜λŠ” λ°μ΄ν„°μ˜ 쑰각을 μ²˜λ¦¬ν•˜κΈ° μœ„ν•΄ if 와 for 와 같은 Swift ꡬ문을 포함할 수 μžˆμŠ΅λ‹ˆλ‹€. μ•„λž˜ μ½”λ“œλŠ” 별과 ν…μŠ€νŠΈλ₯Ό μ‚¬μš©ν•˜μ—¬ ν•œμ€„λ‘œ 그리기 μœ„ν•΄ λͺ‡κ°€μ§€ νƒ€μž…μ„ μ •μ˜ν•©λ‹ˆλ‹€.

Result BuilderλŠ” ν•˜λ‚˜μ˜ Type 으둜, List λ‚˜ Tree 와 같은 Nested Data λ₯Ό μžμ—°μŠ€λŸ½κ³  μ„ μ–Έμ μœΌλ‘œ μƒμ„±ν•˜κΈ° μœ„ν•œ Syntax λ₯Ό μ •μ˜ν•œλ‹€.

λ‹€μŒ μ—μ œλŠ” ν•œ 쀄에 별과 문자λ₯Ό 그리기 μœ„ν•΄ λͺ‡ 가지 Types λ₯Ό μ •μ˜ν•œλ‹€.

protocol Drawable {
    func draw() -> String
}

struct Line: Drawable {
    var elements: [Drawable]
    func draw() -> String {
        elements.map { $0.draw() }.joined(separator: "")
    }
}

struct Text: Drawable {
    var content: String
    init(_ content: String) {
        self.content = content
    }
    func draw() -> String {
        content
    }
}

struct Space: Drawable {
    func draw() -> String {
        " "
    }
}

struct Stars: Drawable {
    var length: Int
    func draw() -> String {
        String(repeating: "*", count: length)
    }
}

struct AllCaps: Drawable {
    var content: Drawable
    func draw() -> String {
        content.draw().uppercased()
    }
}
  • Drawable protocol 은 draw() λ©”μ„œλ“œλ₯Ό κ΅¬ν˜„ν•˜λ„λ‘ κ°•μ œν•¨μœΌλ‘œμ¨ μ„ μ΄λ‚˜ λͺ¨μ–‘κ³Ό 같은 그릴 수 μžˆλŠ” ν•­λͺ©μ— λŒ€ν•œ μš”κ΅¬μ‚¬ν•­μ„ μ •μ˜ν•œλ‹€.
  • Line structure λŠ” λ‹€λ₯Έ Drawable 을 μžμ‹ μ˜ property 에 λ°°μ—΄λ‘œ μ €μž₯ν•¨μœΌλ‘œμ¨ λŒ€λΆ€λΆ„μ˜ κ·Έλ¦¬λŠ” 것에 λŒ€ν•΄ μ΅œμƒμœ„ μ»¨ν…Œμ΄λ„ˆμ˜ 역할을 ν•œλ‹€. Line structure λŠ” 쀄을 그리기 μœ„ν•΄ draw()λ₯Ό ν˜ΈμΆœν•˜κ³  이 λ©”μ„œλ“œλŠ” μ»¨ν…Œμ΄λ„ˆ λ‚΄ λ‹€λ₯Έ Drawable 이 μžμ‹ μ˜ draw()λ₯Ό ν˜ΈμΆœν•΄ 그림을 그리도둝 ν•œ λ’€ joined(separator:) λ©”μ„œλ“œλ₯Ό μ΄μš©ν•΄ λ¬Έμžμ—΄ κ²°κ³Όλ₯Ό 단일 String 으둜 λ§Œλ“ λ‹€.
  • Text structure λŠ” λ¬Έμžμ—΄μ„ ν•˜λ‚˜μ˜ 그리기둜 wrapping μ‹œν‚€κ³ , Space structure λŠ” ν•˜λ‚˜μ˜ 곡백을 그리고, Stars structure λŠ” 주어진 개수 만큼 별을 κ·Έλ¦°λ‹€.
  • AllCaps structure λŠ” λ‹€λ₯Έ Drawable 을 λŒ€λ¬Έμžλ‘œ λ³€κ²½ν•˜λŠ” 역할을 ν•œλ‹€.


이 Structures λ₯Ό μ‚¬μš©ν•΄ λ‹€μŒκ³Ό 같이 One Line String 을 그릴 수 μžˆλ‹€.

let name: String? = "Hogwarts"
let manualDrawing = Line(elements: [
    Stars(length: 3),
    Text("Hello"),
    Space(),
    AllCaps(content: Text("\(name ?? "World")!")),
    Stars(length: 2)
])

print(manualDrawing.draw()) // ***Hello HOGWARTS!**

μ½”λ“œλŠ” 잘 μž‘λ™ν•˜μ§€λ§Œ AllCaps μ•ˆμ— 또 λ‹€λ₯Έ κ΄„ν˜Έλ₯Ό ν¬ν•¨ν•˜λŠ” μΈμŠ€ν„΄μŠ€ 생성 ꡬ문이 λ“€μ–΄κ°€λŠ” 것은 μ½”λ“œλ₯Ό 읽기 μ–΄λ ΅κ²Œ λ§Œλ“ λ‹€.

2. Define Result Builders

Result BuilderλŠ” μ½”λ“œλ₯Ό μ’€ 더 Swift 슀럽고 읽기 μ‰½κ²Œ λ§Œλ“€μ–΄μ€€λ‹€. Result Builder λŠ” νƒ€μž… 선언에 @resultBuilder Attribute λ₯Ό μž‘μ„±ν•΄ μ •μ˜ν•œλ‹€. λ‹€μŒ μ˜ˆμ œλŠ” Declarative Syntax λ₯Ό μ‚¬μš©ν•΄ drawing μž‘μ—…μ„ λ¬˜μ‚¬ν•˜λŠ” DrawingBuilder λ₯Ό μ •μ˜ν•œλ‹€.

@resultBuilder
struct DrawingBuilder {
    static func buildBlock(_ components: Drawable...) -> Drawable {
        Line(elements: components)
    }
    static func buildEither(first: Drawable) -> Drawable {
        first
    }
    static func buildEither(second: Drawable) -> Drawable {
        second
    }
}

DrawingBuilder structure λŠ” Result Builder Syntax 의 일뢀λ₯Ό κ΅¬ν˜„ν•˜λŠ” 3개의 λ©”μ„œλ“œλ₯Ό μ •μ˜ν•œλ‹€.

  • buildBlock(_:) λ©”μ„œλ“œλŠ” μ½”λ“œ λΈ”λŸ­μ— Line을 그리기 μœ„ν•œ 지원을 μΆ”κ°€ν•œλ‹€.
  • buildEither(first:) λ©”μ„œλ“œμ™€ buildEither(second:) λ©”μ„œλ“œλŠ” if-else에 λŒ€ν•œ 지원을 μΆ”κ°€ν•œλ‹€.

3. Result Builders in Action

μœ„μ—μ„œ μ •μ˜ν•œ DrawingBuilderλ₯Ό μ‚¬μš©ν•˜κΈ° μœ„ν•΄ ν•¨μˆ˜μ˜ Parameter 에 @DrawingBuilder attribute λ₯Ό μ μš©ν•  수 있으며, μ΄λŠ” ν•¨μˆ˜μ— μ „λ‹¬λœ Closure λ₯Ό Result Builder κ°€ ν•΄λ‹Ή Closure μ—μ„œ μƒμ„±ν•˜λŠ” κ°’μœΌλ‘œ λ³€ν™˜ν•œλ‹€.

func draw(@DrawingBuilder content: () -> Drawable) -> Drawable {
    content()
}

func caps(@DrawingBuilder content: () -> Drawable) -> Drawable {
    AllCaps(content: content())
}

func makeGreeting(for name: String? = nil) -> Drawable {
    let greeting = draw(content: {
        Stars(length: 3)
        Text("Hello")
        Space()
        caps(content: {
            Text("\(name ?? "World")!")
        })
        Stars(length: 2)
    })
    return greeting
}
  • draw(_:)와 caps(_:) ν•¨μˆ˜λŠ” λ‘˜ λ‹€ @DrawingBuilder attribute κ°€ 적용된 Single Closure λ₯Ό arguemnt 둜 λ°›λŠ”λ‹€. 이것은 일반 ν•¨μˆ˜μ—μ„œ Parameter Type 이 Closure 일 λ•Œμ˜ μ‚¬μš©λ²•κ³Ό λ™μΌν•˜λ‹€. μš°λ¦¬λŠ” 이것을 이미 Autoclosure Type Parameters μ—μ„œ Autoclosure λ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šμ€ 일반 ν•¨μˆ˜μ˜ Parameter κ°€ Closure 일 λ•Œ μ‚¬μš©ν•΄λ³Έ 적이 μžˆλ‹€.
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]

func serve(customer customerProvider: () -> String) {
    print("Now serving \(customerProvider())!")
}

// serve(customer: { customersInLine.remove(at: 0) })  // Now serving Chris!

// with trailing closure
serve {
    customersInLine.remove(at: 0)
}
Now serving Chris!
  • makeGreeting(for:) ν•¨μˆ˜λŠ” name 을 parameter 둜 λ°›μ•„ κ°œμΈν™” 인사말을 κ·Έλ¦¬λŠ” 데 μ‚¬μš©ν•œλ‹€. μ•žμ—μ„œ Result Builders λŠ” List λ‚˜ Tree 와 같은 Nested Data λ₯Ό μžμ—°μŠ€λŸ½κ³  μ„ μ–Έμ μœΌλ‘œ μƒμ„±ν•˜κΈ° μœ„ν•œ Syntax λ₯Ό μ •μ˜ν•˜λŠ” Type 이라고 ν–ˆλ‹€. 즉, 이것은 Swift κ°€ μ–Έμ–΄ λ ˆλ²¨μ—μ„œ μ§€μ›ν•˜λŠ” Monad 라 λ³Ό 수 μžˆλ‹€. 이것은 pipe 와 reduce 의 νŠΉμ„±λ“€μ„ μ‘°κΈˆμ”© μ„žμ–΄ 놓은 κ²ƒμ²˜λŸΌ 보이기도 ν•œλ‹€. ν•œκ°€μ§€ ν™•μ‹€ν•œ 것은 Result Builders λŠ” κ²°κ΅­ Monad 둜 데이터λ₯Ό μ‰½κ²Œ 닀루기 μœ„ν•œ Container 역할을 ν•œλ‹€λŠ” 것이닀.


이제 makeGreeting(for:) ν•¨μˆ˜λ₯Ό Trailing Closures λ₯Ό μ‚¬μš©ν•΄ μ’€ 더 κ°„λž΅ν•˜κ²Œ ν‘œν˜„ν•΄λ³΄μž.

func draw(@DrawingBuilder content: () -> Drawable) -> Drawable {
    content()
}

func caps(@DrawingBuilder content: () -> Drawable) -> Drawable {
    AllCaps(content: content())
}

func makeGreeting(for name: String? = nil) -> Drawable {
    draw {
        Stars(length: 3)
        Text("Hello")
        Space()
        caps {
            Text("\(name ?? "World")!")
        }
        Stars(length: 2)
    }
}

기쑴의

let manualDrawing = Line(elements: [
    Stars(length: 3),
    Text("Hello"),
    Space(),
    AllCaps(content: Text("\(name ?? "World")!")),
    Stars(length: 2)
])

와 비ꡐ해보면 훨씬 선언적인 문법이 λ˜μ—ˆλ‹€. 잘 μž‘λ™ν•˜λŠ”μ§€ ν™•μΈν•΄λ³΄μž.

let genericGreeting = makeGreeting()
print(genericGreeting.draw())   // ***Hello WORLD!**

ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•΄ μ„ μ–Έμ μœΌλ‘œ λ³€κ²½ν–ˆκΈ° λ•Œλ¬Έμ— 가독성이 μ’‹μ•„μ‘Œμ„ 뿐 μ•„λ‹ˆλΌ μž¬μ‚¬μš©μ„±λ„ μ’‹μ•„μ‘Œλ‹€.

let personGreeting = makeGreeting(for: "Hogwarts")
print(personGreeting.draw())    // ***Hello Hogwarts!**


@DrawingBuilderλ₯Ό attribute 둜 μ‚¬μš©ν•œλ‹€λŠ” 것은 DrawingBuilder κ°€ μ •μ˜ν•œ Syntax λ₯Ό μ‚¬μš©ν•œλ‹€λŠ” κ²ƒμ΄λ―€λ‘œ Closure 에 μ‹€ν–‰μ‹œν‚€κΈΈ μ›ν•˜λŠ” μ½”λ“œλ₯Ό λͺ¨μ•„ ν•˜λ‚˜μ˜ μ½”λ“œ λΈ”λŸ­μœΌλ‘œ Wrapping μ‹œν‚€κ³ , 이것을 do λͺ…λ ΉμœΌλ‘œ evalution ν•˜λŠ” 것과 κ°™λ‹€κ³  λ³Ό 수 μžˆλ‹€. λ”°λΌμ„œ draw(content:) ν•¨μˆ˜μ— Trailing Closures λ₯Ό μ‚¬μš©ν•΄ μ—¬λŸ¬ μ½”λ“œλ₯Ό ν•˜λ‚˜μ˜ λΈ”λŸ­μœΌλ‘œ 묢은 것 처럼 caps(content:) ν•¨μˆ˜ μ—­μ‹œ λ™μΌν•˜κ²Œ μ—¬λŸ¬ μ½”λ“œλ₯Ό ν•˜λ‚˜μ˜ λΈ”λŸ­μœΌλ‘œ 묢을 수 μžˆλ‹€.

let name: String? = "Hogwarts"
let capsDrawing = caps {
    let partialDrawing: Drawable
    if let name = name {
        partialDrawing = DrawingBuilder.buildEither(first: Text("\(name)!"))
    } else {
        partialDrawing = DrawingBuilder.buildEither(second: Text("World!"))
    }
    return partialDrawing
}
print(capsDrawing)          // AllCaps(content: __lldb_expr_156.Text(content: "Hogwarts!"))
print(capsDrawing.draw())   // HOGWARTS!

μ—¬κΈ°μ„œ caps(content:)κ°€ μ‹€ν–‰ν•˜κ³ μž ν•˜λŠ” μ½”λ“œ λΈ”λŸ­μ„ 보자. if-else ꡬ문을 buildEither(first:)와 buildEither(second:) λ©”μ„œλ“œμ— λŒ€ν•œ 호좜둜 λ³€ν™˜ν•œλ‹€. 즉, Monad 의 μΌμ’…μ΄λ―€λ‘œ λͺ¨λ“  데이터λ₯Ό Drawable둜 λ‹€λ£¨κ²Œ ν•˜λŠ” 것이닀. ν˜„μž¬ μ½”λ“œμ—μ„œ buildEither(first:)와 buildEither(second:)κ°€ ν•˜λŠ” 일이 λ™μΌν•˜κΈ° λ•Œλ¬Έμ— μœ„μ—μ„œ Ternary Operator λ₯Ό μ‚¬μš©ν•΄ μ²˜λ¦¬ν–ˆμ§€λ§Œ μ„œλ‘œ λ‹€λ₯Έ λ‘œμ§μ„ μΆ”κ°€ν•΄ 고유의 λ™μž‘μ„ ν•˜λ„λ‘ μ„ μ–Έν•  수 μžˆμŒμ„ μ˜λ―Έν•œλ‹€.


μ΄λ²ˆμ—λŠ” DrawingBuilder 에 buildArray(_:) λ©”μ„œλ“œλ₯Ό μΆ”κ°€ν•΄λ³΄μž.

extension DrawingBuilder {
    static func buildArray(_ components: [Drawable]) -> Drawable {
        Line(elements: components)
    }
}

이 λ©”μ„œλ“œλŠ” Drawable 데이터λ₯Ό Collection 으둜 λ§Œλ“€μ–΄ for-loopλ₯Ό μ‚¬μš© κ°€λŠ₯ν•˜κ²Œ λ§Œλ“€μ–΄μ€€λ‹€.

let manyStars = draw {
    Text("Stars:")
    for length in 1...3 {
        Space()
        Stars(length: length)
    }
}
print(manyStars.draw()) // Stars: * ** ***


ν˜„μž¬ Result Buildersλ₯Ό 톡해 μ •μ˜ν•  수 μžˆλŠ” λ©”μ„œλ“œλŠ” buildBlock(_:)κ³Ό buildEither(first:), buildEither(second:)λ₯Ό 포함해 10κ°œκ°€ μ‘΄μž¬ν•œλ‹€.

@resultBuilder
struct ArrayBuilder {
    typealias Component = [Int]
    typealias Expression = Int
    static func buildExpression(_ element: Expression) -> Component {
        return [element]
    }
    static func buildOptional(_ component: Component?) -> Component {
        guard let component = component else { return [] }
        return component
    }
    static func buildEither(first component: Component) -> Component {
        return component
    }
    static func buildEither(second component: Component) -> Component {
        return component
    }
    static func buildArray(_ components: [Component]) -> Component {
        return Array(components.joined())
    }
    static func buildBlock(_ components: Component...) -> Component {
        return Array(components.joined())
    }
    // ...
}

Result Builders λ¬Έλ²•μ˜ 전체 λ ˆνΌλŸ°μŠ€λŠ” Swift Docs Language Reference - Attributes/Result Builder λ₯Ό μ°Έκ³ ν•œλ‹€.




Reference

  1. β€œAdvanced Operators.” The Swift Programming Language Swift 5.9. accessed Oct. 14, 2023, Swift Docs Chapter 27 - Advanced Operators.
  2. β€œOperator Declarations.” Apple Developer Documentation. accessed Oct. 17, 2023, Apple Developer Documentation - Swift/Swift Standard Library/Operator Declarations.
  3. β€œLexical Structure.” The Swift Programming Language Swift 5.9. accessed Oct. 23, 2023, Swift Lexical Structure.
  4. β€œAttributes.” The Swift Programming Language Swift 5.9. accessed Oct. 24, 2023, Swift Docs Language Reference - Attributes/Result Builder.