内存管理(二)

上一篇我们简单的介绍了NSTimer、NSProxy、GCD定时器、自定义time、iOS程序的内存布局、Tagged Pointer、copy和mutableCopy等,下面我们继续看看内存的管理
Demo代码可见MemoryManagement


自定义copy

YDPerson.h
#import 
@interface YDPerson : NSObject
@property (assign, nonatomic) int age;
@property (assign, nonatomic) double height;
@end

YDPerson.m
#import "YDPerson.h"
@implementation YDPerson
- (id)copyWithZone:(NSZone *)zone{
    YDPerson *person = [[YDPerson allocWithZone:zone] init];
    person.age = self.age;
//    person.weight = self.weight;
    person.height = 180.0f;
    return person;
}

- (NSString *)description{
    return [NSString stringWithFormat:@"age = %d, weight = %f", self.age, self.height];
}
@end

YDPerson *p = [[YDPerson alloc]init];
p.age = 18;
p.height = 200.0f;
NSLog(@"p.age = %d  p.height = %f",p.age,p.height);
    
YDPerson *p1 = [p copy];
NSLog(@"p1.age = %d  p1.height = %f",p1.age,p1.height);

打印:
MemoryManagement[31345:1581114] p.age = 18  p.height = 200.000000
MemoryManagement[31345:1581114] p1.age = 18  p1.height = 180.000000

引用计数的存储

详见runtime源码:NSObject.mm

memoryManagerment_13.png

weak对象销毁

MyPerson.h
#import 
@interface MyPerson : NSObject
@end

MyPerson.m
#import "MyPerson.h"
@implementation MyPerson
-(void)dealloc{
    NSLog(@"%s",__FUNCTION__);
}
@end

__strong MyPerson *p1;
NSLog(@"111");
{
   MyPerson *person = [[MyPerson alloc]init];
   p1 = person;
}
NSLog(@"222");

打印:
MemoryManagement[31529:1592495] 111
MemoryManagement[31529:1592495] 222
MemoryManagement[31529:1592495] -[MyPerson dealloc]

===============================
__weak MyPerson *p2;
NSLog(@"111");
{
  MyPerson *person = [[MyPerson alloc]init];
  p2 = person;
}
NSLog(@"222");

打印:
MemoryManagement[31576:1595781] 111
MemoryManagement[31576:1595781] -[MyPerson dealloc]
MemoryManagement[31576:1595781] 222

================================
__unsafe_unretained MyPerson *p3;
NSLog(@"111");
{
    MyPerson *person = [[MyPerson alloc]init];
    p3 = person;
}
NSLog(@"222");

打印:
MemoryManagement[31670:1603150] 111
MemoryManagement[31670:1603150] -[MyPerson dealloc]
MemoryManagement[31670:1603150] 222

从runtime的底层源码可见:

memoryManagerment_14.png
memoryManagerment_15.png
  • weak对象销毁会自动将其指的对象置为nil
  • obj->clearDeallocating();会根据当前对象的地址值,然后通过hash查找到当前的引用计数和弱引用(弱引用表 ),将弱引用清除掉

dealloc

memoryManagerment_16.png

具体的见上流程图(weak)


自动释放池

#import 
#import "myPerson.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        myPerson *person = [[[myPerson alloc] init] autorelease];
        NSLog(@"%@",person);
    }
    return 0;
}

查看源码,打断点objc4-750

可以看到下面的图:


memoryManagerment_17.png

AutoreleasePoolPage的结构

memoryManagerment_18.png
memoryManagerment_19.png
memoryManagerment_20.png
memoryManagerment_21.png

Runloop和Autorelease

memoryManagerment_22.png
- (void)viewDidLoad {
    [super viewDidLoad];
    NSObject *obj = [[[NSObject alloc]init] autorelease];
    NSLog(@"%@",[NSRunLoop mainRunLoop]);
}

打印:
截取其中关于:AutoreleasePool

    "{valid = Yes, activities = 0x1, repeats = Yes, order = -2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler (0x114c601b1), context = {type = mutable-small, count = 1, values = (\n\t0 : <0x7fcf47800058>\n)}}",
    "{valid = Yes, activities = 0xa0, repeats = Yes, order = 2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler (0x114c601b1), context = {type = mutable-small, count = 1, values = (\n\t0 : <0x7fcf47800058>\n)}}"

其中:activities的类型为:
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry = (1UL << 0),
    kCFRunLoopBeforeTimers = (1UL << 1),
    kCFRunLoopBeforeSources = (1UL << 2),
    kCFRunLoopBeforeWaiting = (1UL << 5),
    kCFRunLoopAfterWaiting = (1UL << 6),
    kCFRunLoopExit = (1UL << 7),
    kCFRunLoopAllActivities = 0x0FFFFFFFU
};

activities = 0x1:kCFRunLoopEntry 监听RunLoop对象进入循环的事件
activities = 0xa0:kCFRunLoopBeforeWaiting|kCFRunLoopExit 监听RunLoop即将进入休眠和RunLoop对象退出循环的事件

程序运行后产生的两个CFRunLoopObserver一个监听RunLoop对象进入循环的事件,执行回调函数_wrapRunLoopWithAutoreleasePoolHandler,并且优先级order为-2147483647即32位整数的最小值,保证了它的优先级最高。在回调内会调用_objc_autoreleasePoolPush函数来创建AutoreleasePool,由于它的优先级最高,所以能够保证自动释放池在其他回调执行前得到创建。

另一个监听器监听RunLoop对象进入休眠和退出循环的事件,回调函数同样是_wrapRunLoopWithAutoreleasePoolHandler,而优先级为2147483647即32位整数的最大值,保证它的优先级最低。对于监听进入休眠状态时回调函数内首先会调用_objc_autoreleasePoolPop函数来释放AutoreleasePool然后使用_objc_autoreleasePoolPush函数重新创建一个自动释放池。优先级最低保证了释放操作是在其他所有回调执行之后发生


autoreleasePool到底是什么时机创建和释放?
- 当开启或者唤醒runloop的时候,会创建一个autoreleasePool;
- kCFRunLoopBeforeWaiting | kCFRunLoopExit当runloop睡眠之前或者退出runloop的时候会释放autoreleasePool;


//结论
- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 这个Person什么时候调用release,是由RunLoop来控制的
    // 它可能是在某次RunLoop循环中,RunLoop休眠之前调用了release
        Person *person = [[[Person alloc] init] autorelease];
     
    Person *person = [[Person alloc] init];ARC下,会出这个大括号就被释放了,主动调用了. [ person release];
    NSLog(@"%s", __func__);  
}

友情链接:

  • gitHub_Demo

  • 内存管理(一)

  • weak原理实现

你可能感兴趣的:(内存管理(二))