why are metaclasses created in ruby?

后端 未结 4 1882
醉酒成梦
醉酒成梦 2021-02-08 11:43

I m trying to understand the Ruby Object Model. I understood that the instance methods are saved in the class rather than in the objects of the class because it removes redundan

相关标签:
4条回答
  • 2021-02-08 12:00

    This isn't quite an answer to your question, but it might be useful. Two things to think about that might help:

    • metaclass is not really a good name for what's going on here when you think of how the meta prefix is used in other scenarios. eigenclass which you will see used in other documentation is probably a better name, meaning "an object's own class"
    • It's not just classes that have an eigenclass, every object does

    The eigenclass is used to store methods that are specific to a particular object. e.g. we can add a method to a single String object:

    my_string = 'Example'
    def my_string.example_method
      puts "Just an example"
    end
    

    This method can only be called on my_string and not on any other String object. We can see that it is stored in my_string's eigenclass:

    eigenclass = class << my_string; self; end # get hold of my_string's eigenclass
    eigenclass.instance_methods(false) # => [:example_method]
    

    Remembering that classes are objects, in this context, it makes sense that the methods specific to a particular class should be stored in that class's eigenclass.


    Update: actually, there is an eigenclass for the eigenclass. We can see this more easily if we add eigenclass as a method to Object:

    class Object 
      def eigenclass 
        class << self
          self
        end 
      end 
    end
    

    and then we can do:

    irb(main):049:0> my_string.eigenclass
    => #<Class:#<String:0x269ec98>>
    irb(main):050:0> my_string.eigenclass.eigenclass
    => #<Class:#<Class:#<String:0x269ec98>>>
    irb(main):051:0> my_string.eigenclass.eigenclass.eigenclass # etc!
    => #<Class:#<Class:#<Class:#<String:0x269ec98>>>>
    

    whilst this seemingly creates an infinite regress, this is avoided because Ruby only creates the eigenclasses on as they are needed. I think the name "metaclass" really is a source of part your confusion because you are expecting a "metaclass" to hold some kind of information that it actually doesn't.

    0 讨论(0)
  • 2021-02-08 12:06

    The reason really boils down to what self is. For any given method, self is an instance of the object that the method is defined one.

    In an instance method, stored on MyClass, self will be the instance of MyClass that you called #hi from. In a class method, self will be the instance of the metaclass (that is, the class itself). Doing it with the metaclass means that the concept of self is unchanged, even when operating on a class, which is itself just a singleton instance of its metaclass.

    0 讨论(0)
  • Just to be super duper clear.

    Here is a quick ruby script that explains the question:

    #!/usr/bin/env ruby
    puts ObjectSpace.count_objects[:T_CLASS] #>> 471
    class X
      def self.foo
      end
      def bar
      end
    end
    puts ObjectSpace.count_objects[:T_CLASS] #>> 473
    

    This is what the OP meant by "ObjectSpace.count_objects[:T_CLASS] increments the count by 2." Let's call the extra class the singleton class of X, because that appears to be what Ruby calls it internally.

    irb> X
    => X
    irb> X.singleton_class
    => <Class: X>
    

    Notice that the #foo method is an instance method of X.singleton_class, not X.

    irb> X.instance_methods(false)
    => [:baz]
    irb> X.singleton_class.instance_methods(false)
    => [:foo]
    

    So why is :foo stored in X.singleton_class instead of X? Isn't there only ever going to be one X?

    I believe the main reason is for consistency. Consider the following, simpler scenario concerning plain instance objects.

    car = Car.new
    def car.go_forth_and_conquer
    end
    

    As @mikej explained superbly, this new method is stored in car's singleton class.

    irb> car.singleton_class.instance_methods(false)
    => [:go_forth_and_conquer]
    

    Classes are Objects

    Now, classes are objects too. Each class is an instance of Class. Thus, when a class (say, X) is defined, ruby is really creating an instance of Class, and then adding methods to the instance (similar to what we did to car above.) For example, here is an alternative way to create a new class

    Car = Class.new do
      def go_forth_and_conquer
        puts "vroom"
      end
    end
    Car.new.go_forth_and_conquer
    

    Therefore, it is much easier to just reuse the code and do it the same way (i.e. keep foo in X.singleton_class.) This probably requires less effort and will lead to fewer surprises, so no one will ever need to write code to handle Class instances differently from other instances.

    Probably Doesn't Matter

    You might be thinking that if Ruby did not have singleton classes for instances of Class, there could be some memory savings. However, it sounds to me that where bar is actually stored is an implementation detail that we probably shouldn't count on. Rubinius, MRI, and JRuby could all store methods and instances differently, as long as the behavior is consistent. For all we know, there could be a reasonable implementation of Ruby that doesn't eagerly create singleton classes for class objects, for the exact same reasons you outlined, as long as the overall behavior conforms to the ruby spec. (E.g. an actual singleton class doesn't exist until the #singleton_class method is first invoked.)

    0 讨论(0)
  • 2021-02-08 12:19

    As per The Ruby Programming Language the class methods, are infact singleton methods on an instance of the class that got same name as the class.

    Class Foo
      def self.bar
        "Am a class method"
      end
    end
    

    here method self.bar can be depicted as a singleton method on an instance Foo of type Class Foo.

    #the following code is just to explain on what actually are class methods called
    Foo.bar #=> "Am a class method" 
    #the call is done on an instance of class Foo which got ref name Foo
    

    We can go on adding more class/singleton/metaclass methods on Foo by

    class<<Foo
      def self.another_bar
        "Am another class method"
      end
    end
    
    Foo.another_bar #=>"Am another class method"
    

    More formally singleton methods are defined as instance methods of an anonymous eigenclass/meta class.

    Though conceptually wrong we can assume classes are objects in this context, so as to have a better grasp.

    This concept is there to bring in true Object Oriented - ness in all levels of the language. Objective-C implements class methods in a similar fashion.

    In Obj-C metaclasses bails out as classes which contain information about the classes it meta. And the principles of inheritance do apply for meta-classes as well, there super class is its classe's superclasse's metaclass and climbs up so until it reaches the base object, whoe's metaclass is the metaclass itself. More reading on this can be done here.

    0 讨论(0)
提交回复
热议问题