Directly Accessing an Instance Variable VS. Using an Accessor Method

Directly accessing an instance variable vs. Using an accessor method

self.attribute calls the method attribute.

self.attribute = value calls the method attribute= with the argument value.

@attribute and @attribute = value get/set the value of the instance variable @attribute.

So basically they're two entirely different things.

However if you call attr_accessor :attribute it defines the method attribute to return @attribute and the method attribute=(value) to set @attribute = value. So in that case, there is no difference.

is getter method call to access variable better than direct variable access within a class

In Java it is a convention to access all fields via getter/setters from outside the class. From inside the class, you usually access fields directly. However, you can also access them via getter/setter is you want to.

It is important to know that this is just a convention. Many other programming languages don't have such strict rules or other concepts. So you are not forced to do that. But it is a good practice.

And: Don't mind performance! Using getters/setters will not affect the performance of your app. The JVM/Java is designed to work exactly like this. Every JVM will optimize your code and handle getters/setters in a very effective way. Try to write clear and good readable code.

Difference between @instance_variable and attr_accessor

An instance variable is not visible outside the object it is in; but when you create an attr_accessor, it creates an instance variable and also makes it visible (and editable) outside the object.

Example with instance variable (not attr_accessor)

class MyClass
def initialize
@greeting = "hello"
end
end

m = MyClass.new
m.greeting #results in the following error:
#NoMethodError: undefined method `greeting' for #<MyClass:0x007f9e5109c058 @greeting="hello">

Example using attr_accessor:

class MyClass
attr_accessor :greeting

def initialize
@greeting = "hello"
end
end

m2 = MyClass.new
m2.greeting = "bonjour" # <-- set the @greeting variable from outside the object
m2.greeting #=> "bonjour" <-- didn't blow up as attr_accessor makes the variable accessible from outside the object

Hope that makes it clear.

In ruby, should I use instance variables or direct method calls

Instead of defining the method bigcat you may rename your instance variable and use a accessor method.

Example based on dimitris answer:

class Foo
def cat
@bigcat ||= "This is a cat"
end
attr_reader :bigcat
end

mycat = Foo.new
mycat.cat
mycat.bigcat

If you want to keep your variable name, you could combine it with an alias:

class Foo
def cat
@cat_talk ||= "This is a cat"
end
attr_reader :cat_talk
alias :bigcat :cat_talk
end

mycat = Foo.new
mycat.cat
mycat.cat_talk
mycat.bigcat

Beyond your question, just for information: You could also define a writter method with attr_writer.

Should I use properties or direct reference when accessing instance variables internally?

I don't think any way is 'better'. You see both styles in common use, so there isn't even a usual/best practice now. In my experience, the style used has very little impact on how well I digest some implementation file I am looking. You certainly want to be comfortable with both styles (and any in between) when looking at other people's code.

Using a property for every internal ivar might be going slightly overboard, in terms of maintenance. I've done it, and it added a non-trivial amount of work that I don't think paid off for me. But if you have a strong desire/OCD for seeing consistent code like self.var everywhere, and you have it in the back of your mind every time you look at a class, then use it. Don't discount the effect that a nagging feeling can have on productivity.

Exceptions- Obviously, for custom getters (e.g. lazy creation), you don't have much of a choice. Also, I do create and use a property for internal setters when it makes it more convenient (e.g. setting objects with ownership semantics).

"just in case", "might" is not be a compelling reason to do something without more data, since the time required to implement it is non-zero. A better question might be, what is the probability that all the private ivars in some class will require KVC notifications in the future, but not now? For most of my own classes, the answer is exceedingly low, so I now avoid a hard rule about creating properties for every private ivar.

I've found that when dealing with internal implementations, I quickly get a good handle on how each ivar should be accessed regardless.

If you are interested, my own approach is this:

  • Reading ivars: Direct access, unless there is a custom getter (e.g. lazy creation)
  • Writing ivars: Directly in alloc/dealloc. Elsewhere, through a private property if one exists.

How to read instance variables set by accessors inside the same class?

It's best to go through the accessors. For instance, if you access the instance variable directly, and then you later convert the value with the reader, then accessing the instance variable directly won't see that change.

Using the accessors rather than accessing the instance variables directly causes one small quirk when you want to use the accessor to set the value of the instance variable.

Normally when you write:

some_meth 10

...ruby will interpret that as:

self.some_meth(10)

But if you write:

age = 10

...ruby will not interpret that as:

self.age=(10)

Instead, ruby will create a local variable named age and set it to 10, which has no affect on an instance variable named @age.

In order to call the setter for @age, you have to explicitly write self:

self.age = 10

Here is a complete example:

class Dog
attr_reader :age

def age=(val)
@age = val * 7
end

def initialize val
self.age = val #age = val will not call the setter
end

end

d = Dog.new 10
puts d.age #=> 70

Accessing Instance Variable Directly in Ruby?

The example provided by the OP is a class instance variable. These can only be accessed by class methods.

"regular" attribute accessors won't allow access from outside the class. Here are a couple of ways to create accessors that work:

class A
@class_instance_var = "foo"

class << self
attr_accessor :class_instance_var
end

end

puts A::class_instance_var # Output: foo

OR

class A
@class_instance_var = "foo"

def self.class_instance_var
@class_instance_var
end
end

puts A::class_instance_var # Output: foo

Class instance variables

Class instance variable names also begin with @. However, they are defined at class level, outside any methods. Class instance variables can only be accessed by class methods. They are shared amongst all instances of a class but not its subclasses. In other words, they are not inheritable. If the value of a class instance variable is changed in one instance of the class, all other instances are affected. Earlier we saw how all classes are instances of a built-in class called Class. That is what makes class instance variables possible.

class Vehicle
@count = 0 # This is a class instance variable

def initialize
self.class.increment_count
self.class.show_count
end

def self.increment_count # This is a class method
@count += 1
end

def self.show_count # This is a class method
puts @count
end

end

class Car < Vehicle
@count = 0
end

v1 = Vehicle.new # Output: 1
v2 = Vehicle.new # Output: 2
v3 = Vehicle.new # Output: 3

car1 = Car.new # Output: 1
car2 = Car.new # Output: 2

v3 = Vehicle.new # Output: 4

Let's review the example above. A class instance variable called @count is set in the Vehicle class, with an initial value of 0. Every time the Vehicle class is instantiated, the initialize method calls self.increment_count to increment the value of @count and self.show_count to return the new value. Then, we have the Car class, which is a subclass of Vehicle and inherits all of its methods. However, it does not inherit the @count class instance variable, as this type of variable is not inheritable. That's why the counter works within the Car class, but it has its own count.

Methods prefixed with self., such as self.increment_count and self.show_count, are class methods. That is the only kind of method capable of accessing class instance variables.

Is it more efficient to call an accessor method several times or to declare a temporary variable?

The cost of storing a value in a local variable is next to nothing. The cost of calling an inlined "fetch this member variable" is also next to nothing.

If, on the other hand, the object is not a vector, and length is not held in a member variable, but has to be counted - say like strlen(), then there is a great benefit of storing it in a local variable. Particularly if the string is more than a few characters long.

The other problem is of course that you do something like:

int number_of_widgets = my_widgets.length();
... // more code here, but none that affect my_widgets.

last_widget = my_widgets[number_of_widgets-1];
...

And then someone else goes and edits the cdoe:

int number_of_widgets = my_widgets.length();
... some code.
my_widgets.erase(some_widget_iterator);

... // more code here

last_widget = my_widgets[number_of_widgets-1];
...

Now your code is accessing outside the valid range and may crash and burn...

As always, the devil is in the detail. If you want to know what's fastest IN YOUR CODE, then benchmark it using your code...

When to use instance variables and when to use properties


can you stop creating instance variables altogether

No, you can't (in a sense). What you can do is stop declaring them if you have properties. If you synthesize a property and you haven't declared the instvar, it will get declared for you, so you are creating an instance variable, just not explicitly.

do they still serve a purpose where properties would be inappropriate?

It used to be the advice to create properties for everything because having synthesized properties does almost all of the retains and releases for you. However, with ARC that reason for using properties to wrap the memory management has gone away. The advice now (for ARC) is, I believe, use properties to declare your external interface, but use direct instance variables where the variable is part of the object's internal state.

That's a good reason to adopt ARC: properties revert to their true purpose only of being part of the class's API and it's no longer necessary to use them as a hacky way to hide memory management work.

Edit

One more thing: you can now declare instance variables in the @implementation so there is now no need to leak any implementation details in the @interface. i.e.

@implementation MyClass
{
NSString* myString;
}
// method definitions
@end

And I'm pretty sure it works in categories too. - see comment below

What is an example of an accessor method?

Three are accessor methods. An accessor "accesses" the variable. Only three of them directly return the private variables. All of the others are mutators because they "mutate" the variables. The last accessor listed below is not a great accessor because it doesn't follow encapsulation best practices.

These are the accessors.

 public int getA() { return myA; }
public int getB() { return myB; }
public String toString( ) { return getA() + " " + getB(); }


Related Topics



Leave a reply



Submit