我只想知道我是否理解正确。因此,根据苹果文档,当您创建一个闭包作为类实例的属性并且该闭包引用 self(创建闭包属性的类)时,这将导致强保留循环,最终类和闭包都不会被释放.因此,用外行的话来说,这意味着如果我有一个具有属性的类并且该属性是一个闭包,并且一旦我在声明闭包属性的类中分配该闭包的功能,这将导致强保留循环。这是我的意思的一个简单示例
class SomeViewController{
let myClosure:()->Void
public func someFunction(){
....bunch of code
myClosure = {
self.dismiss(blahBlahBlah)
}
}
}
这最终会导致一个保留循环,因为闭包保持对 self 的强引用,self 是创建闭包属性的类。现在根据苹果来解决这个问题,我会像这样定义一个捕获列表
class SomeViewController{
let myClosure:()->Void
public func someFunction(){
....bunch of code
myClosure = { [weak self] in
self?.dismiss(blahBlahBlah)
}
}
}
请注意我是如何将 [weak self] 放在 in 语句之前的。这让闭包知道只持有对 self 的弱引用而不是强引用。 IM 应该在 self 可以活出闭包时使用 weak,或者当闭包和 self 存活相同的持续时间时使用 unowned。
我从这里得到这些信息Automatic Reference Counting并且在该链接的“闭包的强引用循环”部分中,有这句话 “如果将闭包分配给类实例的属性,并且该闭包的主体捕获实例,也可能发生强引用循环” 我大约 90% 确定我理解正确,但只有 10% 的怀疑。那么我的判断正确吗?
我问这个的原因是因为我在我的 View 中为我的一些按钮使用了回调。这些回调会调用 self,但在那种情况下,self 是响应回调的 View Controller ,而不是实际 View 本身。这就是我怀疑自己的地方,因为我从那句话中强调了我认为我不需要将 [weak self] 放在所有这些按钮回调上,但我只是确定一下。这是一个例子
class SomeViewController {
let someSubview:UIView
override viewDidLoad() {
//Some Subview has a button and in that view I just have some action that gets fired off calling the callback here in the view controller I don't need to use the [weak self] in this scenario because closure property is not in this class correct?
someSubview.someButtonsCallback = {
....run code then
self?.dismiss(blahBlahBlah)
}
}
最佳答案
是的,这仍然会导致保留周期。
最简单的保留循环是 2 个对象,每个对象都具有彼此的强引用,但 3 向和更大的保留循环也是可能的。
在你的例子中,你有一个 View Controller ,它的 View 包含一个按钮(一个强引用)。该按钮对一个闭包有一个强引用。闭包使用 self 强烈引用 View Controller 。所以 View 拥有按钮。该按钮拥有闭包。闭包拥有 View Controller 。如果您关闭 View Controller (假设它是模态的),那么它应该被释放。然而,因为你有这个 3-way retain cycle,它不会被释放。您应该使用打印语句向 View Controller 添加一个 deinit 方法并尝试一下。
解决方案是添加一个捕获列表([weak self] 位),就像您在第一个示例中所做的那样。
注意一个常见的模式是添加一个捕获列表,然后将弱变量映射到闭包内的强变量:
let myClosure = { [weak self] in
guard let strongSelf = self else { return }
//...
strongSelf.doSomething()
}
这样,如果闭包仍然处于事件状态但拥有它的对象已被释放,则开头的 guard 语句会检测到 self 为 nil 并在闭包开始时退出。否则,您每次引用它时都必须打开可选的包装。
在某些情况下,捕获列表中的对象(在这些示例中为 self)也可能在正在执行的闭包中间被释放,这可能导致不可预测的行为。 (血淋淋的细节:只有当闭包运行在与捕获列表中对象的所有者不同的线程上时才会发生这种情况,但是完成处理程序通常在后台线程上运行,所以它确实会发生)
想象一下:
let myClosure = { [weak self] in
self?.step1() //1
//time-consuming code
self?.property = newValue //2
//more time-consuming code
self?.doSomething() //3
//even more time-consuming code
self?.doSomethingElse() //4
}
对于上面的代码,如果闭包在后台线程上运行,self 可能在第 1 步仍然有效,但是当您执行第 2 步时,self 已被释放。第 3 步和第 4 步也是如此。通过在闭包的开头添加 guard strongSelf = self else { return } ,您可以在闭包的入口处进行测试以确保 self 仍然有效,并且如果是,你让闭包创建一个强引用,它只在闭包运行时存在,并且防止 self 在闭包代码执行时被释放。)
关于Swift 闭包导致与 self 的强保留循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50261303/
我脑子里浮现出一些关于一种新编程语言的想法,所以我想我会尝试实现它。一位friend建议我尝试使用Treetop(Rubygem)来创建一个解析器。Treetop的文档很少,我以前从未做过这种事情。我的解析器表现得好像有一个无限循环,但没有堆栈跟踪;事实证明很难追踪到。有人可以指出入门级解析/AST指南的方向吗?我真的需要一些列出规则、常见用法等的东西来使用像Treetop这样的工具。我的语法分析器在GitHub上,以防有人希望帮助我改进它。class{initialize=lambda(name){receiver.name=name}greet=lambda{IO.puts("He
我有多个ActiveRecord子类Item的实例数组,我需要根据最早的事件循环打印。在这种情况下,我需要打印付款和维护日期,如下所示:ItemAmaintenancerequiredin5daysItemBpaymentrequiredin6daysItemApaymentrequiredin7daysItemBmaintenancerequiredin8days我目前有两个查询,用于查找maintenance和payment项目(非排他性查询),并输出如下内容:paymentrequiredin...maintenancerequiredin...有什么方法可以改善上述(丑陋的)代
我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("
#app/models/product.rbclassProduct我从Controller调用方法1。当我运行程序时。我收到一个错误:method_missing(atlinemethod2(param2)).rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/activerecord-5.0.0/lib/active_record/relation/batches.rb:59:in`block(2levels)infind_each... 最佳答案 classProduct说明:第一个是类
我明白了defa(&block)block.call(self)end和defa()yieldselfend导致相同的结果,如果我假设有这样一个blocka{}。我的问题是-因为我偶然发现了一些这样的代码,它是否有任何区别或者是否有任何优势(如果我不使用变量/引用block):defa(&block)yieldselfend这是一个我不理解&block用法的具体案例:defrule(code,name,&block)@rules=[]if@rules.nil?@rules 最佳答案 我能想到的唯一优点就是自省(introspecti
我目前正在用Ruby编写一个项目,它使用ActiveRecordgem进行数据库交互,我正在尝试使用ActiveRecord::Base.logger记录所有数据库事件具有以下代码的属性ActiveRecord::Base.logger=Logger.new(File.open('logs/database.log','a'))这适用于迁移等(出于某种原因似乎需要启用日志记录,因为它在禁用时会出现NilClass错误)但是当我尝试运行包含调用ActiveRecord对象的线程守护程序的项目时脚本失败并出现以下错误/System/Library/Frameworks/Ruby.frame
我是Ruby的新手,有些闭包逻辑让我感到困惑。考虑这段代码:array=[]foriin(1..5)array[5,5,5,5,5]这对我来说很有意义,因为i被绑定(bind)在循环之外,所以每次循环都会捕获相同的变量。使用每个block可以解决这个问题对我来说也很有意义:array=[](1..5).each{|i|array[1,2,3,4,5]...因为现在每次通过时都单独声明i。但现在我迷路了:为什么我不能通过引入一个中间变量来修复它?array=[]foriin1..5j=iarray[5,5,5,5,5]因为j每次循环都是新的,我认为每次循环都会捕获不同的变量。例如,这绝对
我正在尝试获得良好的Ruby编码风格。为防止意外调用具有相同名称的局部变量,我总是在适当的地方使用self.。但是现在我偶然发现了这个:classMyClass上面的代码导致错误privatemethodsanitize_namecalled但是当删除self.并仅使用sanitize_name时,它会起作用。这是为什么? 最佳答案 发生这种情况是因为无法使用显式接收器调用私有(private)方法,并且说self.sanitize_name是显式指定应该接收sanitize_name的对象(self),而不是依赖于隐式接收器(也是
我的rails3.1.6应用程序中有一个自定义访问器方法,它为一个属性分配一个值,即使该值不存在。my_attr属性是一个序列化的哈希,除非为空白,否则应与给定值合并指定了值,在这种情况下,它将当前值设置为空值。(添加了检查以确保值是它们应该的值,但为简洁起见被删除,因为它们不是我的问题的一部分。)我的setter定义为:defmy_attr=(new_val)cur_val=read_attribute(:my_attr)#storecurrentvalue#makesureweareworkingwithahash,andresetvalueifablankvalueisgiven
下面的代码工作正常:person={:a=>:A,:b=>:B,:c=>:C}berson={:a=>:A1,:b=>:B1,:c=>:C1}kerson=person.merge(berson)do|key,oldv,newv|ifkey==:aoldvelsifkey==:bnewvelsekeyendendputskerson.inspect但是如果我在“ifblock”中添加return,我会得到一个错误:person={:a=>:A,:b=>:B,:c=>:C}berson={:a=>:A1,:b=>:B1,:c=>:C1}kerson=person.merge(berson