[Swift] Advanced Operators
Bitwise Operators : 비트
데이터 구조 내에서 데이터를 비트 단위로 조작할 수 있는 연산자
- 큰 데이터를 사용하는 그래픽, 영상 데이터를 조작 할때 유용하다.
- 장치 드라이버 생성과 같은 낮은 수준의 프로그래밍에도 활용이 가능하다.
- 사용자 지정 프로토콜을 통한 통신을 위한 인코딩 및 디코딩 데이터와 같은 외부 소스의 원시 데이터로 작업할 때도 유용할 수 있다.
Bitwise NOT Operator
~A
: A 의 비트를 반전한 결과를 반환한다.- 숫자의 모든 비트를 반전한 결과를 반환한다.
let initialBits: UInt8 = 0b00001111
let invertedBits = ~initialBits // equals 11110000
Bitwise AND Operator
A & B
: A 와 B 의 비트 AND 논리 연산을 실행한다.- 두 숫자의 비트를 결합한다. 비트가 두 입력 번호 모두에서
1과 같은 경우에만 비트가 1로 설정된 새 숫자를 반환한다.
let firstSixBits: UInt8 = 0b11111100
let lastSixBits: UInt8 = 0b00111111
let middleFourBits = firstSixBits & lastSixBits
// equals 00111100
firstSixBits
와lastSixBits
의 값은 모두 1과 같은 네 개의 중간 비트를 가지고 있다.- 비트 AND 연산자는 그것들을 결합하여 같은 숫자 00111100을 만든다.
Bitwise OR Operator
A|B
: A 와 B 의 비트 OR 논리 연산을 실행한다.- 두 숫자의 비트를 비교한다.
- 비트가 입력 번호에서 1과 같으면 비트가 1로 설정된 새 숫자를 반환한다.
let someBits: UInt8 = 0b10110010
let moreBits: UInt8 = 0b01011110
let combinedbits = someBits | moreBits
// equals 11111110
someBits
와moreBits
의 값은 다른 비트를 1로 설정한다.- 비트 OR 연산자는 그것들을 결합하여 같은 숫자 11111110을 만든다.
Bitwise XOR Operator = Exclusive OR operator
A ^ B
: A 와 B 의 비트 XOR 논리 연산을 실행한다.- 두 숫자의 비트를 비교하지만, 비트가 1로 설정된 새로운 숫자를 반환하며 입력 비트는 다른 0으로 설정되며 입력 비트는 동일하다.
let firstBits: UInt8 = 0b00010100
let otherBits: UInt8 = 0b00000101
let outputBits = firstBits ^ otherBits
// equals 00010001
firstBits
와otherBits
의 값은 각각 다른 비트가 그렇지 않은 위치에서 1로 설정된다.- 비트 XOR 연산자는 이 두 비트를 출력 값에서 1로 설정한다.
firstBits
와otherBits
의 다른 모든 비트는 출력 값에서 0으로 설정된다.
Bitwise Left and Right Shift Operators
A >> B
A << B
: A 의 비트를 B 만큼 비트를 이동한다.- 정의된 규칙에 따라 숫자의 모든 비트를 특정 수의 장소에 의해 왼쪽이나 오른쪽으로 이동한다.
- 좌우 이동은 정수를 2배로 곱하거나 나누는 효과가 있다.
- 정수의 비트를 한 위치로 왼쪽으로 이동하면 값이 두 배가 되는 반면, 한 위치로 오른쪽으로 이동하면 값이 절반으로 줄어든다.
Shifting Behavior for Unsigned Integers : 서명되지 않은 동작의 이동
- 부호 없는 정수에 대한 비트 이동 동작은 다음과 같다.
- 기존 비트는 요청된 장소 수에 따라 왼쪽이나 오른쪽으로 이동한다.
- 정수 저장소의 경계를 넘어 움직이는 모든 비트는 폐기된다.
- 0은 원래 비트가 왼쪽이나 오른쪽으로 이동한 후 뒤에 있는 공간에 삽입된다.
11111111 << 1
(1111111111이 왼쪽으로 이동)과11111111 >> 1
(11111111이 오른쪽으로 이동)의 결과를 보여준다.- 파란색 숫자는 이동되고, 회색 숫자는 버려지고, 분홍색 0이 삽입된다.
let shiftBits: UInt8 = 4 // 00000100 in binary
shiftBits << 1 // 00001000
shiftBits << 2 // 00010000
shiftBits << 5 // 10000000
shiftBits << 6 // 00000000
shiftBits >> 2 // 00000001
Shifting Behavior for Signed Integers : 서명 된 동작의 이동
- 이동 동작은 부호 있는 정수가 이진수로 표현되는 방식 때문에 부호 없는 정수보다
서명된 정수에 더 복잡하다. - 서명된 정수는 정수가 양수인지 음수인지 나타내기 위해
첫 번째 비트(기호 비트라고도 함)를 사용한다. - 0의 기호 비트는 양수, 1의 기호 비트는 음수임을 의미합니다.
- 나머지 비트(값 비트라고도 함)는 실제 값을 저장한다.
양수는 부호 없는 정수와 정확히 같은 방식으로 저장되며,0
에서 위쪽으로 계산된다.
- 음수 비트를 양수처럼 왼쪽과 오른쪽으로 이동할 수 있으며, 왼쪽으로의 모든 교대마다 두 배로 늘리거나 오른쪽으로 전환할 때마다 절반으로 줄일 수 있다.
- 이를 달성하기 위해, 서명된 정수가 오른쪽으로 이동할 때 추가 규칙이 사용된다. 서명된 정수를 오른쪽으로 이동할 때, 부호 없는 정수와 동일한 규칙을 적용하지만,
왼쪽의 빈 비트를 0이 아닌 기호 비트로 채워야한다.
- 양수와 음수가 저장되는 특별한 방식 때문에, 둘 중 하나를 오른쪽으로 이동하면 0에 가까워진다.
이 교대 동안 기호 비트를 동일하게 유지한다는 것은 값이 0에 가까워지면서 음수 정수가 음수로 유지된다는 것을 의미한다.
Overflow Operators = Underflow
overflow 연산자를 이용하면 overflow를 자동으로 처리할 수 있다.
- 그 값을 보유할 수 없는 정수 상수나 변수에 숫자를 삽입하려고 하면,
기본적으로 Swift는 잘못된 값을 생성하기보다는 오류를 보고한다. overflow
는 너무 크거나 너무 작은 숫자로 작업할 때 추가적인 안전을 제공한다. 경계 값 조건을 코딩할 때 훨씬 더 많은 유연성을 가질 수 있다.
var potentialOverflow = Int16.max
// potentialOverflow equals 32767, which is the maximum value an Int16 can hold
potentialOverflow += 1
// this causes an error
-32768
과32767
사이의 서명된 정수를 저장할 수 있다.
이 범위를 벗어난 숫자로 설정하려고 하면 오류가 발생한다.- Swift는 정수 계산을 위한 오버플로 동작을 선택하는 세 개의 산술 오버플로 연산자를 제공한다.
- 오버플로 추가 `(&+)
- 오버플로 뺄셈
(&-)
- 오버플로 곱셈
(&*)
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
unsignedOverflow
변수는 UInt8이 보유할 수 있는 최대 값(255 또는 11111111)으로 초기화된다.- 그런 다음 추가 연산자
(&+)
를 사용하여 1씩 증가한다.
이것은 UInt8이 보유할 수 있는 크기 바로 위에 이진 표현을 밀어내어 아래 다이어그램과 같이 경계를 넘어 넘치게 된다. - 오버플로 추가 후 UInt8의 범위 내에 남아있는 값은
00000000
또는0
이 된다.
- 부호 없는 정수가 음수 방향으로
overflow
할 수 있을 때 비슷한 일이 일어난다.
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
- UInt8이 보유할 수 있는 최소값은
0
또는 바이너리로00000000
이다. - 뺄셈 연산자
(&-)
를 사용하여00000000
에서 1을 빼면,
숫자가 넘쳐나고11111111
또는 소수점255
가 된다.
overflow
는 부호 있는 정수에 대해서도 발생한다.- 부호 있는 정수에 대한 모든 덧셈과 뺄셈은 비트 방식으로 수행되며,
부호 비트를 더하거나 빼는 숫자의 일부로 포함되어 있다.
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
- Int8이 보유할 수 있는 최소값은
-128
또는10000000
이다. overflow
로 1을 빼면01111111
의 값이 주어지며,
이는 기호 비트를 전환하고 Int8이 보유할 수 있는 최대 양의 값인
127
을 제공한다.
- 부호 있는 정수와 부호 없는 정수 모두에 대해,
양수 방향의 오버플로우는 최대 유효한 정수 값에서최소값
으로 감싸고, 음수 방향의 오버플로우는최소값
에서최대값
으로 감싼다.
var unsignedInteger: UInt8 = 0
let errorUnderflowResult: UInt8 = unsignedInteger - 1
// 런타임 오류
let underflowedValue: UInt8 = unsignedInteger &- 1
// 255
unsignedInteger = UInt8.max // 255
let errorOverflowresult: UInt8 = unsignedInteger + 1
// 런타임 오류
let overflowedValue: UInt8 = unsignedInteger & +1
// 0
Precedence and Associativity : 우선순위와 연관성
Swift
는 연산자 우선순위(Precedence)를 지정해놓았기 때문에
코딩하다가 헷갈리는 경우 확인하면 된다.- 우선순위가 높은 연산자는 낮은 연산자보다 먼저 실행된다.
- Swift에서 C와 마찬가지로 나머지 연산자
(%)
와 곱셈 연산자(*)
는
덧셈 연산자(+)
보다 우선 순위가 높다. 나머지와 곱셈은 서로 같은 우선순위를 가지고 있다. - 사용할 정확한 평가 순서를 작성하려면, 그들의 연관성을 고려해야한다.
나머지와 곱셈은 모두 왼쪽의 표현과 관련이 있다. - 프로그래머가 임의로 정의하는
사용자 정의 연산자(Custom Operators)
도 이 규칙에 따른다.
- 연관성 즉, 결합방향(Associativity)은 같은 우선순위에 있는 연산자끼리 나열되었을 때 어느 방향부터 그룹지을 것인지 나타낸다.
1+2+3+4 ➡️ (((1+2)+3)+4)
- 연산자
+
은 모두 같은 우선도이며 결합방향은 왼쪽이기때문에
(((1+2)+3)+4)
로 왼쪽부터 그룹이 묶이며.1+2
가 가장 먼저 연산이 되어3
이 되고
그 결과가 다시4
와 연산이 된다. - 만약 결합방향이 오른쪽이라면
(1+(2+(3+4)))
처럼 묶여서 연산이 된다.
Custom Operators : 사용자 정의
Swift
에서 제공하는 표준 연산자 외에도 자신만의 사용자 지정 연산자를 선언하고 구현할 수 있다.- 기존에 존재하지 않던 연산자 기호를 만들어 추가할 수 있다.
- BUT 할당 연산자
(=)
와 삼항 연산자(?:)
는 사용자 정의 역할을 부여할 수 없다. - 사용자 정의 연산자는 아스키(ASCH)문자
/, =, -, +, !, *, %, <,>,&, ^, ?, ~
를 결합해서 사용한다. - 마침표
(.)
는 연산자를 표현하는 문자 중 맨 처음의 문자가 마침표(.+.
)일때만 연산자에 포함된 마침표가 연산자로 인식된다. - 전위연산자는 물음표
(?)
, 느낌표(!)
로 시작하는 사용자 정의 연산자로 정의할 수 없다.
Prefix : 전위연산자
prefix
: 전위연산자!A
연산자가 피연산자 앞에 위치하는 연산자. 대표적인 예로 부울부정논리연산(NOT)
, 연산자(!)
가 있다.
- 기존에 없던 전위 연산자를 만들고 싶다면 연산자 정의를 먼저 해주어야한다.
정의한다
: '이제 이 연산자를 사용하겠다'는 뜻.- 전위연산자 함수를 구현할때는 함수
func
키워드 앞에prefix
키워드를 추가해준다.prefix operator ** prefix func ** (value: Int) -> Int { return value * value } let minusFive: Int = -5 let sqrtMinusFive: Int = **minusFive print(sqrtMinuFive) // 25
- 전위연산자에 기능을 추가할 때는 함수만 중복정의(
**
)하면 된다. - 느낌표
(!)
를 문자열 앞에 사용하게 되면 문자열이 비어있는지 확인하는
연산자로 사용하기 위해 함수를 중복 정의해준다.
prefix func ! (value: String) -> Bool {
return value.isEmpty
}
var stringValue: String = "yagom"
var isEmptyString : Bool = !StringValue
print(isEmptyString) // false
stringValue = ""
isEmptyString = !stringValue
print(isEmptyString) // true
-
**
전위 연산자를String
타입에서도 동작할 수 있도록 중복정의할 수 있다.prefix operator ** prefix func ** (value: String -> String { return value + " " + value } let resultString: String = **"yagom" print(resultString) // yagom yagom
Infix : 중위연산자
Infix
:중위연산자A+B
피연산자 사이에 위치하는 연산자.
많은 수의 연산자가 여기 속하며 우선순위 그룹을 명시해줄 수 있다.
- 연산자 우선그룹 그룹은 중위 연산자에서만 사용된다.
- 전위 연산자 및 후위 연산자는 결합방향 및 우선순위를 지정하지 않는다.
- 연산자 우선순위 그룹은
precedenegroup
뒤에 그룹이름을 써주어 정의할 수 있다. - 만약 우선순위 그룹을 명시해주지 않으면
우선순위가 가장 높은Defaultprecedenegroup
그룹을 우선순위 그룹으로 갖게된다.
precedenegroup 우선순위 그룹 이름 {
higherthan: 더 낮은 우선순위 그룹 이름
lowerThan: 더 높은 우선순위 그룹 이름
associativity: 결합방향(left / right / none)
assignment: 할당방향 사용(true / false)
}
higherThan
,lowerThan
: 새로 만들어 줄 우선순위 그룹과의 상화관계를 설정해 줄 수 있다.lowerThan
은 현재 정의된 우선순위 그룹만 명시할 수 있다.associativity
: 빼고 그룹을 정의하면 기본적으로none
이 설정된다.- 결합방향이 없는 연산자(
<
) 는 여러 번 연달아 사용할 수 없다. 1 > 2 > 3
연달아 사용할 수 없다는 뜻.assignment
: Optional Chaining을 포함한 연산에 있을 경우 연산자의 우선순위를 지정한다.true
일 경우 : 오른쪽부터 체이닝이 시작된다.false
혹은assignment
를 따로 명시해주지 않을 경우 :
할당을 하지 않는 연산자와 같은 Optional Chaining 규칙을 따른다.
- 문자열과 문자열 사이에
**
연산자를 사용하면 뒤에 오는 문자열이 앞의 문자열 안에
속해있는지 확인하는 연산을 실행할 수 있다.
// String 타입의 contains(_:) 메서드를 사용하기 위햐 Foundation 프레임워크를 임포트한다.
import Foundation
infix operator ** : MultiplicationPrecedence
func ** (1hs: String, rhs:String) -> Bool {
return 1hs.contains(rhs)
}
let helloYagom: String = "Hello yagom"
let yagom: String = "yagom"
let isContainYagom: Bool = helloYagom ** yagom // true
- 데이터타입(클래스, 구조체 등)에서 유용하게 사용할 수 있는 연산자도
새로 정의하거나 중복정의할 수 있음을 알 수 있다.
class Car {
var modelYear: Int? // 연식
var modelName: String? // 모델 이름
}
struct Smartphone {
var company: String? // 제조사
var model: String? // 모델
}
// Car클래스의 인스턴스끼리 == 연산했을 때 modelName이 같다면 True를 반환
func == (1hs: Car, rhs: Car) -> Bool {
return 1hs.modelName == rhs.modelName
}
// smartphone 구조체의 인스턴스끼리 == 연산했을 때 model이 같다면 True를 반환
func == (1hs: smartphone, rhs: smartphone) -> Bool {
return 1hs.model == rhs.model
}
let myCar = Car()
myCar.modelName = "S"
let yourCar = Car()
yourCar.modelName = "S"
var myPhone = SmartPhone()
myPhone.model = "SE"
var yourPhone = SmartPhone()
yourPhone.model = "6"
print(myCar == yourCar) // true
print(myPhone == yourPhone) // false
- 특정 타입에 국한된 연산자 함수라면 그 타입 내부에 구현되는 것이 읽고 이해하기에 더 쉽기에
타입 내부에 타입 메서드로 구현이 가능하다. - 타입 메서드로 구현한 사용자 정의 연산자는 각 타입의 익스텐션으로도 구현할 수 있다.
class Car {
var modelYear: Int? // 연식
var modelName: String? // 모델 이름
}
// Car클래스의 인스턴스끼리 == 연산했을 때 modelName이 같다면 True를 반환
static func == (1hs: Car, rhs: Car) -> Bool {
return 1hs.modelName == rhs.modelName
}
struct Smartphone {
var company: String? // 제조사
var model: String? // 모델
}
// smartphone 구조체의 인스턴스끼리 == 연산했을 때 model이 같다면 True를 반환
func == (1hs: smartphone, rhs: smartphone) -> Bool {
return 1hs.model == rhs.model
Postfix : 후위연산자
postfix
: 후위연산자0!
피연산자 뒤에 위치하는 연산자.
옵셔널 강제 추출 연산자 등이 있다.
- 전위 연산자와 다르지 않다.
postfix operator **
postfix func ** (value: Int) -> Int {
return value + 10
}
let five: Int = 5
let fivePlusTen: Int = five**
print(fivePlusTen) // 15
- 하나의 피연산자에 전위 연산과 후위 연산을 한 줄에 사용하게 되면 후위 연산을 먼저 수행한다.
prefix operator **
postfix operator **
prefix func ** (value: Int) -> Int {
return value * value
}
postfix func ** (value: Int) -> Int {
return value + 10
}
let five: Int = 5
let fivePlusTen: Int = **five**
print(sqrtfivePlusTen) // (10 + 5 ) * ( 10 + 5 ) == 225
기타 연산자
💡
Nil-Coalescing Operator
(nil 병합 연산자) :
a != nil ? a! : b
의 삼항 조건부 연산자를A ?? B
로 줄인 것.
간결하고 읽기 쉬운 형태로 만들 수 있다.
A가 nil이 아니면 A를 반환하고, A가 nil이면 B를 반환한다.
옵셔널을 사용할 때 아주 유용한 연산자.
let valueInt: Int = someOptionalInt != nil! ? someOptionalInt! : 0
// 위 코드와 같은 결과를 볼 수 있지만 훨씬 안전하게 옵셔널을 다룰 수 있다.
let valueInt: Int = someOptionalInt ?? 0
- 부호변경 연산자 :
-A
A(수)의 부호를 변경한다. - 옵셔널 강제 추출 연산자 :
O!
O(옵셔널 개체)의 값을 강제로 추출한다. - 옵셔널 연산자 :
V?
V(옵셔널 값)를 안전하게 추출하거나 데이터타입이 옵셔널임을 표현한다.
🔗 참고자료
[Swift] 고급 연산자 Part 1 - 비트연산자들1 Bitwise Operators1
https://blog.naver.com/badwin/221178028123
[Swift] 고급 연산자 Part 2 - 비트연산자들2 Bitwise Operators2 시프트연산 Shift Operators, 연산자 우선순위,
https://blog.naver.com/badwin/221180694420
스위프트 오버플로우 연산
https://hyerios.tistory.com/175
[iOS / Swift] Swift 문법을 알아보자! : 11편 : 옵셔널 체이닝 (Optional Chaining)
https://velog.io/@wook4506/iOS-Swift-옵셔널-체이닝-Optional-Chaining
Operator Declarations | Apple Developer Documentation
https://developer.apple.com/documentation/swift/swift_standard_library/operator_declarations
Advanced Operators — The Swift Programming Language (Swift 5.6)
https://docs.swift.org/swift-book/LanguageGuide/AdvancedOperators.html
야곰 Swift - 옵셔널 체이닝과 nil 병합 연산자
https://youtu.be/UANvklNnDeg
📕 야곰의 스위프트 프로그래밍 3판 p.120-136
Author And Source
이 문제에 관하여([Swift] Advanced Operators), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@many_anne/Swift-Advanced-Operators저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)