Ruby instance_eval 是如何做到修改 Block 里的 self 引用的?

lululau · 2013年08月29日 · 最后由 fredwu 回复于 2013年09月17日 · 2741 次阅读

比如这段代码

class Person
    def hello
        yield
    end
    def world(&proc)
        proc.call
    end
end

p = Person.new
p.hello {puts self.class}
p.world {puts self.class}
p.instance_eval {puts self.class}

输出:

Object
Object
Person

如果我想写一个叫做haha方法,然后通过的调用 p.haha {puts self.class} 输出 Person,如果不借助 instance_eval (包括不借助那些已经借助了instance_eval的方法:Module#define_method 等),可以写出这个 haha 方法吗?说到底就是像 instance_eval 那样修改一个 Block 里的 self 引用是可行的吗?

共收到 14 条回复

说到底就是像 instance_eval 那样修改一个 Block 里的 self 引用是可行的吗?

我觉得可行

可以,sinatra就是把block 转换成method, 然后unbind,然后再bind到一个object上调用,这时self就是object 具体是 define_method &blk --> method(:xxx) #得到UnbindMethod --> undefine_method(:xxx) --> unbind_method.bind(obj).call

instance_eval内部c实现是调用了specific_eval,specific_eval里判断如果参数是block,就调用yield_under(klass, self, Qundef);,会改变block的self,如果既不用instance_eval/exec,也不用define_method来用UnbindMethod去bind对象,貌似没办法的样子。

呼唤大神 @luikore

#3楼 @kenshin54 感谢!其实我不是非要自己做一个 instance_eval,就是想明白instance_eval 是如何做到这一点的。通过的你的回复我已经差不多明白了。

class Person
  def haha(&blk)
     blk.bind(self).call
  end
end

 Person.new.haha { p self.class }
=> Person

#5楼 @zhenjunluo Proc 貌似没有 bind 这个实例方法

#3楼 @kenshin54 #6楼 @lululau

ActiveSupport 加了 Proc#bind, 但是每次 bind 都生成一个新的 symbol, 会引起严重的 GC 问题, 已经 deprecated 了

#7楼 @luikore 请问下,还有哪些使用方式会引起GC问题,hash中用string代替symbol做key,GC的释放上会有多大差别, 局部变量array,hash中存了大量的数据,用clear是否能达到释放内存的目的?

#8楼 @zhenjunluo symbol 只存一份, 但不释放, string 没有 symbol 那样的优化, 但可以释放. 不该让程序存在生成无穷多 symbol 的可能 (例如把用户输入转换成 symbol).

params[:a] # 随便用
params['a'].to_sym # 不好
"#{i += 1}".to_sym # 如果调用次数不多, 也没问题

用 clear 可以, 不过退出局部作用域就全释放了, 不用处处 clear.

#9楼 @luikore 谢谢答复,rails中的instance variables在页面渲染完后会及时释放内存吗?

#5楼 @zhenjunluo 看了一下 ActiveSupport,发现 Proc#bind 也是调用了 define_method,define_method 又是调用了 instance_eval

13楼 已删除
class Person
  def hello
    yield
  end

  def world(&proc)
    proc.call
  end

  def haha(&block)
    instance_exec(&block)
  end
end

p = Person.new
p.hello {puts self.class}
p.world {puts self.class}
p.instance_eval {puts self.class}
p.haha {puts self.class}
Object
Object
Person
Person
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册