• @zw963 , 怎么能看出来,我就是说明 alias 是引用计数?

    参见我的第一个回复,entry1->var = entry2->var;重点就是这句, 对于每一个全局变量,ruby vm 会产生一个 global_variable 实例,

    对于$a = 1 ruby vm 会产生一个新的 global_variable 实例, 并且$a对应产生一个 global_entry 实例,这个 global_entry 中的 var 指向这个 global_variable 实例。

    对于 alias $b, $a, ruby vm 执行了 entry1->var = entry2->var 之后 $b对应的 global_entry 的 var 就指向了$a对应的 global_entry 的 var. 也就是两者都指向了相同的 global_variable 实例,

    即说白了,$b, $a都引用同一份 global_variable 实例,这一点,大家也都说过了,我只是想用源码来证明这一点的存在。

    关键源码,列出来了,大家都是搞程序的,难道非要说的一清二白吗? 这一问题,我已尽力了,不在多说了,就这样,谢谢观看!

  • @Anleb , 不好意思,我说过了,不太会画图,所以只能这样了,其实我觉得你如果细看那段 c 的代码,应该会很清楚的。

    侯捷老师说过,源码之前,了无秘密。

    如果想弄得透一些,这点 c 代码,都懒得分析的话,我就没什么话说了。

  • @skandhas , 嘿嘿,俺不擅长画图啊,画不好,感觉 lz 属于打破砂锅的类型,还是觉得代码最直接。

  • @Anleb

    直接把代码贴过来:

    void
    rb_alias_variable(ID name1, ID name2)
    {
        struct global_entry *entry1, *entry2;
        st_data_t data1;
    
        if (rb_safe_level() >= 4)
        rb_raise(rb_eSecurityError, "Insecure: can't alias global variable");
    
        entry2 = rb_global_entry(name2);
        if (!st_lookup(rb_global_tbl, (st_data_t)name1, &data1)) {
        entry1 = ALLOC(struct global_entry);
        entry1->id = name1;
        st_add_direct(rb_global_tbl, name1, (st_data_t)entry1);
        }
        else if ((entry1 = (struct global_entry *)data1)->var != entry2->var) {
        struct global_variable *var = entry1->var;
        if (var->block_trace) {
            rb_raise(rb_eRuntimeError, "can't alias in tracer");
        }
        var->counter--;
        if (var->counter == 0) {
            struct trace_var *trace = var->trace;
            while (trace) {
            struct trace_var *next = trace->next;
            xfree(trace);
            trace = next;
            }
            xfree(var);
        }
        }
        else {
        return;
        }
        entry2->var->counter++;
        entry1->var = entry2->var;
    }
    

    对于 alias $b, $a, 对应的是调用这里的 rb_alias_variable(:"$b", :"$a") 而 entry2->var 是$a对应的全局变量实体,它的类型为

    struct global_variable {
        int   counter;
        void *data;
        gvar_getter_t *getter;
        gvar_setter_t *setter;
        gvar_marker_t *marker;
        int block_trace;
        struct trace_var *trace;
    };
    

    代码中的 entry1->var = entry2->var; 就是把$b的 var 指向$a的 var, 同时$a的 var 会增加它的 alias 引用计数。

    另外,当给全局变量赋值时,会调用 rb_gvar_set, rb_gvar_set 会调用 val_setter.

    
    VALUE
    rb_gvar_set(struct global_entry *entry, VALUE val)
    {
        struct trace_data trace;
        struct global_variable *var = entry->var;
    
        if (rb_safe_level() >= 4)
        rb_raise(rb_eSecurityError, "Insecure: can't change global variable value");
        (*var->setter)(val, entry->id, var->data, var);
    
        if (var->trace && !var->block_trace) {
        var->block_trace = 1;
        trace.trace = var->trace;
        trace.val = val;
        rb_ensure(trace_ev, (VALUE)&trace, trace_en, (VALUE)var);
        }
        return val;
    }
    
    void
    val_setter(VALUE val, ID id, void *data, struct global_variable *var)
    {
        var->data = (void*)val;
    }
    

    当获取全局变量时,会调用 rb_gvar_get, rb_gvar_get 会调用 val_getter.

    
    VALUE
    rb_gvar_get(struct global_entry *entry)
    {
        struct global_variable *var = entry->var;
        return (*var->getter)(entry->id, var->data, var);
    }
    
    VALUE
    val_getter(ID id, void *data, struct global_variable *var)
    {
        return (VALUE)data;
    }
    
    

    仔细看看这里的代码,这就不再多说了,你应该能明白原因了。

  • @Anleb

    1. alias :$b :$a 这个是不符合语法的。

    alias 一个用法是可以 alias 方法,在一个用法是可以 alias 全局变量。

    参见镐头书 Aliasing

    alias new_name old_name

    This creates a new name that refers to an existing method, operator, global variable, or regular expression backreference ($&, $`, $', and $+). Local variables, instance variables, class variables, and constants may not be aliased. The parameters to alias may be names or symbols.

    如果你用 alias :$b :$a, symbol 这种形式 ruby 会认为你要 alias 一个 method,当然是找不到了, 就出现了 undefined method $a' for classObject' (NameError)

    2. 对于给方法 alias,ruby 调用的是 rb_alias, 对于给全局变量 alias, ruby 调用的是 rb_alias_variable.

    对于 rb_alias, 当一个方法被 alias 的,如果稍后在重定义它,那么 ruby 会保留原有的定义,如果 没有被 alias,原有的定义被丢掉。

    这即是你看到的 "修改 ask 或者 old_ask 不会相互影响"的效果。

    如下可以证明。

    def ask
        p 11111
    end 
    
    def ask
        p 22222 
    end
    

    执行 ruby -d test.rb, 会看到输出 test.rb:7: warning: method redefined; discarding old ask.

    如果

    def ask
        p 11111
    end 
    
    alias :old_ask :ask
    
    def ask
        p 22222 
    end
    

    执行 ruby -d test.rb, 则没有任何输出。

    对于 rb_alias_variable, ruby 是直接把 2 个全局变量都指向了一份变量实体,要证明这一点,只能看 它的代码,参见 rb_alias_variabe 的代码,其中最后一句 entry1->var = entry2->var;

  • The bastards book of Ruby at 2012年05月17日

    多谢分享,相当不错。

  • @junlai , 多谢,学习了!

  • Topic.find(@user.favorite_topic_ids).sort_by { |topic| @user.favorite_topic_ids.index(topic.id) }
    
    
    
  • define_method :hello do keys += [:hello,:bye] end

    这个代码块创建了一个 proc 对象并创建了一个 define_keys 方法的 binding,关联到 这个 proc 对象,也就是闭包了,不知道咋描述。

    而这个 keys, 可以认为是这个 proc 对象,有个引用,初始为 keys, 每次调用 a.hello, 都会调用 这个 proc 对象,并更新那个 keys 的引用,所以会出现这样的结果。

    就跟如下的代码,效果一样。

    module M
      def define_keys(*keys)
        local_keys = keys
        define_method :hello do 
            local_keys += [:hello,:bye]
        end
      end
    end
    
    
    
  • 求助多个 Hash 操作问题 at 2012年04月22日

    @mobiwolf 没用过,Nokogiri,不过可以试试 send, 直接写成:

    builder.send(:"joy_button_#{f}", "value" =>"1")
    
    
    
  • 好文章,感谢,给的链接也好!

  • Rails 3.2 的 Ajax 向导 at 2012年04月21日

    非常感谢!

  • 求助多个 Hash 操作问题 at 2012年04月20日

    提供另一个. Hash[ hash1.map { |k, v| [k, hash3[hash2[v]]] } ]

  • 请教一个关于 lambda 和 Proc at 2012年04月20日

    create_b 输出 self, 就清楚了。

    def create_b
       B.new{ puts "self = #{self}"; method_a }
    end
    
    
    

    第一种情况 输出

    self =  #<A::B:0xb48940>
    
    

    第二种情况 输出

    self = #<A:0xb487a8>
    
    
  • fiber 版的:

    def insert_str(long, short)
      require 'fiber'
    
      longer = Fiber.new do |shorter, result|
        long.each_char do |char|
          result << char
          shorter.transfer(longer, result)
        end
        result
      end
    
      shorter = Fiber.new do |longer, result|
        short.each_char.cycle do |char|
          result << char
          longer.transfer(shorter, result)
        end
      end
    
      longer.transfer(shorter, "")  
    end
    
    
  • 在来复杂地,纯属恶搞了,别介意。

    递归版的

    def insert_str(long, short, index = 0)
      long[index] ? long[index] << short[index % short.length] << insert_str(long, short, index + 1) 
                      : ""
    end
    
    
  • 不好意思,忘用代码风格了

    def insert_str(str1, str2)
      i = -1
      str1.split(//).join("\0").gsub("\0") do
        p $&
        i = (i + 1) % str2.length
        str2[i]
      end
    end
    
    
  • zip + cycle 真是经典,过瘾看着。

    我也来个,不用 enumerator 的

    def insert_str(str1, str2) i = -1 str1.split(//).join("\0").gsub("\0") do i = (i + 1) % str2.length str2[i] end end

  • 不好意思,孤陋寡闻了,此贴无意义, http://ruby-china.org/wiki/gems早有了,

  • @willmouse 呵,搞错了,不是我

  • 好贴,多谢!

  • @huacnlee 主要是现在 gem 很多了,在 Github 和 rubygems.org 上,不知道具体的 gem 名,想找某一方面的 gem,感觉不是很好找,ruby-toolbox 分类感觉不错,现在的 ios,android 有很多的 App 推荐的应用,gem 方面如果有这种推荐的网站或应用,应该不错,呵。

  • @Rei 呵呵,好,找找去!

  • 程序员,保重身体。 at 2012年03月14日

    @Victor 多谢指教,看来以后真得好好研究研究了,人只有在自己生病的时候,才会关心这些,人体真是很神奇!

    @raven 多谢多谢,你说的早睡+心情平静,这两点,我很认同,我最近也有所体会,我的出生年月是:77 年 10 月。

    真是十分感谢 @kevin__liu 的这个帖子,让大家可以互相讨论讨论健康话题,ruby 倡导让程序员幸福的编程,但人的身体更是重要,只有一个健康的身体,我们才能用 ruby 幸福的编程,才能更好的活着!

  • 程序员,保重身体。 at 2012年03月13日

    @kevin__liu 多谢多谢,研究研究!

  • 程序员,保重身体。 at 2012年03月13日

    @raven 我最近也胃病缠身,能否告知一二,十分感谢!!

  • topics 被 release 的那点,

    • (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjects:(NSArray *)objects { self.topics = [objects copy]; NSLog(@"KKKKKK"); [self.topicTableView reloadData]; [self stopLoading]; } self.topics = [objects copy];这句会导致释放吧,

    可以写个

    • (void)setTopics:(NSArray *)newTopics { }

    在这个函数里当释放已有的 topics 时,removeObserver

  • 关于"/" 的含义 at 2012年03月08日

    正则表达式的字面值常量就这种写法,/RegExp/ ,后面可能还会接一些参数,如 i, m 等。

    / / 就如同字符串要用" "一样

  • ruby 中的浅拷贝 at 2012年03月08日

    应该为 *(b[1]) = 8; 则*(a[1]) 会变成 8

    忘了 markdown,会把 * 处理了,不好意思

  • ruby 中的浅拷贝 at 2012年03月08日

    @loveky 应该了解 c 语言吧。

    我觉得也可以这样理解: 你的 a,b 两个数组例子类似

    int a[4] = { 1, 2, 3, 4 };
    
    // clone
    int *b    = (int *)malloc(sizeof(a);
    memcpy(b, a, sizeof(a));
    
    

    如果写 b[1] = 8; 则 a[1] 仍然为 2.

    而如果写成如下的代码

    int *a[4];
    int e1 = 1, e2 = 2, e3 = 3, e4 = 5;
    
    a[0] = &e1;
    a[1] = &e2;
    a[2] = &e3;
    a[3] = &e4;
    
    // clone
    int *b[4];
    memcpy( b, a, sizeof(a) );
    
    

    如果写 (b[1]) = 8; 则(a[1]) 会变成 8

    语言不一样,道理类似。