15

オブジェクト プロパティのクラスまたはプリミティブ型を特定できるかどうか疑問に思っていました。すべてのプロパティの名前と値を取得するのは非常に簡単です。そう答え

プロパティに値がない、または nil 値がない場合に、プロパティ クラス タイプを取得する方法はありますか?

サンプルコード

@interface MyObject : NSObject
@property (nonatomic, copy) NSString *aString;
@property (nonatomic, copy) NSDate *aDate;
@property                   NSInteger aPrimitive;
@end

@implementation MyObject
@synthesize aString;
@synthesize aDate;
@synthesize aPrimitive;

- (void)getTheTypesOfMyProperties {
    unsigned int count;
    objc_property_t* props = class_copyPropertyList([self class], &count);
    for (int i = 0; i < count; i++) {
        objc_property_t property = props[i];

        // Here I can easy get the name or value
        const char * name = property_getName(property);

        // But is there any magic function that can tell me the type?
        // the property can be nil at this time
        Class cls = magicFunction(property);
    }
    free(props);
}

@end
4

2 に答える 2

4

@arndt-bieberstein による ObjC の回答に触発されて、Swift 3 でソリューションを作成しました (以前のバージョンの Swift と同じではないにしても、おそらく非常に似ています)。Githubで見つけることができます。ポッドを作成しようとしていますがpob lib lint、Swift 3 コードを操作する際に問題が発生しています (おそらく CLIxcodebuildまたは Xcode 8 関連の問題です)。とにかく、クラス メソッドfunc getTypesOfProperties(inClass clazz: NSObject.Type) -> Dictionary<String, Any>?は名前と型を抽出できます。から継承する任意の Swift クラスNSObject

プロジェクトの主力はこれらのメソッドですが、Githubで完全なコードを確認してください。

  func getTypesOfProperties(in clazz: NSObject.Type) -> Dictionary<String, Any>? {
        var count = UInt32()
        guard let properties = class_copyPropertyList(clazz, &count) else { return nil }
        var types: Dictionary<String, Any> = [:]
        for i in 0..<Int(count) {
            guard let property: objc_property_t = properties[i], let name = getNameOf(property: property) else { continue }
            let type = getTypeOf(property: property)
            types[name] = type
        }
        free(properties)
        return types
    }

   func getTypeOf(property: objc_property_t) -> Any {
        guard let attributesAsNSString: NSString = NSString(utf8String: property_getAttributes(property)) else { return Any.self }
        let attributes = attributesAsNSString as String
        let slices = attributes.components(separatedBy: "\"")
        guard slices.count > 1 else { return getPrimitiveDataType(withAttributes: attributes) }
        let objectClassName = slices[1]
        let objectClass = NSClassFromString(objectClassName) as! NSObject.Type
        return objectClass
    }

   func getPrimitiveDataType(withAttributes attributes: String) -> Any {
        guard let letter = attributes.substring(from: 1, to: 2), let type = primitiveDataTypes[letter] else { return Any.self }
        return type
    }

   func getNameOf(property: objc_property_t) -> String? {
        guard let name: NSString = NSString(utf8String: property_getName(property)) else { return nil }
        return name as String
    }

(Swift3: )、(Swift3: ?) など、NSObject.Typeクラス タイプが継承するすべてのプロパティを抽出できますが、タイプに保存されます (方法)。これは、Int、Int32、Bool などの制限によるものです。これらの型は NSObject から継承されないため、たとえば Int - を呼び出しても NSObject.Type ではなく type が返されます。したがって、このメソッドはではなく を返します。NSObjectNSDateDateNSStringStringNSNumberAnyvalue types.selfInt.selfAnyDictionary<String, Any>?Dictionary<String, NSObject.Type>?

この方法は次のように使用できます。

class Book: NSObject {
    let title: String
    let author: String?
    let numberOfPages: Int
    let released: Date
    let isPocket: Bool

    init(title: String, author: String?, numberOfPages: Int, released: Date, isPocket: Bool) {
        self.title = title
        self.author = author
        self.numberOfPages = numberOfPages
        self.released = released
        self.isPocket = isPocket
    }
}

guard let types = getTypesOfProperties(inClass: Book.self) else { return }
for (name, type) in types {
    print("'\(name)' has type '\(type)'")
}
// Prints:
// 'title' has type 'NSString'
// 'numberOfPages' has type 'Int'
// 'author' has type 'NSString'
// 'released' has type 'NSDate'
// 'isPocket' has type 'Bool'

から継承するすべてのプロパティで成功するAnytoのキャストを試みることもできます。その後、標準演算子を使用して型を確認できます。NSObject.TypeNSObject==

func checkPropertiesOfBook() {
    guard let types = getTypesOfProperties(inClass: Book.self) else { return }
    for (name, type) in types {
        if let objectType = type as? NSObject.Type {
            if objectType == NSDate.self {
                print("Property named '\(name)' has type 'NSDate'")
            } else if objectType == NSString.self {
                print("Property named '\(name)' has type 'NSString'")
            }
        }
    }
}

このカスタム==オペレータを宣言すると、次のようになります。

func ==(rhs: Any, lhs: Any) -> Bool {
    let rhsType: String = "\(rhs)"
    let lhsType: String = "\(lhs)"
    let same = rhsType == lhsType
    return same
}

value types次に、次のようにタイプを確認することもできます。

func checkPropertiesOfBook() {
    guard let types = getTypesOfProperties(inClass: Book.self) else { return }
    for (name, type) in types {
        if type == Int.self {
            print("Property named '\(name)' has type 'Int'")
        } else if type == Bool.self {
            print("Property named '\(name)' has type 'Bool'")
        }
    }
}

制限事項がオプションである 場合、私はまだこのプロジェクトのサポートを提供できていませんvalue types。次のように NSObject サブクラスでプロパティを宣言した場合:メソッドがそれらのプロパティを見つけることができないvar myOptionalInt: Int?ため、私のソリューションは機能しません。class_copyPropertyList

誰かがこれに対する解決策を持っていますか?

于 2016-09-02T10:34:03.813 に答える