Objective-C的本质(objective-c c区别)
yuyutoo 2024-11-17 03:42 1 浏览 0 评论
我们平时编写的Objective-C代码,底层实现其实都是C\C++代码,所以Objective-C的面向对象都是基于C\C++的数据结构实现的
OC对象的本质
Objective-C的对象、类主要是基于C\C++的结构体实现的
通过下面的命令可以将OC代码转换为C++代码来查看
clang -rewrite-objc OC源文件 -o 输出的CPP文件
由于Clang会根据不同平台转换的C++代码有所差异,所以针对iOS平台用下面的命令来转换
// 意为:通过Xcode运行iPhone平台arm64架构,重写OC文件到C++文件
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc OC源文件 -o 输出的CPP文件
如果需要链接其他框架,使用-framework参数。比如-framework UIKit
凡是继承自NSObject的对象,都会自带一个类型是Class的isa的成员变量,将其转成C++,就可以看到NSObject本质上是一个叫做NSObject_IMPL的结构体,其成员变量isa本质上也是一个指向objc_class结构体的指针
OC对象的内存布局
一个OC对象在内存中的布局是这样的,系统会在堆中开辟一块内存空间存放该对象,这块空间里还包含成员变量和isa指针。然后栈里的局部变量指向这块存储空间的地址
OC对象的内存占用大小
系统会给NSObject对象自动分配16个字节的内存,而NSObject对象实际只占用了8个字节的内存。这8个字节的大小就是成员变量isa指针的大小,多余的8个字节是系统为了内存对齐而分配的
// 获取实例对象的内存大小,实际是获取对象成员变量的内存大小
#import <objc/runtime.h>class_getInstanceSize([NSObject class]);// 获取实例对象的内存大小,实际是获取系统真正分配了多少内存#import <malloc/malloc.h>malloc_size((__bridge const void *)obj);
NSObject *obj = [[NSObject alloc] init];
// 获得NSObject实例对象的成员变量所占用的大小 >> 8
NSLog(@"%zd", class_getInstanceSize([NSObject class]));
// 获得obj指针所指向内存的大小 >> 16
NSLog(@"%zd", malloc_size((__bridge const void *)obj));
验证方法
1.源码验证
下载苹果开源框架 https://opensource.apple.com/tarballs/objc4/
选择最大版本下载
在头文件objc-runtime-new.h中找到对应代码
inline size_t instanceSize(size_t extraBytes) const {
if (fastpath(cache.hasFastInstanceSize(extraBytes))) {
return cache.fastInstanceSize(extraBytes);
}
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
// 只要小于16个字节都会被赋值16
if (size < 16) size = 16;
return size;
}
2.内存验证
运行Xcode,选择Debug->Debug Workflow -> View Memory查看内存数据
输入obj的内存地址可以看到只有前8个字节有值,但已经分配了16个字节的内存空间
3.LLDB打印验证
利用LLDB的memory read读取对象的内存地址,可以看到也是分配的16个字节
OC对象的分类
OC对象主要分为三种
- instance对象(实例对象)
- class对象(类对象)
- meta-class对象(元类对象)
instance对象
instance对象就是通过类alloc出来的对象,每次调用alloc都会产生新的instance对象
// object1、object2是NSObject的instance对象(实例对象)
NSObject *object1 = [[NSObject alloc] init];
NSObject *object2 = [[NSObject alloc] init];
// 通过打印可以看出,它们是不同的两个对象,分别占据着两块不同的内存
NSLog(@"instance - %p %p",
object1,
object2);
instance对象在内存中存储的信息
- isa指针
- 其他成员变量的具体值
class对象
每个类在内存中有且只有一个class对象
Class objectClass1 = [object1 class];
Class objectClass2 = [object2 class];
Class objectClass3 = object_getClass(object1);
Class objectClass4 = object_getClass(object2);
Class objectClass5 = [NSObject class];
// 通过打印可以看出,上面几个方法返回的都是同一个类对象,内存地址都一样
NSLog(@"class - %p %p %p %p %p %d",
objectClass1,
objectClass2,
objectClass3,
objectClass4,
objectClass5);
注意: class方法返回的一直是类对象,所以哪怕这样写还是会返回类对象
Class objectMetaClass2 = [[[NSObject class] class] class];
class对象在内存中存储的信息
- isa指针- superclass指针- 类的属性信息(@property)、类的对象方法信息(instance method)- 类的协议信息(protocol)、类的成员变量信息(ivar)
- ....
meta-class对象
objectMetaClass是NSObject的meta-class对象(元类对象),每个类在内存中有且只有一个meta-class对象
Class objectMetaClass = object_getClass(objectClass5);
meta-class对象和class对象的内存结构是一样的,但是用途不一样,在内存中存储的信息主要包括
- isa指针- superclass指针- 类的类方法信息(class method)
- ....
使用class_isMetaClass(Class _Nullable cls)来查看Class是否为meta-class的方法
NSLog(@"objectMetaClass - %p %d", objectMetaClass, class_isMetaClass(objectMetaClass));
isa和superclass
每个类的实例对象、类对象、元类对象都有一个isa指针
- instance的isa指向class - 当调用对象方法时,通过instance的isa找到class,最后找到对象方法的实现进行调用
- class的isa指向meta-class - 当调用类方法时,通过class的isa找到meta-class,最后找到类方法的实现进行调用
- meta-class的isa指向基类的meta-class每个类的类对象、元类对象都有一个superclass指针
- class的superclass指针指向父类的class
- 如果没有父类,superclass指针为nil
- meta-class的superclass指向父类的meta-class
- 基类的meta-class的superclass指向基类的class
instance调用对象方法的轨迹
- isa找到class,方法不存在,就通过superclass找父类
class调用类方法的轨迹
- isa找meta-class,方法不存在,就通过superclass找父类
Class类型的底层结构
我们可以从源码objc-runtime-new.h文件中找到Class类型的本质是结构体objc_class类型,里面包含了superclass指针、cache方法缓存,以及获取具体的类信息的class_data_bits_t类型的属性表
struct objc_class : objc_object {
// Class ISA;
// superclass指针
Class superclass;
// 方法缓存
cache_t cache; // formerly cache pointer and vtable
// 用于获取具体的类信息
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
// rw意为readwrite,可读可写,t意为table,表格
class_rw_t *data() const {
return bits.data();
}
void setData(class_rw_t *newData) {
bits.setData(newData);
}
}
继承的父类objc_object里有一个isa指针
// 继承的父类结构体里面有一个isa指针
struct objc_object {
private:
isa_t isa;
public:
Class ISA(bool authenticated = false);
Class rawISA();
Class getIsa();
uintptr_t isaBits() const;
....
};
分析class_data_bits_t这个类型里面的结构可以看出,bits & FAST_DATA_MASK就可以得到class_rw_t类型的表的内存
// class_data_bits_t结构体里的具体分析
struct class_data_bits_t {
friend objc_class;
class_rw_t* data() const {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
void setData(class_rw_t *newData)
{
ASSERT(!data() || (newData->flags & (RW_REALIZING | RW_FUTURE)));
uintptr_t newBits = (bits & ~FAST_DATA_MASK) | (uintptr_t)newData;
atomic_thread_fence(memory_order_release);
bits = newBits;
}
}
分析class_rw_t这个类型里面的结构可以看出,里面有方法列表、属性列表、协议列表,以及class_ro_t类型的属性表
// class_rw_t结构体里的具体分析
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint16_t witness;
#if SUPPORT_INDEXED_ISA
uint16_t index;
#endif
// ro意为readonly,只读
const class_ro_t *ro() const {
auto v = get_ro_or_rwe();
if (slowpath(v.is<class_rw_ext_t *>())) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->ro;
}
return v.get<const class_ro_t *>(&ro_or_rw_ext);
}
void set_ro(const class_ro_t *ro) {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
v.get<class_rw_ext_t *>(&ro_or_rw_ext)->ro = ro;
} else {
set_ro_or_rwe(ro);
}
}
// 方法列表
const method_array_t methods() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->methods;
} else {
return method_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseMethods()};
}
}
// 属性列表
const property_array_t properties() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->properties;
} else {
return property_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProperties};
}
}
// 协议列表
const protocol_array_t protocols() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->protocols;
} else {
return protocol_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProtocols};
}
}
分析class_ro_t这个类型的结构可以看出,instanceSize意为实例对象所占用的内存空间,name存储的是类名,ivars存储的成员变量列表
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
// 实例对象占用内存大小空间
uint32_t instanceSize;
#ifdef __LP64__
uint32_t reserved;
#endif
// 类名
explicit_atomic<const char *> name;
void *baseMethodList;
protocol_list_t * baseProtocols;
// 成员变量列表
const ivar_list_t * ivars;
}
总结:
上述分析可以简单用一张图来概述
isa指针
在arm64架构之前,isa就是一个普通的指针,存储着Class、Meta-Class对象的内存地址
从arm64架构开始,对isa进行了优化,变成了一个isa_t类型的共用体(union)结构,共用体就是多种数据结构都共用同一块存储空间,里面包含了bits、cls、ISA_BITFIELD结构体以及其他的函数或变量,它们都是共用同一块内存空间的
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
uintptr_t bits;
private:
Class cls;
public:
#if defined(ISA_BITFIELD)
struct {
ISA_BITFIELD; // 现在的版本用一个宏来定义
};
}
在isa.h中查看ISA_BITFIELD这个结构体,里面的每一个值都是位域。不同架构下的掩码和位域都是不一样的,我们只以arm64架构的来分析
// 在isa.h中查看ISA_BITFIELD
// 每个变量后面标的数字就是位域
// 类似ISA_MASK这种宏的都叫掩码
# if __arm64__
# if __has_feature(ptrauth_calls) || TARGET_OS_SIMULATOR
# define ISA_MASK 0x007ffffffffffff8ULL
# define ISA_MAGIC_MASK 0x0000000000000001ULL
# define ISA_MAGIC_VALUE 0x0000000000000001ULL
# define ISA_HAS_CXX_DTOR_BIT 0
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t weakly_referenced : 1; \
uintptr_t shiftcls_and_sig : 52; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 8
# define RC_ONE (1ULL<<56)
# define RC_HALF (1ULL<<7)
# else
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
# define ISA_HAS_CXX_DTOR_BIT 1
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t unused : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 19
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
# endif
# elif __x86_64__
....
# else
# error unknown architecture for packed isa
# endif
// SUPPORT_PACKED_ISA
#endif
每一位位域对应的二进制位的排序都是从右向左的,下面是对应的每个位域的含义
上述代码里类似ISA_MASK这样的值都是掩码,以掩码ISA_MASK为例,转成二进制发现对应是1的部分都是用来取值的
而且一共有33位的1,正好对应着shiftcls这个位域的位数,shiftcls又是存储着类对象和元类对象的地址值,那么就能说明在arm64架构之后的isa里存储着更多的信息,需要&ISA_MASK进行一次位运算之后才能将类对象和元类对象的真实地址值取出来
位运算的运用实例
利用共用体和位运算来优化属性的内存空间
创建Person.h文件,然后手动实现setter和getter
@interface Person : NSObject
//@property (assign, nonatomic, getter=isTall) BOOL tall;
//@property (assign, nonatomic, getter=isRich) BOOL rich;
//@property (assign, nonatomic, getter=isHansome) BOOL handsome;
- (void)setTall:(BOOL)tall;
- (void)setRich:(BOOL)rich;
- (void)setHandsome:(BOOL)handsome;
- (BOOL)isTall;
- (BOOL)isRich;
- (BOOL)isHandsome;
@end
利用共用体的本质,在Person.m的类扩展中创建一个私有的共用体类型的变量
@interface Person()
{
union {
char bits;
struct {
char tall : 1;
char rich : 1;
char handsome : 1;
};
} _tallRichHandsome;
}
@end
该共用体一共只占有1个字节,都是根据char bits来分配大小的。而sturct结构体是对这1个字节大小的占用做说明的,里面每一个的1就是位域,指明占用了1个二进制位,虽然是char类型的,但都是根据位域后面给定的值来确定实际占用大小的。tall、rich、handsome三个变量都是占用着同一个内存区域,也就是值都会存储在一个字节里,这就是共用体的本质,这么做主要是为了做优化,节省内存空间。而且写不写这个结构体都是根据char bits来确定了分配空间大小的,没有影响的
由于上述结构体里的三个变量占用一个字节大小就足够了,那么我们对应每一个变量用一个二进制位来存取值。我们先分别设定三个掩码对应三个值
// 0x0000 0001
#define TallMask (1<<0)
// 0x0000 0010
#define RichMask (1<<1)
// 0x0000 0100
#define HandsomeMask (1<<2)
setter的实现如下,如果参数为YES,那么将掩码进行按位或运算;如果参数为NO,那么先将掩码取反,然后再进行按位与运算
@implementation Person
- (void)setTall:(BOOL)tall
{
if (tall) {
_tallRichHandsome.bits |= TallMask;
} else {
_tallRichHandsome.bits &= ~TallMask;
}
}
- (void)setRich:(BOOL)rich
{
if (rich) {
_tallRichHandsome.bits |= RichMask;
} else {
_tallRichHandsome.bits &= ~RichMask;
}
}
- (void)setHandsome:(BOOL)handsome
{
if (handsome) {
_tallRichHandsome.bits |= HandsomeMask;
} else {
_tallRichHandsome.bits &= ~HandsomeMask;
}
}
@end
getter的实现如下,先将掩码进行按位与运算,然后再取反两次;因为返回值是BOOL类型,那么不是0就是1,所以按位与运算后的值只要不是0的都是有值的,那么取反两次肯定就得到的不是0就是1了
- (BOOL)isTall
{
return !!(_tallRichHandsome.bits & TallMask);
}
- (BOOL)isRich
{
return !!(_tallRichHandsome.bits & RichMask);
}
- (BOOL)isHandsome
{
return !!(_tallRichHandsome.bits & HandsomeMask);
}
如此一来,我们就做到了优化了属性的内存空间,而且也实现了setter和getter
利用位运算进行位移枚举的实现
创建一个位移枚举,每一个值都对应一个二进制位
typedef enum {
OptionsNone = 0, // 0b0000
OptionsOne = 1<<0, // 0b0001
OptionsTwo = 1<<1, // 0b0010
OptionsThree = 1<<2, // 0b0100
OptionsFour = 1<<3 // 0b1000
} Options;
和对应的枚举值进行按位与运算,就能得到是否存在该枚举值
@implementation ViewController
- (void)setOptions:(Options)options
{
if (options & OptionsOne) {
NSLog(@"包含了OptionsOne");
}
if (options & OptionsTwo) {
NSLog(@"包含了OptionsTwo");
}
if (options & OptionsThree) {
NSLog(@"包含了OptionsThree");
}
if (options & OptionsFour) {
NSLog(@"包含了OptionsFour");
}
}
- (void)viewDidLoad {
[super viewDidLoad];
[self setOptions: OptionsOne | OptionsFour];
}
@end
面试题
1.一个NSObject对象占用多少内存?
系统分配了16个字节给NSObject对象(通过malloc_size函数获得)
但NSObject对象内部只使用了8个字节的空间(64bit环境下,可以通过class_getInstanceSize函数获得)
2.看下面代码,分别描述Person和Student对应的内存占用
@interface Person : NSObject
{
int _height;
}
@end
@interface Student : Person
{
int _weight;
}
@end
Person *p = [[Person alloc] init];
NSLog(@"%zd %zd", class_getInstanceSize([Person class]), // 16
malloc_size((__bridge const void *)(p))); // 16
Student *s = [[Student alloc] init];
NSLog(@"%zd %zd", class_getInstanceSize([Student class]), // 16
malloc_size((__bridge const void *)(s))); // 16
默认在64bit处理器下,由于Person继承自NSObject,所以根据内存对齐,系统给NSObject对象分配了16个字节存放isa指针。Person的成员变量height由于是Int类型,占用4个字节。因为isa指针实际只占用了8个字节,还有多余的8个字节空间,所以无需再多分配内存,那么Person的实际占用和系统分配都是16个字节(内存对齐一般以成员变量占比最大的倍数来增加:isa指针占用8个字节,占用最大,所以是8的倍数)
Student继承自Person,isa指针和成员变量height实际占用了12个字节,还有多余的4个字节。而成员变量weight正好又占用4个字节,那么也不用再分配更多的内存空间,Stuent对象的实际占用和系统分配也都是16个字节
3.看下面代码,描述Person的内存占用
@interface Person : NSObject
{
int _age;
int _height;
int _no;
}
@end
Person *p = [[Person alloc] init];
NSLog(@"%zd %zd", class_getInstanceSize([Person class]), // 24
malloc_size((__bridge const void *)(p))); // 32
默认在64bit处理器下,由于Person继承自NSObject,里面的isa指针实际占用了8个字节,而Person里面有三个Int类型的成员变量,实际占用是12个字节,由于结构体的内存对齐原则,系统要分配24个字节(也就是3倍的isa指针的8个字节)才能容纳所有的成员变量,所以Person对象的实际占用为24个字节。
但系统本身都会以16的倍数来进行内存分配,所以要分配大于实际占用字节的两倍才可以,所以Person对象的系统分配为分配32个字节
4.看下面代码,简述Student的对象方法调用轨迹,然后分别注释掉 + (void)test 方法和 - (void)test 方法后会怎样调用
@interface NSObject (Test)
+ (void)test;
- (void)test;
@end
@implementation NSObject (Test)
+ (void)test
{
NSLog(@"+[NSObject test] - %p", self);
}
- (void)test
{
NSLog(@"-[NSObject test] - %p", self);
}
@interface Person : NSObject
+ (void)test;
- (void)test;
@end
@interface Student : Person
+ (void)test;
- (void)test;
@end
Student *s = [[Student alloc] init];
[s test];
[Student test];
1.[s test] 这个方法调用,首先Student的实例对象会根据isa指针去Student的类对象里面查找- (void)test方法,如果找到了则调用该方法。如果没找到,那么就根据superclass指针去父类Person的类对象里查找,如果找到了则调用Person的- (void)test方法。如果没找到,那么就根据superclass指针去基类NSObject的类对象里查找,如果找到了则调用NSObject的- (void)test方法。
如果注释掉了NSObject的- (void)test方法,那么Student实例对象在基类NSObject的类对象里也找不到该方法,由于NSObject类对象的superclass指针指向nil,那么就会crash
2.[Student test] 这个方法调用,首先Student的类对象会根据isa指针去Student的元类对象里查找+ (void)test方法,如果找到了则调用该方法。如果没找到,那么就根据superclass指针去父类Person的元类对象里查找,如果找到了则调用Person的+ (void)test方法。如果没找到,那么就根据superclass指针去基类NSObject的元类对象里查找,如果找到了则调用NSObject的+ (void)test方法。
如果注释掉了NSObject的+ (void)test方法,那么Student的类对象在基类NSObject的元类对象里也找不到该方法,由于NSObject元类对象的superclass指针指向NSObject的类对象,所以就会调用NSObject类对象的- (void)test方法。
如果NSObject的两个方法都注释掉了,那么由于上一步的逻辑会去NSObject类对象里调用- (void)test方法,该方法也找不到,那么NSObject类对象的superclass指针是指向nil的,最后还是会crash
iOS的消息机制本质就是消息调用,所以不会真的区分类方法和对象方法,都是根据方法名进行查找
5.isMemberOfClass、isKindOfClass、isSubclassOfClass的区别,并说下原理
我们先通过一段代码打印可以得知
Person *person = [[Person alloc] init]; // Person对象
NSObject *obj = [[NSObject alloc] init]; // NSObject对象
Class person_class = [person class]; // Person类对象
Class obj_class = [obj class]; // NSObject类对象
Class person_meta_class = object_getClass(person_class); // Person元类对象
Class obj_meta_class = object_getClass(obj_class); // NSObject元类对象
Class person_meta_meta_class = object_getClass(person_meta_class); // NSObject元类对象
Class obj_meta_meta_class = object_getClass(obj_meta_class); // NSObject元类对象
// Person对象, NSObject对象, Person类对象,NSObject类对象
NSLog(@"%@, %@, %@, %@", person, obj, person_class, obj_class);
// Person元类对象, NSObject元类对象, NSObject元类对象,NSObject元类对象
NSLog(@"%@, %@, %@, %@", person_meta_class, obj_meta_class, person_meta_meta_class, obj_meta_meta_class);
isMemberOfClass
我们在objc4源码的NSObject.mm里可以看到,isMemberOfClass的类方法会拿到isa指针所指的对象和传进来的类型做比较;对象方法会拿当前类对象来做比较
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
我们可以通过一段代码打印来分析比较
// Person类对象, Person类对象
NSLog(@"%d", [person isMemberOfClass:person_class]); // 1
// Person类对象, NSObject类对象
NSLog(@"%d", [person isMemberOfClass:obj_class]); // 0
// NSObject类对象, NSObject类对象
NSLog(@"%d", [obj isMemberOfClass:obj_class]); // 1
// Person元类对象, Person类对象
NSLog(@"%d", [person_class isMemberOfClass:person_class]); // 0
// Person元类对象, NSObject类对象
NSLog(@"%d", [person_class isMemberOfClass:obj_class]); // 0
// NSObject元类对象, NSObject类对象
NSLog(@"%d", [obj_class isMemberOfClass:obj_class]); // 0
// Person元类对象, Person元类对象
NSLog(@"%d", [person_class isMemberOfClass:person_meta_class]); // 1
// Person元类对象, NSObject元类对象
NSLog(@"%d", [person_class isMemberOfClass:obj_meta_class]); // 0
// NSObject元类对象, NSObject元类对象
NSLog(@"%d", [obj_class isMemberOfClass:obj_meta_class]); // 1
// 所有类型的元类对象的isa指针都指向NSObject的元类对象,包括NSObject的元类对象自己
// NSObject元类对象, Person元类对象
NSLog(@"%d", [person_meta_class isMemberOfClass:person_meta_class]); // 0
// NSObject元类对象, NSObject元类对象
NSLog(@"%d", [person_meta_class isMemberOfClass:obj_meta_class]); // 1
// NSObject元类对象, NSObject元类对象
NSLog(@"%d", [obj_meta_class isMemberOfClass:obj_meta_class]); // 1
isKindOfClass
isKindOfClass的类方法会拿到isa指针所指向的对象以及该对象的superclass指针所指向的对象和传进来的类型做比较;对象方法会拿当前类对象以及该对象的superclass指针所指向的对象来做比较
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = self->ISA(); tcls; tcls = tcls->getSuperclass()) {
if (tcls == cls) return YES;
}
return NO;
}
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->getSuperclass()) {
if (tcls == cls) return YES;
}
return NO;
}
我们可以通过一段代码打印来分析比较
// Person类对象, Person类对象
NSLog(@"%d", [person isKindOfClass:person_class]); // 1
// Person类对象, NSObject类对象
NSLog(@"%d", [person isKindOfClass:obj_class]); // 1
// NSObject类对象, NSObject类对象
NSLog(@"%d", [obj isKindOfClass:obj_class]); // 1
// Person元类对象, Person类对象
NSLog(@"%d", [person_class isKindOfClass:person_class]); // 0
// Person元类对象的superclass指向NSObject元类对象,而NSObject元类对象的superclass指向的就是NSObject类对象
// Person元类对象, NSObject类对象
NSLog(@"%d", [person_class isKindOfClass:obj_class]); // 1
// NSObject元类对象, NSObject类对象
NSLog(@"%d", [obj_class isKindOfClass:obj_class]); // 1
// Person元类对象, Person元类对象
NSLog(@"%d", [person_class isKindOfClass:person_meta_class]); // 1
// Person元类对象, NSObject元类对象
NSLog(@"%d", [person_class isKindOfClass:obj_meta_class]); // 1
// NSObject元类对象, NSObject元类对象
NSLog(@"%d", [obj_class isKindOfClass:obj_meta_class]); // 1
// NSObject元类对象, Person元类对象
NSLog(@"%d", [person_meta_class isKindOfClass:person_meta_class]); // 0
// NSObject元类对象, NSObject元类对象
NSLog(@"%d", [person_meta_class isKindOfClass:obj_meta_class]); // 1
// NSObject元类对象, NSObject元类对象
NSLog(@"%d", [obj_meta_class isKindOfClass:obj_meta_class]); // 1
isSubclassOfClass
isSubclassOfClass的类方法会拿到当前类对象以及superclass指针所指向的对象和传进来的类型做比较;该方法没有对象方法
+ (BOOL)isSubclassOfClass:(Class)cls {
for (Class tcls = self; tcls; tcls = tcls->getSuperclass()) {
if (tcls == cls) return YES;
}
return NO;
}
我们可以通过一段代码打印来分析比较
// Person类对象, Person类对象
NSLog(@"%d", [person_class isSubclassOfClass:person_class]); // 1
// Person类对象, NSObject类对象
NSLog(@"%d", [person_class isSubclassOfClass:obj_class]); // 1
// NSObject类对象, NSObject类对象
NSLog(@"%d", [obj_class isSubclassOfClass:obj_class]); // 1
// Person类对象, Person元类对象
NSLog(@"%d", [person_class isSubclassOfClass:person_meta_class]); // 0
// Person类对象, NSObject元类对象
NSLog(@"%d", [person_class isSubclassOfClass:obj_meta_class]); // 0
// NSObject类对象, NSObject元类对象
NSLog(@"%d", [obj_class isSubclassOfClass:obj_meta_class]); // 0
// Person元类对象, Person元类对象
NSLog(@"%d", [person_meta_class isSubclassOfClass:person_meta_class]); // 1
// Person元类对象, NSObject元类对象
NSLog(@"%d", [person_meta_class isSubclassOfClass:obj_meta_class]); // 1
// NSObject元类对象, NSObject元类对象
NSLog(@"%d", [obj_meta_class isSubclassOfClass:obj_meta_class]); // 1
// Person元类对象, Person类对象
NSLog(@"%d", [person_meta_class isSubclassOfClass:person_class]); // 0
// Person元类对象, NSObject类对象
NSLog(@"%d", [person_meta_class isSubclassOfClass:obj_class]); // 1
// NSObject元类对象, NSObject类对象
NSLog(@"%d", [obj_meta_class isSubclassOfClass:obj_class]); // 1
相关推荐
- 掌握 Objective-C 中的类和对象:构建面向对象的世界
-
掌握Objective-C中的类和对象:构建面向对象的世界Objective-C是一门面向对象的编程语言,它的核心概念之一就是类和对象。在这篇文章中,我们将深入探讨Objective-C中的...
- 深入探索 Objective-C 数据类型:从基本类型到对象类型
-
深入探索Objective-C数据类型:从基本类型到对象类型Objective-C是一门引入了面向对象概念的编程语言,它在C语言的基础上扩展了更多的功能和特性。在Objective-C中...
- 深入了解 Objective-C 中的协议与代理模式
-
深入了解Objective-C中的协议与代理模式在Objective-C中,协议和代理是两个关键的概念,它们为对象之间的通信和交互提供了灵活的机制。在本文中,我们将深入探讨协议的定义、遵循以及...
- 理解 Objective-C 基础:从 C 扩展到面向对象
-
理解Objective-C基础:从C扩展到面向对象Objective-C作为一门面向对象的编程语言,在苹果的开发生态系统中扮演着重要的角色。它融合了C语言的功能和面向对象的思想,为开发者...
- 使用Objective-C绘制基本图形的两种方式
-
Objective-C是一门面向对象的编程语言,广泛应用于iOS和macOS应用程序的开发。除了常规的编程功能之外,Objective-C还提供了绘制图形的API,可以用来绘制线条、矩形...
- 4月编程语言排行Fortran 击败 Objective-C
-
时隔7年,Objective-C首次跌出TOP20,Swift上位成功。随着跨平台工具的崛起,以及全球数字化进程的加快,还有多少单一做移动开发的程序员?...
- IT博物馆之Objective-C诞生(博物馆icon)
-
1984年,Objective-C诞生。设计者:布莱德·考克斯(BradCox)、汤姆·洛夫(TomLove)Objective-C是面向对象的通用、高级编程语言。它扩展了标准的ANSIC,将...
- Objective C浅拷贝和深拷贝(浅拷贝与深拷贝java)
-
ObjectiveC浅拷贝和深拷贝##浅拷贝浅拷贝就是对内存地址的复制,让目标对象指针和源对象指向同一片内存空间。如:char*str=(char*)malloc(100);char*s...
- 将Swift与Objective-C相结合(swift和objective-c)
-
原文:SwiftyObjective-C作者:PeterSteinberger/MichaelOchs/MatejBukovinski译者:孙薇审校:唐小引(@唐门教主),欢迎技术投...
- 使用Objective-C语言写一个"Hello, World!" 程序
-
本文默认您已安装好Objective-C开发环境。即在macOS上使用Xcode或者在命令行中使用clang编译器。Objective-C,是扩充C的面向对象编程语言,用于开发基于macO...
- Objective-c 代理模式-面试之一(cglib代理原理)
-
一·什么是代理模式?代理模式是在oc中经常遇到的一种设计模式,那什么叫做代理模式呢?举个例子:有一个工人,他要找工作,可是他不知道哪儿有工作,于是他就找了人才中介,让中介去帮他找工作,于是他和中...
- objective-C中Category分类你知多少
-
在OC中,使用category会让我们在开发中非常方便,可以为某个类增添方法,对类别自己有一点小小的体会,首先先来介绍一下类别1.分类的创建:1.类别的作用(1)可以将类的实现分散到多个不同的文件或...
- 深入了解 Objective-C 中的属性和方法
-
深入了解Objective-C中的属性和方法Objective-C是一门面向对象的编程语言,其中属性和方法是构建对象的核心组成部分。在本文中,我们将深入探讨属性和方法的概念,了解它们在Obje...
- Apple离开了Objective-C,使iOS 13中Swift的使用率翻了一番
-
分析表明,苹果自己在iOS13中对Swift的使用增加了一倍,因为它与Objective-C的距离越来越远。Swift最初由Apple于2014年推出,现在已成为跨Cupertino平台进行开发的主...
- 大势所趋:Swift受欢迎度即将赶超Objective C
-
Swift是Apple在WWDC2014所发布的一门编程语言,用来撰写OSX和iOS应用程序。不到两年时间,在iOS开发者中Swift语言便凭借着简洁的语法和优秀的特性打动了开发者,之前用于iOS和...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- 掌握 Objective-C 中的类和对象:构建面向对象的世界
- 深入探索 Objective-C 数据类型:从基本类型到对象类型
- 深入了解 Objective-C 中的协议与代理模式
- 理解 Objective-C 基础:从 C 扩展到面向对象
- 使用Objective-C绘制基本图形的两种方式
- 4月编程语言排行Fortran 击败 Objective-C
- IT博物馆之Objective-C诞生(博物馆icon)
- Objective C浅拷贝和深拷贝(浅拷贝与深拷贝java)
- 将Swift与Objective-C相结合(swift和objective-c)
- 使用Objective-C语言写一个"Hello, World!" 程序
- 标签列表
-
- mybatis plus (70)
- scheduledtask (71)
- css滚动条 (60)
- java学生成绩管理系统 (59)
- 结构体数组 (69)
- databasemetadata (64)
- javastatic (68)
- jsp实用教程 (53)
- fontawesome (57)
- widget开发 (57)
- vb net教程 (62)
- hibernate 教程 (63)
- case语句 (57)
- svn连接 (74)
- directoryindex (69)
- session timeout (58)
- textbox换行 (67)
- extension_dir (64)
- linearlayout (58)
- vba高级教程 (75)
- iframe用法 (58)
- sqlparameter (59)
- trim函数 (59)
- flex布局 (63)
- contextloaderlistener (56)