56123


Realmを初めて使っている者です。  
Realmで単語のデータベースを作り、その初期データを読み込んで単語クイズアプリを作っています。

書籍を参考に読み取り専用で使用する場合のスクリプトを書いてみたのですが、エラーが出てしまいました。

> Cannot use instance member 'fileURL' within property initializer; property
> initializers run before 'self' is available

自分でも調べてみたのですが理解できず、お力をお借りしたいです。  
よろしくお願いします。

なお、Xcodeのバージョンは 10.2.1 です。

* * *
[code]

    import UIKit
    import RealmSwift
    
    class ViewController: UIViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view.
    
            //デフォルトRealmnファイルのファイルURL
            let defaultFileURL = Realm.Configuration.defaultConfiguration.fileURL!
    
            //新しいデフォルトRealmファイルを生成したいため、存在する場合は削除
            if FileManager.default.fileExists(atPath: defaultFileURL.path){
                try! FileManager.default.removeItem(at: defaultFileURL)
            }
    
            let realm = try! Realm()
    
            print(Realm.Configuration.defaultConfiguration.fileURL!)
    
            let word = Word()
    
            try! realm.write{
                word.id = 1
                word.noun = "acceptance"
                word.verb = "accept"
                word.adj = "acceptable"
                word.adv = "acceptably"
    
                word.id = 2
                word.noun = "preference"
                word.verb = "prefer"
                word.adj = "preferable"
                word.adv = "preferably"
    
                realm.add(word)
            }
    
            //最適化したRealmファイルのファイルURL
            let fileURL = realm.configuration.fileURL!
            .deletingLastPathComponent()
            .appendingPathComponent("default-old.realm")
    
            //新しく最適化したRealmファイルを生成したいので存在する場合は削除
            if FileManager.default.fileExists(atPath: fileURL.path){
                try! FileManager.default.removeItem(at: fileURL)
            }
            //Realmファイルを別ファイルに保存して（コピー）してファイルサイズを最適化する
            try! realm.writeCopy(toFile: fileURL)
        }
    
        //初期データの入ったRealmファイルを利用
        let oldRealmURL = Bundle.main.url(forResource: "default-old", withExtension: "realm")!
    
        //読み取り専用で使用
        let fileURL = Bundle.main.url(forResource: "template", withExtension: "realm")!
        let config = Realm.Configuration(fileURL: fileURL, readOnly: true) //ここでエラーが発生
        let templateRealm = try! Realm(configuration: config)
    }
    
[/code]


CC BY-SA 4.0
2019-06-26T04:23:16.667
56105
2019-06-27T00:12:54.660
2019-06-27T00:12:54.660
3060
34585
question
swift xcode realm
Realm"Cannot use instance member..... "のエラーについて


Realmと言うよりSwift言語特有の問題ですね。

エラーメッセージの内容は、

> Cannot use instance member 'fileURL' within property initializer;  
>  property initializers run before 'self' is available
>
>
> プロパティ初期化の中でインスタンスメンバーの`'fileURL'`を使うことはできません。プロパティ初期化は`'self'`が利用可能になる前に呼び出されます

  * 「インスタンスメンバー」インスタンスプロパティやインスタンスメソッドのこと

  * 「`'self'`が利用可能」Swiftではクラスの初期化に厳密な制限があり、初期化が正常に終了するまで`self`を利用すること、例えば`self.fileURL`のようなインスタンスプロパティを使用することはできない。(`self.`は多くの場合省略可能ですが、省略していても使っちゃダメ。)

と言うわけで回避策はいろいろあるわけですが、あなたのコードの場合、そもそも「インスタンスプロパティ」にする必要が全然ないものを「インスタンスプロパティ」として宣言してしまっているように思われます。

以下、あなたの今のコードがどうなっているのかを示すコメントを付け加えてみました。

[code]

    class ViewController: UIViewController {
    
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view.
    
            //...
    
            //Realmファイルを別ファイルに保存して（コピー）してファイルサイズを最適化する
            try! realm.writeCopy(toFile: fileURL)
        } //<-ここで`viewDidLoad()`メソッドは終わっている
        //↓以下は全部「インスタンスプロパティの宣言」ということになる
        //初期データの入ったRealmファイルを利用
        let oldRealmURL = Bundle.main.url(forResource: "default-old", withExtension: "realm")!
    
        //読み取り専用で使用
        let fileURL = Bundle.main.url(forResource: "template", withExtension: "realm")!
        let config = Realm.Configuration(fileURL: fileURL, readOnly: true)
                     //__________________________________________________/ この部分がproperty initializer
        let templateRealm = try! Realm(configuration: config)} //<-こんなところにクラス宣言を終わらせる`}`を置いてはいけない
    
[/code]

あなたが参考にした書籍で「読み取り専用で使用する場合」のコードはインスタンスプロパティとしてではなく、何かのメソッドの中に記載されていたのではないですか?

そう言ったコードを利用する場合には、メソッド定義の中で使用しないといけません。

[code]

    class ViewController: UIViewController {
    
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view.
    
            //...
    
            //Realmファイルを別ファイルに保存して（コピー）してファイルサイズを最適化する
            try! realm.writeCopy(toFile: fileURL)
        } //<-ここで`viewDidLoad()`メソッドは終わっている
    
        //↓メソッドの中に書けば全く同じ行の並びがエラーにならない
        func prepareReadOnly() {
            //初期データの入ったRealmファイルを利用
            let oldRealmURL = Bundle.main.url(forResource: "default-old", withExtension: "realm")!
    
            //読み取り専用で使用
            let fileURL = Bundle.main.url(forResource: "template", withExtension: "realm")!
            let config = Realm.Configuration(fileURL: fileURL, readOnly: true)
            let templateRealm = try! Realm(configuration: config) //<-変なところに`}`を置かない
            //...
        } //<-メソッド定義を終わらせる`}`
    
        //...
    } //<-クラス宣言を終わらせる`}`
    
[/code]

Swiftに限らず、プログラミング言語では全く同じ内容でも、どこに書かれているかによって、意味が変わってきたり、エラーになったりすることが多くあります。書籍やサイトなんかのコードを見る場合には、その辺にも気をつけるようにして下さい。

* * *

ところで、エラーが出た時にメッセージを日本語に翻訳してみようとしましたか?
(意味不明の不適切なメッセージもちょくちょくありますが…)原因をズバリ指摘してくれている場合、対応方法まで書いている場合もあります。エラーメッセージに出てくる単語・表現に理解できないところがあれば、まだ使用している言語についての理解が浅いわけなので、言語そのものを学習するための書籍やサイトで基本を学び直して下さい。

また、あなたのコードは`try!`を多用しすぎですね。これではエラーが発生した時に肝心なエラー情報が捨てられて、あまり役に立たないメッセージだけ出してアプリがクラッシュしてしまいます。「自分はこのメソッドを使う場合に、どんな条件でエラーになるか完全に理解していて、このコードでは100%間違いなくエラーにはならない」なんてくらい理解を深めるまでは`try!`や`try?`は使わず、ちゃんとdo-
catchで囲んで使いましょう。

[code]

        do {
            //...
            try ... //<- `try!`でなく`try`
            //...
        } catch {
            print(error) //<-エラー発生時には最低でもエラー情報を出力する
            //...
        }
    
[/code]


CC BY-SA 4.0
2019-06-26T21:56:24.543
56123
2019-06-26T21:56:24.543
13972
56105
answer