Swift에 배열이 있고 범위를 벗어난 인덱스에 액세스하려고하면 예기치 않은 런타임 오류가 있습니다.
var str = ["Apple", "Banana", "Coconut"]
str[0] // "Apple"
str[3] // EXC_BAD_INSTRUCTION
그러나 Swift가 제공하는 모든 옵션 체인과 안전 을 생각 했을 것입니다.
let theIndex = 3
if let nonexistent = str[theIndex] { // Bounds check + Lookup
print(nonexistent)
...do other things with nonexistent...
}
대신에:
let theIndex = 3
if (theIndex < str.count) { // Bounds check
let nonexistent = str[theIndex] // Lookup
print(nonexistent)
...do other things with nonexistent...
}
그러나 이것은 사실이 아닙니다. 저는 ol ‘ if
문을 사용하여 색인이보다 작거나 같은지 확인해야합니다 str.count
.
내 subscript()
구현을 추가하려고 시도했지만 호출을 원래 구현으로 전달하거나 첨자 표기법을 사용하지 않고 항목 (인덱스 기반)에 액세스하는 방법을 모르겠습니다.
extension Array {
subscript(var index: Int) -> AnyObject? {
if index >= self.count {
NSLog("Womp!")
return nil
}
return ... // What?
}
}
답변
Alex의 답변 에는 질문에 대한 좋은 조언과 해결책이 있지만이 기능을 구현하는 더 좋은 방법을 발견했습니다.
스위프트 3.2 이상
extension Collection {
/// Returns the element at the specified index if it is within bounds, otherwise nil.
subscript (safe index: Index) -> Element? {
return indices.contains(index) ? self[index] : nil
}
}
스위프트 3.0 및 3.1
extension Collection where Indices.Iterator.Element == Index {
/// Returns the element at the specified index if it is within bounds, otherwise nil.
subscript (safe index: Index) -> Generator.Element? {
return indices.contains(index) ? self[index] : nil
}
}
Swift 3 용 솔루션을 개발 한 Hamish에게 감사의 뜻을 전 합니다.
스위프트 2
extension CollectionType {
/// Returns the element at the specified index if it is within bounds, otherwise nil.
subscript (safe index: Index) -> Generator.Element? {
return indices.contains(index) ? self[index] : nil
}
}
예
let array = [1, 2, 3]
for index in -20...20 {
if let item = array[safe: index] {
print(item)
}
}
답변
이 동작을 정말로 원한다면 배열 대신 사전을 원하는 것처럼 냄새가납니다. 사전 반환 nil
에 접근하는 열쇠가 어디에 배열의 키, 무엇이든 될 수 있기 때문에이 키가 사전에 존재 알고 더 힘들어 때문에 의미가있는 키가없는 경우 필수 :의 범위 0
에 count
. 그리고 그것은 당신이 할 수있는 범위, 반복하는 매우 일반적입니다 확신 루프의 각 반복에 실제 값을 가지고 있습니다.
이 방법으로 작동하지 않는 이유는 Swift 개발자가 선택한 디자인 선택이라고 생각합니다. 예를 들어 보자.
var fruits: [String] = ["Apple", "Banana", "Coconut"]
var str: String = "I ate a \( fruits[0] )"
배열을 사용하는 대부분의 경우처럼 인덱스가 존재한다는 것을 이미 알고 있다면이 코드가 좋습니다. 첨자에 액세스하는 가능성이 반환 할 경우, nil
당신은 반환 형식 변경 의 Array
의 subscript
옵션으로 방법을. 코드가 다음과 같이 변경됩니다.
var fruits: [String] = ["Apple", "Banana", "Coconut"]
var str: String = "I ate a \( fruits[0]! )"
// ^ Added
즉, 범위를 벗어난 인덱스에 거의 액세스 할 수 없기 때문에 배열을 반복하거나 알려진 인덱스로 다른 작업을 수행 할 때마다 선택 사항을 래핑 해제해야합니다. Swift 설계자는 범위를 벗어난 인덱스에 액세스 할 때 런타임 예외를 희생하면서 옵션의 언 래핑을 줄 이도록 선택했습니다. 그리고 nil
데이터 어딘가에서 예상하지 못한 논리 오류보다 충돌이 바람직합니다 .
그리고 나는 그들에 동의합니다. 따라서 Array
선택 사항이 아닌 값을 기대하는 모든 코드를 배열에서 분리하므로 기본 구현을 변경하지 않습니다 .
대신에 서브 클래스 Array
를 작성하고 subscript
옵션을 리턴하도록 대체 할 수 있습니다 . 또는 더 실제로 Array
는이를 수행하는 비 첨자 방법으로 확장 할 수 있습니다 .
extension Array {
// Safely lookup an index that might be out of bounds,
// returning nil if it does not exist
func get(index: Int) -> T? {
if 0 <= index && index < count {
return self[index]
} else {
return nil
}
}
}
var fruits: [String] = ["Apple", "Banana", "Coconut"]
if let fruit = fruits.get(1) {
print("I ate a \( fruit )")
// I ate a Banana
}
if let fruit = fruits.get(3) {
print("I ate a \( fruit )")
// never runs, get returned nil
}
스위프트 3 업데이트
func get(index: Int) ->
T?
로 교체해야합니다 func get(index: Int) ->
Element?
답변
Nikita Kukushkin의 답변을 바탕으로 때로는 때로는 색인뿐만 아니라 배열 색인에 안전하게 할당해야합니다.
myArray[safe: badIndex] = newValue
다음은 safe : 매개 변수 이름을 추가하여 가변 배열 인덱스에 안전하게 쓸 수있는 Nikita의 답변 (Swift 3.2)에 대한 업데이트입니다.
extension Collection {
/// Returns the element at the specified index iff it is within bounds, otherwise nil.
subscript(safe index: Index) -> Element? {
return indices.contains(index) ? self[ index] : nil
}
}
extension MutableCollection {
subscript(safe index: Index) -> Element? {
get {
return indices.contains(index) ? self[ index] : nil
}
set(newValue) {
if let newValue = newValue, indices.contains(index) {
self[ index] = newValue
}
}
}
}
답변
스위프트 2에서 유효
이것은 이미 시간을 많이 대답되었지만, 나는 피 각질의 words¹에있는 스위프트 프로그래밍의 패션이 어디로 가는지에 라인에서 더 많은 답을 제시하고 싶습니다 : “생각해 protocol
최초의”
• 우리는 무엇을하고 싶은가?
– 안전 할 때만 주어진 인덱스 의 요소를 가져 Array
옵니다. nil
그렇지 않으면
•이 기능은 무엇을 구현해야합니까?
– Array
subscript
보내고
그것은 어디에서이 기능을 얻을 않습니다 •?
– 모듈 의 정의는 다음 struct Array
과 같습니다.Swift
• 더 일반적인 / 추상적 인 것은 없습니까?
– 그것은 또한 protocol CollectionType
그것을 보장합니다 채택
• 더 일반적인 / 추상적 인 것은 없습니까?
– 그것은 또한 채택 protocol Indexable
한다 …
그렇습니다, 우리가 할 수있는 최선의 소리. 그런 다음 원하는 기능을 갖도록 확장 할 수 있습니까?
– 그러나 우리는 매우 유형 (없음 제한 Int
없음 ()와 속성을count
)와 함께 작동합니다!
• 충분할 것입니다. Swift의 stdlib는 꽤 잘 수행됩니다.)
extension Indexable {
public subscript(safe safeIndex: Index) -> _Element? {
return safeIndex.distanceTo(endIndex) > 0 ? self[safeIndex] : nil
}
}
¹ : 사실이 아니지만 아이디어를 제공합니다.
답변
extension Array {
subscript (safe index: Index) -> Element? {
return 0 <= index && index < count ? self[index] : nil
}
}
- O (1) 성능
- 안전한 타입
- [MyType?]에 대한 Optionals를 올바르게 처리합니다 (MyType ??을 반환합니다. 두 수준에서 모두 줄 바꿈 할 수 없음).
- 세트에 문제를 일으키지 않습니다
- 간결한 코드
다음은 내가 당신을 위해 실행 한 테스트입니다.
let itms: [Int?] = [0, nil]
let a = itms[safe: 0] // 0 : Int??
a ?? 5 // 0 : Int?
let b = itms[safe: 1] // nil : Int??
b ?? 5 // nil : Int?
let c = itms[safe: 2] // nil : Int??
c ?? 5 // 5 : Int?
답변
- 배열은 nil 값을 저장할 수 있기 때문에 array [index] 호출이 범위를 벗어난 경우 nil을 반환하는 것은 의미가 없습니다.
- 사용자가 범위를 벗어난 문제를 처리하는 방법을 모르기 때문에 사용자 지정 연산자를 사용하는 것은 적합하지 않습니다.
- 반대로, 포장 풀기 대상물에는 기존의 제어 흐름을 사용하고 유형 안전을 보장하십시오.
index = array.checkIndexForSafety (index : Int) 인 경우
let item = array[safeIndex: index]
index = array.checkIndexForSafety (index : Int) 인 경우
array[safeIndex: safeIndex] = myObject
extension Array {
@warn_unused_result public func checkIndexForSafety(index: Int) -> SafeIndex? {
if indices.contains(index) {
// wrap index number in object, so can ensure type safety
return SafeIndex(indexNumber: index)
} else {
return nil
}
}
subscript(index:SafeIndex) -> Element {
get {
return self[index.indexNumber]
}
set {
self[index.indexNumber] = newValue
}
}
// second version of same subscript, but with different method signature, allowing user to highlight using safe index
subscript(safeIndex index:SafeIndex) -> Element {
get {
return self[index.indexNumber]
}
set {
self[index.indexNumber] = newValue
}
}
}
public class SafeIndex {
var indexNumber:Int
init(indexNumber:Int){
self.indexNumber = indexNumber
}
}
답변
스위프트 4
보다 전통적인 구문을 선호하는 사람들을위한 확장 :
extension Array {
func item(at index: Int) -> Element? {
return indices.contains(index) ? self[index] : nil
}
}