EXC_BAD_ACCESS when appending to array class property - arrays

I'm new to working with swift and have been converting some ios firebase cordova plugin stuff, but ran into a situation that I don't fully understand regarding arrays. I have two snippets of code, one that works and one that doesn't.
Works
var admobTestDevices: [Any] = []
var randomString: String = "abc123"
#objc(FirebasePlugin)
class FirebasePlugin : CDVPlugin {
func something() {
admobTestDevices.append(randomString)
}
}
Doesn't work
#objc(FirebasePlugin)
class FirebasePlugin : CDVPlugin {
var admobTestDevices: [Any] = []
var randomString: String = "abc123"
func something() {
admobTestDevices.append(randomString)
}
}
The one that doesn't work produces Thread 1: EXC_BAD_ACCESS (code=1, address=0x8) as an error. Why does one work and the other doesn't? What is the proper way to have a mutable array as a class property?

For some reason collections become immutable in the subclass in some cases.
You could fix this two ways:
1. override init() initialise the array then call super.init
class FirebasePlugin : CDVPlugin {
var admobTestDevices: [Any] = []
var randomString: String = "abc123"
override init() {
admobTestDevices = [Any]()
super.init()
}
func something() {
admobTestDevices.append(randomString)
}
}
2. Use the lazy modifier so that the array is initialised when first used.
class FirebasePlugin : CDVPlugin {
lazy var admobTestDevices: [Any] = []
var randomString: String = "abc123"
func something() {
admobTestDevices.append(randomString)
}
}

I’m not trying to store an array directly on my CDVPlugin subclass, but I’m storing a struct Queue<T> that has an Array<T> as a top-level member and it was causing the same error that you saw. My solution was to repeat the array initialization in a pluginInitialize method:
struct Queue<T>: ExpressibleByArrayLiteral {
public private(set) var elements: Array<T>
public init() {
self.elements = []
}
public init(arrayLiteral elements: T...) {
self.elements = elements
}
// ...other members...
}
#objc(MyPlugin) class MyPlugin: CDVPlugin {
var requestQueue: Queue<DownloadRequest> = []
override func pluginInitialize() {
requestQueue = Queue<DownloadRequest>()
}
// ...other members...
}
This sort of code would ordinarily not be necessary go in a custom initializer, but the CDVPlugin source warns against subclassing the initializer and says to use pluginInitialize instead. Cordova is clearly doing something unusual here so I’m inclined to trust them on that.

Related

How to avoid a retain cycle when using an array of delegates in Swift [duplicate]

This question already has answers here:
Using as a concrete type conforming to protocol AnyObject is not supported
(6 answers)
Closed 5 years ago.
In one of my classes I use an array of delegates (the class is a singleton). This is causing an retain cycle. I know I can avoid the retain cycle when I use only one delegate by making the delegate weak. But this is not working for my array of delegates.
How can I avoid this retain cycle.
Example:
protocol SomeDelegate: class {
func someFunction()
}
My Class
class SomeClass {
// This class is a singleton!
static let sharedInstance = SomeClass()
var delegates = [SomeDelegate]() // this is causing a retain cycle
weak var delegate: SomeDelegate? // this is ok.
... other code...
}
The problem is that weakDelegates is a strong reference and its reference to its elements of type WeakDelegateContainer is a strong reference.
Your situation is why the class NSHashTable exists. Initialize using weakObjects(). This will give you a set of ARC-weak references, each of which will be nilified and removed when the referenced object goes out of existence (with no need for any extra bookkeeping on your part, and no need for your WeakDelegateContainer type).
Your set will have to be typed as holding AnyObject, but you can easily mediate to ensure that you are supplying and retrieving SomeDelegate-conformant objects:
let list = NSHashTable<AnyObject>.weakObjects()
func addToList(_ obj:SomeDelegate) {
list.add(obj)
}
func retrieveFromList(_ obj:SomeDelegate) -> SomeDelegate? {
if let result = list.member(obj) as? SomeDelegate {
return result
}
return nil
}
func retrieveAllFromList() -> [SomeDelegate] {
return list.allObjects as! [SomeDelegate]
}
The function retrieveAllFromList() lists only objects that still exist. Any object that has gone out existence has been changed to nil in the NSHashTable and is not included in allObjects. That is what I mean by "no extra bookkeeping"; the NSHashTable has already done the bookkeeping.
Here is code that tests it:
func test() {
let c = SomeClass() // adopter of SomeDelegate
self.addToList(c)
if let cc = self.retrieveFromList(c) {
cc.someFunction()
}
print(self.retrieveAllFromList()) // one SomeClass object
delay(1) {
print(self.retrieveAllFromList()) // empty
}
}
Alternatively, you can use NSPointerArray. Its elements are pointer-to-void, which can be a little verbose to use in Swift, but you only have to write your accessor functions once (credit to https://stackoverflow.com/a/33310021/341994):
let parr = NSPointerArray.weakObjects()
func addToArray(_ obj:SomeDelegate) {
let ptr = Unmanaged<AnyObject>.passUnretained(obj).toOpaque()
self.parr.addPointer(ptr)
}
func fetchFromArray(at ix:Int) -> SomeDelegate? {
if let ptr = self.parr.pointer(at:ix) {
let obj = Unmanaged<AnyObject>.fromOpaque(ptr).takeUnretainedValue()
if let del = obj as? SomeDelegate {
return del
}
}
return nil
}
Here is code to test it:
let c = SomeClass()
self.addToArray(c)
for ix in 0..<self.parr.count {
if let del = self.fetchFromArray(at:ix) {
del.someFunction() // called
}
}
delay(1) {
print(self.parr.count) // 1
for ix in 0..<self.parr.count {
if let del = self.fetchFromArray(at:ix) {
del.someFunction() // not called
}
}
}
Interestingly, after our SomeClass goes out of existence, our array's count remains at 1 — but cycling through it to call someFunction, there is no call to someFunction. That is because the SomeClass pointer in the array has been replaced by nil. Unlike NSHashTable, the array is not automatically purged of its nil elements. They do no harm, because our accessor code has guarded against error, but if you would like to compact the array, here's a trick for doing it (https://stackoverflow.com/a/40274426/341994):
self.parr.addPointer(nil)
self.parr.compact()
I found the solution in Using as a concrete type conforming to protocol AnyObject is not supported. All credits to Kyle Redfearn.
My solution
protocol SomeDelegate: class {
func someFunction()
}
class WeakDelegateContainer : AnyObject {
weak var weakDelegate: SomeDelegate?
}
class SomeClass {
// This class is a singleton!
static let sharedInstance = SomeClass()
fileprivate var weakDelegates = [WeakDelegateContainer]()
func addDelegate(_ newDelegate: SomeDelegate) {
let container = WeakDelegateContainer()
container.weakDelegate = newDelegate
weakDelegates.append(container)
}
func removeDelegate(_ delegateToRemove: SomeDelegate) {
// In my case: SomeDelegate will always be of the type UIViewController
if let vcDelegateToRemove = delegateToRemove as? UIViewController {
for i in (0...weakDelegates.count - 1).reversed() {
if weakDelegates[i].weakDelegate == nil {
// object that is referenced no longer exists
weakDelegates.remove(at: i)
continue
}
if let vcDelegate = weakDelegates[i].weakDelegate as? UIViewController {
if vcDelegate === vcDelegateToRemove {
weakDelegates.remove(at: i)
}
}
}
}
}
... other code ...
}

Cast to right generic from array in Swift

I have a Protocol called Composite.
This protocol has an array composites: [Composite]
I also have a generic subclass GenericSubclass<T>: Composite
When iterating over the array the best I can come up with looks like this:
for item in composites {
if let item = item as? GenericSubclass<A> {
let sc = SomeOtherClass<A>
} else if let item = item as? GenericSubclass<B> {
let sc = SomeOtherClass<B>
} //and so on...
}
Is there any way to get a hold of GenericSubclass without specifying the Generic? In my use case there is absolutely no need for me to know about the T. I just have to instantiate another class with the same generic type.
Any help is much appreciated.
It's not clear what you're trying to accomplish with the "generic" (pun intended) class names you've chosen. I don't think there's a way to directly accomplish what you want. I.e. you can't just leave it as a generic T because the compiler needs some way to determine what T will be in use at runtime.
However, one way to solve the issue is to hoist the API into the Composite protocol:
protocol Composite {
var composites: [Composite] { get set }
func otherClass() -> OtherProtocol
}
protocol OtherProtocol { }
class GenericSubclass<T>: Composite {
var composites: [Composite] = []
func otherClass() -> OtherProtocol {
return SomeOtherClass<T>()
}
}
class SomeOtherClass<T>: OtherProtocol {}
So now when you implement your loop, you can rely on the fact that since each element is a Composite, you know it must provide an instance of OtherProtocol via the otherClass() method:
var c = GenericSubclass<Int>()
c.composites = [GenericSubclass<Double>(), GenericSubclass<Int>(), GenericSubclass<Character>()]
for item in c.composites {
let sc = item.otherClass()
print(sc)
}
Alternatively, if only GenericSubclass should vend an OtherProtocol, you can make the return type Optional and define an extension for all the other implementations of Composite:
protocol Composite {
var composites: [Composite] { get set }
func optionalClass() -> OtherProtocol?
}
extension Composite {
func optionalClass() -> OtherProtocol? {
return nil
}
}
I did some experiment on this in the playground and i came up with this
protocol Composite {
var composites: [Composite] { get set }
}
class GenericSubclass<T>: Composite {
var composites: [Composite] = []
}
let subclass = GenericSubclass<String>()
for item in subclass.composites {
let className = String(describing: type(of: item))
let aClassType = NSClassFromString(className) as! NSObject.Type
let instance = aClassType.init() // we create a new object
print(instance) //Output: GenericSubclass<String>
}
Hope this will help someone.
I think it's not possible to do that in array.
While you creat some different GenericSubclass<T> then put it in array , you will lose <T> no matter the composites is [Composite] or [Any].
// this line won't compile
let array = [GenericSubclass<Int>(),GenericSubclass<Double>()]
//error: heterogenous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional
You want donging something like this func below, the param should be GenericSubclass<T> to compile success
func genericFunc<T>(param:GenericSubclass<T>) {
let sc = SomeOtherClass<T>()
print(sc)
}
Anyway you can implement it with member var for the instance like the code below:
class Subclass {
var type : Any
init(type : Any) {
self.type = type
}
}
class SomeOtherClass : CustomDebugStringConvertible{
var type : Any
init(type : Any) {
self.type = type
}
var debugDescription: String{
return String(describing: type.self)
}
}
let array : [Subclass] = [Subclass(type : Int.self),Subclass(type : Double.self),Subclass(type : String.self)]
let scArray = array.flatMap {SomeOtherClass(type:$0.type.self)}
print(scArray) // prints [Int, Double, String]
You need to add one method to protocol which creates new item of Type supported this protocol. So now you can use enums, structs and classes without any knowledge of creating object of specific type.
You can play in playground with the following code:
import UIKit
//This is your protocol
protocol MyAwesomeProtocol {
//this methods leaves implementaion detailes
//to concrete type
func createNewObject()->MyAwesomeProtocol
}
//Just create empty string
extension String: MyAwesomeProtocol {
func createNewObject() -> MyAwesomeProtocol {
return String()
}
}
//create Enum with default value
extension UIControlState: MyAwesomeProtocol {
func createNewObject() -> MyAwesomeProtocol {
return UIControlState.normal
}
}
//create viewController of any type
extension UIViewController: MyAwesomeProtocol {
func createNewObject() -> MyAwesomeProtocol {
return type(of:self).init()
}
}
//This is test function
//it creates array of newly created items and prints them out
//in terminal
func doSomeCoolStuffWith(items:[MyAwesomeProtocol]){
var newItems = [MyAwesomeProtocol]()
for anItem in items {
let newOne = anItem.createNewObject()
newItems.append(newOne)
}
print("created new ones:\n\(newItems)\nfrom old ones:\n\(items)\n")
}
doSomeCoolStuffWith(items: [UIControlState.focused,UIControlState.disabled])
doSomeCoolStuffWith(items: [UISplitViewController(),UINavigationController(),UICollectionViewController()])
doSomeCoolStuffWith(items: ["I","love","swift"])
This will produce the following result:
created new ones:
[__C.UIControlState(rawValue: 0), __C.UIControlState(rawValue: 0)]
from old ones:
[__C.UIControlState(rawValue: 8), __C.UIControlState(rawValue: 2)]
created new ones:
[<UISplitViewController: 0x7fa8ee7092d0>, <UINavigationController: 0x7fa8f0044a00>, <UICollectionViewController: 0x7fa8ee705f30>]
from old ones:
[<UISplitViewController: 0x7fa8ee7011e0>, <UINavigationController: 0x7fa8f004e600>, <UICollectionViewController: 0x7fa8ee708fb0>]
created new ones:
["", "", ""]
from old ones:
["I", "love", "swift"]

How to check if an object is in array

Edit: The problem is already solved by #vacawama. But if you are looking for an answer for NSObject classes, you should implement isEqual function which is NSObjectProtocol. Otherwise you gonna get an error says: " Redundant conformance of 'classname' to protocol 'Equatable' "
You can check this for details: Swift 2.2, Contains Method not working
In swift, how can i check if an object is in array?
I have a simple class like this;
class Test: {
private var _number: Int!
private var _type: String!
var number: Int {
return _number
}
var type: String {
return _type
}
init (number: Int, type: String) {
self._number = number
self._type = type
}
}
Also i have this class;
class TestRandom {
private let _numberArr: [Int] = [1,2,3,4,5,6,7,8,9,10]
private let _typeArr: [String] = ["x","y","z"]
public private(set) var _testArr: [Test] = []
private var _randomTest: Test!
func randomTestPicker () {
repeat {
let randomNumber = Int(arc4random_uniform(UInt32(self._numberArr.count)))
let randomType = Int(arc4random_uniform(UInt32(self._typeArr.count)))
self._randomTest = Test(number: self._numberArr[randomNumber], type: self._typeArr[randomType])
} while self._testArr.contains(_randomTest)
}
}
All i want to do is to pick different objects. Lets say i have x2,y4,x6,z3,z8,y2 in _testArr. When i call randomTestPicker, it should not pick x2 or z8. Because they are already in array.
I have tried contains as you see. However it did not work for me. Is there any solution that i can use for this purpose? Or what is the best way to do this?
Edit: I tried self._testArr.contains{$0 === _randomTest} but not working neither.
You can't use contains that way since your class doesn't conform to the Equatable protocol.
Add :Equatable to your class definition and implement the == function which compares two of your objects:
class Test: Equatable {
private var _number: Int!
private var _type: String!
var number: Int {
return _number
}
var type: String {
return _type
}
init (number: Int, type: String) {
self._number = number
self._type = type
}
}
func ==(lhs: Test, rhs: Test) -> Bool {
return lhs.number == rhs.number && lhs.type == rhs.type
}
The other way this could have been done is to use the predicate form of contains. The predicate takes two objects and returns a Bool indicating if they match. In that case, you would write:
self._testArr.contains { $0.number == _randomTest.number && $0.type == _randomTest.type }
As you can see, in this case the closure is essentially the == function from above, so implementing the Equatable protocol is the cleaner way to do it.
The closure { $0 === _randomTest } doesn't work because that only tests if the objects are the same instance. In your case, you need to check if the two objects have the same properties, and you are not interested if they are same instance. The way you are creating the objects, you never would create an instance that is already in the array, so this check would always return false.

Using KVO to tell when elements have been added to an array

I want to check if elements have been added to an array in swift using KVO, and I essentially copied the example from Apple's documentation, but when the code runs, it does not catch when the size of the array updates. Here is what I have now:
class ShowDirectory: NSObject {
var shows = [Show]()
dynamic var showCount = Int()
func updateDate(x: Int) {
showCount = x
}
}
class MyObserver: NSObject {
var objectToObserve = ShowDirectory()
override init() {
super.init()
objectToObserve.addObserver(self, forKeyPath: "showCount", options: .New, context: &myContext)
}
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if context == &myContext {
if let newValue = change?[NSKeyValueChangeNewKey] {
print("\(newValue) shows were added")
}
} else {
super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
}
}
deinit {
objectToObserve.removeObserver(self, forKeyPath: "myDate", context: &myContext)
}
}
After I add the shows to the array, I set showCount equal to the number of elements in the array, however, it does not print "X shows were added" to console. My viewDidLoad() function simply calls the function that adds elements to the array, and nothing else at the moment.
You unfortunately cannot add as an observer to an Int, as it does not subclass NSObject
See the Apple Docs and search for "Key-Value Observing"
You can use key-value observing with a Swift class, as long as the class inherits from the NSObject class.
Otherwise, your KVO boiler-plate code looks good to me.
If you want to be notified when your array's contents change, you could try what #Paul Patterson recommends and use a proxy object

How to observe an array of NSObjects in swift?

I am new in swift language and my problem is about how to use observable/observer pattern in swift.
I want to make my array to be observable in my SocketManager class so it can be observed by my UIViewController class. I have used the Observable class written by Andrew J Wagner which I got from this link:
http://www.drewag.me/posts/swift-kvo-substitute-observable-variables
I have the array:
var marketIndexList: Array< MarketIndex > = []
which will get its data from a server. This list will be updated every time a new data received from server. After I got the values of my Array from server I want to make it of type Observable class which is implemented by the above link:
marketIndexList = Observable(marketIndexList)
But I got this error:
'MarketIndex' is not identical to 'AnyObject'
MarketIndex is a class of type NSObject which has some properties of type String.
This is the Observable class that I have used:
import Foundation
class Observable {
typealias DidChangeHandler = (oldValue: Array<MarketIndex>?, newValue: Array<MarketIndex>) -> ()
var value : Array<MarketIndex> = [] {
didSet {
for (owner, handlers) in self.observers {
for handler in handlers {
handler(oldValue: oldValue, newValue: value)
}
}
}
}
init(_ value: Array<MarketIndex>) {
self.value = value
}
func addObserverForOwner(owner: IndexViewController, triggerImmediately: Bool, handler: DidChangeHandler) {
if let index = self.indexOfOwner(owner) {
// since the owner exists, add the handler to the existing array
self.observers[index].handlers.append(handler)
} else {
// since the owner does not already exist, add a new tuple with the
// owner and an array with the handler
self.observers.append(owner: owner, handlers: [handler])
}
if (triggerImmediately) {
// Trigger the handler immediately since it was requested
handler(oldValue: nil, newValue: self.value)
}
}
func removeObserversForOwner(owner: AnyObject) {
if let index = self.indexOfOwner(owner) {
self.observers.removeAtIndex(index)
}
}
// #pragma mark - Private Properties
var observers: [(owner: IndexViewController, handlers: [DidChangeHandler])] = []
// #pragma mark - Private Methods
func indexOfOwner(owner: AnyObject) -> Int? {
var index : Int = 0
for (possibleOwner, handlers) in self.observers {
if possibleOwner === owner {
return index
}
index++
}
return nil
}
}
Can anyone tell me what the problem is?
Also does anyone know a way to observe an array of objects in swift?
I would appreciate any help.
Thanks in advance.
The error is because marketIndexList is defined as Array<MarketIndex> but you assigned Observable instance. Perhaps you wanted to do something like this:
var observableList: Observable = Observable([])
var marketIndexList: Array<MarketIndex> = [MarketIndex(), MarketIndex()]
observableList.value = marketIndexList
// Or maybe
observableList = Observable(marketIndexList)
By the way, you can also use Objective-C KVO from Swift. Just mark the property as dynamic and make sure the class inherits NSObject to make the property observable. For example:
class ObservableClass: NSObject {
dynamic var value = [Int]()
}
This post is good to read for KVO in Swift in addition to what you referred to.
https://medium.com/proto-venture-technology/the-state-of-kvo-in-swift-aa5cb1e05cba

Resources