上一篇我们简单的介绍了NSTimer、NSProxy、GCD定时器、自定义time、iOS程序的内存布局、Tagged Pointer、copy和mutableCopy等,下面我们继续看看内存的管理
Demo代码可见MemoryManagement
自定义copy
YDPerson.h
#import <Foundation/Foundation.h>
@interface YDPerson : NSObject<NSCopying>
@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
weak对象销毁
MyPerson.h
#import <Foundation/Foundation.h>
@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的底层源码可见:
- weak对象销毁会自动将其指的对象置为nil
- obj->clearDeallocating();会根据当前对象的地址值,然后通过hash查找到当前的引用计数和弱引用(弱引用表 ),将弱引用清除掉
dealloc
具体的见上流程图(weak)
自动释放池
#import <Foundation/Foundation.h>
#import "myPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
myPerson *person = [[[myPerson alloc] init] autorelease];
NSLog(@"%@",person);
}
return 0;
}
查看源码,打断点objc4-750
可以看到下面的图:
AutoreleasePoolPage的结构
Runloop和Autorelease
- (void)viewDidLoad {
[super viewDidLoad];
NSObject *obj = [[[NSObject alloc]init] autorelease];
NSLog(@"%@",[NSRunLoop mainRunLoop]);
}
打印:
截取其中关于:AutoreleasePool
"<CFRunLoopObserver 0x6000036406e0 [0x10c5b7b68]>{valid = Yes, activities = 0x1, repeats = Yes, order = -2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler (0x114c601b1), context = <CFArray 0x60000090c7b0 [0x10c5b7b68]>{type = mutable-small, count = 1, values = (\n\t0 : <0x7fcf47800058>\n)}}",
"<CFRunLoopObserver 0x6000036405a0 [0x10c5b7b68]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler (0x114c601b1), context = <CFArray 0x60000090c7b0 [0x10c5b7b68]>{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__);
}
友情链接: