Alternative to performSelector in Swift? - TagMerge
6Alternative to performSelector in Swift?Alternative to performSelector in Swift?

Alternative to performSelector in Swift?

Asked 9 months ago
19
6 answers

Using closures

class A {
    var selectorClosure: (() -> Void)?

    func invoke() {
        self.selectorClosure?()
    }
}

var a = A()
a.selectorClosure = { println("Selector called") }
a.invoke()

Note that this is nothing new, even in Obj-C the new APIs prefer using blocks over performSelector (compare UIAlertView which uses respondsToSelector: and performSelector: to call delegate methods, with the new UIAlertController).

Using performSelector: is always unsafe and doesn't play well with ARC (hence the ARC warnings for performSelector:).

Source: link

16

As of Xcode 7, the full family of performSelector methods are available in Swift, including performSelectorOnMainThread() and performSelectorInBackground(). Enjoy!

Source: link

14

Approach A

Use NSThread.detachNewThreadSelector, good thing about this approach is that we can attach object to the message. Example code in ViewController:

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    let delay = 2.0 * Double(NSEC_PER_SEC)
    var time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
    dispatch_after(time, dispatch_get_main_queue(), {
        NSThread.detachNewThreadSelector(Selector("greetings:"), toTarget:self, withObject: "sunshine")
        })
}

func greetings(object: AnyObject?) {
    println("greetings world")
    println("attached object: \(object)")
}

Console log:

greetings world

attached object: sunshine

Approach B

This alternative was discovered earlier, I have also tested on device and simulator. The idea is to use following method of UIControl:

func sendAction(_ action: Selector, to target: AnyObject!, forEvent event: UIEvent!)

Example code in ViewController:

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    
    var control: UIControl = UIControl()
    control.sendAction(Selector("greetings"), to: self, forEvent: nil) // Use dispatch_after to invoke this line as block if delay is intended 
}

func greetings() {
    println("greetings world")
}

Console log:

greetings world

Approach C

NSTimer

class func scheduledTimerWithTimeInterval(_ seconds: NSTimeInterval,
                                      target target: AnyObject!,
                                 selector aSelector: Selector,
                                  userInfo userInfo: AnyObject!,
                                    repeats repeats: Bool) -> NSTimer!

Source: link

0

The function above, is the representation of this:
class ASampleClass
 {
    func aTestFunction() -> NSString
    {
        println("called correctly")
        return NSString(string: "test")
    }
 }
Well, on the c side, I was able to create this function
#include <stdio.h>
#include <dlfcn.h>

typedef struct objc_object *id;

id _performMethod(id stringMethod, id onObject)
{
    // ...
    // here the code (to be created) to translate stringMethod in _TFC14FirstSwiftTest12ASampleClass13aTestFunctionfS0_FT_CSo8NSString
    // ...

    id (*functionImplementation)(id);
    *(void **) (&functionImplementation) = dlsym(RTLD_DEFAULT, "_TFC14FirstSwiftTest12ASampleClass13aTestFunctionfS0_FT_CSo8NSString");

    char *error;

    if ((error = dlerror()) != NULL)  {
        printf("Method not found \n");
    } else {
        return functionImplementation(onObject); // <--- call the function
    }
    return NULL
}
And then called it on the swift side
let sampleClassInstance = ASampleClass()
println(_performMethod("aTestFunction", sampleClassInstance))

Source: link

0

I want to reload my table data inside a block in this method:
import UIKit
import AssetsLibrary

class AlbumsTableViewController: UITableViewController {

    var albums:ALAssetsGroup[] = []

    func loadAlbums(){
        let library = IAAssetsLibraryDefaultInstance

        library.enumerateGroupsWithTypes(ALAssetsGroupType(ALAssetsGroupAll),
            usingBlock: {(group, stop) in
                if group {
                    self.albums.append(group)
                }
                else {
                    dispatch_async(dispatch_get_main_queue(), {

                        self.tableView.reloadData()

                    })
                }
            }, failureBlock: { (error:NSError!) in println("Problem loading albums: (error)") })

    }

    override func viewDidLoad() {
        super.viewDidLoad()
        loadAlbums()

        // Uncomment the following line to preserve selection between presentations
        // self.clearsSelectionOnViewWillAppear = false

        // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
        //self.navigationItem.rightBarButtonItem = self.editButtonItem
    }
But the else block will not execute. The error I get is:
'performSelectorOnMainThread' is unavailable: 'performSelector' methods are unavailable
This simple C-function:
dispatch_async(dispatch_get_main_queue(), {

        // DO SOMETHING ON THE MAINTHREAD
        self.tableView.reloadData()
        })
What about launching your function with:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {

        loadAlbums()

})
Swift 2.X:
@IBAction func handlePan(gestureRecognizer: UIPanGestureRecognizer) {
    if gestureRecognizer.state == .Began || gestureRecognizer.state == .Changed {

        let translation = gestureRecognizer.translationInView(self.view)  
        // note: 'view' is optional and need to be unwrapped
        gestureRecognizer.view!.center = CGPointMake(gestureRecognizer.view!.center.x + translation.x, gestureRecognizer.view!.center.y + translation.y)  
        gestureRecognizer.setTranslation(CGPointMake(0,0), inView: self.view)  
    }  
}

Source: link

0

Using closures
class A {
    var selectorClosure: (() -> Void)?

    func invoke() {
        self.selectorClosure?()
    }
}

var a = A()
a.selectorClosure = { println("Selector called") }
a.invoke()
Use NSThread.detachNewThreadSelector, good thing about this approach is that we can attach object to the message. Example code in ViewController:
override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    let delay = 2.0 * Double(NSEC_PER_SEC)
    var time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
    dispatch_after(time, dispatch_get_main_queue(), {
        NSThread.detachNewThreadSelector(Selector("greetings:"), toTarget:self, withObject: "sunshine")
        })
}

func greetings(object: AnyObject?) {
    println("greetings world")
    println("attached object: \(object)")
}
This alternative was discovered earlier, I have also tested on device and simulator. The idea is to use following method of UIControl:
func sendAction(_ action: Selector, to target: AnyObject!, forEvent event: UIEvent!)
Example code in ViewController:
override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    
    var control: UIControl = UIControl()
    control.sendAction(Selector("greetings"), to: self, forEvent: nil) // Use dispatch_after to invoke this line as block if delay is intended 
}

func greetings() {
    println("greetings world")
}
NSTimer
class func scheduledTimerWithTimeInterval(_ seconds: NSTimeInterval,
                                      target target: AnyObject!,
                                 selector aSelector: Selector,
                                  userInfo userInfo: AnyObject!,
                                    repeats repeats: Bool) -> NSTimer!

Source: link

Recent Questions on swift

    Programming Languages