我想知道是否可以确定Objects属性的类或基本类型。获取所有属性名称和值非常简单。 SO answer
有没有办法获取属性类类型,而属性没有值或零值?
示例代码
@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
答案 0 :(得分:33)
在搜索Apples Documentation about objc Runtime并根据this SO answer后,我终于明白了。我只是想分享我的结果。
unsigned int count;
objc_property_t* props = class_copyPropertyList([MyObject class], &count);
for (int i = 0; i < count; i++) {
objc_property_t property = props[i];
const char * name = property_getName(property);
NSString *propertyName = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];
const char * type = property_getAttributes(property);
NSString *attr = [NSString stringWithCString:type encoding:NSUTF8StringEncoding];
NSString * typeString = [NSString stringWithUTF8String:type];
NSArray * attributes = [typeString componentsSeparatedByString:@","];
NSString * typeAttribute = [attributes objectAtIndex:0];
NSString * propertyType = [typeAttribute substringFromIndex:1];
const char * rawPropertyType = [propertyType UTF8String];
if (strcmp(rawPropertyType, @encode(float)) == 0) {
//it's a float
} else if (strcmp(rawPropertyType, @encode(int)) == 0) {
//it's an int
} else if (strcmp(rawPropertyType, @encode(id)) == 0) {
//it's some sort of object
} else {
// According to Apples Documentation you can determine the corresponding encoding values
}
if ([typeAttribute hasPrefix:@"T@"]) {
NSString * typeClassName = [typeAttribute substringWithRange:NSMakeRange(3, [typeAttribute length]-4)]; //turns @"NSDate" into NSDate
Class typeClass = NSClassFromString(typeClassName);
if (typeClass != nil) {
// Here is the corresponding class even for nil values
}
}
}
free(props);
答案 1 :(得分:4)
受到@ arndt-bieberstein的ObjC回答的启发,我已经在Swift 3中编写了一个解决方案(可能非常相似 - 如果不相同 - 在早期版本的Swift中)。您可以在Github上找到它。我正在尝试创建它但我遇到问题pob lib lint
可能使用Swift 3代码(CLI xcodebuild
或Xcode 8相关问题。)无论如何,类方法func getTypesOfProperties(inClass clazz: NSObject.Type) -> Dictionary<String, Any>?
可以提取从NSObject
继承的任何Swift类的名称和类型。
项目的工作是这些方法,但请查看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
}
它可以提取类NSObject.Type
继承的所有属性的NSObject
,例如NSDate
(Swift3:Date
),NSString
(Swift3: String
?)和NSNumber
,但它存储在Any
类型中(您可以看到该方法返回的Dictionary类型的值)。这是由于value types
的限制,例如Int,Int32,Bool。由于这些类型不是从NSObject继承,因此请在{0}上调用.self
。 Int - Int.self
不返回NSObject.Type,而是返回类型Any
。因此,该方法返回Dictionary<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'
您还可以尝试将Any
转换为NSObject.Type
,这对于从NSObject
继承的所有属性都会成功,然后您可以使用标准==
运算符检查类型:
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
无法找到这些属性。
有人有解决方案吗?