文章目录
- Working with Classes (一)
- class_getName
- class_isMetaClass
- class_getSuperclass
- class_setSuperclass
- class_getVersion
- class_setVersion
- class_getInstanceSize
- class_getInstanceVariable
- class_getClassVariable
- class_copyIvarList
- class_getInstanceMethod
- class_getClassMethod
- class_getMethodImplementation
- class_getMethodImplementation_stret
- class_respondsToSelector
- class_copyMethodList
- class_conformsToProtocol
- class_copyProtocolList
- class_getProperty
- class_copyPropertyList
Working with Classes (一)
class_getName
OBJC_EXPORT const char * _Nonnull
class_getName(Class _Nullable cls)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
Returns the name of a class
,返回一个类的类名。
const char *className1 = class_getName([p class]);
const char *className2 = class_getName([Person class]);
NSLog(@"%s %s",className1,className2);// -> Person Person
class_isMetaClass
OBJC_EXPORT BOOL
class_isMetaClass(Class _Nullable cls)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
判断一个类是否是元类
BOOL isMeta1 = class_isMetaClass([p class]);
BOOL isMeta2 = class_isMetaClass([Person class]);
NSLog(@"%d %d",isMeta1,isMeta2);// -> 0 0
Class metaClass = objc_getMetaClass("Person");
NSLog(@"%d",class_isMetaClass(metaClass));// -> 1
class_getSuperclass
OBJC_EXPORT Class _Nullable
class_getSuperclass(Class _Nullable cls)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
获取一个类的父类
Class superClass = class_getSuperclass([Person class]);
NSLog(@"%@",superClass); //-> NSObject
class_setSuperclass
/**
* Sets the superclass of a given class.
*
* @param cls The class whose superclass you want to set.
* @param newSuper The new superclass for cls.
*
* @return The old superclass for cls.
*
* @warning You should not use this function.
*/
OBJC_EXPORT Class _Nonnull
class_setSuperclass(Class _Nonnull cls, Class _Nonnull newSuper)
__OSX_DEPRECATED(10.5, 10.5, "not recommended")
__IOS_DEPRECATED(2.0, 2.0, "not recommended")
__TVOS_DEPRECATED(9.0, 9.0, "not recommended")
__WATCHOS_DEPRECATED(1.0, 1.0, "not recommended")
#ifndef __APPLE_BLEACH_SDK__
__BRIDGEOS_DEPRECATED(2.0, 2.0, "not recommended")
#endif
;
Sets the superclass of a given class
,set一个类的父类
You should not use this function
,注意了,苹果不让开发者用这个函数。
class_getVersion
OBJC_EXPORT int
class_getVersion(Class _Nullable cls)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
Returns the version number of a class definition
class_setVersion
OBJC_EXPORT void
class_setVersion(Class _Nullable cls, int version)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
set一个类的版本号
NSLog(@"%d",class_getVersion([Person class])); // -> 0
NSLog(@"%d",class_getVersion([NSObject class])); // -> 0
class_setVersion([Person class], 2);
NSLog(@"%d",class_getVersion([Person class])); // -> 2
class_getInstanceSize
OBJC_EXPORT size_t
class_getInstanceSize(Class _Nullable cls)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
获取实例大小
size_t size_0_1 = class_getInstanceSize([p class]);
size_t size_0_2 = class_getInstanceSize([Person class]);
NSLog(@"%zu",size_0_1); // -> 40
NSLog(@"%zu",size_0_2); // -> 40
sizeof()
size_t size1 = sizeof([Person class]);
size_t size2 = sizeof([p class]);
size_t size3 = sizeof(p);
NSLog(@"%zu",size1); // -> 8
NSLog(@"%zu",size2); // -> 8
NSLog(@"%zu",size3); // -> 8
注意class_getInstanceSize和sizeof()打印结果并不是一样的。
这里需要继续深入学习一下有关对象内存分配的原理、iOS中class_getInstanceSize和sizeof()区别
class_getInstanceVariable
OBJC_EXPORT Ivar _Nullable
class_getInstanceVariable(Class _Nullable cls, const char * _Nonnull name)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
获取实例成员变量信息。
Returns the \c Ivar for a specified instance variable of a given class
返回给定类的指定实例变量的Ivar,即实例对象的ivar。
通过变量名获取ivar,给变量赋值并打印:
//获取实例变量
Ivar ivar = class_getInstanceVariable([p class], "_age");
//给变量赋值
object_setIvar(p, ivar, @18);
//读变量的最新值
id age = object_getIvar(p, ivar);
NSLog(@"%@",age); // -> 18
NSLog(@"%f",age); // -> -0.000000
注意变量名一般都是加下划线。
这里有一个问题:为什么没有给p的height附上值
在Person.m的setAge方法打断点未执行,所以p.age应该是没赋上值的。为什么object_setIvar函数没有给p的age赋值呢,或者赋值了未执行set方法?
class_getClassVariable
OBJC_EXPORT Ivar _Nullable
class_getInstanceVariable(Class _Nullable cls, const char * _Nonnull name)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
获取类成员变量的信息。
Returns the Ivar for a specified class variable of a given class
返回给定类的指定类的变量的Ivar,即类对象的ivar。
class_copyIvarList
/**
* Describes the instance variables declared by a class.
*
* @param cls The class to inspect.
* @param outCount On return, contains the length of the returned array.
* If outCount is NULL, the length is not returned.
*
* @return An array of pointers of type Ivar describing the instance variables declared by the class.
* Any instance variables declared by superclasses are not included. The array contains *outCount
* pointers followed by a NULL terminator. You must free the array with free().
*
* If the class declares no instance variables, or cls is Nil, NULL is returned and *outCount is 0.
*/
返回一个array of pointers of type Ivar describing the instance variables declared by the class.
例如获取所有的变量
//获取所有变量
unsigned int count = 0;
Ivar *ivarList = class_copyIvarList([p class], &count);
for (int i = 0; i < count; i ++) {
Ivar ivar = ivarList[i];
NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
NSLog(@"%d %@",i,ivarName);
//这里获取到的是所有“_xx”类型的变量,如果需要获取属性,可做如下处理
//ivarName = [ivarName substringFromIndex:1];
}
free(ivarList);
class_getInstanceMethod
OBJC_EXPORT Method _Nullable
class_getInstanceMethod(Class _Nullable cls, SEL _Nonnull name)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
获取实例方法,返回一个Method
关于Method:
/// An opaque type that represents a method in a class definition.
typedef struct objc_method *Method;
是一个objc_method类型的结构体指针
关于objc_method
struct objc_method {
SEL _Nonnull method_name OBJC2_UNAVAILABLE;
char * _Nullable method_types OBJC2_UNAVAILABLE;
IMP _Nonnull method_imp OBJC2_UNAVAILABLE;
} OBJC2_UNAVAILABLE;
这里我们应该再次了解和学习一下SEL和IMP。
class_getClassMethod
OBJC_EXPORT Method _Nullable
class_getClassMethod(Class _Nullable cls, SEL _Nonnull name)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
获取类方法
Method runMethod_class = class_getClassMethod([p class], @selector(copy));
NSLog(@"%p",runMethod_class); //打印方法地址 -> 0x10f4712a8
如果这个函数找不到类方法会怎么样?用获取类方法的函数获取实例方法会怎么样?
//获取实例方法
Method runMethod = class_getInstanceMethod([p class], @selector(run));
NSLog(@"%p",runMethod); //打印方法地址 -> 0x10a903b10
//获取类方法
Method runMethod1 = class_getClassMethod([p class], @selector(run));
NSLog(@"%p",runMethod1); //打印方法地址 -> 0x0
//打印0x0说明通过@selector(run)并未找到run这个类方法
Method runMethod_class = class_getClassMethod([p class], @selector(copy));
NSLog(@"%p",runMethod_class); //打印方法地址 -> 0x10f4712a8
这么看来class_getClassMethod和class_getInstanceMethod需要区分开,一个获取类方法,一个获取实例方法。
class_getMethodImplementation
OBJC_EXPORT IMP _Nullable
class_getMethodImplementation(Class _Nullable cls, SEL _Nonnull name)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
获取方法实现
//获取方法实现方法
IMP runIMP = class_getMethodImplementation([Person class], @selector(run));
//IMP runIMP = [p methodForSelector:runSEL];
//获取方法SEL
SEL runSEL = @selector(run);
//定义新的函数,函数地址指向IMP指针所在的地址
void (*runFunction)(id, SEL) = (void *)runIMP;
//调用runFunction函数
runFunction(p, runSEL);
class_getMethodImplementation_stret
/**
* Returns the function pointer that would be called if a particular
* message were sent to an instance of a class.
*
* @param cls The class you want to inspect.
* @param name A selector.
*
* @return The function pointer that would be called if \c [object name] were called
* with an instance of the class, or \c NULL if \e cls is \c Nil.
*/
OBJC_EXPORT IMP _Nullable
class_getMethodImplementation_stret(Class _Nullable cls, SEL _Nonnull name)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0)
OBJC_ARM64_UNAVAILABLE;
这里有个问题
class_getMethodImplementation_stret与class_getMethodImplementation都是返回方法的实现,区别是什么呢?
class_respondsToSelector
/**
* Returns a Boolean value that indicates whether instances of a class respond to a particular selector.
*
* @param cls The class you want to inspect.
* @param sel A selector.
*
* @return \c YES if instances of the class respond to the selector, otherwise \c NO.
*
* @note You should usually use \c NSObject's \c respondsToSelector: or \c instancesRespondToSelector:
* methods instead of this function.
*/
OBJC_EXPORT BOOL
class_respondsToSelector(Class _Nullable cls, SEL _Nonnull sel)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
这个函数是查看某各类的实例是否相应某个方法。
注意: @note You should usually use \c NSObject’s \c respondsToSelector: or \c instancesRespondToSelector:
- methods instead of this function.
使用NSObject提供的respondsToSelector:方法来替代使用这个方法,意思是开发过程中这个方法不要常用,用respondsToSelector:。
class_copyMethodList
/**
* Describes the instance methods implemented by a class.
*
* @param cls The class you want to inspect.
* @param outCount On return, contains the length of the returned array.
* If outCount is NULL, the length is not returned.
*
* @return An array of pointers of type Method describing the instance methods
* implemented by the class—any instance methods implemented by superclasses are not included.
* The array contains *outCount pointers followed by a NULL terminator. You must free the array with free().
*
* If cls implements no instance methods, or cls is Nil, returns NULL and *outCount is 0.
*
* @note To get the class methods of a class, use \c class_copyMethodList(object_getClass(cls), &count).
* @note To get the implementations of methods that may be implemented by superclasses,
* use \c class_getInstanceMethod or \c class_getClassMethod.
*/
OBJC_EXPORT Method _Nonnull * _Nullable
class_copyMethodList(Class _Nullable cls, unsigned int * _Nullable outCount)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
`@note To get the class methods of a class, use \c class_copyMethodList(object_getClass(cls), &count).
- @note To get the implementations of methods that may be implemented by superclasses,
- use \c class_getInstanceMethod or \c class_getClassMethod.`
class_getInstanceMethod获取实例方法
class_getClassMethod获取类方法
class_copyMethodList可以获取一个类所有的方法,包括私有方法
unsigned int count;
Method *methods = class_copyMethodList([Person class], &count);
for (int i = 0; i < count; i++) {
NSLog(@"%s", sel_getName(method_getName(methods[i])));
}
free(methods);
利用这个函数可以获取和查看一个类的私有方法,对我妈分析一个私有类的实现是很有帮助的。
class_conformsToProtocol
/**
* Returns a Boolean value that indicates whether a class conforms to a given protocol.
*
* @param cls The class you want to inspect.
* @param protocol A protocol.
*
* @return YES if cls conforms to protocol, otherwise NO.
*
* @note You should usually use NSObject's conformsToProtocol: method instead of this function.
*/
OBJC_EXPORT BOOL
class_conformsToProtocol(Class _Nullable cls, Protocol * _Nullable protocol)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
@note You should usually use NSObject's conformsToProtocol: method instead of this function.
也是不建议我们直接使用这个函数,建议使用NSObject的 conformsToProtocol:方法来判断某各类是否遵循某协议。
NSLog(@"%d",class_conformsToProtocol([Person class], @protocol(NSObject)));// -> 0
NSLog(@"%d",class_conformsToProtocol([p class], @protocol(NSObject)));// -> 0
NSLog(@"%d",[Person conformsToProtocol:@protocol(NSObject)]); // -> 1
这里有个问题,为什么前面两个打印会是0?
class_copyProtocolList
OBJC_EXPORT Protocol * __unsafe_unretained _Nonnull * _Nullable
class_copyProtocolList(Class _Nullable cls, unsigned int * _Nullable outCount)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
打印一个类遵循的协议,例如Person类遵循了NSObject协议
@interface Person : NSObject <NSObject>
@end
获取Person所遵循的协议并打印:
unsigned int outCount = 0;
Protocol * __unsafe_unretained *protocols = class_copyProtocolList([Person class], &outCount);
for (int i = 0; i < outCount; i++) {
Protocol *protocol = protocols[i];
NSLog(@"%@",NSStringFromProtocol(protocol));
}
free(protocols);
打印出一个 NSObject 。
class_getProperty
OBJC_EXPORT objc_property_t _Nullable
class_getProperty(Class _Nullable cls, const char * _Nonnull name)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
objc_property_t,是objc_property类型的结构体指针
/// An opaque type that represents an Objective-C declared property.
typedef struct objc_property *objc_property_t;
获取指定名字的Property
class_copyPropertyList
OBJC_EXPORT objc_property_t _Nonnull * _Nullable
class_copyPropertyList(Class _Nullable cls, unsigned int * _Nullable outCount)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
获取属性列表,以Person为例
@interface Person : NSObject <NSObject>
{
NSUInteger _age; //年龄
}
@property (nonatomic, strong) Car *car;
@property (nonatomic) BOOL isGirl; //是否是女孩,默认为NO
@property (nonatomic) CGFloat height; //身高
@end
添加一个私有属性
@interface Person ()
@property (nonatomic, copy) NSMutableArray *testArr;
@end
获取Person的所有属性列表,包括私有属性
unsigned int outCount = 0;
objc_property_t *properties = class_copyPropertyList([Person class], &outCount);
for (int i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
NSLog(@"%s",property_getName(property));
}
free(properties);
打印如下:
2020-03-08 16:19:30.836937+0800 Test-2[29087:2513088] testArr
2020-03-08 16:19:30.837083+0800 Test-2[29087:2513088] car
2020-03-08 16:19:30.837200+0800 Test-2[29087:2513088] isGirl
2020-03-08 16:19:30.837312+0800 Test-2[29087:2513088] height
2020-03-08 16:19:30.837409+0800 Test-2[29087:2513088] hash
2020-03-08 16:19:30.837502+0800 Test-2[29087:2513088] superclass
2020-03-08 16:19:30.837606+0800 Test-2[29087:2513088] description
2020-03-08 16:19:30.837704+0800 Test-2[29087:2513088] debugDescription
私有的属性testArr也打印了。
这个方法只是打印property,变量是不会打印的,age就没被打印出来。