如何将类类型作为函数参数传递
Posted
技术标签:
【中文标题】如何将类类型作为函数参数传递【英文标题】:How to pass a class type as a function parameter 【发布时间】:2014-06-19 14:24:21 【问题描述】:我有一个通用函数,它调用 Web 服务并将 JSON 响应序列化回对象。
class func invokeService<T>(service: String, withParams params: Dictionary<String, String>, returningClass: AnyClass, completionHandler handler: ((T) -> ()))
/* Construct the URL, call the service and parse the response */
我想要完成的是这个 Java 代码的等价物
public <T> T invokeService(final String serviceURLSuffix, final Map<String, String> params,
final Class<T> classTypeToReturn)
我尝试完成的方法签名是否正确?
更具体地说,将AnyClass
指定为参数类型
正确的做法?
调用该方法时,我将 MyObject.self
作为返回类值传递,但我得到一个编译错误“无法将表达式的类型 '()' 转换为类型 'String'”
CastDAO.invokeService("test", withParams: ["test" : "test"], returningClass: CityInfo.self) cityInfo in /*...*/
编辑:
我尝试使用object_getClass
,正如holex 所提到的,但现在我得到了:
错误:“类型‘CityInfo.Type’不符合协议‘AnyObject’”
符合协议需要做什么?
class CityInfo : NSObject
var cityName: String?
var regionCode: String?
var regionName: String?
【问题讨论】:
我认为 Swift 的泛型不像 java 那样工作。因此推断者不可能那么聪明。 id 省略 ClassCastDAO.invokeService("test", withParams: ["test" : "test"]) (ci:CityInfo) in
您以错误的方式处理它:在 Swift 中,与 Objective-C 不同,类具有特定类型,甚至具有继承层次结构(即,如果类 B
继承自 A
,则 B.Type
也继承自A.Type
):
class A
class B: A
class C
// B inherits from A
let object: A = B()
// B.Type also inherits from A.Type
let type: A.Type = B.self
// Error: 'C' is not a subtype of 'A'
let type2: A.Type = C.self
这就是为什么你不应该使用AnyClass
,除非你真的想允许任何类。在这种情况下,正确的类型应该是T.Type
,因为它表达了returningClass
参数和闭包参数之间的联系。
实际上,使用它代替AnyClass
可以让编译器正确推断方法调用中的类型:
class func invokeService<T>(service: String, withParams params: Dictionary<String, String>, returningClass: T.Type, completionHandler handler: ((T) -> ()))
// The compiler correctly infers that T is the class of the instances of returningClass
handler(returningClass())
现在存在构造T
的实例以传递给handler
的问题:如果您现在尝试运行代码,编译器将抱怨T
不能用()
构造。理所当然地:T
必须被明确限制为要求它实现特定的初始化程序。
这可以通过如下协议来完成:
protocol Initable
init()
class CityInfo : NSObject, Initable
var cityName: String?
var regionCode: String?
var regionName: String?
// Nothing to change here, CityInfo already implements init()
那么您只需将invokeService
的通用约束从<T>
更改为<T: Initable>
。
提示
如果您遇到奇怪的错误,例如“无法将表达式的类型 '()' 转换为类型 'String'”,将方法调用的每个参数移至其自己的变量通常很有用。它有助于缩小导致错误的代码范围并发现类型推断问题:
let service = "test"
let params = ["test" : "test"]
let returningClass = CityInfo.self
CastDAO.invokeService(service, withParams: params, returningClass: returningClass) cityInfo in /*...*/
现在有两种可能性:错误转移到变量之一(这意味着存在错误的部分),或者您收到一条神秘消息,例如“无法将表达式的类型 ()
转换为类型 ($T6) -> ($T6) -> $T5
”。
后一个错误的原因是编译器无法推断您编写的内容的类型。在这种情况下,问题在于 T
仅用于闭包的参数中,并且您传递的闭包不指示任何特定类型,因此编译器不知道要推断什么类型。通过将returningClass
的类型更改为包含T
,您可以为编译器提供一种确定泛型参数的方法。
【讨论】:
感谢 T.Type 提示 - 正是我将类类型作为参数传递所需要的。 不使用T
用于表达returningClass
与传递给completionHandler
的对象之间的关系。如果使用Initiable.Type
,则此关系丢失。
与?泛型不允许写func somefunc<U>()
加戈,你指的是什么?【参考方案2】:
你可以通过这种方式获得AnyObject
的类:
斯威夫特 3.x
let myClass: AnyClass = type(of: self)
斯威夫特 2.x
let myClass: AnyClass = object_getClass(self)
如果您愿意,您可以稍后将其作为参数传递。
【讨论】:
你也可以self.dynamicType
每当我尝试使用 myClass 时,都会导致“使用未声明的类型“myClass”的错误。Swift 3,Xcode 和 ios 的最新版本
@Confused,我认为没有这样的问题,你可能需要给我更多关于上下文的信息。
我正在制作一个按钮。在那个按钮的代码中(它是 SpriteKit,所以在 touchesBegan 里面)我希望按钮调用一个类函数,所以需要一个对该类的引用。所以在按钮类中,我创建了一个变量来保存对该类的引用。但是我不能让它持有一个类,或者那个类的类型,不管正确的措辞/术语是什么。我只能让它存储对实例的引用。最好,我想将对 Class Type 的引用传递到按钮中。但我刚开始创建内部引用,甚至无法正常工作。
这是我在这里使用的方法和想法的延续:***.com/questions/41092440/…。从那以后,我得到了一些使用协议的逻辑,但很快就遇到了这样一个事实,我还需要找出关联的类型和泛型来获得我认为我可以用更简单的机制做的事情。或者只是复制并粘贴大量代码。这是我通常做的;)【参考方案3】:
我在 swift5 中有一个类似的用例:
class PlistUtils
static let shared = PlistUtils()
// write data
func saveItem<T: Encodable>(url: URL, value: T) -> Bool
let encoder = PropertyListEncoder()
do
let data = try encoder.encode(value)
try data.write(to: url)
return true
catch
print("encode error: \(error)")
return false
// read data
func loadItem<T: Decodable>(url: URL, type: T.Type) -> Any?
if let data = try? Data(contentsOf: url)
let decoder = PropertyListDecoder()
do
let result = try decoder.decode(type, from: data)
return result
catch
print("items decode failed ")
return nil
return nil
【讨论】:
我正在尝试做类似的事情,但在呼叫站点出现错误:Static method … requires that 'MyDecodable.Type' conform to 'Decodable'
。您介意更新您的答案以给loadItem
打个电话吗?
原来这是我必须了解.Type
和.self
(类型和元类型)之间区别的关键。【参考方案4】:
只需将此处的每个代码复制粘贴到 swift 文件中即可:
#另存为:APICaller.swift
import Foundation
struct APICaller
public static func get<T: Decodable>(url: String, receiveModel: T.Type, completion:@escaping (Decodable) -> ())
send(url: url, json: nil, receiveModel: receiveModel, completion: completion, httpMethod: "GET")
public static func post<T: Decodable>(url: String, json: [String: Any]?, receiveModel: T.Type, completion:@escaping (Decodable) -> ())
send(url: url, json: nil, receiveModel: receiveModel, completion: completion, httpMethod: "POST")
public static func delete<T: Decodable>(url: String, json: [String: Any]?, receiveModel: T.Type, completion:@escaping (Decodable) -> ())
send(url: url, json: nil, receiveModel: receiveModel, completion: completion, httpMethod: "DELETE")
private static func send<T: Decodable>(url: String, json: [String: Any]?, receiveModel: T.Type, completion:@escaping (Decodable) -> (), httpMethod: String)
// create post request
let urlURL: URL = URL(string: url)!
var httpRequest: URLRequest = URLRequest(url: urlURL)
httpRequest.httpMethod = httpMethod
if(json != nil)
// serialize map of strings to json object
let jsonData: Data = try! JSONSerialization.data(withJSONObject: json!)
// insert json data to the request
httpRequest.httpBody = jsonData
httpRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
// create an asynchronus task to post the request
let task = URLSession.shared.dataTask(with: httpRequest)
jsonData, response, error in
// on callback parse the json into the receiving model object
let receivedModelFilled: Decodable = Bundle.main.decode(receiveModel, from: jsonData!)
// cal the user callback with the constructed object from json
DispatchQueue.main.async
completion(receivedModelFilled)
task.resume()
#另存为:TestService.swift
import Foundation
struct TestService: Codable
let test: String
那么你可以这样使用它:
let urlString: String = "http://localhost/testService" <--- replace with your actual service url
// call the API in post request
APICaller.post(url: urlString, json: ["test": "test"], receiveModel: TestService.self, completion: testReponse in
// when response is received - do something with it in this callback
let testService: TestService = testReponse as! TestService
print("testService: \(testService)")
)
提示: 我使用在线服务将我的 JSON 转换为 swift 文件,所以我剩下的就是编写调用并处理响应 我用这个:https://app.quicktype.io 但你可以搜索你喜欢的那个
【讨论】:
【参考方案5】:斯威夫特 5
不完全相同的情况,但我遇到了类似的问题。最终帮助我的是:
func myFunction(_ myType: AnyClass)
switch type
case is MyCustomClass.Type:
//...
break
case is MyCustomClassTwo.Type:
//...
break
default: break
然后你可以像这样在所述类的实例中调用它:
myFunction(type(of: self))
希望这对我遇到同样情况的人有所帮助。
【讨论】:
【参考方案6】:使用obj-getclass
:
CastDAO.invokeService("test", withParams: ["test" : "test"], returningClass: obj-getclass(self)) cityInfo in /*...*/
假设 self 是一个城市信息对象。
【讨论】:
【参考方案7】:我最近遇到了这种情况,正在寻找一种方法来使我的 UINavigationController 对除子视图按钮之外的所有内容都不可见。我把它放在一个自定义导航控制器中:
// MARK:- UINavigationBar Override
private extension UINavigationBar
override open func point(inside point: CGPoint, with event: UIEvent?) -> Bool
// Make the navigation bar ignore interactions unless with a subview button
return self.point(inside: point, with: event, type: UIButton.self)
// MARK:- Button finding hit test
private extension UIView
func point<T: UIView>(inside point: CGPoint, with event: UIEvent?, type: T.Type) -> Bool
guard self.bounds.contains(point) else return false
if subviews.contains(where: $0.point(inside: convert(point, to: $0), with: event, type: type) )
return true
return self is T
不要忘记使用边界而不是框架,因为在调用之前转换点。
【讨论】:
以上是关于如何将类类型作为函数参数传递的主要内容,如果未能解决你的问题,请参考以下文章