Ruby

Rubyのattr_accessorとインスタンス変数

More than 1 year has passed since last update.

Rubyのattr_accessor@インスタンス変数について調べたのでまとめました。

外部からのインスタンス変数へのアクセス

class Foo
  attr_accessor :bar
end

foo = Foo.new
foo.bar = 'instance!!!'
foo.bar # => "instance!!!"

foo.bar = 'instance!!!' は一見、変数へ代入しているように見えますが、実態はattr_accessor が作成した以下のようなFoo#bar= メソッドを呼び出しています。

def bar=(val)
  @bar = val
end

従ってfoo.bar = 'instance!!!' の右辺はメソッドの引数です。また、Rubyにはsyntax sugarが用意されており、foo.bar= と書かなくてもfoo.bar = のように代入しているみたいに書けるのです。

参照についても同じくfoo.barは、@bar を返すbar という以下のようなメソッドを呼び出しています。

def bar
  @bar
end

インスタンスメソッドからのアクセス

class Foo
  attr_accessor :bar

  def method_baz
    p "#{@bar} from method_baz"
  end 

  # attr_accessor :bar が定義されていなければ、barメソッドを呼び出せすNameErrorとなる
  def method_qux
    p "#{bar} from method_qux"
  end
end

foo = Foo.new
foo.bar = 'instance!!!'
foo.method_baz # => "instance!!! from method_baz"
foo.method_qux # => "instance!!! from method_qux"

method_qux では@ を付けなくてもインスタンス変数を参照しています。
これも上記と同じくattr_accessor が作成した@bar を返すbar というメソッドを呼び出しているからです。
従ってアクセサが定義されていなければ、method_qux はエラーになります。

インスタンスメソッドからのアクセス 2

class Foo
  attr_accessor :bar

  def method_quux
    p bar
    bar = 'blue!!!'
    p bar
    p @bar
    p bar()
  end 
end

foo = Foo.new
foo.bar = 'red!!!'
foo.method_quux # => "red!!!"
                # => "blue!!!"
                # => "red!!!"
                # => "red!!!"

method_quux では1回目のp bar と2回目のp bar で出力される値が異なります。
これは1回目のp bar ではattr_accessor が作成している@bar を返すbar というメソッドを呼び出しているのに対して、2回目のp bar では直前に定義された同名のローカル変数を参照しているためです。
この場合、インスタンス変数を参照したい時は素直に@bar とするかbar()と括弧を付けて明示的にメソッド呼び出しにする必要があります。

参考

以下の記事が参考になりました。
http://qiita.com/mogulla3/items/cd4d6e188c34c6819709