锁定老帖子 主题:慎用类变量 - 实例变量靠谱量又足
该帖已经被评为良好帖
|
|
---|---|
作者 | 正文 |
发表时间:2007-11-24
报告首长,实际项目中就是用了class_inheritable_array
我嫌排BUG麻烦才改成class_inheritable_accessor,但他们的本质都是一样的,最终都是用到write_inheritable_attribute和read_inheritable_attribute,所以他们都有同生共死的BUG牵绊。 |
|
返回顶楼 | |
发表时间:2007-11-24
哦,上面的回贴正是解决这个问题,陷阱陷阱~
不过 array/hash的value对象里如果再引用其它东西,最终还是到了一个地方。根结在于,它们最初的来源就不是两个,而dup的程度从来没达到过deepcopy。现在就是幸灾乐祸的说,早晚要出事,呵呵~ |
|
返回顶楼 | |
发表时间:2007-11-24
嗯嗯!
代码都在那个百来行的inheritable_attribute.rb里。 拷贝的时机在Class被继承的回调里,一个类的inherited方法当有子类定义时自动调用: class Class # Prevent this constant from being created multiple times EMPTY_INHERITABLE_ATTRIBUTES = {}.freeze unless const_defined?(:EMPTY_INHERITABLE_ATTRIBUTES) def inherited_with_inheritable_attributes(child) inherited_without_inheritable_attributes(child) if respond_to?(:inherited_without_inheritable_attributes) if inheritable_attributes.equal?(EMPTY_INHERITABLE_ATTRIBUTES) new_inheritable_attributes = EMPTY_INHERITABLE_ATTRIBUTES else new_inheritable_attributes = inheritable_attributes.inject({}) do |memo, (key, value)| memo.update(key => (value.dup rescue value)) end end child.instance_variable_set('@inheritable_attributes', new_inheritable_attributes) end alias inherited_without_inheritable_attributes inherited alias inherited inherited_with_inheritable_attributes end |
|
返回顶楼 | |
发表时间:2007-11-24
但那个补丁已经整合到RAILS1.2.3中了,这么说还是有BUG?果然每个补丁后面都隐藏着更大的BUG,今天终于华丽的孵化了。
那RUBY有没dup!! 这种支持深层的遍历方法啊 |
|
返回顶楼 | |
发表时间:2007-11-24
由potian老大的图看出,类的继承链上,在类本身的层次上,不会共享实例变量,也没有查找机制。如果想完全控制,还是自己手工定义类上面的accessor方法吧,想放到哪放到哪,想从哪取从哪取。不然,深度拷贝的效果也不合心意呢?关键在于,我们怎么定义继承下来的属性应该是什么样的
|
|
返回顶楼 | |
发表时间:2007-11-24
但我目前需要实现的:
1.类与实例可共用变量 2.父类可以初始化变量值 3.子类继承父类的初始值,但子类改变变量值时,不影响父类的值 怎么看怎么像是class_inheritable_accessor的存在理由啊。 木有理由再造这么一个轮啊,修修或许还行,只是小弟功力尚浅,请前辈扶下小弟(不是那里,汗)。 |
|
返回顶楼 | |
发表时间:2007-11-25
inherited是core extension,不敢对它贸然下手,但若要只影响到A所牵连的部分,还是可以quick hack一下:
class Base class_inheritable_accessor :var class << self def set_var(new_var) self.var ||= [{:a=>"aa"}, {:b=>"bb"}] self.var.each do |v| v[:a] = new_var end end end end class A < Base class << self def inherited_with_inheritable_attributes_deep_copy(child) inherited_without_inheritable_attributes(child) if respond_to?(:inherited_without_inheritable_attributes) child.instance_variable_set('@inheritable_attributes', Marshal.load(Marshal.dump(inheritable_attributes))) end alias_method_chain :inherited, :inheritable_attributes_deep_copy end class_inheritable_accessor :var set_var "a" end class B < Base end class C < A end A.var #=> [{:a=>"a"}, {:b=>"bb", :a=>"a"}] B.set_var('b') A.var C.set_var('c') # *notice* A.var #=> [{:a=>"a"}, {:b=>"bb", :a=>"a"}] 用到The ruby way(11.1.9. Copying an Object)提到的Marshal(也只支持简单的Hash),估计它不会指条黑路给我吧。 但这里只能解决我写的简单演示代码,而实际项目代码复杂得多,所作的HACK并没有解决到问题,还需要继续探索。 |
|
返回顶楼 | |
发表时间:2007-11-25
既要继承,又要相互独立,牵扯到复杂的场景,对象的引用关系不是单一一种方案能解决的吧,我还是觉得需要开发人员自己去判断。 比如上面用Marshal,那么从父类继承过来的Array/Hash整个也被克隆了,如果在子类上修改某个从父类继承过来的属性时,根据“继承”的推测,理所当然认为改了父类的,出错了。
引用 3.子类继承父类的初始值,但子类改变变量值时,不影响父类的值 用copy on write的模式实现setter/getter就行,可是能彻底解决理解上的歧义和功能上的需求吗?我觉得还是不会。 我的结论是,知道这个陷阱和解决办法是最务实的,大一统的方案我还没想明白,总结一下是 一、理解上的歧义 何时它是继承的、何时它又是独立的 二、功能需求 1.类与实例可共用变量,也可独立 2.类和实例都方便对内对外access这个"属性" 经过上面的讨论,我理解更清楚了,谢谢~ go on... |
|
返回顶楼 | |
发表时间:2007-12-13
看了好久,终于知道你们在说什么了...
1.copy bug 2.类变量(或类的实例变量)共享实现 我猜对了么? 其实我有很多关于Ruby方面的问题,想和你们交个朋友 |
|
返回顶楼 | |
发表时间:2008-08-28
class A @x = 1 #<<---- 按我说, 这里应该发出一个警告才对, “你是不是想用 @@x 少写了一个@” @@z = 1 #<<---- 这个是啥, 挺常用的 def initialize @y = 2 end end liusong1111说: 引用 但怎么说子类父类引用同一个东东称之为class variable太诡异了。
底层数据结构 @x和 @@z 应该一样, @@z 的行为应该比较容易理解, @x的这种用法我根本不知道用在哪好, 似乎有少写一个 @ 的嫌疑,只会把自己弄乱, 毕竟 对 前缀@ 的处理只是在Ruby语言层次的。 可以看一下 http://rhg.rubyforge.org/ 里面的 的 第四章 和 第六章 挺有用的。 |
|
返回顶楼 | |