上一篇我們簡單的介紹了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
memoryManagerment_13.png
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的底層源碼可見:
memoryManagerment_14.png
memoryManagerment_15.png
- weak對象銷毀會自動將其指的對象置為nil
- obj->clearDeallocating();會根據當前對象的地址值,然后通過hash查找到當前的引用計數和弱引用(弱引用表 ),將弱引用清除掉
dealloc
memoryManagerment_16.png
具體的見上流程圖(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
可以看到下面的圖:
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
"<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__);
}
友情鏈接: