Swift에서도 여러 비트 연산자를 지원한다.
비트 연산자 NOT는 비트를 뒤집는다.
let initialBits: UInt8 = 0b00001111
let invertedBits = ~initialBits
// equals 11110000
비트 연산자 AND는 두 비트가 같은 값인 경우 1 다른 경우 0으로 반환한다.
let firstSixBits: UInt8 = 0b11111100
let lastSixBits: UInt8 = 0b00111111
let middleFourBits = firstSixBits & lastSixBits
// equals 00111100
비트 연산자 OR은 두 비트 가운데 하나라도 1이면 1 값을 갖는다.
let someBits: UInt8 = 0b10110010
let moreBits: UInt8 = 0b01011110
let combinedbits = someBits | moreBits
// equals 11111110
비트 연산자 XOR은 두 비트가 다른 경우에 1, 같은 경우에 0을 갖는다.
let firstBits: UInt8 = 0b00010100
let otherBits: UInt8 = 0b00000101
let outputBits = firstBits ^ otherBits
// equals 00010001
비트를 왼쪽 혹은 오른쪽으로 미는 시프트 연산자도 지원한다.
let shiftBits: UInt8 = 4 // 00000100 in binary
shiftBits << 1 // 00001000
shiftBits << 2 // 00010000
shiftBits << 5 // 10000000
shiftBits << 6 // 00000000
shiftBits >> 2 // 00000001
아래 예제에서 pink
와 16진수와 AND 연산을 하게 되는데 여기서 FF는 윈도우 역할을 해서 AND 연산에서 FF의 위치에 있는 값만 취해 시프트 연산을 하게 된다.
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
부호가 있는 정수형에서는 최상단의 비트가 부호를 표현한다. 0일때는 양수, 1일때는 음수를 의미한다.
Int16의 값은 범위 -32768~32767 값을 갖는다. Int16의 최대값인 Int16.max에 값을 하나 더하면 Int16이 저장할 수 있는 값의 범위를 벗어나므로 에러가 발생한다.
var potentialOverflow = Int16.max
// potentialOverflow equals 32767, which is the maximum value an Int16 can hold
potentialOverflow += 1
// this causes an error
만약 값이 넘치는 오버플로우가 발생하더라도 그 뒷값을 자르고 연산을 수행하려면 다음과 같이 각 연산자 앞에 &
를 붙여 수행하면 된다.
&+
: 오버플로우 덧셈&-
: 오버플로우 뺄셈&*
: 오버플로우 곱셈값은 양수쪽 혹은 음수쪽 양쪽으로 넘칠 수 있다.
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
부호를 갖는 Int형의 경우 오버플로우 뺄셈 연산을 하면 결과는 다음과 같다.
var signedOverflow = Int8.min
// signedOverflow equals -128, which is the minimum value an Int8 can hold
signedOverflow = signedOverflow &- 1
// signedOverflow is now equal to 127
연산자 별로 우선순위가 있어서 어떤 등식의 순서대로 연산이 수행되는 것이 아니라 연산자의 우선순위 별로 연산을 수행한다.
2 + 3 % 4 * 5
// this equals 17
+, ++ 같은 연산자는 메소드로 수행되고 이 메소드를 오버라이ㅣㅇ 할 수 있다. 아래 예제는 2D Vector 구조체를 더하는 + 메소드를 선언한 예이다.
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)
vector + anotherVector
를 수행하면 + 메소드가 수행되면서 두 2차원 좌표의 x, y 값을 각각 더한다.
어떤 피 연산자 앞에 붙이는 접두 연산자를 정의할 수 있다. 아래와 같이 -를 붙이면 이후 값을 음수로 변환하도록 함수를 정의한다.
extension Vector2D {
static prefix func - (vector: Vector2D) -> Vector2D {
return Vector2D(x: -vector.x, y: -vector.y)
}
}
아래코드를 수행하면 2차원 좌표가 음수로 변한 것을 확인할 수 있다.
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)
할당 연산자를 오버라이딩 할 수 있다. 아래는 +=
연산자를 재정의한 예이다, 좌변은 in-out 파라미터로 좌변에 좌변과 우변을 더한 값을 할당한다.
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)
두 값 혹은 객체를 비교하는 등위 연산자 ==
를 오버라이딩 할 수 있다. 다음은 2차원 좌표의 등위를 비교하는 ==
연산자를 오버라이딩한 예제이다.
extension Vector2D: Equatable {
static func == (left: Vector2D, right: Vector2D) -> Bool {
return (left.x == right.x) && (left.y == right.y)
}
}
두 좌표의 x, 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."
Swift에서는 등위 연산자의 구현을 자동으로 합성해 주기도 한다. 다음의 경우 Swift에서 등위 연산에 대해 자동으로 제공해 준다.
구조체가 저장 프로퍼티만 갖는 경우 Equatable 프로토콜을 따르게 된다.
열거형이 연관 타입만 갖는 경우 Equatable 프로토콜을 따르게 된다.
열거형이 연관 타입이 없는 경우 Swift가 자동으로 제공하는 등위 연산을 이용해 3차원 좌표의 등위도 아래와 같이 비교할 수 있다. 3차원 좌표 구조체는 저장 프로퍼티만 있기 때문에 Swift에서 자동으로 등위연산을 제공해 준다.
struct Vector3D: Equatable {
var x = 0.0, y = 0.0, z = 0.0
}
let twoThreeFour = Vector3D(x: 2.0, y: 3.0, z: 4.0)
let anotherTwoThreeFour = Vector3D(x: 2.0, y: 3.0, z: 4.0)
if twoThreeFour == anotherTwoThreeFour {
print("These two vectors are also equivalent.")
}
// Prints "These two vectors are also equivalent."
기본 연산자에는 커스텀 전위 연산자 +++
를 사용할 수 있다.
prefix operator +++
이 연산은 자신의 x, y 값을 각각 2배하는 연산을 수행하도록 정의한다.
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)
정의한대로 +++
연산자가 동작하는 것을 확인할 수 있다.
다음과 같이 두 피연산자 사이에 존재하는 중위 연산자를 정의해 사용할 수 있다. x 값은 더하고 y 값은 빼는 연산으로 정의한 예제이다.
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)
+-
중위 연산이 정의한 대로 동작하는 것을 확인 할 수 있다.