@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 属于打破砂锅的类型,还是觉得代码最直接。
直接把代码贴过来:
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;
}
仔细看看这里的代码,这就不再多说了,你应该能明白原因了。
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;
多谢分享,相当不错。
@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
@mobiwolf 没用过,Nokogiri,不过可以试试 send, 直接写成:
builder.send(:"joy_button_#{f}", "value" =>"1")
好文章,感谢,给的链接也好!
非常感谢!
提供另一个. Hash[ hash1.map { |k, v| [k, hash3[hash2[v]]] } ]
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 呵呵,好,找找去!
@Victor 多谢指教,看来以后真得好好研究研究了,人只有在自己生病的时候,才会关心这些,人体真是很神奇!
@raven 多谢多谢,你说的早睡+心情平静,这两点,我很认同,我最近也有所体会,我的出生年月是:77 年 10 月。
真是十分感谢 @kevin__liu 的这个帖子,让大家可以互相讨论讨论健康话题,ruby 倡导让程序员幸福的编程,但人的身体更是重要,只有一个健康的身体,我们才能用 ruby 幸福的编程,才能更好的活着!
@kevin__liu 多谢多谢,研究研究!
@raven 我最近也胃病缠身,能否告知一二,十分感谢!!
topics 被 release 的那点,
可以写个
在这个函数里当释放已有的 topics 时,removeObserver
正则表达式的字面值常量就这种写法,/RegExp/ ,后面可能还会接一些参数,如 i, m 等。
/ / 就如同字符串要用" "一样
应该为 *(b[1]) = 8; 则*(a[1]) 会变成 8
忘了 markdown,会把 * 处理了,不好意思
@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
语言不一样,道理类似。