このページは「はじめてのSwiftプログラミング」(清水美樹著、工学社刊、2014年7月11日発売、ISBN978-4-7775-1841-8)の著者によるサポートページです

目次へ

このサイトのトップへ

参照とは

参照とは

メモリ上の位置(間接表現)

「参照(Reference、リファレンス)」とは、値の置かれているメモリ上の位置を変数名で表すことです。
「メモリ上の位置」といっても、OSにより間接的な表現に変換されているので、その表現を直接読み取ることには、意味はありません。


参照タイプか値タイプか

第7章で説明したように、「参照」を論ずる時、Swiftでは「その変数はメモリ上の場所を示すのか、値を示すのか」が問題になります。
前者を「参照タイプ」後者を「値タイプ」と呼ぶ事にしましょう。

参照コピーか値のコピーか

変数同士で値を受け渡した時

「参照タイプ」か「値タイプ」かという問題でまず注目されるのは、

「変数と変数で値の受け渡しをしたとき、一方の値を勝手に変更してもよいか?」

という問題です。

リスト1 変数同士で値の受け渡しをする問題
var a = なにかの値
var b = a
bのなにかの性質 = 別の物
aのなにかの性質は?

本書において随時議論してきたように、リスト1のような操作をした場合、aが「値タイプ」だった場合は、bの性質を変えてもaの性質は変わりません。
なぜなら、aが持つ「なにかの値」と同じ値を持つ「別のインスタンス」が新しく作成されて、bに渡されるからです。
そのあとbの性質を変えても、それはaとは別のインスタンスですから、aの性質は変わりません。

一方、aが「参照タイプ」だった場合、bには「aの値が置かれている場所」が伝えられるだけです。
「同じインスタンスの呼び方」が二通りになるのです。
そこで、「bを変更する」というのは「a を変更する」のと同じ意味になります。

プログラミングをしていくと、このような「参照」に関わるトラブルで困惑することがあります。
どのようなトラブルが起こり、それをどう克服して行くか、いくつかの例を紹介します。

工夫で乗り越える参照トラブル

参照タイプと値タイプを間違える

 参照で陥りやすい単純な間違いは、今扱っている変数が参照タイプか値タイプかを間違えることです。
「値タイプを参照タイプと間違えて、同じインスタンスを指すはずだった二つの変数が、別のインスタンスになってしまった」 「逆の間違いで、元の値を退避させたつもりが上書きされてしまった」などのトラブルです。

これらは、以下のような工夫で解決できます。


「値タイプ」のインスタンスを複数の変数で共有

リスト2は本書で何度か解説してきた文字列の「値タイプ」としての性質です。  変数stringBは、stringAと同じ内容で別のインスタンスになりますので、stringBの内容を変えてもstringAの内容は変わりません。

リスト2 stringAとstringBは別のインスタンス
var stringA = "Apple"
var stringB = stringA

stringB = "リンゴ"

myStr = "stringAの文字列は\(stringA)"
//”stringAの文字列はApple”

もし、同じ文字列を別名で使いたい場合はリスト3のように、クラスを作成し、 そのプロパティとしての文字列を扱います。

リスト3 クラスShareableString,プロパティが文字列
class ShareableString{
  var str:String
  func change(#newStr:String){
      str = newStr
   }
   init(str:String){

      self.str = str
    }
}

変数sStringAとsStringBは同じインスタンスを共有するので、そのプロパティstrも共有することになります。 よって、sStringBのプロパティstrを変更すればsStringAのstrも変わります。

リスト4 同じインスタンスのプロパティを共有
var sStringA = ShareableString(str:"Apple")
var myStr = "sStringAの文字列は\(sStringA.str)"
//”sStringAの文字列はApple”

var sStringB = sStringA
sStringB.change(newStr:"リンゴ")

var myStr = "sStringAの文字列は\(sStringA.str)"
//”sStringAの文字列はリンゴ”


「参照タイプ」のインスタンスのコピーを作成

「参照タイプ」のインスタンスのコピーを作成するには、 リスト5のクラス「noClone」のようにクラスに自身をコピーするメソッドを定義すると便利です。
リスト5 自身をコピーするメソッドのあるクラス
class noClone{
    var name:String
    var age:Int
    init(name:String, age:Int){
        self.name = name
        self.age = age
    }

  //自身をコピーした別のインスタンスを戻す
    func makeClone()->noClone{
        return noClone(
            name:self.name, age:self.age)
    }

   //自身のプロパティを記述する
    func introduce()->String{
        return "私は\(self.name), 年は\(self.age)歳"
    }
}

 まず、インスタンスを普通にコピーした場合を考えます。 次のリスト6のインスタンスcloneBは、その名の通り、「cloneAのクローンではありません」。同じインスタンスです。 そのため、cloneBのプロパティ「age」を変更すると、cloneAのageも変わります。

リスト6 cloneBとcloneAの関係
var cloneA = noClone(name:"黒音 瑛", age:18)
var cloneB = cloneA

++cloneB.age

cloneA.introduce()
//"私は黒音 瑛, 年は19歳"

そこで、メソッドmakeCloneによって、本当の「クローン」を作ります。 こうして作成されたcloneCのプロパティを変更しても、cloneAのプロパティは変更されません。

リスト7 自身のコピーを変更してみる
var cloneC = cloneA.makeClone()
cloneC.introduce()
//"私は黒音 瑛, 年は19歳"

cloneC.name = "久楼雲 椎"
cloneC.introduce()
//”私は久楼雲 椎, 年は19歳”

cloneA.introduce()
//"私は黒音 瑛, 年は19歳"

このように、「値タイプ」と「参照タイプ」の違いの問題は、特別な技術を必要としません。 「工夫」で行けます。


目次へ

このサイトのトップへ