我正在处理网络请求类,我担心崩溃。例如,当您将回调方法传递给函数时,使用闭包非常容易:
// some network client
func executeHttpRequest(#callback: (success: Bool) -> Void) {
// http request
callback(true)
}
// View Controller
func reload() {
networkClient.executeHttpRequest() { (success) -> Void in
self.myLabel.text = "it succeeded" // NOTE THIS CALL
}
}
然而,由于应该执行回调的进程是异步的,当回调与容器类元素(在本例中为 UIKit 类)交互时,它可能容易在以下情况下崩溃
因此,当回调最终被触发时,self.myLabel.text 可能会导致崩溃,因为 self 所引用的 View Controller 可能已经被释放.
到此为止。我是对的还是在内部 swift 实现了一些事情,这样就不会发生这种情况?
如果我是对的,那么这就是委托(delegate)模式派上用场的时候,因为委托(delegate)变量是弱引用,这意味着,如果释放它们,它们不会保留在内存中。
// some network client
// NOTE this variable is an OPTIONAL and it's also a WEAK REFERENCE
weak var delegate: NetworkClientDelegate?
func executeHttpRequest() {
// http request
if let delegate = self.delegate {
delegate.callback(success: true)
}
}
注意 self.delegate,因为它是一个弱引用,如果 View Controller (实现NetworkClientDelegate 协议(protocol))被释放,在这种情况下不会调用回调。
我的问题是:闭包是否有任何特殊之处使它们成为类似于此场景的不错选择,而不是回到委托(delegate)模式?如果提供闭包示例(不会因 nil 指针而导致崩溃),那将是很好的。谢谢。
最佳答案
So, when the callback finally gets fired, self.myLabel.text might result in a crash, as the View Controller to whom self was referring could already be deallocated.
如果 self 已作为强引用导入到闭包中,则可以保证 self 在闭包之前不会被释放已经执行完毕。也就是说,当调用闭包时 View Controller 仍然存在——即使此时它的 View 不可见。语句self.myLabel.text = "it succeeded"会被执行,但即使标签不可见,也不会崩溃。
不过,有一个微妙的问题在某些情况下会导致崩溃:
假设,闭包对 View Controller 有最后也是唯一的强引用。闭包完成,随后被释放,这也释放了对 View Controller 的最后一个强引用。这必然会调用view controller的dealloc方法。 dealloc 方法将在执行闭包的同一线程上执行。现在, View Controller 是一个 UIKit 对象,必须保证发送到该对象的所有方法都将在主线程 上执行。因此,IFF dealloc 将在其他线程上实际执行,您的代码可能会崩溃。
一个合适的方法需要“取消”一个异步任务,当它“关闭”时, View Controller 不再需要其结果。当然,这需要您的“任务”可以取消。
为了减轻您以前方法的一些问题,您可以在定义闭包时捕获 View Controller 的弱引用而不是强引用。这不会阻止异步任务运行直至完成,但在完成处理程序中,您可以检查 View Controller 是否仍然存在,如果它不再存在,则退出。
并且,如果您需要将 UIKit 对象“保留”在某个可能在某个任意线程上执行的闭包中,请注意这可能是最后强引用,并确保这是最后一个强引用在主线程上释放。
另请参阅:Using weak self in dispatch_async function
编辑:
My question would be: do closures have anything special that makes them a good choice in scenarios similar to this one, rather than going back to delegate pattern?
我会说,闭包在许多用例中是“更好”的方法:
委托(delegate)比闭包更容易出现循环引用等问题(因为它们由一个对象“拥有”,并且该对象可能被捕获为委托(delegate)中的变量)。
作为完成处理程序的闭包的经典用例还改进了代码的“局部性”,使其更易于理解:您声明当任务紧接着完成时会发生什么em> 调用任务的语句——无论这可能需要多长时间。
与常规“函数”相比,闭包的巨大优势在于闭包在定义时捕获整个“上下文”。也就是说,它可以在定义时引用变量并将它们“导入”到闭包中 - 并在它执行时使用它,无论何时发生这种情况,以及原始“堆栈”何时"在定义时已经消失了。
关于ios - 闭包与委托(delegate)模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30783819/
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende
这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下
我正在尝试解析一个文本文件,该文件每行包含可变数量的单词和数字,如下所示:foo4.500bar3.001.33foobar如何读取由空格而不是换行符分隔的文件?有什么方法可以设置File("file.txt").foreach方法以使用空格而不是换行符作为分隔符? 最佳答案 接受的答案将slurp文件,这可能是大文本文件的问题。更好的解决方案是IO.foreach.它是惯用的,将按字符流式传输文件:File.foreach(filename,""){|string|putsstring}包含“thisisanexample”结果的
给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最
1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里
了解Rails缓存如何工作的人可以真正帮助我。这是嵌套在Rails::Initializer.runblock中的代码:config.after_initializedoSomeClass.const_set'SOME_CONST','SOME_VAL'end现在,如果我运行script/server并发出请求,一切都很好。然而,在我的Rails应用程序的第二个请求中,一切都因单元化常量错误而变得糟糕。在生产模式下,我可以成功发出第二个请求,这意味着常量仍然存在。我已通过将以上内容更改为以下内容来解决问题:config.after_initializedorequire'some_cl
我是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每次循环都是新的,我认为每次循环都会捕获不同的变量。例如,这绝对
print"Enteryourpassword:"pass=STDIN.noecho(&:gets)puts"Yourpasswordis#{pass}!"输出:Enteryourpassword:input.rb:2:in`':undefinedmethod`noecho'for#>(NoMethodError) 最佳答案 一开始require'io/console'后来的Ruby1.9.3 关于ruby-为什么不能使用类IO的实例方法noecho?,我们在StackOverflow上