一、Objective-C的本质
我们平时编写的Objective-C代码,底层是通过C\C++代码实现的。 编译过程.png
所以Objective-C的面向对象都是基于C\C++的数据结构
- 1、将Objective-C代码转换为C\C++代码
在终端输入下面的命令把OC代码的.m文件转换成.cpp文件
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc [OC文件] -o [.cpp文件]
eg:把ViewController.m文件转出ViewController.cpp文件
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc ViewController.m -o ViewController.cpp
- 1.1、Objective-C转成C++代码后是一个结构体,并且其中有一个Class类型的属性isa NSObject本质是结构体.png
- 1.2、Class是一个struct objc_class类型的指针 Class的类型.png
- 1.3、struct objc_class类型结构 struct objc_class类型结构.png
- 1.4、NSObject类转换为C\C++代码源码
// 1、NSObject
struct NSObject_IMPL {
Class isa;
};
// 2、Class
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;
// 3、struct objc_class
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */
- 2、Objective-C的对象、类主要是基于C\C++的结构体实现的
- 3、NSObject底层原理 NSObject底层原理.png
二、NSObject对象在内存中的布局和所占大小
- (void)viewDidLoad {
[super viewDidLoad];
//1、NSObject实例对象占用内容大小
NSObject *revan_Obj = [[NSObject alloc] init];
/* Returns the size of instances of a class.
表示[NSObject class]实例的内存大小
*/
NSLog(@"class_getInstanceSize([NSObject class]):%zd\n", class_getInstanceSize([NSObject class]));
/* Returns size of given ptr
表示分配给revan_Obj这个对象的内存大小
*/
NSLog(@"malloc_size((__bridge const void *)(revan_Obj)):%zd", malloc_size((__bridge const void *)(revan_Obj)));
}
//输出
class_getInstanceSize([NSObject class]):8
malloc_size((__bridge const void *)(revan_Obj)):16
1、class_getInstanceSize([NSObject class])的大小为什么是8?
- 从上面对NSObject底层原理的分析可知,NSObject中只有一个isa指针,我们知道在ARM64位机中会给指针分配8个内存单元
2、malloc_size((__bridge const void *)(revan_Obj)):的大小为什么是16?
- OC源码
- [NSObject alloc]的时候为实例对象分配内存,我们可以通过查看源码发现分配的内存大小不小于16(在查看源码是直接搜索allocWithZone)
size_t instanceSize(size_t extraBytes) {
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
return size;
}
三、自定义类
1、自定义一个RevanPerson类
#import <Foundation/Foundation.h>
@interface RevanPerson : NSObject {
int _no;
int _age;
}
@end
#import "RevanPerson.h"
@implementation RevanPerson
@end
- 1.1、RevanPerson有2个成员变量时(测试代码)
- (void)viewDidLoad {
[super viewDidLoad];
//1、RevanPerson实例对象占用内容大小
RevanPerson *revan_person = [[RevanPerson alloc] init];
/* Returns the size of instances of a class.
表示[RevanPerson class]实例的内存大小
*/
NSLog(@"class_getInstanceSize([RevanPerson class]):%zd\n", class_getInstanceSize([RevanPerson class]));
/* Returns size of given ptr
表示分配给revan_person这个对象的内存大小
*/
NSLog(@"malloc_size((__bridge const void *)(revan_person)):%zd", malloc_size((__bridge const void *)(revan_person)));
}
//打印输出
class_getInstanceSize([RevanPerson class]):16
malloc_size((__bridge const void *)(revan_person)):16
- 1.2、RevanPerson有3个成员变量
#import <Foundation/Foundation.h>
@interface RevanPerson : NSObject {
int _no;
int _age;
int _height;
}
@end
- 测试代码
- (void)viewDidLoad {
[super viewDidLoad];
//1、RevanPerson实例对象占用内容大小
RevanPerson *revan_person = [[RevanPerson alloc] init];
/* Returns the size of instances of a class.
表示[RevanPerson class]实例的内存大小
*/
NSLog(@"class_getInstanceSize([RevanPerson class]):%zd\n", class_getInstanceSize([RevanPerson class]));
/* Returns size of given ptr
表示分配给revan_person这个对象的内存大小
*/
NSLog(@"malloc_size((__bridge const void *)(revan_person)):%zd", malloc_size((__bridge const void *)(revan_person)));
}
//打印输出
class_getInstanceSize([RevanPerson class]):24
malloc_size((__bridge const void *)(revan_person)):32
- 同样是自定义RevanPerson类,成员变量不同,造成输出也不同
- 分析:RevanPerson有2个成员变量时的内存分配情况
分析:RevanPerson有2个成员变量时的内存分配情况
1、底层实现如下
struct RevanPerson_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int _no;
int _age;
};
2、发现其中包含一个struct NSObject_IMPL类型的成员变量NSObject_IVARS
3、struct NSObject_IMPL结构如下,这个就是上面研究的NSObject的底层实现,
因为struct NSObject_IMPL结构体内部只有一个Class 类型的成员变量isa,
我们知道Class其实是(struct objc_class *),是一个指针,在ARM64架构下会给指针分配8个内存空间
struct NSObject_IMPL {
Class isa;
};
4、在ARM64架构下会给int类型分配4个内存单元
struct RevanPerson_IMPL {
struct NSObject_IMPL NSObject_IVARS; //8
int _no; //4
int _age; //4
};
所以,在RevanPerson为2个成员变量的情况下,会被分配16个内存空间
分析:malloc_size((__bridge const void *)(revan_person)),
revan_person对象占用16个内存空间
1、下面是在创建实例对象时为对象分配内存空间源码,可以看出会为一个对象至少分配16个内存空间
size_t instanceSize(size_t extraBytes) {
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
return size;
}
2、由于我们现在RevanPerson有2个成员变量,只占用16个内存空间,
小于等于 创建对象时的16个内存空间,所以系统不会再为revan_person对象分配更多内存空间
Person2个成员变量.png
- 分析:RevanPerson有3个成员变量时的内存分配情况
分析:RevanPerson有3个成员变量时的内存分配情况
1、底层实现如下
struct RevanPerson_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int _no;
int _age;
int _height;
};
2、发现其中包含一个struct NSObject_IMPL类型的成员变量NSObject_IVARS
3、struct NSObject_IMPL结构如下,这个就是上面研究的NSObject的底层实现,
因为struct NSObject_IMPL结构体内部只有一个Class 类型的成员变量isa,
我们知道Class其实是(struct objc_class *),是一个指针,在ARM64架构下会给指针分配8个内存空间
struct NSObject_IMPL {
Class isa;
};
4、在ARM64架构下会给int类型分配4个内存单元
struct RevanPerson_IMPL {
struct NSObject_IMPL NSObject_IVARS; // 8
int _no; // 4
int _age; // 4
int _height; // 4
};
RevanPerson_IMPL占用内存空间大小计算: 8 + 4 + 4 + 4 = 20,但是打印输出是24
这是因为结构体中有一个内存对齐的要求,在RevanPerson_IMPL这个结构体中各个变量占用最大内存是8,
所以这个RevanPerson_IMPL结构体以后分配的内存就会按照 8 的整数倍来分配,因为20不是8的整数倍,
所以RevanPerson_IMPL这个结构体会被分配24个内存地址
分析:malloc_size((__bridge const void *)(revan_person)),
revan_person对象占用32个内存空间
1、创建对象是为对象分配内存空间源码,可以看出会为一个对象至少分配16个内存空间
size_t instanceSize(size_t extraBytes) {
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
return size;
}
2、由于我们现在RevanPerson有3个成员变量,24个内存空间,但是在内存分配中也是有内存对齐的原则,由于为每一个对象分配的内存空间至少16个,所以以后类实例分配的内存空间都是16的倍数,所以是32
Person3个成员变量.png
2、int 和 NSInteger 的区别
- RevanPerson新增一个NSInteger类型的成员变量
#import <Foundation/Foundation.h>
@interface RevanPerson : NSObject {
int _no;
int _age;
int _height;
// int _cla;
NSInteger _cla;
}
@end
- 测试代码
- (void)viewDidLoad {
[super viewDidLoad];
//1、RevanPerson实例对象占用内容大小
RevanPerson *revan_person = [[RevanPerson alloc] init];
/* Returns the size of instances of a class.
表示[RevanPerson class]实例的内存大小
*/
NSLog(@"class_getInstanceSize([RevanPerson class]):%zd\n", class_getInstanceSize([RevanPerson class]));
/* Returns size of given ptr
表示分配给revan_person这个对象的内存大小
*/
NSLog(@"malloc_size((__bridge const void *)(revan_person)):%zd", malloc_size((__bridge const void *)(revan_person)));
}
//打印输出
class_getInstanceSize([RevanPerson class]):32
malloc_size((__bridge const void *)(revan_person)):32
- int 和 NSInteger 的区别分析
上面已经相信分析了结构体分配原则,这里就不在细说,现在这个结构体和上面结构体不同的是这个结构体多了一个NSInteger类型的成员变量,现在占用了32个内存空间
struct RevanPerson_IMPL {
struct NSObject_IMPL NSObject_IVARS;// 8
int _no;//4
int _age;//4
int _height;//4
NSInteger _cla;//?
};
RevanPerson_IMPL内存空间:8 + 4 + 4 + 4 + ? = 32 = 20 + ?
由于结构体的内存对齐原则,也就是这个结构体的内存大小是8的整数倍
在上面的分析RevanPerson_IMPL结构体中分配了24个内存单元,但是实际使用了20个内存单元(8 + 4 + 4 + 4),
struct RevanPerson_IMPL {
struct NSObject_IMPL NSObject_IVARS;// 8
int _no;//4
int _age;//4
int _height;//4
};
所以可以推断NSInteger类型占用的内存空间大于4
网友评论