runtime.h-Functions-Working with Classes (一)

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就没被打印出来。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Morris_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值