內(nèi)存管理(二)

上一篇我們簡單的介紹了NSTimer、NSProxy、GCD定時器、自定義time、iOS程序的內(nèi)存布局、Tagged Pointer、copy和mutableCopy等,下面我們繼續(xù)看看內(nèi)存的管理
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

引用計數(shù)的存儲

詳見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();會根據(jù)當前對象的地址值,然后通過hash查找到當前的引用計數(shù)和弱引用(弱引用表 ),將弱引用清除掉

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 監(jiān)聽RunLoop對象進入循環(huán)的事件
activities = 0xa0:kCFRunLoopBeforeWaiting|kCFRunLoopExit 監(jiān)聽RunLoop即將進入休眠和RunLoop對象退出循環(huán)的事件

程序運行后產(chǎn)生的兩個CFRunLoopObserver一個監(jiān)聽RunLoop對象進入循環(huán)的事件,執(zhí)行回調(diào)函數(shù)_wrapRunLoopWithAutoreleasePoolHandler,并且優(yōu)先級order為-2147483647即32位整數(shù)的最小值,保證了它的優(yōu)先級最高。在回調(diào)內(nèi)會調(diào)用_objc_autoreleasePoolPush函數(shù)來創(chuàng)建AutoreleasePool,由于它的優(yōu)先級最高,所以能夠保證自動釋放池在其他回調(diào)執(zhí)行前得到創(chuàng)建。

另一個監(jiān)聽器監(jiān)聽RunLoop對象進入休眠和退出循環(huán)的事件,回調(diào)函數(shù)同樣是_wrapRunLoopWithAutoreleasePoolHandler,而優(yōu)先級為2147483647即32位整數(shù)的最大值,保證它的優(yōu)先級最低。對于監(jiān)聽進入休眠狀態(tài)時回調(diào)函數(shù)內(nèi)首先會調(diào)用_objc_autoreleasePoolPop函數(shù)來釋放AutoreleasePool然后使用_objc_autoreleasePoolPush函數(shù)重新創(chuàng)建一個自動釋放池。優(yōu)先級最低保證了釋放操作是在其他所有回調(diào)執(zhí)行之后發(fā)生


autoreleasePool到底是什么時機創(chuàng)建和釋放?
- 當開啟或者喚醒runloop的時候,會創(chuàng)建一個autoreleasePool;
- kCFRunLoopBeforeWaiting | kCFRunLoopExit當runloop睡眠之前或者退出runloop的時候會釋放autoreleasePool;


//結論
- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 這個Person什么時候調(diào)用release,是由RunLoop來控制的
    // 它可能是在某次RunLoop循環(huán)中,RunLoop休眠之前調(diào)用了release
        Person *person = [[[Person alloc] init] autorelease];
     
    Person *person = [[Person alloc] init];ARC下,會出這個大括號就被釋放了,主動調(diào)用了. [ person release];
    NSLog(@"%s", __func__);  
}

友情鏈接:

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容