How to implement hashCode() in Swift for use by NSDictionary? - objective-c

I'm trying to use a Swift object as a key in an NSMutableDictionary. I've implemented hashValue and equality as usual and it works in a native Swift dict. But NSMutableDictionary never calls my hash function. What am I missing?
public final class Holder : Hashable, NSCopying
{
var val : Int = 0
public init( _ val : Int ) { self.val = val }
public var hashValue: Int {
return val
}
#objc public func copyWithZone(zone: NSZone) -> AnyObject {
return self
}
}
public func == (lhs: Holder, rhs: Holder) -> Bool {
return lhs.val==rhs.val
}
//var dict = Dictionary<Holder,String>()
var dict = NSMutableDictionary()
dict[ Holder(42) ]="foo"
dict[ Holder(42) ] // NSMutableDictionary will not call my hash function, returns nil here.
ANSWER:
I had to implement explicitly hash() and isEqual() like so:
#objc public func hash() -> Int {
return hashValue
}
#objc public func isEqual( obj : AnyObject ) -> Bool {
return (obj as? Holder)?.val == val
}

NSDictionary uses the hash and isEqual methods from NSObjectProtocol:
#objc var hash: Int { return hashValue }
#objc func isEqual(o: AnyObject?) -> Bool { return o as? Holder == self }
// ...
dict[ Holder(42) ] = "foo"
dict[ Holder(42) ] // returns "foo"
However, if possible I would recommend sticking with a Swift dictionary [Holder:String], as you tried previously.

Related

How to solve this strong dependency between those classes?

I have two classes A and B.
Class A has several properties. One of them are an instance of class B.
At some point in the main function I will define an instance a of A. I will need to do some computation on its property of type B.
This computation, however, needs another property of a.
The result is a.property3.computation(a.property1,someValue). I think it's ugly.
Here is some "pseudo-code" (in Kotlin but I am facing the same problem using other languages as well):
class B {
val property : Map<String,Int>
fun computation(val parameter1: Int, val parametre2: Double) : Int {
//doing some computation
return result
}
}
class A {
var property1 : Int
var property2 : Stirng
var property3 : B
}
fun main (){
val someValue = 0.4 //It's here to show the idea that the function `computation` needs also some other arguments that does not come from `A`'s propery
val a = A()
val result = a.property3.computation(a.property1,someValue) // here is the line that bothers me !
}
I think you should use Builder design pattern to remove computation function from B class like this:
class B {
val properties : MutableMap<String,Int> = HashMap()
fun setProperty(name: String, value: Int) {
this.properties[name] = value
}
}
class A {
var property1 : Int = 0
var property2 : String = ""
var property3 : B = B()
}
class Calculator(builder: Builder) {
private val property1 = builder.property1
private val someValue = builder.someValue
private val properties = builder.properties
companion object {
fun builder() = Builder()
}
fun computation() : Int {
//doing some computation
val result = property1 + someValue.toInt() + properties.getOrDefault("value", 0)
return result
}
class Builder {
var property1: Int = 0
var someValue: Double = 0.0;
var properties : MutableMap<String,Int> = HashMap()
fun property1(property1: Int): Builder {
this.property1 = property1
return this
}
fun someValue(someValue: Double): Builder {
this.someValue = someValue
return this
}
fun properties(properties : Map<String,Int>): Builder {
this.properties.putAll(properties);
return this
}
fun build(): Calculator {
return Calculator(this)
}
}
}
fun main (){
val someValue = 0.4 //It's here to show the idea that the function `computation` needs also some other arguments that does not come from `A`'s propery
val a = A()
a.property1 = 10
a.property3.setProperty("value", 20)
val result = Calculator.builder()
.properties(a.property3.properties)
.property1(a.property1)
.someValue(someValue)
.build()
.computation()
println(result)
}
May be you want this?
fun main (){
val someValue = 0.4
val a = A()
val result = with(a) {
property3.computation(property1,someValue)
}
}

Anonymous interface instance as interface property in Kotlin

I started learning Kotlin recently and I'd like to implement a simple postfix notation parser were instances of MathOperator interface pick values from the stack and return the result.
The implementation is trivial in plain Java
public interface MathOperator {
public float evaluate(Deque<Float> stack);
public MathOperator ADD = stack -> stack.pop() + stack.pop();
public MathOperator SUB = stack -> -stack.pop() + stack.pop();
public MathOperator MUL = stack -> stack.pop() * stack.pop();
}
However, when I try the same in Kotlin I get "Property getter or setter expected" error.
interface KTMathOperator {
fun evaluate(stack: Deque<Float>): Float
companion object {
val ADD: KTMathOperator = (stack) -> stack.pop() + stack.pop()
}
}
How can I implement the same interface and its properties in Kotlin concisely?
Unfortunately, in Kotlin SAM conversions are not so handy. You can rewrite your example like:
interface KTMathOperator {
fun evaluate(stack: Deque<Float>): Float
companion object {
val ADD: KTMathOperator = object : KTMathOperator {
override fun evaluate(stack: Deque<Float>): Float = stack.pop() + stack.pop()
}
}
}
also you can change evaluate to invoke. Then you can your ADD just like ADD(stack)
interface KTMathOperator {
operator fun invoke(stack: Deque<Float>): Float
companion object {
val ADD: KTMathOperator = object : KTMathOperator {
override fun invoke(stack: Deque<Float>): Float = stack.pop() + stack.pop()
}
}
}
fun main() {
val list = LinkedList<Float>().apply {
add(1.0f)
add(1.0f)
}
println(KTMathOperator.ADD(list)) // will print 2.0
}
If you're not too worried about maximum performance and a little strange code, you can also write like this :)
class KTMathOperator(private val operation: (Deque<Float>) -> Float) : (Deque<Float>) -> Float {
override fun invoke(stack: Deque<Float>): Float = operation(stack)
companion object {
val ADD: KTMathOperator = KTMathOperator { stack -> stack.pop() + stack.pop() }
}
}

Set property by string in Kotlin using reflection?

I have an object:
class User {
var id: String? = null
var name: String? = null
}
and list of pairs:
val fieldsToChange = listOf<Pair<String, String>>(Pair("name", "foo"), Pair("id", "bar"))
I would like to iterate trough list of pairs and set appropriate values for given properties using reflection.
Given class instance obj we can extract properties with names using obj::class.memberProperties.
We can construct a mapping from property name to property:
val nameToProperty = obj::class.memberProperties.associateBy(KProperty<*>::name)
Then we can iterate over fieldsToChange and retrieve property and set it:
fieldsToChange.forEach { (propertyName, propertyValue) ->
nameToProperty[propertyName]
.takeIf { it is KMutableProperty<*> } // take only "settable" (var) properties
?.let { it as KMutableProperty<*> } // cast it to mutable property so we can access setter
?.let { it.setter.call(obj, propertyValue) } // call the setter
}
Additionally, we can make this generic:
fun setFields(obj: Any, fieldsToChange: List<Pair<String, Any?>>) {
val nameToProperty = obj::class.memberProperties.associateBy(KProperty<*>::name)
fieldsToChange.forEach { (propertyName, propertyValue) ->
nameToProperty[propertyName]
.takeIf { it is KMutableProperty<*> }
?.let { it as KMutableProperty<*> }
?.let { it.setter.call(obj, propertyValue) }
}
}
val user = User()
setFields(user, fieldsToChange)
assert(user.name == "foo")
assert(user.id == "bar")
Possible improvement would be to optimize nameToProperty to contain only MutableProperties already casted to KMutableProperty
You can use a map as a delegate for that:
class User(map: Map<String, String>) {
val id: String by map
val name: String by map
}
val fieldsToChange = listOf(Pair("name", "foo"), Pair("id", "bar"))
val map = fieldsToChange.map { it.first to it.second }.toMap()
val user = User(map)

Calling class in swift from kotlin/native framework

I followed the tutorial: https://kotlinlang.org/docs/tutorials/native/mpp-ios-android.html and successfully export a jar file for Android and a framework for iOS. After I want to implement something more complex. I uses Android Studio Kotlin with the codes below:
Model.kt:
package org.kotlin.mpp.mobile.BusinessLogic
abstract class Model{
var _id:Long = 0
abstract fun PolymorphismTest()
}
Sales.kt:
package org.kotlin.mpp.mobile.BusinessLogic
class Sales : Model() {
init {
this._id = _counter
_counter++
}
companion object {
private var _counter: Long = 0
}
fun get_counter(): Long {
return _counter
}
private val _salesItems:MutableList<SalesItem> = ArrayList()
fun SalesItems(): MutableList<SalesItem> {
return _salesItems
}
fun TotalAmount():Double
{
var totalAmount:Double = 0.0
for(aSalesItem in _salesItems)
{
totalAmount += aSalesItem.SubTotal()
}
return totalAmount
}
fun AddSalesItem(salesItem: SalesItem)
{
this._salesItems.add(salesItem)
}
fun AddSalesItem(itemName:String, itemCode:String, quantity:Double, amount:Double )
{
val aSalesItem = SalesItem()
aSalesItem._itemCode = itemCode
aSalesItem._itemName = itemName
aSalesItem._quantity = quantity
aSalesItem._amount = amount
this.AddSalesItem(aSalesItem)
}
fun ToString(): String {
return "Sales: $this._id"
}
override fun PolymorphismTest() {
println("This is method from Sales")
}
}
SalesItem.kt:
package org.kotlin.mpp.mobile.BusinessLogic
class SalesItem : Model() {
init {
this._id = _counter
_counter++
}
companion object {
private var _counter: Long = 0
}
fun get_counter(): Long {
return _counter
}
var _sales: Sales? = null
var _amount:Double = 0.toDouble()
var _quantity:Double = 0.toDouble()
fun SubTotal(): Double {
return _amount * _quantity
}
var _itemName:String? = null
var _itemCode:String? = null
fun Sales():Sales?{
return _sales
}
fun SalesItem(sales:Sales)
{
_sales = sales
this._id = _counter
_counter++
}
fun ToString(): String {
return "Sales: $this._id"
}
override fun PolymorphismTest() {
println("This is method from SalesItem")
}
}
I export these codes into a framework then imported into Xcode and using Swift to call
ViewController.swift
import UIKit
import SharedCode
class ViewController: UIViewController{
override func viewDidLoad(){
super.viewDidLoad()
print("Creating Sales Object")
let sales = Sales() //error here
}
}
After that I met the errors of
Instances of kotlin.Error, kotlin.RuntimeException and subclasses aren't propagated from Kotlin to Objective-C/Swift.
Other exceptions can be propagated as NSError if method has or inherits #Throws annotation.
Uncaught Kotlin exception: kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen org.kotlin.mpp.mobile.BusinessLogic.Sales.Companion#228b588
at 0 SharedCode
Kotlin/Native has different threading model. The idea is that an instance of an object has to be frozen to be accessed from all threads. There is .freeze() extension method for that.
By default, object Smth are also frozen. In the code sniper you have mutable fields in companion object.
A possible workaround could be to replace companion object with an ordinary class, that you create explicitly
https://kotlinlang.org/docs/reference/native/concurrency.html#concurrency-in-kotlinnative
https://kotlinlang.org/docs/reference/native/immutability.html#immutability-in-kotlinnative

Kotlin nullable variable assignment

In Kotlin, is there any shorter syntax for this code:
if(swipeView == null){
swipeView = view.find<MeasureTypePieChart>(R.id.swipeableView)
}
First i tried this:
swipeView ?: view.find<MeasureTypePieChart>(R.id.swipeableView)
but then i realised that wasn't an assignment, so that code does nothing. Then i tried:
swipeView = swipeView ?: view.find<MeasureTypePieChart>(R.id.swipeableView)
Which works, but it a bit verbose. I would expect something like this:
swipeView ?= view.find<MeasureTypePieChart>
But unfortunately that doesn't work. Is there any way of accomplish this with a short syntax?
I know i can do this:
variable?.let { it = something } which works.
Shorter syntax would be to avoid swipeView from ever being null.
Local variable
If swipeView is a local variable then you can declare it non-null when initially assigning it:
val swipeView = ... ?: view.find<MeasureTypePieChart>(R.id.swipeableView)
Function argument
If swipeView is a function argument then you can use a default argument to ensure it is never null:
fun something(swipeView: View = view.find<MeasureTypePieChart>(R.id.swipeableView))
Class property
Read-only
If swipeView is a read-only class property (i.e. val) then you can use Kotlin's built-in Lazy:
val swipeView by lazy { view.find<MeasureTypePieChart>(R.id.swipeableView) }
Mutable
If swipeView is a mutable class property (i.e. var) then you can define your own delegate similar to Lazy but mutable. e.g. The following is based on kotlin/Lazy.kt:
interface MutableLazy<T> : Lazy<T> {
override var value: T
}
fun <T> mutableLazy(initializer: () -> T): MutableLazy<T> = SynchronizedMutableLazyImpl(initializer)
fun <T> mutableLazy(lock: Any?, initializer: () -> T): MutableLazy<T> = SynchronizedMutableLazyImpl(initializer, lock)
operator fun <T> MutableLazy<T>.getValue(thisRef: Any?, property: KProperty<*>): T = value
operator fun <T> MutableLazy<T>.setValue(thisRef: Any?, property: KProperty<*>, value: T) {
this.value = value
}
private object UNINITIALIZED_VALUE
private class SynchronizedMutableLazyImpl<T>(initializer: () -> T, lock: Any? = null) : MutableLazy<T>, Serializable {
private var initializer: (() -> T)? = initializer
#Volatile private var _value: Any? = UNINITIALIZED_VALUE
// final field is required to enable safe publication of constructed instance
private val lock = lock ?: this
override var value: T
get() {
val _v1 = _value
if (_v1 !== UNINITIALIZED_VALUE) {
#Suppress("UNCHECKED_CAST")
return _v1 as T
}
return synchronized(lock) {
val _v2 = _value
if (_v2 !== UNINITIALIZED_VALUE) {
#Suppress("UNCHECKED_CAST") (_v2 as T)
} else {
val typedValue = initializer!!()
_value = typedValue
initializer = null
typedValue
}
}
}
set(value) {
val _v1 = _value
if (_v1 !== UNINITIALIZED_VALUE) {
_value = value
} else synchronized(lock) {
_value = value
initializer = null
}
}
override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUE
override fun toString(): String = if (isInitialized()) value.toString() else "MutableLazy value not initialized yet."
}
Usage:
var swipeView by mutableLazy { view.find<MeasureTypePieChart>(R.id.swipeableView) }
The initializer will only be called if swipeView is read and is not initialized yet (from a previous read or write).