简体   繁体   English

Swift闭包为Dictionary中的值

[英]Swift closure as values in Dictionary

I'm trying to use an Objective-C library which expects a NSDictionary as its return type. 我正在尝试使用一个Objective-C库,它希望NSDictionary作为其返回类型。 Within the NSDictionary , I can return values of any type, including blocks. NSDictionary ,我可以返回任何类型的值,包括块。

I cannot figure out if there is a way to write an analogous swift method that returns a Dictionary with a closure or a string as a possible value type. 我无法弄清楚是否有办法编写一个类似的swift方法,它返回一个带有闭包或字符串作为可能的值类型的Dictionary。

I can't use AnyObject as the value type for the dictionary so this doesn't work: 我不能使用AnyObject作为字典的值类型,所以这不起作用:

Dictionary<String,AnyObject> = ["Key":{(value:AnyObject) -> String in return value.description]

I get a Does not conform to protocol error from the compiler regarding the closure and AnyObject . 我得到一个Does not conform to protocol error编译器关于闭包和AnyObject Does not conform to protocol error

Is there a higher level type or protocol that both closures and basic types adhere to that I can use as the value type in a Dictionary? 是否有更高级别的类型或协议,闭包和基本类型都遵循我可以用作词典中的值类型?

Your basic problem is that in Objective-C closures (aka blocks) are represented as NSObject (or more precisely are transparently converted to NSObjects) while in Swift there is no such mapping. 你的基本问题是Objective-C闭包(aka块)表示为NSObject(或更准确地说是透明地转换为NSObjects),而在Swift中没有这样的映射。 This means that closures can not be directly stored in a Dictionary (short of using objective-c glue) 这意味着闭包不能直接存储在Dictionary中(不能使用objective-c glue)

The closest I can come up with is something along the lines of wrapping the value in an enum: 我能想出的最接近的是将值包装在枚举中:

enum DataType {
    case AsString(String)
    case AsClosure((AnyObject)->String)
}

var dict:Dictionary<String,DataType> = [
    "string":DataType.AsString("value"),
    "closure":DataType.AsClosure({(argument:AnyObject) -> String in
        return "value"
        }
    )
]

Which is probably a better solution anyway, because this way you have an explicit typing associated with individual arguments instead of it being implicit using some sort of inflection. 无论如何,这可能是一个更好的解决方案,因为通过这种方式,您可以使用与单个参数关联的显式类型,而不是使用某种变形隐式输入。

Alternatively, you could only wrap the closure and use a dictionary of type Dictionary<String,Any> . 或者,您只能包装闭包并使用Dictionary<String,Any>类型的Dictionary<String,Any>

If you still need a workaround, here is one; 如果你仍然需要一个解决方法,这里有一个; usage looks like this: 用法如下:

var d : [String : AnyObject] = [:]
d["a"] = Blocks.voidBlockToId({ println("Doing something") })
d["b"] = "Some string"
d["c"] = Blocks.intBlockToId({ i in println("Called with integer: \(i)") })

Blocks.toIntBlock(d["c"])(1)
Blocks.toVoidBlock(d["a"])()
println(d["b"])

Output is: 输出是:

  1. Called with integer: 1 用整数调用:1
  2. Doing something 做点什么
  3. Some string 一些字符串

The Blocks class is defined like this in Objective-C (with corresponding header and bridging header, I won't put those here): Blocks类在Objective-C中定义如下(带有相应的头和桥接头,我不会把它们放在这里):

typedef void(^VoidBlock)(void);
typedef void(^IntBlock)(int);

@implementation Blocks

+ (id) voidBlockToId: (VoidBlock) block { return block; }
+ (VoidBlock) toVoidBlock: (id) block { return (VoidBlock)block; }

+ (id) intBlockToId: (IntBlock) block { return block; }
+ (IntBlock) toIntBlock:(id)block { return (IntBlock)block; }

@end

You also need to add a new xyzBlockToId and toXyzBlock method for every new closure-type you want to use. 您还需要为要使用的每个新闭包类型添加新的xyzBlockToIdtoXyzBlock方法。 It's pretty ugly, but it works. 这很难看,但它确实有效。

There is another type, Any , that object, structs and primitives all conform to but functions do not. 还有另一种类型, Any ,该对象,结构和基元都符合但功能不符合。 There is no general function type, but you can describe a function type as its arguments and return value like this: 没有通用函数类型,但您可以将函数类型描述为其参数并返回值,如下所示:

Dictionary<String, (AnyObject) -> String>

Function Types 功能类型

Could you use an NSMutableDictionary ? 你能使用NSMutableDictionary吗?

Alternatively, this seemed to work for me using your example: 或者,这似乎适用于我使用您的示例:

1> import Foundation
2> var myDict: [String: (NSObject) -> String] = ["Key":{(value:NSObject) -> String in return value.description}]
myDict: [String : (NSObject) -> String] = {
  [0] = {
    key = "Key"
    value =
  }
}
3> myDict["Key"]!("Testing")

$R2: String = "Testing" $ R2:String =“测试”

Hmm, maybe this Swift-Code doesn't really help, because you want to have heterogenous dictionaries. 嗯,也许这个Swift-Code并没有真正帮助,因为你想拥有异类词典。

It's also not possible to put closures into an NSDictionary , it seems (as a closure does not conform to AnyObject ). 它似乎也不可能将闭包放入NSDictionary (因为闭包不符合AnyObject )。

You could also roll your own higher type using an enum . 您也可以使用enum滚动自己的更高类型。 You need the dictionary values to be either strings or functions which return strings, so define a type to represent that: 您需要字典值为字符串或返回字符串的函数,因此定义一个类型来表示:

enum MyDictVal {
  case ValString(String)
  case ValFunc(AnyObject -> String)
}

Then, you can put it in a dictionary: 然后,您可以将其放在字典中:

let d: Dictionary<String, MyDictVal> = [
  "a": .ValString("a")
, "b": .ValFunc({ (value) in value.description })
]

Then you'll need to process the dictionary values using pattern matching: 然后,您需要使用模式匹配来处理字典值:

switch d["b"] {
  case .ValString(let s):
    ...
  case .ValFunc(let f):
    ...
}

A more "generic" solution which should work with Any object, but shown with closures and function references. 一个更“通用”的解决方案,它应该与Any对象一起使用,但是带有闭包和函数引用。 Drop it into a playground and try it out! 把它扔进游乐场然后尝试一下!

// Wrapper for sticking non-objects in NSDictionary instances
class ObjectWrapper {
    let value: T
    init(_ value: T) {
        self.value = value
    }
}

// convenience to downcast `as! ObjectWrapper` and return its value
func getValueFromObjectWrapper(a: AnyObject) -> T {
    return (a as! ObjectWrapper).value
}

func wrappedObjectsInDictionary() -> NSDictionary {
    var dict = NSMutableDictionary()
    let appendToFoo: (String) -> String = NSString.stringByAppendingString("foo")
    let firstChar: (String) -> Character = { $0[$0.startIndex] }
    dict.setObject(ObjectWrapper(firstChar), forKey: "stringToChar")
    dict.setObject(ObjectWrapper(appendToFoo), forKey: "stringTransformer")
    return dict.copy() as! NSDictionary
}

let dict = wrappedObjectsInDictionary()
let appendToFoo: (String) -> String = getValueFromObjectWrapper(dict["stringTransformer"]!)
let strToChar: (String) -> Character = getValueFromObjectWrapper(dict["stringToChar"]!)

appendToFoo("bar") // "foobar"
strToChar("bar") // "b"

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM