質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.35%

  • Swift

    9414questions

    Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Array index out of rangeの解決。

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 1,467

UsagiPerry

score 9

前提・実現したいこと

swiftでクイズアプリを作成しています。各問題では選択肢ボタンを押すと次の問題に移行するシステムにしています。時間制も設けており同じく制限時間を過ぎると次の問題に移行します。
問題なのが、3,4問(タイミングはランダム)を過ぎてから、ボタンを押すor制限時間を越えるとアプリが落ちてしまいます。

発生している問題・エラーメッセージ

 fatal error: Array index out of range

既存のアプリを参考に作成しており、それとの変更点は2つあります

①クイズの画面でされるtextviewを1→6つに増やしている。
csvファイルのテキストデータは、","ごとに設問、正解番号、選択肢の順に設定しているのですが

csvファイル変更前

一般的に胃腸の働きを弱めてしまう効果があるので、「天ぷら」と食べ合わせが良くないとされている食べ物は次のうちどれか?,1,スイカ,梅干し,ゆで卵,ソーセージ


上記の設問に当たるところを5つ増やし、連想クイズのようなものにしています

csvファイル変更後

ゴリラ,りんご,黄色,朝ごはん,皮,スリップ,2,卵焼き,バナナ,鶏肉,雨


csvファイルの","で区切られた要素はtargetProblemという配列に格納されていたため、その配列に新しく作ったtextviewを5つ、targetProblem[1]〜[5]として宣言し、解答番号と選択肢のインデックスはそれぞれ5ずつ後ろにずらしました

該当のソースコード

@IBOutlet var keyword: UITextView!   //textviewを5つ分増やす
@IBOutlet var keyword2: UITextView!
@IBOutlet var keyword3: UITextView!
@IBOutlet var keyword4: UITextView!
@IBOutlet var keyword5: UITextView!
@IBOutlet var keyword6: UITextView!

keyword.text = targetProblem[0] as! String  //targetProblem[1]〜[5]にkeyword2〜6.text
keyword2.text = targetProblem[1] as! String
keyword3.text = targetProblem[2] as! String
keyword4.text = targetProblem[3] as! String
keyword5.text = targetProblem[4] as! String
keyword6.text = targetProblem[5] as! String


//選択肢であるtargetProblem[2,3,4,5]→[7,8,9,10]
answerButtonOne.setTitle("1." + String(describing: targetProblem[7]), for: UIControlState())
answerButtonTwo.setTitle("2." + String(describing: targetProblem[8]), for: UIControlState())
answerButtonThree.setTitle("3." + String(describing: targetProblem[9]), for: UIControlState())
answerButtonFour.setTitle("4." + String(describing: targetProblem[10]), for: UIControlState())

バグの原因

Thread1にはkeyword2.text = targetProblem[1] as! StringがEXC_BREAKPOINTだと述べてありましたが、なぜこの宣言が問題なのかわかりません。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • Bongo

    2017/05/27 02:55

    Array index out of rangeとなると、targetProblemにデータを格納する部分のコードがどうなっているか気になります。また、確認しますが、最初のうちは選択肢ボタンを押したり制限時間を超えたりすれば正しく次の問題に移るが、たとえ同じ問題CSVで同じように実行しても数問過ぎるとクラッシュし、しかもクラッシュするまでの問題数もバラバラ、ということでよろしいでしょうか。

    キャンセル

回答 2

checkベストアンサー

+1

さきほど問題格納部分が気になると申し上げましたが、参考とされたのはこちらの方の製作例でしょうか?
もしそうであれば、この場合setProblemsFromCSVが格納部分のようですが、問題格納後にproblemArrayをシャッフルしております。ということは、もしCSV内に不正な行がある場合、ランダムなタイミングでその問題に到達した際にクラッシュする、ということもありそうに思われました。
problemArrayにCSVからデータを取り込み終えたら、problemArrayの内容を確認してみるといいかもしれません。

[「shutffleなしでは全問題表示できたため、csvファイルには不正な行はない」とのご見解について追記]
文章だけで意図を伝えるのはなかなか難しいですね...
データ自体は全問分だけありますので、全問表示できるのは確かでしょう。私がご確認いただきたかったのは、CSVファイルをXcodeで開いた時に

こうなるはずが

こうなるはずが

こうなって

こうなってしまっていないかということなんです...

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/05/31 17:14

    返信ありがとうございます。
    " CSVからの読み込み部分の「let parts = line.components(separatedBy: ",")」と「problemArray.add(parts)」の間に「if (parts.count != 11) continue」を入れてみるとか... "

    →実装すると、問題がシャッフルした状態で出題されました。ありがとうございます。

    相変わらず1回目のビルドでは1問目も開けませんが。
    QuizControllerの
    var csvData: String = try String(contentsOfFile: csvBundle!, encoding: String.Encoding.utf8)
    がbreakpointとなっています。コンソール画面にはArray index out of rangeのようなクラッシュのメッセージはなくただ 11db と表示された状態です。
    何か手立てはないでしょうか?(質問しっぱなしでたいへん申し訳ありません)

    個人的にはcsvファイルを、mBaaSというアプリのデータ管理を安全に行ってくれる外部のサービスに置いて、そこから取得しようかと考えています。(おそらく効果はないでしょうが)

    キャンセル

  • 2017/05/31 18:01

    念のため確認しますが、var csvData: String = try String(contentsOfFile: csvBundle!, encoding: String.Encoding.utf8)の行にブレークポイントは設定されていたりしますか?Xcodeのエディタ画面の行の左端に、青いマーカーのようなものがあるでしょうか。

    キャンセル

  • 2017/05/31 22:31

    返信ありがとうございます。
    ご指摘の通り、breakpointが設定されていました。breakpointとは、クラッシュの原因となる行を表しているものと勝手に勘違いしておりました。

    shutffleありでの実装が、最初のビルドでできるようになりました。初心者のわかりづらい質問にここまでお付き合いいただき、ありがとうございました。

    キャンセル

+1

まず、fatal error: Array index out of rangeとは何か、から。

let array = [0, 1, 2, 3] // 要素数は4

let e = array[8]  // 9番目の要素にアクセス ... (a)

(a)の箇所でfatal error: Array index out of rangeが発生します。
(要素数 - 1)を超えたインデックスを指定しているためです。

つまり、あなたの場合ですと targetProblem の要素数が 1 のためクラッシュしているということです。

対処療法的ではありますが、

// 要素数が11であるかをチェック
if targetProblem.count != 11 {
   // エラー処理
}


のような処理をどこかに入れてください。


---余計な話---
上の回答は対処療法でしかありません。
問題を [Any] のような形で保持せず

struct Quiz {
  let hints: [String]
  let choices: [String]
  let answer: Int

  init?(hints: [String], choices: [String], answer: Int) {
    guard hints.count == 6,
      choices.count == 4,
      0..<5 ~= answer
      else { return nil }
    self.hints = hints
    self.choices = choices
    self.answer = answer
  }
}


のような専用の型を用意して安全に利用できるようにすべきです。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/05/27 18:37

    丁寧にありがとうございます。
    問題番号を扱う、配列problemArrayの引数をシャッフルする操作をコメントアウトしたところ一時的にArray index out of rangeの問題は解決しました。しかしおっしゃる通りguardを使った方が別のクラッシュも起きづらいので、そうさせていただきます。

    ```
    do {

    //CSVデータを読み込む
    var csvData: String = try String(contentsOfFile: csvBundle!, encoding: String.Encoding.utf8)

    csvData = csvData.replacingOccurrences(of: "\r", with: "")

    //改行を基準にしてデータを分割する読み込む
    let csvArray = csvData.components(separatedBy: "\n")

    //CSVデータの行数分ループさせる
    for line in csvArray {

    //カンマ区切りの1行を["aaa", "bbb", ... , "zzz"]形式に変換して代入する
    let parts = line.components(separatedBy: ",")
    problemArray.add(parts)
    }

    //配列を引数分の要素をランダムにシャッフルする(※Extension.swift参照)
    problemArray.shuffle(self.problemArray.count)

    } catch let error as NSError {
    print(error.localizedDescription)
    }

    }

    ```
    上のコードはcsvファイル(改行が問題番号を次に進ませるサイン)から問題番号を取得したproblemArrayに関するものです。
    先述した通り problemArray.shuffle(self.problemArray.count)を行うと問題の途中でArray index out of rangeが原因のクラッシュが起きてしまうのですが、原因についてわかることがあれば教えていただけないでしょうか。
    shutffleなしでは全問題表示できたため、csvファイルには不正な行はないと考えております。

    キャンセル

  • 2017/05/27 20:58

    回答の後半部分は無視してください

    僕以外の人が何がわからないのかが僕にはわかりません。
    僕の回答のどこがわからなかったか教えてもらえませんか?

    fatal error: Array index out of range
    がどのような時に発生するのかはわかりましたか?

    if targetProblem.count != 11 {
    を使うとどのような問題が解決されるのかはわかりましたか?

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.35%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • Swift

    9414questions

    Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています