31627


学校のプロジェクトで作っているTo Do Listアプリについて、困っています。

私が使っているのは  
Xcode Version 8.1  
Swift 2.0 です。

私はまだプログラミングを始めたばかりの初心者で、このアプリもチュートリアルを見て作っています。

こちらが私の見ているチュートリアルです:  
<https://www.youtube.com/watch?v=Md0KDy6DCAc>

また、この方のフルコードが載っているサイトがこれです：  
<https://github.com/mobilespace/ToDoList>

コードを全て書き終えてシミュレーターを起動し、To Do Itemを足そうとしたのですが、Itemをセーブしようと Save を押したらコードへ戻り、

"Thread 1: EXC_BAD_INSTRUCTION(code=EXC_1386_INVOP, subcode=0×0)"

と出てきました。

出てきたところは AddToDoItemController.swiftの

[code]

     delegate?.addToDoItemToList(toDoItemField.text!)
    
[/code]

というラインです。

デバッグコンソールには

> To Do List[55266:2935900] [MC] Reading from private effective user  
>  settings. fatal error: unexpectedly found nil while unwrapping an  
>  Optional value (lldb)

と出ていました。

bad instructionということは、きちんと指示ができていない、ということだというのは分かるのですが、何が足りないのでしょうか？  
また、何が足りないのかを調べる方法はありますか？

コードのコピーです：

AddToDoItemController.swift

[code]

    import UIKit
    
    protocol AddToDoItemControllerDelegate: class {
        func addToDoItemToList(_ text:String)
    }
    
    class AddToDoItemController: UIViewController {
    
        @IBOutlet weak var toDoItemField: UITextField!
    
        weak var delegate: AddToDoItemControllerDelegate?
    
        override func viewDidLoad() {
            super.viewDidLoad()
        }
    
        @IBAction func cancelBarButtonPressed(_ sender: AnyObject) {
            self.dismiss(animated: true, completion: nil)
        }
    
        @IBAction func saveBarButtonPressed(_ sender: AnyObject) {
            delegate?.addToDoItemToList(toDoItemField.text!)
            self.dismiss(animated: true, completion: nil)
        }
    
    }
    
[/code]

ViewController.swift

[code]

    import UIKit
    
    class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, AddToDoItemControllerDelegate {
    
        @IBOutlet weak var tableView: UITableView!
    
        var toDoList: NSMutableArray = ["Go get groceries", "Walk the dog", "Watch a movie", "Do your homework"]
    
        var completedToDoList: [Int:String] = [:]
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            tableView.delegate = self
            tableView.dataSource = self
        }
    
        override func viewWillAppear(_ animated: Bool) {
            resetAccessoryType()
            tableView.reloadData()
        }
    
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return toDoList.count 
        }
    
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: "ToDoCell", for: indexPath)
            cell.textLabel?.text = toDoList[indexPath.row] as? String
    
            return cell
        }
    
        func numberOfSections(in tableView: UITableView) -> Int {
            return 1
        }
    
        func resetAccessoryType() {
            for row in 0..<toDoList.count {
                if let cell = tableView.cellForRow(at: IndexPath(row: row, section: 0)) {
                    cell.accessoryType = .none
                }
            }
        }
    
        func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
            tableView.deselectRow(at: indexPath, animated: true)
    
            if let cell = tableView.cellForRow(at: indexPath) {
                if cell.accessoryType == .none {
                    cell.accessoryType = .checkmark
                    completedToDoList[completedToDoList.count] = toDoList[indexPath.row] as? String
                    toDoList.removeObject(at: indexPath.row)
                } else {
                    cell.accessoryType = .none
                }
            }
        }
    
        func addToDoItemToList(_ text:String) {
            toDoList[toDoList.count] = text
    
            tableView.reloadData()
        }
    
        override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
            if(segue.identifier == "AddToDoItemSegue") {
                let navigationController = segue.destination as! UINavigationController
                let addToDoItemViewController = navigationController.topViewController as! AddToDoItemController
    
                addToDoItemViewController.delegate = self
            } else if(segue.identifier == "CompletedToDoItemsSegue") {
                let completedToDoItemsController = segue.destination as! CompletedToDoItemsController
                completedToDoItemsController.completedToDoList = completedToDoList 
            }
        }
    
    }
    
[/code]

よろしくお願いします。


CC BY-SA 3.0
2017-01-05T02:19:36.437
31626
2017-01-05T03:33:54.597
2017-01-05T02:42:14.973
20099
20099
question
swift ios xcode
Thread 1: EXC_BAD_INSTRUCTION(code=EXC_1386_INVOP, subcode=0×0)　エラー


実は古いXcode(と言うかSwiftのシステム側の実行時処理)では、 **`fatal error: unexpectedly found nil
while unwrapping an Optional value`**
が発生しても、原因となる行を示してくれなかったのですが、現在は改善されているので、調べなければいけないのは、エラー表示のあった行:

[code]

    delegate?.addToDoItemToList(toDoItemField.text!)
    
[/code]

だけに絞り込んでいいでしょう。

上記のエラーが出るケース(双方とも内部的には同じことですが)は大きく2つあります。

  * 強制アンラップ(後置の`!`)を使用している箇所でアンラップされる値が`nil`である
  * `ImplicitlyUnwrappedOptional`型の値が`nil`なのに、それを`nil`が許されない場所で使っている

該当の行にはもろに後置の`!`が使われていますし`toDoItemField`は`UITextField!`型、つまり`ImplicitlyUnwrappedOptional<UITextField>`型ですから、どちらにも該当していると言うことになります。

ちなみにOptionalだとか`nil`だとか「アンラップ」「強制アンラップ」とかの概念はSwift言語学習の肝と言ってもいい部分の一つなので、まだ十分理解できていない場合には、解説記事などを探し出してしっかり学習しておいてください。

* * *

まずはコードでもろに見えている`toDoItemField.text!`の部分です。

(ちなみに`!`は「私のアプリをクラッシュさせて演算子」(Crash me
operator)と通称されています。可能なかぎり使わないようにしないと予期せぬところでアプリがクラッシュします。)

この式は、もし`toDoItemField.text`の値が`nil`だと上記のメッセージを出してアプリがクラッシュしてしまうのですが、「初期値は`nil`ではなく、空文字列(`""`)」「Saveをタップする前に何か文字を入力していれば`nil`にはならない」と言う2点から今回はおそらく原因ではないでしょう。

ただ、もう少し確実に安全なコードにしたいなら、

[code]

    delegate?.addToDoItemToList(toDoItemField.text ?? "")
    
[/code]

と書く手もあります。`??`はOptional-
bindingとかに比べると紹介している記事が少ないように思いますが、`??`の左辺が`nil`の場合には右辺の値で置き換えてくれる、と言う演算子です。

* * *

さて、残るは`UITextField!`型である`toDoItemField`の値が`nil`である、と言う可能性です。`@IBOutlet`にするプロパティは通常`ImplicitlyUnwrappedOptional`型にするのですが、それが`nil`のままで実際のUI部品につながらない場合がいくつか考えられます。

  1. StoryboardでのConnectionの張り間違い
  2. ViewControllerをstoryboardからインスタンス化せずコンストラクタを直接呼び出した場合

あなたの場合、2.のようなコードは書いておらず、みなstoryboardで設定しているようですから調べるべきは1.の方ということになります。

* * *

と言うわけで、またstoryboardの設定を確認してみたほうがよさそうです。

Storyboardエディターで`AddToDoItemController`内の`UITextField`を選択し、Inspector等で接続を確認してみてください。

[![Outlet接続確認](https://i.stack.imgur.com/9XvTb.png)](https://i.stack.imgur.com/9XvTb.png)

残念ながら今のXcodeでは、storyboardで接続した後でソースコードの方を編集すると、見かけ上は接続されているように見えるのに実際はうまくいかないということがあるようなので、一度
**×** を押して削除した後でもう一度接続しなおしたほうが確実かもしれません。

* * *

いかがでしょうか。これでは上手く動くようにできないという場合には、例によってコメント等でお知らせください。

ちなみにあなたのご質問中で「Swift 2.0
です。」と記載されていますが、「リンク先のGitHub内のコードはSwift2で書かれている」のですが、あなたが質問本文中に記載されたコードはSwift3用になっています。


CC BY-SA 3.0
2017-01-05T03:33:54.597
31627
2017-01-05T03:33:54.597
13972
31626
answer