iOS知识点记录

575 阅读6分钟

一、OC对象相关

1、objective-C --> C\C++ --> 汇编语言 --> 机器语言

2、OC对象是什么

// OC类的定义
@interface NSObject <NSObject>
{
  Class isa OBJC_ISA_AVAILABILITY;
}

// OC的类转成C++的结构体
struct NSObject_IMPL
{
  Class isa;
}

//注:Class也是一个结构体 : typedef struct objc_class Class

结论1:一个 NSObject 类编译后是一个 C++ 的结构体,结构体的成员变量仅包含一个 isa
结论2:一个 NSObject 对象在内存分配上相当于包含一个成员变量的结构体的内存分配
注:该结构体在64bit 系统下只占用8个字节(32bit下占用4个字节),即 obj 对象在内存分配上实际占用8个字节

3、OC对象占用空间的规则

NSObject *obj = [[NSObject alloc] init];

// 通过classgetInstanceSize(Class _Nullable cls)获取 NSObject 的实例对象所占用内存的大小
class_getInstanceSize([NSObject class]); // 8

// 通过 extern sizet malloc_size(const void *ptr) 可以获得 obj 指针所指向的那块内存的大小
malloc_size((__bridge const void *) obj); // 16

原因class_getInstanceSize : alignedInstanceSize --> word-align

alloc: allocWithZone --> objc-rootAllocWithZone --> class-createInstance --> class-createInstanceFromZone --> instanceSize --> alignedInstanceSize --> word-align

注意:最后两步是一样的!关键在于倒数第三步 instanceSize

size_t instanceSize(size_t extraBytes)
{
  size_t size = alignedInstanceSize() + extraBytes;
  if (size < 16) 
    size = 16;
  return size;
}

// alignedInstanceSize() 内存对齐后是8个字节,由于extraBytes等于0,因此 size < 16成立,所以最终的 size 返回的是16

class_getInstanceSize、mallocsize的区别:前者是获得NSObject实例对象的成员变量所占用的大小,后者是操作系统实际上给NSObject的实例对象 obj 分配的内存大小。

4、结构体成员数据对齐

在结构体中,成员数据对齐需满足以下规则:

  • 结构体中的第一个成员的首地址也即是结构体变量的首地址。
  • 结构体中的每一个成员的首地址相对于结构体的首地址的偏移量(offset)是该成员数据类型大小的整数倍。
  • 结构体的总大小是对齐模数(对齐模数等于#pragma pack(n)所指定的n与结构体中最大数据类型的成员大小的最小值)的整数倍。
@interface Person : NSObject
{
    int age;
}
@end
  
// 在包含一个成员变量的 Person 类中,编译后生成的 C++ 结构体中本质上是有两个成员:isa、age,由于 isa 占用8个字节,age 类型为 int 占用4个字节,为了满足规则第三条:结构体的总大小必须是最大数据类型的成员大小的整数倍,就是 8 的整数倍为 16

问:

1、其他常见数据类型分别占多少字节?

2、有没有第二条规则的例子?

3、该条目可能有更多的知识点补充。

**总结: **一个 OC 对象在编译后会生成一个 C++ 结构体,结构体中包含了所有的成员变量和一个 isa,在内存分配上会按照一定的对齐规则进行管理。

5、OC对象分为三大类

  • 实例对象
  • 类对象
  • 元类对象

一个类可以创建 多个不同的实例对象,但是仅可以创建 一个类对象和 一个元类对象!

6、继承关系

struct NSObject_IMPL
{
  Class isa;
};

struct Person_IMPL
{
  struct NSObject_IMPL NSObject_IVARS;
  int _age;
  int _card;
  int _year;
};

struct Man_IMPL
{
  struct Person_IMPL Person_IVARS;
  int _money;
  int _houses;
  int _cars;
};

// 注:ivar的意思是 变量(推测的)

// 所有的实例对象的C++结构体中仅仅包含了成员变量(当然也存储这一个isa指针和一个superclass指针),也就是说实例对象仅仅存储各自的成员变量的值

**思考: **对象可以创建多个,每个对象都有自己的成员变量和对应的值,但是方法大家调用的都是同一个,不管你是实例对象还是类对象都是调用的一个方法。因此OC在设计这门语言的时候,我们有必要将只需要存储一份的数据交给实例对象去管理吗?肯定不需要。所以:实例方法存储于类对象中,类方法存储在元类对象中。

类对象中都存储着以下信息数据:

isa指针
superclass指针
类的成员变量信息(ivar)
类的属性信息(@property)、
类的协议信息(@protocol)、
类的对象方法信息(instance method)、
......

元类对象中存储的信息数据包括:

isa指针
superclass指针
类的类方法信息(classmethod)
......

7、isa指针与superclass指针

img

此处只是概览,细节需另外查资料,比如实例对象有没有superclass,调实例对象的superclass会怎么走?

二、内存管理

1、物理内存与虚拟内存

对于一般的iPhone,实际物理内存都在1G左右。

系统内核维护一套虚拟内存系统,当我们向系统申请内存时,系统并不会给你返回物理内存的地址,而是给你一个虚拟内存地址。

只有我们开始使用申请到的虚拟内存时,系统才会将虚拟地址映射到物理地址上,从而让程序使用真实的物理内存。

2、内存分页

系统会对虚拟内存和物理内存进行分页,虚拟内存到物理内存的映射都是以页为最小粒度的。

基于A9处理器的系统,物理和虚拟内存都是以16KB进行分页。

系统将内存页分为三种状态:

1、活跃内存页(active pages)- 这种内存页已经被映射到物理内存中,而且近期被访问过,处于活跃状态。

2、非活跃内存页(inactive pages)- 这种内存页已经被映射到物理内存中,但是近期没有被访问过。

3、可用的内存页(free pages)- 没有关联到虚拟内存页的物理内存页集合。

当可用的内存页降低到一定的阀值时,系统就会采取低内存应对措施,在OSX中,系统会将非活跃内存页交换到硬盘上,而在iOS中,则会触发Memory Warning,如果你的App没有处理低内存警告并且还在后台占用太多内存,则有可能被杀掉。

3、malloc

alloc、release方法的调用,通常最终都会走到libsystem_malloc.dylib的malloc()和free()函数。

最终libsystem_malloc.dylib也都会向iOS的系统内核发起申请,映射实际内存到App进程的地址空间上。

苹果官方推荐使用calloc代替malloc,calloc返回的内存区域会自动清零,而且只有使用时才会关联到物理内存并清零。