비트 연산자를 사용해 데이터 구조 내에서 개별 원시 데이터 비트를 조작할 수 있음
~
) : 숫자의 모든 비트 반전let initialBits: UInt8 = 0b00001111
let invertedBits = ~initialBits // equals 11110000
// 접두사, 공백x
&
) : 두 숫자의 비트를 결합, 둘 다 1이면 1let firstSixBits: UInt8 = 0b11111100
let lastSixBits: UInt8 = 0b00111111
let middleFourBits = firstSixBits & lastSixBits // equals 00111100
// 교집합. 둘 다 1이면 1
|
) : 두 숫자의 비트를 비교, 하나라도 1이면 1let someBits: UInt8 = 0b10110010
let moreBits: UInt8 = 0b01011110
let combinedbits = someBits | moreBits // equals 11111110
// 합집합. 하나라도 1이면 1
^
) : 두 숫자의 비트를 비교, 비트가 같으면 0, 다르면 1let firstBits: UInt8 = 0b00010100
let otherBits: UInt8 = 0b00000101
let outputBits = firstBits ^ otherBits // equals 00010001
<<
, >>
) : 특정 숫자만큼 왼쪽/오른쪽으로 숫자의 모든 비트 이동부호없는 정수에 대한 이동 동작
- 기존 비트는 요청된 숫자만큼 왼쪽/오른쪽으로 이동됨
- 정수의 저장소 범위를 넘어 이동된 모든 비트는 삭제됨
- 원래 비트가 왼쪽 또는 오른쪽으로 이동한 후 뒤에 남겨진 공백에 0 삽입
→ 이 접근 방식을 논리적 이동 logical shift 라고 함
let shiftBits: UInt8 = 4 // 00000100 in binary
shiftBits << 1 // 00001000 왼쪽으로 1
shiftBits << 2 // 00010000
shiftBits << 5 // 10000000
shiftBits << 6 // 00000000
shiftBits >> 2 // 00000001 오른쪽으로 2
// 다른 데이터 타입 내에서 값을 인코딩/디코딩 하기 위해 사용하기도 함
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
부호있는 정수에 대한 이동 동작
첫번째 비트 : 정수가 양수인지 음수인지 나타내는 부호 비트 sign bit
나머지 비트 : 값 비트 value bit
양수는 이진 표기법으로 작성
음수는 2의 보수 표현 two’s complement : 절대값을 2에서 값 비트의 수인 n의 제곱으로 빼서 저장
예) Int8
내부의 비트에서 숫자 -4
11111100
= -4 : 부호 비트 1, 값 비트 7개 128 - 4
인 124
장점
부호 비트를 포함하여 모든 8비트에 표준 이진 덧셈 수행이 간단함
음수 비트를 양수처럼 왼쪽/오른쪽으로 이동할 수 있음 (2배 곱하거나 나누는 효과)
값이 너무 크거자 너무 작을 때 에러 처리를 제공해야 함
사용 가능한 비트 수를 자르기 위해 특별히 오버플로우 조건을 원하는 경우
→ 3가지 산술 오버플로우 연산자 overflow operators
&+
&-
&*
// 부호 없는 정수
var unsignedOverflow = UInt8.max
// unsignedOverflow equals 255, which is the maximum value a UInt8 can hold
unsignedOverflow = unsignedOverflow &+ 1 // 오버플로우됨
// unsignedOverflow is now equal to 0
var unsignedOverflow = UInt8.min
// unsignedOverflow equals 0, which is the minimum value a UInt8 can hold
unsignedOverflow = unsignedOverflow &- 1
// unsignedOverflow is now equal to 255
// 부호 있는 정수
var signedOverflow = Int8.min // 10000000
// signedOverflow equals -128, which is the minimum value an Int8 can hold
signedOverflow = signedOverflow &- 1 // 01111111
// signedOverflow is now equal to 127
부호가 있는 정수와 부호가 없는 정수에 대해
양의 방향의 오버플로우는 최대 → 최소값으로 돌아가고 음의 방향의 오버플로우는 최소 → 최대값으로 순환
우선순위 %
, *
> +
struct Vector2D {
var x = 0.0, y = 0.0
}
extension Vector2D {
static func + (left: Vector2D, right: Vector2D) -> Vector2D {
return Vector2D(x: left.x + right.x, y: left.y + right.y)
}
}
let vector = Vector2D(x: 3.0, y: 1.0)
let anotherVector = Vector2D(x: 2.0, y: 4.0)
let combinedVector = vector + anotherVector
// combinedVector is a Vector2D instance with values of (5.0, 5.0)
extension Vector2D {
static prefix func - (vector: Vector2D) -> Vector2D {
return Vector2D(x: -vector.x, y: -vector.y)
}
}
let positive = Vector2D(x: 3.0, y: 4.0)
let negative = -positive
// negative is a Vector2D instance with values of (-3.0, -4.0)
let alsoPositive = -negative
// alsoPositive is a Vector2D instance with values of (3.0, 4.0)
extension Vector2D {
static func += (left: inout Vector2D, right: Vector2D) {
left = left + right
}
}
var original = Vector2D(x: 1.0, y: 2.0)
let vectorToAdd = Vector2D(x: 3.0, y: 4.0)
original += vectorToAdd
// original now has values of (4.0, 6.0)
extension Vector2D: Equatable {
static func == (left: Vector2D, right: Vector2D) -> Bool {
return (left.x == right.x) && (left.y == right.y)
}
}
let twoThree = Vector2D(x: 2.0, y: 3.0)
let anotherTwoThree = Vector2D(x: 2.0, y: 3.0)
if twoThree == anotherTwoThree {
print("These two vectors are equivalent.")
}
// Prints "These two vectors are equivalent."
// 새로운 연산자는 operator 키워드를 사용하여 전역으로 선언됨
prefix operator +++
extension Vector2D {
static prefix func +++ (vector: inout Vector2D) -> Vector2D {
vector += vector
return vector
}
}
var toBeDoubled = Vector2D(x: 1.0, y: 4.0)
let afterDoubling = +++toBeDoubled
// toBeDoubled now has values of (2.0, 8.0)
// afterDoubling also has values of (2.0, 8.0)
infix operator +-: AdditionPrecedence
extension Vector2D {
static func +- (left: Vector2D, right: Vector2D) -> Vector2D {
return Vector2D(x: left.x + right.x, y: left.y - right.y)
}
}
let firstVector = Vector2D(x: 1.0, y: 2.0)
let secondVector = Vector2D(x: 3.0, y: 4.0)
let plusMinusVector = firstVector +- secondVector
// plusMinusVector is a Vector2D instance with values of (4.0, -2.0)
리스트나 트리와 같은 중첩된 데이터를 자연스럽게 생성하기 위한 구문을 추가하는 타입
protocol Drawable {
func draw() -> String // draw 메소드 구현해야 함
}
struct Line: Drawable { // 각 선의 구성요소에서 draw()를 호출하고 하나의 문자열로 연결
var elements: [Drawable]
func draw() -> String {
return elements.map { $0.draw() }.joined(separator: "")
}
}
struct Text: Drawable { // 문자열을 감아 그리기의 일부로 만듦
var content: String
init(_ content: String) { self.content = content }
func draw() -> String { return content }
}
struct Space: Drawable {
func draw() -> String { return " " }
}
struct Stars: Drawable {
var length: Int
func draw() -> String { return String(repeating: "*", count: length) }
}
struct AllCaps: Drawable { // 다른 그리기를 감싸고 모든 텍스트를 대문자로 변환
var content: Drawable
func draw() -> String { return content.draw().uppercased() }
}
// 어색한 코드
let name: String? = "Ravi Patel"
let manualDrawing = Line(elements: [
Stars(length: 3),
Text("Hello"),
Space(),
AllCaps(content: Text((name ?? "World") + "!")), // 중첩된 괄호 읽기 어려움
Stars(length: 2),
])
print(manualDrawing.draw())
// Prints "***Hello RAVI PATEL!**"
// 결과 빌더 정의
@resultBuilder
struct DrawingBuilder {
// 코드 블럭에 선을 작성하기 위한 지원 추가
static func buildBlock(_ components: Drawable...) -> Drawable {
return Line(elements: components)
}
static func buildEither(first: Drawable) -> Drawable {
return first
}
static func buildEither(second: Drawable) -> Drawable {
return second
}
}
// @DrawingBuilding 을 함수의 파라미터로 적용하여 함수에 전달된 클로저를
// 결과 빌더가 해당 클로저에서 생성하는 값으로 바꿀 수 있음
func draw(@DrawingBuilder content: () -> Drawable) -> Drawable {
return content()
}
func caps(@DrawingBuilder content: () -> Drawable) -> Drawable {
return AllCaps(content: content())
}
// makeGreeting(for:) 함수는 name 파라미터를 가져와 개인화 인사말을 그리는데 사용
func makeGreeting(for name: String? = nil) -> Drawable {
let greeting = draw {
Stars(length: 3)
Text("Hello")
Space()
caps {
if let name = name {
Text(name + "!")
} else {
Text("World!")
}
}
Stars(length: 2)
}
return greeting
}
let genericGreeting = makeGreeting()
print(genericGreeting.draw())
// Prints "***Hello WORLD!**"
let personalGreeting = makeGreeting(for: "Ravi Patel")
print(personalGreeting.draw())
// Prints "***Hello RAVI PATEL!**"