ObjectC-为什么使用class_copyPropertyList函数无法正确获取属性?
ObjectC-Why can't I get the properties correctly using the class_copyPropertyList function?
- macOS 11.5.2
- Xcode 13.2.1
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <iostream>
int main(int argc, const char * argv[]) {
@autoreleasepool {
Class clazz = NSClassFromString(@"NSString");
uint32_t count = 0;
objc_property_t* properties = class_copyPropertyList(clazz, &count);
for (uint32_t i = 0; i < count; i++){
const char* name = property_getName(properties[i]);
std::cout << name << std::endl;
}
free(properties);
}
return 0;
}
我会截取一些输出片段:
hash
superclass
description
debugDescription
hash
superclass
description
debugDescription
vertexID
sha224
NS_isSourceOver
hash
superclass
description
debugDescription
...
从输出中我们可以发现,hash、description、superclass等属性会重复出现几次,而有些属性(如UTF8String)则不会出现在结果列表中。
我应该如何正确获取属性列表?
我将不胜感激。
您没有看到 UTF8String
作为 属性 出现的原因是它没有在 main[=40= 中声明为 属性 ] NSString
的声明,而是在 类别 中。在 macOS 12.2.1/Xcode 13.2.1 上,NSString
的声明归结为:
@interface NSString : NSObject <NSCopying, NSMutableCopying, NSSecureCoding>
@property (readonly) NSUInteger length;
- (unichar)characterAtIndex:(NSUInteger)index;
- (instancetype)init NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;
@end
NSString
上的所有其他属性和方法随后立即在类别中声明:
@interface NSString (NSStringExtensionMethods)
#pragma mark *** Substrings ***
/* To avoid breaking up character sequences such as Emoji, you can do:
[str substringFromIndex:[str rangeOfComposedCharacterSequenceAtIndex:index].location]
[str substringToIndex:NSMaxRange([str rangeOfComposedCharacterSequenceAtIndex:index])]
[str substringWithRange:[str rangeOfComposedCharacterSequencesForRange:range]
*/
- (NSString *)substringFromIndex:(NSUInteger)from;
- (NSString *)substringToIndex:(NSUInteger)to;
// ...
@property (nullable, readonly) const char *UTF8String NS_RETURNS_INNER_POINTER; // Convenience to return null-terminated UTF8 representation
// ...
@end
当 属性 在类型的类别中声明时,它不会作为实际的 Obj-C 属性 发出,因为类别 can only add methods to classes, and not instance variables。当类别在类型上声明 属性 时,它必须由 方法 支持,而不是传统的 属性.
您也可以通过自定义 class 看到这一点 — 在我的机器上,
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@interface MyClass: NSObject
@property (nullable, readonly) const char *direct_UTF8String NS_RETURNS_INNER_POINTER;
@end
@interface MyClass (Extensions)
@property (nullable, readonly) const char *category_UTF8String NS_RETURNS_INNER_POINTER;
@end
@implementation MyClass
- (const char *)direct_UTF8String {
return "Hello, world!";
}
- (const char *)category_UTF8String {
return "Hi there!";
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Class clazz = NSClassFromString(@"MyClass");
printf("%s properties:\n", class_getName(clazz));
uint32_t count = 0;
objc_property_t* properties = class_copyPropertyList(clazz, &count);
for (uint32_t i = 0; i < count; i++){
printf("%s\n", property_getName(properties[i]));
}
free(properties);
puts("-----------------------------------------------");
printf("%s methods:\n", class_getName(clazz));
Method *methods = class_copyMethodList(clazz, &count);
for (uint32_t i = 0; i < count; i++) {
SEL name = method_getName(methods[i]);
printf("%s\n", sel_getName(name));
}
free(methods);
}
return 0;
}
产出
MyClass properties:
direct_UTF8String
-----------------------------------------------
MyClass methods:
direct_UTF8String
category_UTF8String
如果从 class 中删除 *UTF8String
方法的实际实现,属性 仍然声明,但类别方法消失(因为它实际上没有由于类别的工作方式而综合实施):
MyClass properties:
direct_UTF8String
-----------------------------------------------
MyClass methods:
direct_UTF8String
至于如何对此进行调整:这取决于您尝试获取属性的目的,以及您可能特别需要 UTF8String
的原因。
NSString
在其接口中声明它实现了方法,但实际上并没有实现它们,这就是为什么当您在运行时打印其方法的列表,但它不会打印您期望的内容。
这些方法由其他私有 classes 实现,当您初始化 NSString
的新实例时,您得到的不是 NSString
的实例,而是私有 class 的实例] 有实际实施。
可以看到通过打印class类型的字符串,下面打印的是NSCFString
或者NSTaggedPointerString
,而不是NSString
:
NSString* aString = [NSString stringWithFormat: @"something"];
NSLog(@"%@", [aString class]);
这会打印出 __NSCFConstantString
:
NSLog(@"%@", [@"a constant string" class]);
此模式称为 class 集群模式。
如果您修改以转储 NSCFString
的方法,您将得到一个“redactedDescription”,看起来您是 prevented to query these classes.
- macOS 11.5.2
- Xcode 13.2.1
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <iostream>
int main(int argc, const char * argv[]) {
@autoreleasepool {
Class clazz = NSClassFromString(@"NSString");
uint32_t count = 0;
objc_property_t* properties = class_copyPropertyList(clazz, &count);
for (uint32_t i = 0; i < count; i++){
const char* name = property_getName(properties[i]);
std::cout << name << std::endl;
}
free(properties);
}
return 0;
}
我会截取一些输出片段:
hash
superclass
description
debugDescription
hash
superclass
description
debugDescription
vertexID
sha224
NS_isSourceOver
hash
superclass
description
debugDescription
...
从输出中我们可以发现,hash、description、superclass等属性会重复出现几次,而有些属性(如UTF8String)则不会出现在结果列表中。 我应该如何正确获取属性列表? 我将不胜感激。
您没有看到 UTF8String
作为 属性 出现的原因是它没有在 main[=40= 中声明为 属性 ] NSString
的声明,而是在 类别 中。在 macOS 12.2.1/Xcode 13.2.1 上,NSString
的声明归结为:
@interface NSString : NSObject <NSCopying, NSMutableCopying, NSSecureCoding>
@property (readonly) NSUInteger length;
- (unichar)characterAtIndex:(NSUInteger)index;
- (instancetype)init NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;
@end
NSString
上的所有其他属性和方法随后立即在类别中声明:
@interface NSString (NSStringExtensionMethods)
#pragma mark *** Substrings ***
/* To avoid breaking up character sequences such as Emoji, you can do:
[str substringFromIndex:[str rangeOfComposedCharacterSequenceAtIndex:index].location]
[str substringToIndex:NSMaxRange([str rangeOfComposedCharacterSequenceAtIndex:index])]
[str substringWithRange:[str rangeOfComposedCharacterSequencesForRange:range]
*/
- (NSString *)substringFromIndex:(NSUInteger)from;
- (NSString *)substringToIndex:(NSUInteger)to;
// ...
@property (nullable, readonly) const char *UTF8String NS_RETURNS_INNER_POINTER; // Convenience to return null-terminated UTF8 representation
// ...
@end
当 属性 在类型的类别中声明时,它不会作为实际的 Obj-C 属性 发出,因为类别 can only add methods to classes, and not instance variables。当类别在类型上声明 属性 时,它必须由 方法 支持,而不是传统的 属性.
您也可以通过自定义 class 看到这一点 — 在我的机器上,
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@interface MyClass: NSObject
@property (nullable, readonly) const char *direct_UTF8String NS_RETURNS_INNER_POINTER;
@end
@interface MyClass (Extensions)
@property (nullable, readonly) const char *category_UTF8String NS_RETURNS_INNER_POINTER;
@end
@implementation MyClass
- (const char *)direct_UTF8String {
return "Hello, world!";
}
- (const char *)category_UTF8String {
return "Hi there!";
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Class clazz = NSClassFromString(@"MyClass");
printf("%s properties:\n", class_getName(clazz));
uint32_t count = 0;
objc_property_t* properties = class_copyPropertyList(clazz, &count);
for (uint32_t i = 0; i < count; i++){
printf("%s\n", property_getName(properties[i]));
}
free(properties);
puts("-----------------------------------------------");
printf("%s methods:\n", class_getName(clazz));
Method *methods = class_copyMethodList(clazz, &count);
for (uint32_t i = 0; i < count; i++) {
SEL name = method_getName(methods[i]);
printf("%s\n", sel_getName(name));
}
free(methods);
}
return 0;
}
产出
MyClass properties:
direct_UTF8String
-----------------------------------------------
MyClass methods:
direct_UTF8String
category_UTF8String
如果从 class 中删除 *UTF8String
方法的实际实现,属性 仍然声明,但类别方法消失(因为它实际上没有由于类别的工作方式而综合实施):
MyClass properties:
direct_UTF8String
-----------------------------------------------
MyClass methods:
direct_UTF8String
至于如何对此进行调整:这取决于您尝试获取属性的目的,以及您可能特别需要 UTF8String
的原因。
NSString
在其接口中声明它实现了方法,但实际上并没有实现它们,这就是为什么当您在运行时打印其方法的列表,但它不会打印您期望的内容。
这些方法由其他私有 classes 实现,当您初始化 NSString
的新实例时,您得到的不是 NSString
的实例,而是私有 class 的实例] 有实际实施。
可以看到通过打印class类型的字符串,下面打印的是NSCFString
或者NSTaggedPointerString
,而不是NSString
:
NSString* aString = [NSString stringWithFormat: @"something"];
NSLog(@"%@", [aString class]);
这会打印出 __NSCFConstantString
:
NSLog(@"%@", [@"a constant string" class]);
此模式称为 class 集群模式。
如果您修改以转储 NSCFString
的方法,您将得到一个“redactedDescription”,看起来您是 prevented to query these classes.