`
chenniaoc
  • 浏览: 40100 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Cocoaのメモリ管理(3)

阅读更多
保持と解除という方法は、理屈は分かるし簡単そうに見えます。しかし、実際にやってみると意外と難しいことがわかります。そこでCocoaでは少し楽をするための仕組みを導入しています。簡単に言えば、とりあえずなんでも入れておけるごみ袋を用意して、不要になった時点でごみ袋ごと捨てちゃうという方法です。このごみ袋にあたるのが、NSAutoreleasePoolというクラスです。
Application KitにおけるNSAutoreleasePool

さて、Cocoaの重要なフレームワークの一つであるApplication Kitの話から始めたいと思います。Application Kitは、主にGUIを持つアプリケーションを作成するためのフレームワークです。このフレームワークを利用して作ったアプリケーション(つまり、ぶっちゃけた話、ProjectBuilderとInterfaceBuilderを使って作られたNSApplicationを利用するアプリケーション) には、デフォルトで一つNSAutoreleasePoolオブジェクトが生成されています。さらに、NSApplicationが提供するイベントループ内でも、NSAutoreleasePoolオブジェクトが生成され、ループが一回転するごとに解放され、新しいNSAutoreleasePoolオブジェクトを生成します。つまり、NSAutoreleasePoolそのものを知らなくてもとりあえず使える仕組みが提供されているわけです。このごみ袋にオブジェクトを入れる方法が、NSObjectに用意されているautoreleaseメソッドです。Foundation KitとApplication Kitが提供するクラスは、基本的にNSAutoreleasePoolがある環境での動作を想定しています。

- (id)autorelease

Application Kitに準拠したプログラムの場合、任意のオブジェクトにautoreleaseを送っておくと、このオブジェクトはデフォルトの NSAutoreleasePoolオブジェクトに登録されます。そして、このプログラムが終了する直前にデフォルトの NSAutoreleasePoolオブジェクトとともに、登録されたオブジェクトがすべて解放されます。つまりautoreleaseは、retain & releaseの仕組みを使わないで、楽々とメモリ管理を行う仕組みなのです。

- (void)methodA
{
    id obj = [[Foo alloc] init];

    // objとして参照されているオブジェクトをNSAutoreleasePoolに登録する。
    [obj autorelease];
    // このメソッドを抜けると、変数objによる参照はなくなるが、
    // 割り当てられたメモリは、イベントループのNSAutoreleasePoolが
    // 解放される時に解放される。
}

NSAutoreleasePoolの特徴

ApplicationKitが提供するNSAutoreleasePoolを使いこなすにあたって、いくつかの注意すべき特徴があります。

    * デフォルトのNSAutoreleasePoolは、プログラムの最後に(つまり、メインスレッドの末端で)解放されるので、基本的にこれに登録されたオブジェクトに割り当てられたメモリはプログラムの最後まで解放されない。
    * イベントループ内で毎回生成されるNSAutoreleasePoolは、ループが一回転するごとに解放されるので、基本的にこれに登録されたオブジェクトに割り当てられたメモリはイベント処理が終わると解放される。
    * autoreleaseされたオブジェクトが、基本的にそのオブジェクトが登録されたNSAutoreleasePoolがreleaseされるまで解放されないということは、逆に言えば、NSAutoreleasePoolが生きている間は、オブジェクトの生存が保障されるということを意味している。
    * ただし、releaseメソッドを送ることで、このようなオブジェクトでもプログラムの途中で解放することは可能である。(これは、推奨されない方法である。基本的に、同じオブジェクトに対してautoreleaseとreleaseを併用することは混乱の元になるので、止めるべきである。 deallocについてはいわずもがな。オブジェクトのプログラマブルな解放のためには、後述する方法を用いるべきである。)
    * 例外発生時には、NSAutoreleasePool(と、登録されたオブジェクト)は解放されるので、メモリリークは起こらない。(どっちにしてもプログラムが終了するので、メモリは解放される。)
    * デフォルトのNSAutoreleasePoolだけで動作するようなプログラム場合、参照が切れたオブジェクトは、基本的にメモリリークと同じ状況になる。(あえて言うなら、知った上でのメモリリーク。通常のプログラムではあまり気にする必要がない。)
    * NSAutoreleasePoolに登録されているオブジェクトへのアクセス手段はないので、参照の管理には注意が必要である。

自前のNSAutoreleasePoolの利用

さて、長々と引き伸ばしてきた話題に入ろうと思います。まず、Application Kitが用意しているNSAutoreleasePoolだけでは、一度のイベント処理で大量にオブジェクトを生成するような処理に対応するのが難しい場合があります。また、そもそもApplication Kitを利用しないプログラム(たとえば、コマンドラインで動作するツールなど)は、この仕組みが提供されていないわけです。そこで、自前の NSAutoreleasePoolを作成して利用する意味が出てくるのです。ここであれこれいうより、まずはサンプルコードで説明しましょう。以下のコード内のobj2を見てわかるように、実はautoreleaseメソッドを呼び出すと、スレッド内で一番最近作られた NSAutoreleasePoolにオブジェクトが登録されます。つまり、テンポラリなオブジェクトは、このように自前の NSAutoreleasePoolオブジェクトを適宜用意してやれば、任意の時点でまとめて解放できるのです。

- (void)methodA
{
    id obj1;
    id obj2;
    id arp;

    obj1 = [[Foo alloc] init];
    obj2 = [[Foo alloc] init];

    [obj1 autorelease]; // obj1は、デフォルトのNSAutoreleasePoolに登録される。
    arp = [[NSAutoreleasePool alloc] init];
    [obj2 autorelease]; // obj2は、arpに登録される。
    [arp release]; //arp解放。この時点でobj2は、解放される。

    // obj1は、上位のNSAutoreleasePoolがreleaseされるまで解放されない。
}

理解を深めるためにもう一つ例を示しておきます。

- (void)methodA
{
    id temp;
    id arp;
    int i;

    for (i = 0; i < 10; i++) {
        arp = [[NSAutoreleasePool alloc] init];
        temp = [[[Foo alloc] init] autorelease];
                            // tempで現在参照されているオブジェクトは、
                            // arpに登録される。
        [arp release];      // arp解放。
                            // この時点でtempで参照されるオブジェクトは解放される。
    }
    // この時点で10個のオブジェクトが解放されています。
}

入れ子になったNSAutoreleasePoolの利用

もうお分かりかと思いますが、NSAutoreleasePoolはいくらでも入れ子することができるのです。スレッド上で一番最近作られた NSAutoreleasePoolオブジェクトがカレントのNSAutoreleasePoolオブジェクトとして機能するわけです。さて、まずは自前のNSAutoreleasePoolを活用する前に、いくつかの注意点を示しておきます。

    * NSAutoreleasePoolオブジェクトにはretainを送ってはならない。
    * NSAutoreleasePoolオブジェクトにはautoreleaseを送ってはならない。
    * 無用なバグと混乱を避けるために、NSAutoreleasePoolオブジェクトの生成と解放は、同じ文脈上で行うべきである。(たとえば、前述の例のように、ループ内の処理の前後など)
    * retain回数 + 1 = autorelease回数がプログラム全体で成り立つことを検証する。

さて、スレッドの流れを把握しているなら、NSAutoreleasePoolオブジェクトを複数のメソッドで使うことが可能です。 Application Kit標準のNSAutoreleasePoolオブジェクトも基本的にこの方法の例に他なりません。まずは簡単なサンプルを示します。この例では、 obj2がそれにあたります。

- (void)methodA
{
    id obj1;
    id arp1;


    arp1 = [[NSAutoreleasePool alloc] init];
    obj1 = [[[Foo alloc] init] autorelease]; // obj1は、arp1に登録される。
    [self methodB];
    [arp1 release];     //arp解放。この時点でobj1、obj2は、解放される。
}

- (void)methodB
{
    id obj2;
    id obj3;
    id arp2;

    obj2 = [[Foo alloc] init];
    obj3 = [[Foo alloc] init];

    [obj2 autorelease]; // obj2は、呼出し元のarp1に登録される。
    arp2 = [[NSAutoreleasePool alloc] init];
    [obj3 autorelease]; // obj3は、arp2に登録される。
    [arp2 release];     //arp2解放。この時点でobj3は、解放される。

    // obj2は、arp1解放まで解放されない。
}

いままでは単純な例でしたが、最後に少し複雑な例を示します。それは、メソッドに戻り値がある場合の扱いについてです。以下のサンプルを見て下さい。このコードには、重要な項目が隠されています。つまり、NSAutoreleasePoolオブジェクトは、登録されているオブジェクトに対する登録回数を管理していて、自身が解放される時に、その登録回数分のreleaseを各オブジェクトに送っているだけだということです。つまり登録回数がそのオブジェクトの保持数より少なければ、NSAutoreleasePoolオブジェクトが解放された後でも、そのオブジェクトは生き続けます。この例では、retにretainを送ることでretの寿命を引き伸ばしています。

- (void)methodA
{
    id obj;
    id arp1;

    arp1 = [[NSAutoreleasePool alloc] init];
    obj = [self methodB:1];
    [arp1 release];    // arp1解放。この時点でobjすなわちretは、解放される。
}

- (id)methodB:(int)i
{
    id ret;
    id arp2;

    arp2 = [[NSAutoreleasePool alloc] init];
    ret = [[Foo alloc] init]; // retの保持数は1
    [ret autorelease];
    if (i == 1) {
        [ret retain]; // retの保持数は2になる。
                      // autoreleaseではなく、retainを呼ぶことで、
                      // arp2の寿命を超えて、arp1(もしくは、
                      // もっと外)の文脈まで、retで参照されるオブジェクトの
                      // 寿命を延ばすことができる。
    } else {
        ret = nil;
    }
    [arp2 release];   // 判定で真だったら、この時点でretの保持数は1になる。
                      // 判定で偽だったら、この時点でretは解放される。

    return [ret autorelease]; // retを生成したので、autoreleaseを送ってから返す。
}

autoreleaseか、retain/releaseか

autoreleaseか、retain/releaseかという問題ですが、今まで見てきたようにそんなに大差はありません。状況に応じて、選択、または併用するとよいでしょう。比較的小さなあまりオブジェクトを作らないプログラムでは、Application Kit標準のNSAutoreleasePoolオブジェクトを使うことで、autoreleaseだけでプログラミングするのもよいでしょう。しかし、自前のNSAutoreleasePoolオブジェクトを使うような局面なら、retain/releaseでも同じようなものでしょう。ただ、 NSAutoreleasePoolオブジェクトを使うことでソースコードのカスタマイズが容易になるという利点はあるかも知れません。なぜなら、オブジェクトは、NSAutoreleasePoolオブジェクトが少なくとも解放されるまで有効なのですから。
retain/release/autoreleaseの適用方針
以下に垣内さん、白山さん、高橋さんから教えていただいた適用方針をまとめたものを示します。

    * 基本としてオブジェクトのオーナシップを意識する。すなわち、以下の鉄則を厳守する。

      【鉄則1】自分で生成したオブジェクトは、自分で解放する
      【鉄則2】他人が生成したオブジェクトは、気に留めない
      【鉄則3】他人が生成したオブジェクトが必要なら、必ず保持(retain)して、必要にならなくなった時点で、必ず解除(release or autorelease)する

    * 可能な限り alloc-init系の生成メソッドは使わない。【鉄則2からの派生】

    * alloc、init…を使う場合は必ず autoreleaseを入れる。【鉄則1】

    aFoo = [[[Foo alloc] init] autorelease];

    * alloc-init系以外の生成用クラスメソッドの場合は何もしない。それらのクラスメソッドは内部でalloc-init系の生成メソッドを呼んでいるため、鉄則1が適用されていると考えるべきである。【鉄則2】

    aFoo = [Foo foo];

    * インスタンス変数・グローバル変数・スタティック変数に代入して、参照を残す場合は、retainをかける。【鉄則3】

    globalFoo = [aFoo retain];

    * インスタンス変数・グローバル変数・スタティック変数の値を消す場合には、かならずreleaseかautoreleaseを入れる。【鉄則3】

//この例ではinstanceFooは、このクラスのインスタンス変数と仮定する。
- (void )setFoo:newFoo
{
    [instanceFoo release];
    instanceFoo = [newFoo retain];
    return;
}

- (void )dealloc
{
    [instanceFoo release];
    [super dealloc];
    return;
}

    * 自分でクラスを作るときは、NSStringの+string... NSArrayの+array... のようなそのクラス専用の生成用クラスメソッドを作成して利用させる。【鉄則2の応用】

// 生成用の引数がない場合
+ foo 
{
    return [[[Foo alloc] init] autorelease];
}

// 生成用の引数が必要な場合
+ fooWithBar: bar
{
    return [[[Foo alloc] initWithBar:bar] autorelease];
}

    * 必ずループ内部でNSAutoreleasePoolを適用する。

    for (i = 0; i < 10; i++) {
        arp = [[NSAutoreleasePool alloc] init];
        temp = [[[Foo alloc] init] autorelease];
        [arp release];
    }
分享到:
评论

相关推荐

    基于C++开发的WEB服务器,支持C/C++、Python、Java等多语言混合开发WEB应用

    基于C++开发的WEB服务器,支持C/C++、Python、Java等多语言混合开发WEB应用

    基于STM8单片机的TM1615七段数码管驱动(模拟I2C).zip

    基于STM8单片机的编程实例,可供参考学习使用,希望对你有所帮助

    基于STM8单片机的电位器输入控制继电器输出实验.zip

    基于STM8单片机的编程实例,可供参考学习使用,希望对你有所帮助

    网络安全CTF靶场之rce-labs

    该靶场仅供学习使用!

    python爬虫豆瓣电影TOP250,以及数据化分析程序源代码+数据

    电影详情链接 图片链接 影片中文名 影片外国名 评分 评价人数 概况 相关信息 https://movie.douban.com/subject/1292052/ https://img2.doubanio.com/view/photo/s_ratio_poster/public/p480747492.jpg 肖申克的救赎   The Shawshank Redemption 9.7 2529468 希望让人自由 导演: 弗兰克·德拉邦特 Frank Darabont   主演: 蒂姆·罗宾斯 Tim Robbins ... 1994   美国   犯罪 剧情 https://movie.douban.com/subject/1291546/ https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2561716440.jpg 霸王别姬 9.6 1880353 风华绝代 导演: 陈凯歌 Kaige Chen   主演: 张国荣 Leslie Cheung 张丰毅 ........

    汽车中间件市场调研报告:2023年全球汽车中间件市场销售额达到了78亿美元

    汽车中间件市场调研报告:2023年全球汽车中间件市场销售额达到了78亿美元 在数字化转型的浪潮中,汽车中间件作为连接硬件与软件的关键桥梁,正引领着汽车行业的新一轮变革。随着全球汽车产业的快速发展,中间件市场规模持续扩大,展现出前所未有的增长潜力。然而,面对复杂多变的市场环境和不断涌现的新技术,企业如何精准把握市场脉搏,实现可持续发展?本文将深入探讨全球及中国汽车中间件市场的现状、趋势及竞争格局,为您揭示咨询的重要性。 市场概况: 根据QYResearch(恒州博智)的统计及预测,2023年全球汽车中间件市场销售额达到了78亿美元(约7803百万美元),预计2030年将达到156亿美元(约15630百万美元),年复合增长率(CAGR)为10.3%(2024-2030)。这一数据不仅彰显了中间件市场的强劲增长动力,也预示着未来巨大的市场空间。 技术创新与趋势: 随着自动驾驶、车联网等技术的不断发展,汽车中间件正面临着前所未有的技术挑战与机遇。新一代中间件需要具备更高的实时性、更低的延迟以及更强的数据处理能力,以满足复杂多变的汽车应用场景。同时,云计算、大数据、人工智能等技术的融合应用,将进

    python语言mp3pl爬虫程序代码QZQ.txt

    python语言mp3pl爬虫程序代码QZQ

    小语种字体TTF文件转PNG图片的方法

    # 小语种字体TTF文件转PNG图片的方法 ## 准备工作 1. 下载python3.9,推荐3.6~3.9,几个依赖包在这个版本运行的好。 2. 下载 FontForge-mingw-w64 ,可自行下载,或从文末打包好的工具包直接使用。 3. 下载需要导出的字体ttf文件,最好先装在本机系统上。 ## 导出方法 1. 把 `tts2png2.py` 文件复制到软件的bin目录下。 2. 修改 `tts2png2.py` 文件中的字体路径,注意Windows用双斜杠。 3. 从bin目录打开终端(管理员模式启动)。 4. 运行脚本 `./ffpython .\tts2png2.py`。

    26页-基于AI人工智能的智慧校园综合解决方案AI+智慧校园综合解决方案.pdf

    在21世纪的科技浪潮中,人工智能(AI)无疑是最为耀眼的明星之一,它以惊人的速度改变着我们的生活、工作乃至整个社会的运行方式。而在人工智能的广阔领域中,大模型(Large Models)的崛起更是开启了智能技术的新纪元,引领着AI向更加复杂、高效、智能的方向发展。本文将深入探讨人工智能大模型的内涵、技术特点、应用领域以及对未来的影响。 一、人工智能大模型的内涵 人工智能大模型,顾名思义,是指具有庞大参数规模和数据处理能力的AI模型。这些模型通过深度学习算法,在海量数据上进行训练,能够学习到丰富的知识表示和复杂的模式识别能力。与传统的小型或中型模型相比,大模型在理解自然语言、生成高质量内容、进行跨模态信息处理等方面展现出前所未有的优势。它们不仅能够执行特定的任务,如图像识别、语音识别,还能进行创造性的工作,如文本生成、音乐创作,甚至在某些情况下展现出接近或超越人类的智能水平。 二、技术特点 海量数据与高效训练:大模型依赖于庞大的数据集进行训练,这些数据涵盖了广泛的主题和情境,使得模型能够学习到丰富的语义信息和上下文理解能力。同时,高效的训练算法和硬件加速技术,如TPU(Tensor Processing Unit)和GPU,使得大规模模型的训练成为可能。 自注意力机制与Transformer架构:许多领先的大模型采用了Transformer架构,特别是其自注意力机制,这种设计使得模型在处理序列数据时能够捕捉到长距离依赖关系,极大地提高了模型的表达能力和泛化能力。 多任务学习与迁移学习:大模型通常具备多任务学习的能力,即在一次训练中同时学习多个任务,这有助于模型学习到更通用的知识表示。此外,迁移学习使得这些模型能够轻松适应新任务,只需少量额外数据或微调即可。

    下垂控制-基于T型三电平逆变器的下垂控制,电压电流双闭环,采用LCL滤波,SPWM调制方式 1.提供simulink仿真源文件 2.提供下垂控制原理与下垂系数计算方法 3.中点平衡控制,电压电流双闭环

    下垂控制-基于T型三电平逆变器的下垂控制,电压电流双闭环,采用LCL滤波,SPWM调制方式 1.提供simulink仿真源文件 2.提供下垂控制原理与下垂系数计算方法 3.中点平衡控制,电压电流双闭环控制 4.提供参考文献

    一个仿大众点评、美团的城市选择器,使用如同Rx一样优雅,并且UI和城市数据可以自定义.zip

    城市选择器一个仿大众点评的城市快速选择器, 最少只需 一行 代码即可启动城市选择器, 支持页面样式修改,多元化自定义截屏 版本日志V0.4.6优化地理位置设置时有时会设置不成功问题修复其他若干问题修改UI默认主题色V0.4.5修改设置位置信息方式,由之前必须在打开页面之前获取位置信息改为允许用户在打开页面后设置位置信息,具体使用方式见 Step3简化配置项,不需要在AndroidManifest中再注册Activity,并默认隐藏titlebarV0.4.3修复更新数据库表结构后第一次进入会闪退问题V0.4.0数据库表结构修改,增加了高德地图citycode设置gps城市的api略有改动见 Step3V0.3.3紧急修复一个可能导致内存泄漏问题优化提高滑动检索效率隐藏下拉刷新labelV0.3.1在搜索框后面添加一个清空搜索框按钮修复搜索框中输入空格会搜索出全部城市问题修复搜索结果弹出框中文字在不同theme下显示不同颜色问题,现在已统一为黑色其他调用时参数合法性校验V0.3.0简化api调用形式,修改为Rx形式,见操作步骤

    慢性病大数据分析处理.zip

    慢性病大数据分析处理慢性病项目

    Multisim单片机资源单片机C语言程序设计实训100例

    Multisim单片机资源单片机C语言程序设计实训100例提取方式是百度网盘分享地址

    PMSM永磁同步电机最大转矩电流比MTPA控制仿真,弱磁控制仿真,前馈补偿仿真程序,详细解析教程文档 这是一份非常完美的仿真文件及详细教程,从仿真效果图看转速、电流及转矩跟随非常稳定 该算法架构包

    PMSM永磁同步电机最大转矩电流比MTPA控制仿真,弱磁控制仿真,前馈补偿仿真程序,详细解析教程文档。 这是一份非常完美的仿真文件及详细教程,从仿真效果图看转速、电流及转矩跟随非常稳定。 该算法架构包含如下模块: 1)SVPWM矢量控制模块 2)转速环PI调节器、电流环PI调节器; 3)MTPA调节器; 4)弱磁控制器; 5)前馈补偿; 一份该仿真的算法说明文档,每一步都有详细介绍如何搭建,包括环路参数怎么算,拿来做毕设或者学习都很方便; 几篇参考文献; 一篇作者自己写的算法总结,让你少走弯路; 两个视频;

    手绘卡通儿童人物幼儿园教学课件模板.pptx

    手绘卡通儿童人物幼儿园教学课件模板

    Simulink电动汽车仿真模型(包含行驶阻力模型,工作模式切模型,驾驶员模型,PID控制模块等,NEDC,CLTC工况仿真结果)东西很全

    Simulink电动汽车仿真模型(包含行驶阻力模型,工作模式切模型,驾驶员模型,PID控制模块等,NEDC,CLTC工况仿真结果)东西很全

    2-Perfect Backup v3.3.0 全功能备份神器,支持多种备份方式,免费使用,可商用

    是一款全功能的备份软件,支持增量备份、差异备份、完全备份和同步备份,帮助用户轻松保护关键文件。支持多种存储选项,包括本地硬盘、USB驱动器、网络文件夹、云存储和 FTP 服务器等。 【使用方法】: 1. 下载并安装 Perfect Backup。 2. 打开软件,选择备份类型(增量、差异、完全或同步)。 3. 指定源文件和目标存储位置。 4. 设置备份计划,选择备份频率。 5. 点击“开始备份”,执行备份任务。

    基于STM8单片机的光敏电阻模拟量ADC输入(带中断).zip

    基于STM8单片机的编程实例,可供参考学习使用,希望对你有所帮助

    HTML5实现好看的创意房屋设计公司网页源码.zip

    资源描述: HTML5实现好看的创意房屋设计公司网页源码,好看的创意房屋设计公司网页源码,创意房屋设计公司网页源码模板,HTML创意房屋设计公司网页源码,内置酷炫的动画,界面干净整洁,页面主题,全方位介绍内容,可以拆分多个想要的页面,可以扩展自己想要的,注释完整,代码规范,各种风格都有,代码上手简单,代码独立,可以直接运行使用。也可直接预览效果。 资源使用: 点击 index.html 直接查看效果

Global site tag (gtag.js) - Google Analytics