I'm looking for a way to nullify an escaping closure without calling it.
func should(completion: #escaping () -> Void) {
if something {
completion = nil
} else {
completion()
}
}
As long as I understand, the escaping closure can be called after the should function is finished, it will alive until it is get called.
Is there a way to nullify the closure(and captured values) without calling it?
In your example, the closure is in fact not escaping, since you're not assigning it to anything outside the function, so there's no need to nullify:
func should(completion: () -> Void) {
if !something {
completion()
}
}
But if it was escaping, say by assigning it to a property, then you could nullify the property to release it:
class Foo {
let fn: (() -> Void)?
func should(completion: #escaping () -> Void) {
fn = completion
}
func executeAndRelease() {
fn?()
fn = nil
}
}
Related
I am a Java/Kotlin programmer and new to swift. I want to pass a method reference in a constructor to save it for later use. The method I want to pass looks like this:
func refresh(completion: #escaping (Error?) -> ()) {
...
}
What I want to do is instantiate an object and pass this method as a parameter like this:
refreshControl = Refresher() {
compl -> Void in
self.refresh(completion: compl)
}
The class I want to pass this function to looks like this:
class Refresher {
let refresh: (#escaping (Error?) -> ()) -> Void
init(refresh: (#escaping (Error?) -> ()) -> Void) {
self.refresh = refresh
}
// call refresh somewhere later
}
This does not compile with error "Assigning non-escaping parameter 'refresh' to an #escaping closure. Not entirely sure what escaping does but I know I need it in the actual refresh function. I am not sure how to syntax this right. Any help would be appreciated.
But Xcode tells you what to do. It offers you a Fix-It:
init(refresh: #escaping (#escaping (Error?) -> ()) -> Void) {
Personally I would then get rid of the other #escaping stuff you've put in, as it is not needed. So:
class Refresher {
let refresh: ((Error?) -> ()) -> Void
init(refresh: #escaping ((Error?) -> ()) -> Void) {
self.refresh = refresh
}
}
And elsewhere:
func refresh(completion: (Error?) -> ()) {
// ...
}
func f() {
let refreshControl = Refresher() {
compl -> Void in
self.refresh(completion: compl)
}
// ...
}
If you have a sync function, how would you convert it to an async function?
func syncFunc() -> Int {
//Do something
}
Would this work?
func asyncFunc(_ syncFunc:()->Int, _ completion:(Int)->()) -> Int {
DispatchQueue.background.async{
completion( syncFunc() )
}
}
No, functions containing an asynchronous task cannot return any value from the closure body and both closures must be marked as #escaping
func asyncFunc(_ syncFunc: #escaping ()->Int, completion: #escaping (Int)->()) {
DispatchQueue.global().async {
completion( syncFunc() )
}
}
I have a func getData(completed: #escaping ()->()) that creates alamofire request with completion handler in one class. When alamofire ends it job, inside of this function i call completed() to notify that function ended its work. This func is called in other viewcontroller after button tap, but without completion handler, and then inside of this call i reload tableview with downloaded data as below.
Now i want to add to this func additional parameters to allow user modify URL of alamofire request, and get custom response. Parameters will be setted in other textfields. But now when i call downloadRepositories() i can't omit calling completion parameter.
How can i avoid calling completion handler in it or what other
completion handler should i implement?
Current alamofire request
class DataClass {
func getData(completed: #escaping () -> ()){
//alamofire request
Alamofire.request(url).responseJSON{
//reponse
completed()
}
}
And it's implementation
class OtherVC {
var dataClass = DataClass()
#objc func searchBtnTapped(sender: UIButton!){
dataclass.getData(){
self.TableView.reloadData()
}
}
}
What i would like to do
class DataClass {
func downloadRepositories(completed: #escaping () -> (), parameter1: String, parameter2: String) {
let parameters: Parameters = [ "parameterA": parameter, "parameterB": parameter2 ]
Alamofire.request(url, parameters: parameters).responseJSON{
//response
completed()
}
Implementation of modified func
class OtherVC {
var dataClass = DataClass()
#objc func searchBtnTapped(sender: UIButton!){
dataclass.getData(parameter1: someTextField.text, parameter2: someTextField2.text){
self.TableView.reloadData()
}
}
}
Of course I know that is not possible to pass parameters in func call like this, but how can do this other way?
Use like this:
func downloadRepositories(parameter1: String, parameter2: String, completed: #escaping () -> ()) {
let parameters: Parameters = [ "parameterA": parameter, "parameterB": parameter2 ]
Alamofire.request(url, parameters: parameters).responseJSON {
//response
completed()
}
You are on the right track)
If you want call func like this:
dataclass.getData(parameter1: someTextField.text, parameter2: someTextField2.text){
self.TableView.reloadData()
}
You just need change parameters order like this:
func downloadRepositories(parameter1: String, parameter2: String, completed: #escaping () -> ())
If i understand right and you want use Trailing Closures, then that's all
I'm having trouble tracking down a retain cycle. I think it's to do with the way I subscribe to events. Pseudo code is like this:
override func viewDidLoad() {
func handleEvent() {
self.doSomething()
}
subscribe("eventName", block: handleEvent)
}
deinit {
unsubscribe("eventName")
}
Will this create a retain cycle to self / my ViewController? And if so, how can I get around it? If I was using a closure, I could use [weak self], but since I'm passing a function, is there anyway to use a [weak self] equivalent?
Long story short, your code does retain a reference. (handleEvent->viewDidLoad->self), http://blog.xebia.com/function-references-in-swift-and-retain-cycles/ has some general strategies to avoid the issue. My recommendation would be to create a function reference, rather than declaring a function:
let eventHandler: () -> () = { [weak self] in
self?.doSomething()
}
subscribe("eventName", block: eventHandler)
If you reference a property or method from inside your class it'll create a retain cycle.
class SomeClass {
val a: (block: (() -> ()) -> ()) = ...
func run() {
func b() {
print("Hello, World!")
}
func c() {
self.someMethod()
}
func d() { [weak self]
self?.someMethod()
}
a(block: b) // no retain cycle
a(block: c) // retain cycle
a(block: d) // no retain cycle
}
func someMethod() {
print("Hello, World!")
}
}
I would like to call an #autoclosure parameter inside dispatch_async block.
func myFunc(#autoclosure condition: () -> Bool) {
dispatch_async(dispatch_get_main_queue()) {
if condition() {
println("Condition is true")
}
}
}
I get the following error.
Closure use of #noescape parameter may allow it to escape.
Is it possible to call #autoclosure parameter asynchronously?
Tested in Xcode 6.4 (6E23).
Yes, so long as you declare them #autoclosure(escaping):
Declarations with the autoclosure attribute imply noescape as well, except when passed the optional attribute escaping.
So this should do it:
func myFunc(#autoclosure(escaping) condition: () -> Bool) {
dispatch_async(dispatch_get_main_queue()) {
if condition() {
println("Condition is true")
}
}
}
Updating the answer from Airspeed Velocity, you can pass weak self directly into escaping autoclosure.
var boolValue: Bool = true
func myFunc(condition: #autoclosure #escaping () -> Bool?) {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
if let condition = condition() {
print("Condition is \(condition ? "true" : "false")")
}
}
}
func main() {
weak var weakSelf = self
myFunc(condition: weakSelf?.boolValue)
}