2

I'm following this tutorial, especially I have problems to convert this function in Swift language:

- (id)init
{
    CFRunLoopSourceContext    context = {0, self, NULL, NULL, NULL, NULL, NULL,
                                    &RunLoopSourceScheduleRoutine,
                                    RunLoopSourceCancelRoutine,
                                    RunLoopSourcePerformRoutine};

    runLoopSource = CFRunLoopSourceCreate(NULL, 0, &context);
    commands = [[NSMutableArray alloc] init];

    return self;
}

of this, in the init function is context variable to give me problems.

In the code above, context is a variable of type : CFRunLoopSourceContext and the initialization of this object in the apple documentation is like this

so, I in my initialization, used the following code, concentrating on the schedule parameter :

var context = CFRunLoopSourceContext(version: 0, info: bridge(obj: self) ,
                                         retain: nil,
                                         release: nil,
                                         copyDescription: nil,
                                         equal: nil,
                                         hash: nil,
                                         schedule: RunLoopSourceScheduleRoutine,
                                         cancel: nil,
                                         perform: nil)

The function RunLoopSourceScheduleRoutine is like so :

    func RunLoopSourceScheduleRoutine(info:UnsafeMutableRawPointer? ,rl:CFRunLoop? , mode:CFRunLoopMode?)  {
    
    let obj :  RunLoopSource = Unmanaged<RunLoopSource>.fromOpaque(info!).takeUnretainedValue()
    let theContext = RunLoopContext(withSource: obj, andLoop: rl!)
    performSelector(onMainThread: #selector(myMethod), with: theContext, waitUntilDone: false)
    
}

but the compiler gives me the following error message : a c function pointer can only be formed from a reference to a 'func' or a literal closure

even if I make the following closure :

let runLoopSourceScheduleRoutine = { (info:UnsafeMutableRawPointer? ,rl:CFRunLoop? , mode:CFRunLoopMode?)-> Void in return
        
        let obj :  RunLoopSource = Unmanaged<RunLoopSource>.fromOpaque(info!).takeUnretainedValue()
        let theContext = RunLoopContext(withSource: obj, andLoop: rl!)
        performSelector(onMainThread: #selector(myMethod), with: theContext, waitUntilDone: false)
        
    }

and i put it like this :

var context = CFRunLoopSourceContext(version: 0, info: bridge(obj: self) ,
                                         retain: nil,
                                         release: nil,
                                         copyDescription: nil,
                                         equal: nil,
                                         hash: nil,
                                         schedule: runLoopSourceScheduleRoutine,
                                         cancel: nil,
                                         perform: nil)

gives me the same error. What is the problem ? any tips

0

1 Answer 1

9

If you use func RunLoopSourceScheduleRoutine() as the callback then it needs to be a global function, not an instance method.

If you define the callback as a closure then it needs to be marked as a pure C callback:

let runLoopSourceScheduleRoutine: @convention(c) (UnsafeMutableRawPointer?, CFRunLoop?, CFRunLoopMode?) -> Void =
    { (info, rl, mode) in

        let obj = Unmanaged<RunLoopSource>.fromOpaque(info!).takeUnretainedValue()
        // ...
}

Alternatively, pass a closure expression to that the compiler infers the type as a C callback:

var context = CFRunLoopSourceContext(version: 0, info: UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque()) ,
                 retain: nil,
                 release: nil,
                 copyDescription: nil,
                 equal: nil,
                 hash: nil,
                 schedule: { (info, rl , mode) in

                    let obj = Unmanaged<RunLoopSource>.fromOpaque(info!).takeUnretainedValue()
                    // ...
                 }, cancel: nil,
                 perform: nil)

Note also that you have to call the method on obj, not on self. I would recommend GCD instead of performSelector(), this allows the compiler to check that the method is called with correct arguments:

let obj = Unmanaged<RunLoopSource>.fromOpaque(info!).takeUnretainedValue()
let theContext = RunLoopContext(withSource: obj, andLoop: rl!)
DispatchQueue.main.async {
    obj.myMethod(theContext)
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.