It appears that objc_setAssociatedObject causes objects to be released early.
I followed the method mentioned here to set the association.
import ObjectiveC
// Define a variable whose address we'll use as key.
// "let" doesn't work here.
var kSomeKey = "s"
…
func someFunc() {
var value = MyOtherClass()
objc_setAssociatedObject(target, &kSomeKey, value, UInt(OBJC_ASSOCIATION_RETAIN))
let assocValue : AnyObject! = objc_getAssociatedObject(target, &kSomeKey)
}
This results in the object being released during the objc_setAssociatedObject call. As you can see by this stacktrace.
[MyOtherClass dealloc]
_objc_object::sidetable_release(bool)
_object_set_associative_reference
MyApp.MyClass.someFunc()
I originally thought it might have had something to do with swift classes so I also tried standard Objective-C classes and deinit or dealloc are called during the objc_setAssociatedObject call.
Further adding to my confusion is that objc_getAssociatedObject appears to return a valid object and I can access it variables without error.
Is this a swift bug or have I used objc_setAssociatedObject incorrectly?
I am using Xcode6 beta5 in case that is relevant.
I guess your code would not even compile, because you are using "value" twice as a constant name.
This works fine as an AppDelegate in beta 6:
import ObjectiveC
var kSomeKey = "this_is_a_key"
class MyOtherClass {
var foo = "bar"
deinit {
println("deinit")
}
}
#UIApplicationMain
class AppDelegateTest: UIResponder, UIApplicationDelegate {
func someFunc() {
var value = MyOtherClass()
objc_setAssociatedObject(self, &kSomeKey, value, UInt(OBJC_ASSOCIATION_RETAIN))
let value2: AnyObject! = objc_getAssociatedObject(self, &kSomeKey)
println("type of is \(_stdlib_getTypeName(value2))")
let value3 = value2 as MyOtherClass
println("other is " + value3.foo)
println("end of someFunc()")
}
func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {
self.someFunc()
return true
}
}
I barely changed anything and the deinit method is never called.
Related
In RxSwift / RxCocoa you can create a reactive wrapper for a delegate (e.g. UIScrollViewDelegate or CLLocationManagerDelegate) to enable Rx observable sequences for certain delegate methods.
I am trying to implement this for the UIApplicationDelegate method applicationDidBecomeActive:
What I tried so far is pretty straightforward and similar to the DelegateProxy subclasses that are included in RxCocoa.
I created my DelegateProxy subclass:
class RxUIApplicationDelegateProxy: DelegateProxy, UIApplicationDelegate, DelegateProxyType {
static func currentDelegateFor(object: AnyObject) -> AnyObject? {
let application: UIApplication = object as! UIApplication
return application.delegate
}
static func setCurrentDelegate(delegate: AnyObject?, toObject object: AnyObject) {
let application: UIApplication = object as! UIApplication
application.delegate = delegate as? UIApplicationDelegate
}
}
And an Rx extension for UIApplication:
extension UIApplication {
public var rx_delegate: DelegateProxy {
return proxyForObject(RxUIApplicationDelegateProxy.self, self)
}
public var rx_applicationDidBecomeActive: Observable<Void> {
return rx_delegate.observe("applicationDidBecomeActive:")
.map { _ in
return
}
}
}
In my AppDelegate I subscribe to the observable:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// the usual setup
// and then:
application.rx_applicationDidBecomeActive
.subscribeNext { _ in
print("Active!")
}
.addDisposableTo(disposeBag)
return true
}
When I start my app "Active!" gets printed and then I get the following crash in RxCocoa's _RXDelegateProxy_ class:
Does anybody have an idea what the problem might be? Or has anybody successfully implemented something like rx_applicationDidBecomeActive?
It looks like a really tricky issue with RxSwift and memory management.
The default implementation of DelegateProxyType sets an instance of a delegate proxy (in this case, RxUIApplicationDelegateProxy) to the delegate of UIApplication.
It also stores the original AppDelegate as a property called forwardToDelegate so all the delegate methods can still be passed to it.
The problem is that, when the new app delegate is set:
application.delegate = delegate as? UIApplicationDelegate
the original one is deallocated! You can check it by overriding deinit in AppDelegate. The reasons are explained in this answer. And because the property forwardToDelegate is of type assign, your app crashes as the property points to a deallocated object.
I have found a workaround for that. I'm not really sure if it is a recommended way, so be warned. You can override a method from DelegateProxyType in RxUIApplicationDelegateProxy:
override func setForwardToDelegate(delegate: AnyObject?, retainDelegate: Bool) {
super.setForwardToDelegate(delegate, retainDelegate: true)
}
In normal circumstances, you don't want to retain the delegate as it leads to a retain cycle. But in this special case, this is not a problem: your UIApplication object will exist the entire time while your application is alive anyway.
For a project of mine, I have to create a proxy AppDelegate that will forward calls to a another AppDelegate.
UIApplicationDelegate havs a var window: UIWindow?. My question is, why can't I do this:
private lazy var realAppDelegate: UIApplicationDelegate = {
return AppDelegate()
}()
var window: UIWindow? {
get {
return realAppDelegate.window
}
set {
realAppDelegate.window = newValue
}
}
The problem with that code is that realAppDelegate.window is a UIWindow??.
Does anybody know why?
The property window of UIApplicationDelegate protocol is declared like this:
optional var window: UIWindow? { get set }
That means that it is an optional property (in the sense that "the class implementing the UIApplicationDelegate protocol is not requested to implement/have this property", like when you have #optional in Objective-C), and that property is of optional type Optional<UIWindow> (or UIWindow?).
That's why you have the double-optional type in the end, because that window property may or may not be implemented in realDelegate, and if it is, it will be itself of type Optional<UIWindow>/UIWindow?.
So basically what you want is to return the window property of your realAppDelegate… only if that realAppDelegate decided to declare that property itself (which it isn't requires to do, as it's optional var).
If the realAppDelegate did not implement window itself, you probably intend to return a nil UIWindow? as a result.
If your realAppDelegate did actually implement the window property, then you need to return it as is (wether this implementation returns an actual UIWindow or a nil one).
The easiest way to do that is to use the nil-coalescing operator ?? in Swift. a ?? b meaning that "if a is non-nil, then return a, but if a is nil, return b" (where if a is of type T?, then the whole expression is expected to return an object of type T, where in your case T is the type UIWindow?).
var window: UIWindow? {
get {
// If realAppDelegate.window (of type UIWindow??) is not implemented
// then return nil. Otherwise, return its value (of type UIWindow?)
return realAppDelegate.window ?? nil
// That code is equivalent (but more concise) to this kind of code:
// if let w = realAppDelegate.window { return w } else return nil
}
...
}
To implement the setter, that's another problem. According to this SO answer, directly accessing to the setter of an optional property of a protocol doesn't seem to be possible. But you can imagine a hack to workaround this, by declaring another protocol that makes this window property requirement mandatory, then try to cast to it in the setter:
#objc protocol UIApplicationDelegateWithWindow : UIApplicationDelegate {
var window: UIWindow? { get set }
}
class AppDelegateWrapper : UIApplicationDelegate {
...
var window: UIWindow? {
get {
return realAppDelegate.window ?? nil
}
set {
if let realAppDelWithWindow = realAppDelegate as? UIApplicationDelegateWithWindow
{
// Cast succeeded, so the 'window' property exists and is now accessible
realAppDelWithWindow.window = newValue
}
}
}
...
}
The declaration of the property is
optional var window: UIWindow? { get set }
The optional in the beginning means that the property doesn't have to be there at all, that's the second ?.
UIApplicationDelegate is a protocol, the class that implements it doesn't have to implement everything.
I need to pass a variable from the AppDelegate to another class that I have created to hold global variables of the project and I'm not able to find a way to make it work.
This is the code in the AppDelegate:
func application(application: UIApplication!, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData!) {
println("Device's token is: \(deviceToken)")
//Global Variables Class Instance
let globals:Globals = Globals()
globals.setDeviceToken("test1") //method1 not working
globals.deviceToken = "test2" //method2 not working
}
This is my Globals Class:
public class Globals {
var deviceToken = String()
init() {
//nothing
}
func setDeviceToken(s:String){
deviceToken = s
}
func getDeviceToken() -> String {
return deviceToken
}
}
If i try to print the value, from other files of the project, I'm not able to get anything, just an empty string.
class ViewController: UIViewController {
//Global Variables Class Instance
let globals:Globals = Globals()
override func viewDidLoad() {
println(globals.getDeviceToken()) //return empty string
println(globals.deviceToken) //return empty string
There are several patterns you can use to achieve what you want
You could access the AppDelegate through the UIApplication:
let delegate = UIApplication.sharedApplication().delegate as AppDelegate
let deviceToken = delegate.deviceToken
Look into singletons. A quick google search for 'Swift singleton' will get you a long way. The first result:
class SingletonB {
class var sharedInstance : SingletonB {
struct Static {
static let instance : SingletonB = SingletonB()
}
return Static.instance
}
}
Then use sharedInstance to instantiate the singleton anywhere and access the same variables.
The first one is quick and dirty, so for more serious projects I would recommend the singleton pattern.
There are probably a million ways to do this, but this should get you started
(More at this link, which explores a few ways to implement singletons: https://github.com/hpique/SwiftSingleton )
I simply solved my problem using NSUserDefaults
in the AppDelegate:
NSUserDefaults.standardUserDefaults().setObject(deviceToken, forKey: "deviceToken")
NSUserDefaults.standardUserDefaults().synchronize()
From other classes:
NSUserDefaults.standardUserDefaults().objectForKey("deviceToken")
Honestly I don't know if this is a good way to do it but it's working
I have a problem assigning the delegate for an object that is an instance of a class that defines a protocol in Swift as follows:
I simplified the code to the bare bones to exemplify the issue:
This is the class with the protocol
protocol TheProtocol {
func notifyDelegate()
}
class ClassWithProtocol: NSObject {
var delegate: TheProtocol?
fire() {
delegate?.notifyDelegate()
}
}
This is the class the conforms to the Protocol
class ClassConformingToProtocol: NSObject, TheProtocol {
var object: ClassWithProtocol?
func notifyDelegate() {
println("OK")
}
init() {
object = ClassWithProtocol()
object?.delegate = self // Compiler error - Cannot assign to the result of this expression
object?.fire()
}
}
I have tried all sort of alternatives to assign the delegate without success. Any idea what I am missing?
The Known Issues section of the Release Notes says:
You cannot conditionally assign to a property of an optional object.
(16922562)
For example, this is not supported:
let window: NSWindow? = NSApplication.sharedApplication.mainWindow
window?.title = "Currently experiencing problems"
So you should do something like if let realObject = object { ... }
I need to define a protocol which can be called in a class that use some Objective-c type
But doing that doesn't work:
enum NewsCellActionType: Int {
case Vote = 0
case Comments
case Time
}
#objc protocol NewsCellDelegate {
func newsCellDidSelectButton(cell: NewsCell, actionType: NewsCellActionType)
}
You get he error
Swift enums cannot be represented in Objective-C
If I don't put the #objc tag on my protocol it'll crash the app as soon as it's called in a class which adopt the protocol AND inherit from an Objective-C type class (like a UIViewController).
So my question is, how should I declare and pass my enum with the #objc tag?
Apple just announced today that Swift 1.2 (included with xcode 6.3) will support exposing enums to objective-c
https://developer.apple.com/swift/blog/
Swift enums are very different from Obj-C (or C) enums and they can't be passed directly to Obj-C.
As a workaround, you can declare your method with an Int parameter.
func newsCellDidSelectButton(cell: NewsCell, actionType: Int)
and pass it as NewsCellActionType.Vote.toRaw(). You won't be able to access the enum names from Obj-C though and it makes the code much more difficult.
A better solution might be to implement the enum in Obj-C (for example, in your briding header) because then it will be automatically accessible in Swift and it will be possible to pass it as a parameter.
EDIT
It is not required to add #objc simply to use it for an Obj-C class. If your code is pure Swift, you can use enums without problems, see the following example as a proof:
enum NewsCellActionType : Int {
case Vote = 0
case Comments
case Time
}
protocol NewsCellDelegate {
func newsCellDidSelectButton(cell: UITableViewCell?, actionType: NewsCellActionType )
}
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, NewsCellDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window!.backgroundColor = UIColor.whiteColor()
self.window!.makeKeyAndVisible()
test()
return true;
}
func newsCellDidSelectButton(cell: UITableViewCell?, actionType: NewsCellActionType) {
println(actionType.toRaw());
}
func test() {
self.newsCellDidSelectButton(nil, actionType: NewsCellActionType.Vote)
}
}