If I have a method like:
func someMethod(contextPtr: UnsafeMutablePointer<Void>)
how do I get the object from the contextPtr?
func someMethod(contextPtr: UnsafeMutablePointer<Void>){
let object:MyObject = contextPtr.memory
}
gives:
'Void' is not convertible to 'MyObject'
What's the secret sauce
More detail:
What I'm actually doing here is setting up a global callback function for SCNetworkReachability:
func callback(reachability:SCNetworkReachability, flags: SCNetworkReachabilityFlags, info: UnsafeMutablePointer<Void>) {
let r:Reachability = info.memory
}
and then adding the callback as follows:
var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
var s = self
withUnsafeMutablePointer(&s) {
context.info = UnsafeMutablePointer($0)
}
SCNetworkReachabilitySetCallback(reachability, callback, &context)
This should work: pass the object pointer as an opaque unmanaged pointer
to the callback:
context.info = UnsafeMutablePointer(Unmanaged.passUnretained(myObject).toOpaque())
SCNetworkReachabilitySetCallback(reachability, callback, &context)
and retrieve in the callback via:
func callback(reachability:SCNetworkReachability, flags: SCNetworkReachabilityFlags, info: UnsafeMutablePointer<Void>) {
let myObject = Unmanaged<MyObject>.fromOpaque(COpaquePointer(info)).takeUnretainedValue()
}
Of course this assumes that some strong reference to the object exists
as long as the callback is installed, so that the object is not
deallocated.
Update: Note that both conversions from object pointer to void pointer
and back can be simplified if you are willing to use "unsafe" functions:
context.info = unsafeAddressOf(myObject)
// ...
myObject = unsafeBitCast(info, MyObject.self)
The generated assembly code is – as far as I can see – identical.
Update 2: See also How to cast self to UnsafeMutablePointer<Void> type in swift for more information
about the "bridging" and some helper functions which can be used here.
Swift 3 update (Xcode 8 beta 6):
var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
context.info = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque())
// ...
func callback(reachability:SCNetworkReachability, flags: SCNetworkReachabilityFlags, info: UnsafeMutableRawPointer?) {
if let info = info {
let myObject = Unmanaged<MyObject>.fromOpaque(info).takeUnretainedValue()
// ...
}
}
struct S {
var i: Int = 10
}
var first = S()
func foo(contextPtr: UnsafeMutablePointer<Void>){
let pS = UnsafeMutablePointer<S>(contextPtr)
pS.memory.i = 100
}
print(first.i) // 10
foo(&first)
print(first.i) // 100
if we need pass as UnsafeMutablePointer self to async function
import XCPlayground
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
import Foundation
// can be struct, class ...
class C {
let queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT)
var s: String = ""
func foo() {
var c = self
dispatch_async(queue) { () -> Void in
f(&c)
}
}
}
func f(pV: UnsafeMutablePointer<Void>) {
let pC = UnsafeMutablePointer<C>(pV)
sleep(1)
print(pC.memory.s)
}
var c1: C? = C()
c1!.s = "C1"
c1!.foo() // C1
var c2: C? = C()
c2!.s = "C2"
c2!.foo() // C2
c1 = nil
c2 = nil
print("test")
Related
I just encountered a strange behavior in swift's inheritance handling, when it comes to polymorphism and dynamic types. The following code shows the problem I encounter, which basically is: The dynamic type is recognized correctly (printed by print("type(of: self) = \(classType)")), but the generic function testGeneric uses the wrong type.
class Global {
static func testGeneric<T: TestSuperClass>(of type: T.Type) {
print("T.Type = \(T.self)")
}
}
class TestSuperClass {
func run() {
let classType = type(of: self)
print("type(of: self) = \(classType)")
Global.testGeneric(of: classType)
}
}
class TestClass: TestSuperClass {
}
class TestClass2: TestSuperClass {
override func run() {
let classType = type(of: self)
print("type(of: self) = \(classType)")
Global.testGeneric(of: classType)
}
}
let testClass = TestClass()
let testClass2 = TestClass2()
testClass.run()
testClass2.run()
the printed output is
type(of: self) = TestClass
T.Type = TestSuperClass
type(of: self) = TestClass2
T.Type = TestClass2
So basically when calling testClass.run(), type(of: self) yields TestClass, which I would expect. The problem then is that the generic function testGeneric, which is called immediately afterwards, somehow does not work with type TestClass, but uses TestSuperClass instead.
What I personally would expect is
type(of: self) = TestClass
T.Type = TestClass
type(of: self) = TestClass2
T.Type = TestClass2
i.e., the generic function testGeneric using the type TestClass instead of TestSuperClass when called via testClass.run().
Questions:
- Do you have an explanation for that?
- How can I obtain the behavior I had in mind?
In Swift, the compiler want's to know at compile time which generic type to "infer". Therefore, the type system will bind to the static type. There is no such thing as dynamic type inference.
Therefore the compiler generates the following (see comments):
class TestSuperClass {
func run() {
let classType = type(of: self) // static MetaType TestSuperClass.Type
print("type(of: self) = \(classType)") // dynamic type: TestClass
Global.testGeneric(of: classType) // infer to static type, i.e. testGeneric<TestSuperClass>
}
}
As a result, T.self is TestSuperClass in your case, because that's what the compiler is able to see:
static func testGeneric<T: TestSuperClass>(of type: T.Type) {
print("T.Type = \(T.self)")
}
What you maybe want is the following:
static func testGeneric<T: TestSuperClass>(of type: T.Type) {
print("T.Type = \(type)")
}
Here, you do not print the type of T, but the (dynamic) value of the parameter type, which in your case is TestClass
To answer the second question: You will not be able to change the dynamic type of the retured array; it will always be [TestSuperClass] - although it will contain TestClass objects:
class Global {
static func testGeneric<T: TestSuperClass>(of type: T.Type) {
print("T.Type = \(T.self)")
}
static func returnObjects<T: TestSuperClass>(of theType: T.Type) -> [T] {
let newObj = theType.init()
let newObjType = type(of:newObj)
print("type(of: newObj) = \(newObjType)")
return [newObj]
}
}
class TestSuperClass {
required init() {
print ("TestSuperClass.init")
}
func run() {
let classType = type(of: self)
print("type(of: self) = \(classType)")
Global.testGeneric(of: classType)
let array = Global.returnObjects(of: classType)
let arrayType = type(of:array)
print("type(of: self) = \(arrayType)")
print (array)
}
}
class TestClass: TestSuperClass {
required init() {
super.init()
print("TestClass.init")
}
}
let testClass = TestClass()
testClass.run()
TestSuperClass.init
TestClass.init
type(of: self) = TestClass
T.Type = TestSuperClass
TestSuperClass.init
TestClass.init
type(of: newObj) = TestClass
type(of: self) = Array < TestSuperClass >
[__lldb_expr_21.TestClass]
I'm having the following code in my class:
// MARK: - Lifecycle
init() {
authenticationContext = AuthenticationContext()
synchronizationContext = SynchronizationContext()
employeesCoordinator = EmployeesCoordinator()
serverErrorObserver =
NotificationObserver(notification: serverErrorNotification,
block: handleServerError) // <- Error
}
// MARK: - Listeners
private let serverErrorObserver: NotificationObserver!
private lazy var handleServerError: NSError -> () = {
[unowned self] (error) in
// Currently means that the token is expired, so remove stored instance
self.handleAuthorizationDidExpired()
}
It looks legit, but I'm getting the following complier error:
Use of 'self' in property access 'handleServerError' before all stored
properties are initialized
If it would help, this is the source behind NotificationObserver:
class ValueWrapper<T> {
let value: T
init(_ value: T) { self.value = value }
}
// Notification
struct Notification<A> {
let name: String
}
// Global Functions
func publish<A>(note: Notification<A>, value: A) {
let userInfo = ["value": ValueWrapper(value)]
NSNotificationCenter.defaultCenter().postNotificationName(note.name, object: nil, userInfo: userInfo)
}
//
class NotificationObserver {
let observer: NSObjectProtocol
init<A>(notification: Notification<A>, block aBlock: A -> ()) {
observer = NSNotificationCenter.defaultCenter().addObserverForName(notification.name, object: nil, queue: nil) { note in
let wrapper = note.userInfo?["value"] as? ValueWrapper<A>
if let value = wrapper?.value {
aBlock(value)
} else {
assert(false, "Couldn't understand user info")
}
}
}
deinit {
NSNotificationCenter.defaultCenter().removeObserver(observer)
}
}
// Global variables
let serverErrorNotification: Notification<NSError> = Notification(name: "ServerErrorNotification")
let synchronizationDidCompleteNotification: Notification<Int> = Notification(name: "SynchronizationDidCompleteNotification")
let authorizationDidCompleteNotification: Notification<Authorization> = Notification(name: "SynchronizationDidCompleteNotification")
You cannot call self until you have properly initialized the object using super.init()
if you have un-initialized let variables they should initialize before super.init() call.
so doing so you cannot call to self - closure calls to self
so you have to change let to var, then call super.init() before assigning closure
private let serverErrorObserver: NotificationObserver!
to
private var serverErrorObserver: NotificationObserver!
eg.
init() {
super.init()
authenticationContext = AuthenticationContext()
synchronizationContext = SynchronizationContext()
employeesCoordinator = EmployeesCoordinator()
serverErrorObserver =
NotificationObserver(notification: serverErrorNotification,
block: handleServerError) // <- Error
}
The problem is that you are accessing self in the init() in the following line:
self.handleAuthorizationDidExpired()
You can't do it until all of the stored properties are initialised. And the only property, which is not initialised yet in your case, is serverErrorObserver.
In order to fix it easily, you can mark this property in the following way:
private(set) var serverErrorObserver: NotificationObserver?
By marking it optional you tell the compiler that this property doesn't need to be initialised when the object is created.
If I have a closure in another closure is it enough to use unowned/weak once in the outer closure to avoid retain cycles?
Example:
foo.aClosure({[unowned self] (allowed: Bool) in
if allowed {
self.doStuff()
self.something.anotherClosure({ (s:String) -> (Void) in
self.doSomethingElse(s)
})
}
})
Only declaring weak or unowned self in the capture list of the outer closure is enough to avoid retain cycles if you don't create a strong reference to self within the outer closure (e.g. by doing: guard let strongSelf = self else { return }).
If you do create a strong reference within the closure, you must add a capture list to the inner closure to ensure that it captures your strong reference to self weakly.
Here are some examples:
import Foundation
import PlaygroundSupport
class SomeObject {
typealias OptionalOuterClosure = ((Int) -> Void)?
typealias InnerClosure = () -> Void
var outerClosure: OptionalOuterClosure
func setup() {
// Here are several examples of the outer closure that you can easily switch out below
// All of these outer closures contain inner closures that need references to self
// optionalChecks
// - has a capture list in the outer closure
// - uses the safe navigation operator (?) to ensure that self isn't nil
// this closure does NOT retain self, so you should not see the #2 calls below
let optionalChecks: OptionalOuterClosure = { [weak self] callNumber in
print("outerClosure \(callNumber)")
self?.delayCaller { [weak self] in
print("innerClosure \(callNumber)")
self?.doSomething(callNumber: callNumber)
}
}
// copiedSelfWithInnerCaptureList
// - has a capture list in the outer closure
// - creates a copy of self in the outer closure called strongSelf to ensure that self isn't nil
// - has a capture list in the inner closure
// - uses the safe navigation operator (?) to ensure strongSelf isn't nil
// this closure does NOT retain self, so you should not see the #2 calls below
let copiedSelfWithInnerCaptureList: OptionalOuterClosure = { [weak self] callNumber in
guard let strongSelf = self else { return }
print("outerClosure \(callNumber)")
strongSelf.delayCaller { [weak strongSelf] in
print("innerClosure \(callNumber)")
strongSelf?.doSomething(callNumber: callNumber)
}
}
// copiedSelfWithoutInnerCaptureList
// - has a capture list in the outer closure
// - creates a copy of self in the outer closure called strongSelf to ensure that self isn't nil
// - does NOT have a capture list in the inner closure and does NOT use safe navigation operator
// this closure DOES retain self, so you should see the doSomething #2 call below
let copiedSelfWithoutInnerCaptureList: OptionalOuterClosure = { [weak self] callNumber in
guard let strongSelf = self else { return }
print("outerClosure \(callNumber)")
strongSelf.delayCaller {
print("innerClosure \(callNumber)")
strongSelf.doSomething(callNumber: callNumber)
}
}
// retainingOuterClosure
// - does NOT have any capture lists
// this closure DOES retain self, so you should see the doSomething #2 call below
let retainingOuterClosure: OptionalOuterClosure = { callNumber in
print("outerClosure \(callNumber)")
self.delayCaller {
print("innerClosure \(callNumber)")
self.doSomething(callNumber: callNumber)
}
}
// Modify which outerClosure you would like to test here
outerClosure = copiedSelfWithInnerCaptureList
}
func doSomething(callNumber: Int) {
print("doSomething \(callNumber)")
}
func delayCaller(closure: #escaping InnerClosure) {
delay(seconds: 1, closure: closure)
}
deinit {
print("deinit")
}
}
// Handy delay method copied from: http://alisoftware.github.io/swift/closures/2016/07/25/closure-capture-1/
func delay(seconds: Int, closure: #escaping () -> Void) {
let time = DispatchTime.now() + .seconds(seconds)
DispatchQueue.main.asyncAfter(deadline: time) {
print("🕑")
closure()
}
}
var someObject: SomeObject? = SomeObject()
someObject?.setup()
// Keep a reference to the outer closure so we can later test if it retained someObject
let copiedOuterClosure = someObject!.outerClosure!
// Call the outer closure once just to make sure it works
copiedOuterClosure(1)
// Wait a second before we destroy someObject to give the first call a chance to work
delay(seconds: 1) {
// Run the outerClosure again to check if we retained someObject
copiedOuterClosure(2)
// Get rid of our reference to someObject before the inner closure runs
print("de-referencing someObject")
someObject = nil
}
// Keep the main run loop going so our async task can complete (need this due to how playgrounds work)
PlaygroundPage.current.needsIndefiniteExecution = true
Yes, however I would use weak over unowned because self.doStuff() with throw an exception if nil while if you use weak and its self?.doStuff() there won't be an exception thrown and it just won't execute.
You can test this in the playground with the following code:
typealias Closure = () -> Void
class ClosureObject {
var closure:Closure?
func saveClosure(closure:Closure?) {
self.closure = closure
}
}
let mainClosureObject = ClosureObject()
class TestObject {
let closureObject = ClosureObject()
func log() {
print("logged")
}
func run() {
mainClosureObject.saveClosure() {[weak self] in
self?.closureObject.saveClosure() {
self?.log()
}
}
}
}
var testObject:TestObject? = TestObject()
let closureObject = testObject?.closureObject
testObject?.run()
mainClosureObject.closure?()
closureObject?.closure?()
testObject = nil
closureObject?.closure?()
mainClosureObject.closure?()
closureObject?.closure?()
and compare it with:
typealias Closure = () -> Void
class ClosureObject {
var closure:Closure?
func saveClosure(closure:Closure?) {
self.closure = closure
}
}
let mainClosureObject = ClosureObject()
class TestObject {
let closureObject = ClosureObject()
func log() {
print("logged")
}
func run() {
mainClosureObject.saveClosure() {
self.closureObject.saveClosure() {
self.log()
}
}
}
}
var testObject:TestObject? = TestObject()
let closureObject = testObject?.closureObject
testObject?.run()
mainClosureObject.closure?()
closureObject?.closure?()
testObject = nil
closureObject?.closure?()
mainClosureObject.closure?()
closureObject?.closure?()
I am trying to apply reflection on a swift class (someClass) to invoke an init method that takes one argument (someArg), I managed to get the init selector and IMP that has 1 argument, but when I invoke the IMP it ends up calling the init with no arguments. In the Playground below I always get "called the wrong init" printed.
If I remove the override init I get the following error:
fatal error: use of unimplemented initializer 'init()' for class '__lldb_expr_15.someClass'
What am I missing?
import UIKit
public class someClass:NSObject{
init( num:someArg){
print("called the right init")
}
override init(){
print("called the wrong init")
}
}
public class someArg:NSObject{
override init(){
}
}
public class Test{
func reflect(){
let classType: NSObject.Type = someClass.self as NSObject.Type
let (initializerWithOneArgImp,selector) = getInitializerWithArguments(classType, argumentsCount: 1)
typealias initializerWithOneArgImpType = #convention(c) (AnyObject, Selector, AnyObject) -> (AnyObject)
let callback = unsafeBitCast(initializerWithOneArgImp , initializerWithOneArgImpType.self)
callback(classType,selector,someArg())
}
func getInitializerWithArguments(classType:AnyClass, argumentsCount:Int)->(IMP,Selector){
var methodCount:CUnsignedInt = 0
let methodList = class_copyMethodList(classType.self, &methodCount)
let n : Int = Int(methodCount)
for var i: Int = 0; i < n; i++ {
let methodSelector = method_getName(methodList[i])
let methodName:String = String(_sel:methodSelector)
if(methodName == "init")
{
let methodArgumentsCount = method_getNumberOfArguments(methodList[i])
if(methodArgumentsCount == UInt32(argumentsCount) + 1)
{
return (method_getImplementation(methodList[i]),methodSelector)
}
}
}
return (nil,nil)
}
}
var test = Test()
test.reflect()
Turns out, the non parametrized init has two arguments by default, and the parameterized init would have "initWithNum" as methodName.
if(methodName.hasPrefix("init"))
{
let methodArgumentsCount = method_getNumberOfArguments(methodList[i])
if(methodArgumentsCount == UInt32(argumentsCount) + 2)
{
return (method_getImplementation(methodList[i]),methodSelector)
}
}
I want to update the userInfo of the timer in the selector function every time the timer fires.
userInfo:
var timerDic = ["count": 0]
Timer:
Init: let timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("cont_read_USB:"), userInfo: timerDic, repeats: true)
selector function:
public func cont_read_USB(timer: NSTimer)
{
if var count = timer.userInfo?["count"] as? Int
{
count = count + 1
timer.userInfo["count"] = count
}
}
I get an error on the last line:
'AnyObject?' does not have a member named 'subscript'
What is wrong here?
In Objective_C this task worked with a NSMutableDictionary as userInfo
To make this work, declare timerDic as an NSMutableDictionary:
var timerDic:NSMutableDictionary = ["count": 0]
Then in your cont_read_USB function:
if let timerDic = timer.userInfo as? NSMutableDictionary {
if let count = timerDic["count"] as? Int {
timerDic["count"] = count + 1
}
}
Discussion:
Swift dictionaries are value types, so if you want to be able to update it you have to pass an object. By using an NSMutableDictionary you get an object type that is passed by reference, and it can be modified since it is a mutable dictionary.
Complete example for Swift 4+:
If you don't want to use an NSMutableDictionary, you can create your own class. Here is a complete example using a custom class:
import UIKit
class CustomTimerInfo {
var count = 0
}
class ViewController: UIViewController {
var myTimerInfo = CustomTimerInfo()
override func viewDidLoad() {
super.viewDidLoad()
_ = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(update), userInfo: myTimerInfo, repeats: true)
}
#objc func update(_ timer: Timer) {
guard let timerInfo = timer.userInfo as? CustomTimerInfo else { return }
timerInfo.count += 1
print(timerInfo.count)
}
}
When you run this in the simulator, the count that is printed increases every second.
NSTimer.userInfo is of type AnyObject so you need to cast it to your target object:
public func cont_read_USB(timer: NSTimer)
{
if var td = timer.userInfo as? Dictionary<String,Int> {
if var count = td["count"] {
count += 1
td["count"] = count
}
}
}