स्विफ्ट भाषा में प्रतिबिंब कैसे प्राप्त करें ?
मैं किसी क्लास को कैसे इंस्टेंट कर सकता हूं
[[NSClassFromString(@"Foo") alloc] init];
स्विफ्ट भाषा में प्रतिबिंब कैसे प्राप्त करें ?
मैं किसी क्लास को कैसे इंस्टेंट कर सकता हूं
[[NSClassFromString(@"Foo") alloc] init];
जवाबों:
कम हैसी समाधान यहाँ: https://stackoverflow.com/a/32265287/308315
ध्यान दें कि स्विफ्ट कक्षाओं को "MyViewController" के बजाय अब नाम दिया गया है, यह "AppName.MyViewController" होगा
XCode6- बीटा 6/7 के बाद से पदावनत
XCode6-beta 3 का उपयोग कर विकसित समाधान
एडविन वर्मियर के उत्तर के लिए धन्यवाद कि मैं स्विफ्ट कक्षाओं को ओब्ज-सी वर्ग में ऐसा करने के लिए कुछ बनाने में सक्षम था:
// swift file
// extend the NSObject class
extension NSObject {
// create a static method to get a swift class for a string name
class func swiftClassFromString(className: String) -> AnyClass! {
// get the project name
if var appName: String? = NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleName") as String? {
// generate the full name of your class (take a look into your "YourProject-swift.h" file)
let classStringName = "_TtC\(appName!.utf16count)\(appName)\(countElements(className))\(className)"
// return the class!
return NSClassFromString(classStringName)
}
return nil;
}
}
// obj-c file
#import "YourProject-Swift.h"
- (void)aMethod {
Class class = NSClassFromString(key);
if (!class)
class = [NSObject swiftClassFromString:(key)];
// do something with the class
}
संपादित करें
आप इसे शुद्ध ओब्ज-सी में भी कर सकते हैं:
- (Class)swiftClassFromString:(NSString *)className {
NSString *appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"];
NSString *classStringName = [NSString stringWithFormat:@"_TtC%d%@%d%@", appName.length, appName, className.length, className];
return NSClassFromString(classStringName);
}
मुझे आशा है कि यह किसी की मदद करेगा!
आपको @objc(SwiftClassName)
अपनी स्विफ्ट क्लास के ऊपर रखना होगा ।
पसंद:
@objc(SubClass)
class SubClass: SuperClass {...}
NSClassFromString()
फ़ंक्शन को @objc
एट्रिब्यूशन द्वारा निर्दिष्ट नाम की आवश्यकता होती है ।
@objc(SubClass)
काम करता है, लेकिन @objc class SubClass
नहीं?
@objc class SubClass
नाम में निहित है कि सबक्लास नाम के समान है। और @objc(SubClass) class SubClass
सीधे रूप में यह निर्दिष्ट किया गया है। मुझे लगता है कि कंपाइलर किसी कारण से पहले फॉर्म में ही इसका पता नहीं लगा सकता है।
यह वह तरीका है जिससे मैं वर्ग नाम से यूआईवीवाईकंट्रोलर को प्राप्त करता हूं
var className = "YourAppName.TestViewController"
let aClass = NSClassFromString(className) as! UIViewController.Type
let viewController = aClass()
अधिक जानकारी यहाँ है
IOS 9 में
var className = "YourAppName.TestViewController"
let aClass = NSClassFromString(className) as! UIViewController.Type
let viewController = aClass.init()
अद्यतन: बीटा 6 के साथ शुरू NSStringFromClass एक डॉट द्वारा अलग किए गए आपके बंडल नाम और वर्ग का नाम वापस कर देगा। तो यह कुछ-कुछ MyApp.MyClass जैसा होगा
स्विफ्ट कक्षाओं में एक निर्मित आंतरिक नाम होगा जो निम्नलिखित भागों से बना है:
तो आपका क्लास का नाम कुछ इस तरह होगा _TtC5MyApp7MyClass
आप इस नाम को एक स्ट्रिंग के रूप में निष्पादित करके प्राप्त कर सकते हैं:
var classString = NSStringFromClass(self.dynamicType)
अद्यतन स्विफ्ट 3 में यह बदल गया है:
var classString = NSStringFromClass(type(of: self))
उस स्ट्रिंग का उपयोग करके, आप निष्पादित करके अपने स्विफ्ट वर्ग का एक उदाहरण बना सकते हैं:
var anyobjectype : AnyObject.Type = NSClassFromString(classString)
var nsobjectype : NSObject.Type = anyobjectype as NSObject.Type
var rec: AnyObject = nsobjectype()
यह लगभग समान है
func NSClassFromString(_ aClassName: String!) -> AnyClass!
इस डॉक्टर की जाँच करें:
NSClass
कक्षाओं के साथ काम करता है , स्विफ्ट कक्षाओं के साथ नहीं। NSClassFromString("String")
लौटता है nil
, लेकिन NSClassFromString("NSString")
नहीं।
var myVar:NSClassFromString("myClassName")
String
एक वर्ग नहीं है; यह एक संरचना है
NSClassFromString
रिटर्न nil
।
मैं गतिशील रूप से किसी वस्तु को त्वरित करने में सक्षम था
var clazz: NSObject.Type = TestObject.self
var instance : NSObject = clazz()
if let testObject = instance as? TestObject {
println("yes!")
}
मैं बनाने के लिए एक तरह से नहीं मिला है AnyClass
एक से String
(Obj सी का उपयोग किए बिना)। मुझे लगता है कि वे आपको ऐसा नहीं करना चाहते क्योंकि यह मूल रूप से टाइप सिस्टम को तोड़ता है।
स्विफ्ट 2 के लिए, मैंने इसे और अधिक तेज़ी से करने के लिए एक बहुत ही सरल एक्सटेंशन बनाया https://github.com/damienromito/NSObject-FromClassName
extension NSObject {
class func fromClassName(className : String) -> NSObject {
let className = NSBundle.mainBundle().infoDictionary!["CFBundleName"] as! String + "." + className
let aClass = NSClassFromString(className) as! UIViewController.Type
return aClass.init()
}
}
मेरे मामले में, मैं यह देखना चाहता हूँ कि मुझे क्या चाहिए
override func viewDidLoad() {
super.viewDidLoad()
let controllers = ["SettingsViewController", "ProfileViewController", "PlayerViewController"]
self.presentController(controllers.firstObject as! String)
}
func presentController(controllerName : String){
let nav = UINavigationController(rootViewController: NSObject.fromClassName(controllerName) as! UIViewController )
nav.navigationBar.translucent = false
self.navigationController?.presentViewController(nav, animated: true, completion: nil)
}
इससे आपको उस क्लास का नाम मिल जाएगा, जिसे आप इंस्टेंट करना चाहते हैं। तब आप एडविंस उत्तर का उपयोग कर सकते हैं अपनी कक्षा की एक नई वस्तु को का ।
बीटा 6 के रूप में _stdlib_getTypeName
एक चर का नामांकित प्रकार प्राप्त होता है। इसे एक खाली खेल के मैदान में चिपकाएँ:
import Foundation
class PureSwiftClass {
}
var myvar0 = NSString() // Objective-C class
var myvar1 = PureSwiftClass()
var myvar2 = 42
var myvar3 = "Hans"
println( "TypeName0 = \(_stdlib_getTypeName(myvar0))")
println( "TypeName1 = \(_stdlib_getTypeName(myvar1))")
println( "TypeName2 = \(_stdlib_getTypeName(myvar2))")
println( "TypeName3 = \(_stdlib_getTypeName(myvar3))")
आउटपुट है:
TypeName0 = NSString
TypeName1 = _TtC13__lldb_expr_014PureSwiftClass
TypeName2 = _TtSi
TypeName3 = _TtSS
इवान स्विक की ब्लॉग प्रविष्टि इन तारों को समझने में मदद करती है: http://www.eswick.com/2014/06/inside-swift/
उदाहरण _TtSi
के लिए स्विफ्ट का आंतरिक Int
प्रकार है।
let vcName = "HomeTableViewController"
let ns = NSBundle.mainBundle().infoDictionary!["CFBundleExecutable"] as! String
// Convert string to class
let anyobjecType: AnyObject.Type = NSClassFromString(ns + "." + vcName)!
if anyobjecType is UIViewController.Type {
// vc is instance
let vc = (anyobjecType as! UIViewController.Type).init()
print(vc)
}
कक्षा से स्ट्रिंग
let classString = NSStringFromClass(TestViewController.self)
या
let classString = NSStringFromClass(TestViewController.classForCoder())
स्ट्रिंग से एक UIViewController वर्ग init:
let vcClass = NSClassFromString(classString) as! UIViewController.Type
let viewController = vcClass.init()
मैं स्विफ्ट 3 के लिए इस श्रेणी का उपयोग कर रहा हूं:
//
// String+AnyClass.swift
// Adminer
//
// Created by Ondrej Rafaj on 14/07/2017.
// Copyright © 2017 manGoweb UK Ltd. All rights reserved.
//
import Foundation
extension String {
func convertToClass<T>() -> T.Type? {
return StringClassConverter<T>.convert(string: self)
}
}
class StringClassConverter<T> {
static func convert(string className: String) -> T.Type? {
guard let nameSpace = Bundle.main.infoDictionary?["CFBundleExecutable"] as? String else {
return nil
}
guard let aClass: T.Type = NSClassFromString("\(nameSpace).\(className)") as? T.Type else {
return nil
}
return aClass
}
}
उपयोग होगा:
func getViewController(fromString: String) -> UIViewController? {
guard let viewController: UIViewController.Type = "MyViewController".converToClass() else {
return nil
}
return viewController.init()
}
मुझे लगता है कि मैं यह कहने में सही हूं कि आप कम से कम वर्तमान बीटा (2) के साथ नहीं हो सकते। उम्मीद है कि यह कुछ ऐसा है जो भविष्य के संस्करणों में बदल जाएगा।
आप NSClassFromString
एक प्रकार का चर प्राप्त करने के लिए उपयोग कर सकते हैं, AnyClass
लेकिन स्विफ्ट में इसका कोई रास्ता नहीं दिखता है। आप ऑब्जेक्टिव C के पुल का उपयोग कर सकते हैं और वहां कर सकते हैं या - यदि यह आपके मामले में काम करता है - स्विच स्टेटमेंट का उपयोग करने के लिए वापस गिरें ।
जाहिरा तौर पर, स्विफ्ट में किसी वस्तु को तुरंत बदलना संभव नहीं है (अब) जब क्लास का नाम केवल रनटाइम पर जाना जाता है। NSObject के उपवर्गों के लिए एक उद्देश्य-सी आवरण संभव है।
कम से कम आप एक ही वर्ग के ऑब्जेक्ट को ऑब्जेक्ट-सी रैपर (xCode संस्करण 6.2 - 6C107a का उपयोग किए बिना) रनटाइम में दिए गए ऑब्जेक्ट के रूप में इंस्टेंट कर सकते हैं:
class Test : NSObject {}
var test1 = Test()
var test2 = test1.dynamicType.alloc()
स्विफ्ट 2.0 में (Xcode 7 के बीटा 2 में परीक्षण किया गया) यह इस तरह काम करता है:
protocol Init {
init()
}
var type = NSClassFromString(className) as? Init.Type
let obj = type!.init()
निश्चित रूप से आने वाले प्रकार के लिए NSClassFromString
को इस init प्रोटोकॉल को लागू करना है।
मुझे उम्मीद है कि यह स्पष्ट है, className
एक स्ट्रिंग है जिसमें कक्षा का ओबज-सी रनटाइम नाम है जो डिफ़ॉल्ट रूप से "फू" नहीं है, लेकिन यह चर्चा आईएमएचओ आपके प्रश्न का प्रमुख विषय नहीं है।
आपको इस प्रोटोकॉल की आवश्यकता है क्योंकि डिफ़ॉल्ट हो सभी स्विफ्ट कक्षाएं एक init
विधि को लागू नहीं करती हैं।
लगता है कि सही झुकाव होगा ...
func newForName<T:NSObject>(p:String) -> T? {
var result:T? = nil
if let k:AnyClass = NSClassFromString(p) {
result = (k as! T).dynamicType.init()
}
return result
}
... जहां "पी" का अर्थ "पैक किया हुआ" है - एक अलग मुद्दा।
लेकिन AnyClass से T तक की महत्वपूर्ण कास्ट वर्तमान में एक संकलक दुर्घटना का कारण बनती है, इसलिए इस बीच में एक अलग क्लोजर में k का इनिशियलाइज़ेशन बस्ट करना चाहिए, जो ठीक संकलित करता है।
मैं विभिन्न लक्ष्यों का उपयोग करता हूं, और इस मामले में स्विफ्ट वर्ग नहीं मिला है। आपको CFBundleName को CFBundleExecutable से बदलना चाहिए। मैंने चेतावनी भी दी:
- (Class)swiftClassFromString:(NSString *)className {
NSString *appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleExecutable"];
NSString *classStringName = [NSString stringWithFormat:@"_TtC%lu%@%lu%@", (unsigned long)appName.length, appName, (unsigned long)className.length, className];
return NSClassFromString(classStringName);
}
इस के रूप में सरल रूप में समाधान नहीं है?
// Given the app/framework/module named 'MyApp'
let className = String(reflecting: MyClass.self)
// className = "MyApp.MyClass"
इसके अलावा स्विफ्ट 2.0 में (संभवतः पहले?) आप सीधे टाइप कर सकते हैं dynamicType
संपत्ति के हैं
अर्थात
class User {
required init() { // class must have an explicit required init()
}
var name: String = ""
}
let aUser = User()
aUser.name = "Tom"
print(aUser)
let bUser = aUser.dynamicType.init()
print(bUser)
उत्पादन
aUser: User = {
name = "Tom"
}
bUser: User = {
name = ""
}
मेरे उपयोग के मामले के लिए काम करता है
मैंने इस तरह से लागू किया है,
if let ImplementationClass: NSObject.Type = NSClassFromString(className) as? NSObject.Type{
ImplementationClass.init()
}
स्विफ्ट 5 , उपयोग करने में आसान, @Ondrej Rafaj's के लिए धन्यवाद
सोर्स कोड:
extension String {
fileprivate
func convertToClass<T>() -> T.Type? {
return StringClassConverter<T>.convert(string: self)
}
var controller: UIViewController?{
guard let viewController: UIViewController.Type = convertToClass() else {
return nil
}
return viewController.init()
}
}
class StringClassConverter<T> {
fileprivate
static func convert(string className: String) -> T.Type? {
guard let nameSpace = Bundle.main.infoDictionary?["CFBundleExecutable"] as? String, let aClass = NSClassFromString("\(nameSpace).\(className)") as? T.Type else {
return nil
}
return aClass
}
}
इस तरह कॉल करें:
guard let ctrl = "ViewCtrl".controller else {
return
}
// ctrl do sth
एक पृष्ठ कूद उदाहरण यहाँ दिखाया गया है, आशा आपकी मदद कर सकती है!
let vc:UIViewController = (NSClassFromString("SwiftAutoCellHeight."+type) as! UIViewController.Type).init()
self.navigationController?.pushViewController(vc, animated: true)
// Click the Table response
tableView.deselectRow(at: indexPath, animated: true)
let sectionModel = models[(indexPath as NSIndexPath).section]
var className = sectionModel.rowsTargetControlerNames[(indexPath as NSIndexPath).row]
className = "GTMRefreshDemo.\(className)"
if let cls = NSClassFromString(className) as? UIViewController.Type {
let dvc = cls.init()
self.navigationController?.pushViewController(dvc, animated: true)
}
Swift3 +
extension String {
var `class`: AnyClass? {
guard
let dict = Bundle.main.infoDictionary,
var appName = dict["CFBundleName"] as? String
else { return nil }
appName.replacingOccurrences(of: " ", with: "_")
let className = appName + "." + self
return NSClassFromString(className)
}
}
यहाँ एक अच्छा उदाहरण है:
class EPRocks {
@require init() { }
}
class EPAwesome : EPRocks {
func awesome() -> String { return "Yes"; }
}
var epawesome = EPAwesome.self();
print(epawesome.awesome);