2KB项目,专业的源码交易网站 帮助 收藏 每日签到

Ruby 2.1 详情

  • 时间:2019-01-23 18:36 编辑:2KB 来源:2KB.COM 阅读:326
  • 扫一扫,手机访问
  • 分享
摘要:
Ruby 英文原文:Ruby 2.1 In Detail

2013 年圣诞节发布的 Ruby 2.1 是 Ruby 的下一个重要版本,仅与2.0版本的发布有10个月的间隔。该版本包含了大量的更新和提升,这篇文章就来揭秘新特性的具体细节。

新的版本控制策略

Ruby2.1改为了基于语义化版本控制 版本控制方案。

具体方案是MAJOR.MINOR.TEENY, 因此2.1.0中,主版本号是2, 次版本号是1,以及微版本号是0. 微版本号代表小Bug和安全补丁的修正程度。次版本号代表向后兼容的新特性,主版本号则是无法发布为次版本号的非兼容的更新。

这就意味着而不是说,曾经1.9.3大更新1.9.3-p545小更新变成了2.1大更新2.1.1小更新了。

计划每隔12个月释放一个次版本更新,因此2014年圣诞节我们应该可以看到Ruby2.2了。

必须的关键字参数

Ruby 2.0.0中引入的 关键字参数 在 2.1中加了点小小的改进. 必须的关键字参数允许你在方法定义时删除关键字参数的默认值, 并且在方法被调用时,如果没有提供确切值则跑出异常。

# length is required
def pad(num, length:, char: "0")
  num.to_s.rjust(length, char)
end

pad(42, length: 6)   #=> "000042"
pad(42)              #=> #<ArgumentError: missing keyword: length>

像上面展示的例子中,我们可以看到关键字参数可以帮助我们消除哪个参数是哪个的歧义, 但是默认值并不总是必须的. 现在我们不必总是加默认值了。

字符串#freeze优化

Ruby中字符串是易变的,任何字符串文字在每次调用他们时都将产生一个新的字符串,例如

def env
  "development"
end

# returns new String object on each call
env.object_id   #=> 70329318373020
env.object_id   #=> 70329318372900

这是十分浪费的,创建并垃圾回收了许多的对象。为了让你能够避免这种情况,在字符串文字上直接调用#freeze,这将导致在冻结字符串表中查找字符串。这就意味着同样的字符串将被再次使用。

def env
  "development".freeze
end

# returns the same String object on each call
env.object_id   #=> 70365553080120
env.object_id   #=> 70365553080120

在Hash表中字符串文字作为键时,将被同样对待,但不需要调用#freeze

a = {"name" => "Arthur"}
b = {"name" => "Ford"}

# same String object used as key in both hashes
a.keys.first.object_id   #=> 70253124073040
b.keys.first.object_id   #=> 70253124073040

在2.1开发期间,这个功能开始是作为一个附加语法,用"string"f导致一个冻结字符串。最终决定切换到在文字上调用#freeze的特定技术,这可以让代码向前和向后兼容,另外主观上很多人不喜欢新的语法。

def 返回方法的名字作为标志符

定义一个方法的结果不在是nil, 取而代之的是方法名字的标识符。一个规范的例子是使得一个方法称为私有的。

class Client
  def initialize(host, port)
    # ...
  end

  private def do_request(method, path, body, **headers)
    # ...
  end

  def get(path, **headers)
    do_request(:get, path, nil, **headers)
  end
end

同样它也提供了更好的添加方法修饰符的方式, 下面是一个使用 Module#prepend 来包装一个方法完成前/后调用。

module Around
  def around(method)
    prepend(Module.new do
      define_method(method) do |*args, &block|
        send(:"before_#{method}") if respond_to?(:"before_#{method}", true)
        result = super(*args, &block)
        send(:"after_#{method}") if respond_to?(:"after_#{method}", true)
        result
      end
    end)
    method
  end
end

class Example
  extend Around

  around def call
    puts "call"
  end

  def before_call
    puts "before"
  end

  def after_call
    puts "after"
  end
end

Example.new.call

输出

before
call
after

define_method和define_singleton_method方法也被更改为返回标识符而不是他们的参数。

有理数和复数字面量

我们已经有了整数(1) 和浮点数(1.0) 字面量, 现在我们也有有理数(1r)和复数(1i)字面量了。

他们配合Ruby的类型转换机制可以轻松搞定数学操作,好比,一个数学上的有理数1/3可以在Ruby中写作1/3r。3i会生成复数0+3i。这意味着复数可以写成标准的标准的数学符号, 2+3i 生成复数2+3i!

数组/枚举 #to_h

Ruby 2.0.0中许多类都有一个#to_h方法,现在数组和任何其他包含枚举的类也都有#to_h方法了。

[[:id, 42], [:name, "Arthur"]].to_h      #=> {:id=>42, :name=>"Arthur"}

require "set"
Set[[:id, 42], [:name, "Arthur"]].to_h   #=> {:id=>42, :name=>"Arthur"}

这将在所有Hash上返回数组的枚举方法中派上用场。

headers = {"Content-Length" => 42, "Content-Type" => "text/html"}
headers.map {|k, v| [k.downcase, v]}.to_h
#=> {"content_length" => 42, "content_type" => "text/html"}

细粒度方法缓存

Ruby2.1之前使用一个全局方法缓存,当代码中任何一个地方定义一个方法,模块引入,模块对象拓展时,这一全局方法缓存都会失效。这使得一些类--如OpenStruct -- 以及一些技术 -- 如exception tagging -- 出于性能原因而不可用。

现在不会有这个问题了, Ruby 2.1 使用基于类层次的方法缓存, 只有讨论中的类和任意子类才会失效缓存。

一个已经加入到 RubyVM 类的方法会返回一些方法缓存状态的调试信息。

class Foo
end

RubyVM.stat   #=> {:global_method_state=>133, :global_constant_state=>820, 
                    :class_serial=>5689}

# setting constant increments :global_constant_state

Foo::Bar = "bar"

RubyVM.stat(:global_constant_state)   #=> 821

# defining instance method increments :class_serial

class Foo
  def foo
  end
end

RubyVM.stat(:class_serial)            #=> 5690

# defining global method increments :global_method_state

def foo
end

RubyVM.stat(:global_method_state)     #=> 134

异常

现在异常有一个#cause方法,会返回造成的异常。当你从一个异常恢复并引发其他异常时,这个异常会被自动设置。

require "socket"

module MyProject
  Error = Class.new(StandardError)
  NotFoundError = Class.new(Error)
  ConnectionError = Class.new(Error)

  def self.get(path)
    response = do_get(path)
    raise NotFoundError, "#{path} not found" if response.code == "404"
    response.body
  rescue Errno::ECONNREFUSED, SocketError => e
    raise ConnectionError
  end
end

begin
  MyProject.get("/example")
rescue MyProject::Error => e
  e         #=> #<MyProject::ConnectionError: MyProject::ConnectionError>
  e.cause   #=> #<Errno::ECONNREFUSED: Connection refused - connect(2) for 
            "example.com" port 80>
end

目前引发的错误不会输出到任何地方,并且rescue不会关注原因。但是只有当调试时,自动设置异常原因才有些帮助。

Exceptions 也添加了#backtrace_locations方法  不知为啥2.0.0奇怪的消失了. 他返回Thread::Backtrace::Location 对象,而不是字符串,这更加方便访问调用栈信息了。

分代垃圾回收

Ruby2.1引入了分代垃圾回收器,将所有的对象分到青年代和老年代。在标记阶段,只会对青年代运行一次常规的垃圾回收,因为较老的对象被标记的频率更低。1.9.3引入的延迟清理系统会执行清理工作。一个对象会被放入老年代如果它活过一次青年代垃圾回收。

如果老年代中有对象引用青年代中的对象, 但是你仅仅看到青年代似乎没有引用其他对象,并且你可能回收了一个正在使用的对象。写屏障可以通过对引用青年代对象的老年代对象(像old_arry.push(young_string))添加“记忆位”避免这一行为。当青年代标记阶段会考虑进这个“记忆位”。

大部分的分代垃圾回收器需要在所有对象上加上写屏障, 但是随着许多用于Ruby的第三方C拓展的出现使得这变得不可能, 因此一个变通方案是那些没有写屏障保护的对象(“阴暗”对象)将不会被放入老年代中。 这样并不理想,因为你不能体会到分代回收器的所有好处, 但是它最大化了向后兼容。

然而标记阶段是目前写屏障的最大开销,并且你的代码决定了你的性能。

垃圾回收

GC.start方法有两个新的关键字参数,full_mark和immediate_sweep. 两个默认都是true。

full_mark如果设为true,则青年代和老年代都会被标记,否则只标记青年代。 immediate_sweep如果设为true,一次‘stop the world’的清理将会被执行,否则将会执行延迟清理, 直到被要求清理时,并且以最低要求进行清理。

GC.start # trigger a full GC run
GC.start(full_mark: false) # only collect young generation
GC.start(immediate_sweep: false) # mark only
GC.start(full_mark: false, immediate_sweep: false) # minor GC

GC.stress调试选项现在可以设置一个integer的标志,来控制强调垃圾回收器的哪一部分。

GC.stress = true # full GC at every opportunity
GC.stress = 1    # minor marking at every opportunity
GC.stress = 2    # lazy sweep at every opportunity

GC.stat的输出已经被更新为包括更多细节,以及方法本身现在接受一个关键字参数来返回该关键字对应的值,而不是构建并返回完整的散列。

GC.stat                    #=> {:count=>6, ... }
GC.stat(:major_gc_count)   #=> 2
GC.stat(:minor_gc_count)   #=> 4

GC 也添加了一个latest_gc_info方法,来返回最近垃圾回收的相关信息。

GC.latest_gc_info   #=> {:major_by=>:oldgen, :gc_by=>:newobj, :have_finalizer=>false, :immediate_sweep=>false}
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。 2KB翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。


2KB项目(www.2kb.com,源码交易平台),提供担保交易、源码交易、虚拟商品、在家创业、在线创业、任务交易、网站设计、软件设计、网络兼职、站长交易、域名交易、链接买卖、网站交易、广告买卖、站长培训、建站美工等服务

  • 全部评论(0)
资讯详情页最新发布上方横幅
最新发布的资讯信息
【计算机/互联网|】Nginx出现502错误(2020-01-20 21:02)
【计算机/互联网|】网站运营全智能软手V0.1版发布(2020-01-20 12:16)
【计算机/互联网|】淘宝这是怎么了?(2020-01-19 19:15)
【行业动态|】谷歌关闭小米智能摄像头,因为窃听器显示了陌生人家中的照片(2020-01-15 09:42)
【行业动态|】据报道谷歌新闻终止了数字杂志,退还主动订阅(2020-01-15 09:39)
【行业动态|】康佳将OLED电视带到美国与LG和索尼竞争(2020-01-15 09:38)
【行业动态|】2020年最佳AV接收机(2020-01-15 09:35)
【行业动态|】2020年最佳流媒体设备:Roku,Apple TV,Firebar,Chromecast等(2020-01-15 09:31)
【行业动态|】CES 2020预览:更多的流媒体服务和订阅即将到来(2020-01-08 21:41)
【行业动态|】从埃隆·马斯克到杰夫·贝佐斯,这30位人物定义了2010年代(2020-01-01 15:14)
联系我们

Q Q: 7090832

电话:400-0011-990

邮箱:7090832@qq.com

时间:9:00-23:00

联系客服
商家入住 服务咨询 投拆建议 联系客服
0577-67068160
手机版

扫一扫进手机版
返回顶部