Overview
Class instance variables are created by defining instance variables at the class level, outside of any method definitions. Each class maintains its own separate set of these variables, and they are not inherited by subclasses. This makes them useful for storing data that should be associated with a particular class but not shared across a class hierarchy.
class User
@count = 0
def self.increment_count
@count += 1
end
def self.count
@count
end
end
User.count # => 0
User.increment_count
User.count # => 1
Basic Usage
Defining Class Instance Variables
Class instance variables are defined by using the @
symbol at the class level:
class Product
@inventory = []
@total_sales = 0
def self.add_to_inventory(item)
@inventory << item
end
def self.inventory
@inventory
end
def self.record_sale(amount)
@total_sales += amount
end
def self.total_sales
@total_sales
end
end
Product.add_to_inventory("Widget")
Product.record_sale(100)
Product.inventory # => ["Widget"]
Product.total_sales # => 100
Accessing Class Instance Variables
Class instance variables can only be accessed through class methods or within the class definition itself:
class Counter
@value = 0
def self.increment
@value += 1
end
def self.value
@value
end
def self.reset
@value = 0
end
end
Inheritance Behavior
Unlike class variables (@@variable
), class instance variables are not inherited:
class Animal
@species_count = 0
def self.add_species
@species_count += 1
end
def self.species_count
@species_count
end
end
class Dog < Animal
@breed_count = 0
def self.add_breed
@breed_count += 1
end
def self.breed_count
@breed_count
end
end
Animal.add_species
Dog.add_breed
Animal.species_count # => 1
Dog.species_count # => 0 (not inherited)
Dog.breed_count # => 1 (Dog's own variable)
Advanced Usage
Using Class Instance Variables with Modules
Class instance variables work within modules and can be used to create sophisticated patterns:
module Trackable
def self.included(base)
base.instance_variable_set(:@tracked_objects, [])
end
def track_creation
self.class.instance_variable_get(:@tracked_objects) << self
end
module ClassMethods
def tracked_objects
@tracked_objects ||= []
end
def clear_tracking
@tracked_objects = []
end
end
def self.included(base)
base.extend(ClassMethods)
base.instance_variable_set(:@tracked_objects, [])
end
end
class User
include Trackable
def initialize(name)
@name = name
track_creation
end
end
user1 = User.new("Alice")
user2 = User.new("Bob")
User.tracked_objects.length # => 2
Dynamic Class Instance Variable Management
You can manipulate class instance variables dynamically using Ruby's metaprogramming capabilities:
class ConfigurableClass
def self.set_config(key, value)
instance_variable_set("@#{key}", value)
end
def self.get_config(key)
instance_variable_get("@#{key}")
end
def self.config_keys
instance_variables.map { |var| var.to_s[1..-1] }
end
end
ConfigurableClass.set_config(:database_url, "postgres://localhost")
ConfigurableClass.set_config(:api_key, "secret123")
ConfigurableClass.get_config(:database_url) # => "postgres://localhost"
ConfigurableClass.config_keys # => ["database_url", "api_key"]
Common Pitfalls
Confusion with Class Variables
A common mistake is confusing class instance variables with class variables:
class Example
@@class_var = "shared" # Class variable (inherited)
@class_instance_var = "not shared" # Class instance variable (not inherited)
def self.class_var
@@class_var
end
def self.class_instance_var
@class_instance_var
end
end
class Child < Example
end
Example.class_var # => "shared"
Child.class_var # => "shared" (inherited)
Example.class_instance_var # => "not shared"
Child.class_instance_var # => nil (not inherited)
Uninitialized Variable Access
Accessing an uninitialized class instance variable returns nil
:
class Test
def self.uninitialized_var
@never_set
end
end
Test.uninitialized_var # => nil
Thread Safety Concerns
Class instance variables are not automatically thread-safe:
class ThreadUnsafe
@counter = 0
def self.increment
# This is not atomic and can cause race conditions
@counter += 1
end
def self.counter
@counter
end
end
# Better approach with synchronization
class ThreadSafe
@counter = 0
@mutex = Mutex.new
def self.increment
@mutex.synchronize { @counter += 1 }
end
def self.counter
@mutex.synchronize { @counter }
end
end
Testing Strategies
When testing classes that use class instance variables, remember to reset state between tests:
# RSpec example
RSpec.describe MyClass do
before(:each) do
MyClass.instance_variable_set(:@count, 0)
end
it "increments count" do
expect { MyClass.increment }.to change { MyClass.count }.by(1)
end
end
Reference
Key Characteristics
- Scope: Belong to the class object itself, not instances
- Inheritance: Not inherited by subclasses
- Access: Only accessible through class methods
- Naming: Use
@variable_name
syntax - Thread Safety: Not thread-safe by default
Common Methods for Management
Method | Purpose | Example |
---|---|---|
instance_variable_get |
Read variable value | self.instance_variable_get(:@var) |
instance_variable_set |
Set variable value | self.instance_variable_set(:@var, value) |
instance_variable_defined? |
Check if variable exists | self.instance_variable_defined?(:@var) |
instance_variables |
List all instance variables | self.instance_variables |
remove_instance_variable |
Remove a variable | self.remove_instance_variable(:@var) |
Comparison with Related Concepts
Concept | Inheritance | Scope | Access |
---|---|---|---|
Instance variables (@var ) |
No | Per instance | Instance methods |
Class instance variables (@var at class level) |
No | Per class | Class methods |
Class variables (@@var ) |
Yes | Shared across hierarchy | Both class and instance methods |
Constants (CONST ) |
Yes (with warnings if redefined) | Class hierarchy | Both class and instance methods |