Ry’s Objective-C Tutorial---Memory Management

本文深入探讨Objective-C中的内存管理概念,包括手动引用计数(MRR)与自动引用计数(ARC)。从创建对象、所有权转移、释放内存等方面详细解析,同时介绍了如何启用和禁用ARC,以及如何避免内存泄露和悬挂指针的问题。通过实例代码演示了如何在实际项目中应用这些内存管理技巧。
RyPress - Quality Software Tutorials

You’re reading Ry’s Objective-C Tutorial

Memory Management

As discussed in the Properties module, the goal of any memory management system is to reduce the memory footprint of a program by controlling the lifetime of all its objects. iOS and OS X applications accomplish this through object ownership, which makes sure objects exist as long as they have to, but no longer.

This object-ownership scheme is implemented through a reference-counting system that internally tracks how many owners each object has. When you claim ownership of an object, you increase it’s reference count, and when you’re done with the object, you decrease its reference count. While its reference count is greater than zero, an object is guaranteed to exist, but as soon as the count reaches zero, the operating system is allowed to destroy it.

Destroying an object with zero references

In the past, developers manually controlled an object’s reference count by calling special memory-management methods defined by the NSObject protocol. This is called Manual Retain Release (MRR). However, Xcode 4.2 introduced Automatic Reference Counting (ARC), which automatically inserts all of these method calls for you. Modern applications should always use ARC, since it’s more reliable and lets you focus on your app’s features instead of its memory management.

This module explains core reference-counting concepts in the context of MRR, then discusses some of the practical considerations of ARC.

Manual Retain Release

In a Manual Retain Release environment, it’s your job to claim and relinquish ownership of every object in your program. You do this by calling special memory-related methods, which are described below.

Method Behavior
allocCreate an object and claim ownership of it.
retainClaim ownership of an existing object.
copyCopy an object and claim ownership of it.
releaseRelinquish ownership of an object and destroy it immediately.
autoreleaseRelinquish ownership of an object but defer its destruction.

Manually controlling object ownership might seem like a daunting task, but it’s actually very easy. All you have to do is claim ownership of any object you need and remember to relinquish ownership when you’re done with it. From a practical standpoint, this means that you have to balance every allocretain, and copy call with a release or autorelease on the same object.

When you forget to balance these calls, one of two things can happen. If you forget to release an object, its underlying memory is never freed, resulting in a memory leak. Small leaks won’t have a visible effect on your program, but if you eat up enough memory, your program will eventually crash. On the other hand, if you try to release an object too many times, you’ll have what’s called a dangling pointer. When you try to access the dangling pointer, you’ll be requesting an invalid memory address, and your program will most likely crash.

Balancing ownership claims with reliquishes

The rest of this section explains how to avoid memory leaks and dangling pointers through the proper usage of the above methods.

Enabling MRR

Before we can experiment with manual memory management, we need to turn off Automatic Reference Counting. Click the project icon in the Project Navigator, make sure the Build Settings tab is selected, and start typing automatic reference counting in the search bar. TheObjective-C Automatic Reference Counting compiler option should appear. Change it from Yes to No.

Turning off Automatic Reference Counting

Remember, we’re only doing this for instructional purposes—you should never use Manual Retain Release for new projects.

The alloc Method

We’ve been using the alloc method to create objects throughout this tutorial. But, it’s not just allocating memory for the object, it’s also setting its reference count to 1. This makes a lot of sense, since we wouldn’t be creating the object if we didn’t want to keep it around for at least a little while.

// main.m
#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSMutableArray *inventory = [[NSMutableArray alloc] init];
        [inventory addObject:@"Honda Civic"];
        NSLog(@"%@", inventory);
    }
    return 0;
}

The above code should look familiar. All we’re doing is instantiating a mutable array, adding a value, and displaying its contents. From a memory-management perspective, we now own the inventory object, which means it’s our responsibility to release it somewhere down the road.

But, since we haven’t released it, our program currently has a memory leak. We can examine this in Xcode by running our project through the static analyzer tool. Navigate to Product > Analyze in the menu bar or use the Shift+Cmd+B keyboard shortcut. This looks for predictable problems in your code, and it should uncover the following in main.m.

A memory leak! Oh no!

This is a small object, so the leak isn’t fatal. However, if it happened over and over (e.g., in a long loop or every time the user clicked a button), the program would eventually run out of memory and crash.

The release Method

The release method relinquishes ownership of an object by decrementing its reference count. So, we can get rid of our memory leak by adding the following line after the NSLog() call in main.m.

[inventory release];

Now that our alloc is balanced with a release, the static analyzer shouldn’t output any problems. Typically, you’ll want to relinquish ownership of an object at the end of the same method in which it was created, as we did here.

Releasing an object too soon creates a dangling pointer. For example, try moving the above line to before the NSLog() call. Since releaseimmediately frees the underlying memory, the inventory variable in NSLog() now points to an invalid address, and your program will crash with a EXC_BAD_ACCESS error code when you try to run it:

Trying to access an invalid memory address

The point is, don’t relinquish ownership of an object before you’re done using it.

The retain Method

The retain method claims ownership of an existing object. It’s like telling the operating system, “Hey! I need that object too, so don’t get rid of it!” This is a necessary ability when other objects need to make sure their properties refer to a valid instance.

As an example, we’ll use retain to create a strong reference to ourinventory array. Create a new class called CarStore and change its header to the following.

// CarStore.h
#import <Foundation/Foundation.h>

@interface CarStore : NSObject

- (NSMutableArray *)inventory;
- (void)setInventory:(NSMutableArray *)newInventory;

@end

This manually declares the accessors for a property called inventory. Our first iteration of CarStore.m provides a straightforward implementation of the getter and setter, along with an instance variable to record the object:

// CarStore.m
#import "CarStore.h"

@implementation CarStore {
    NSMutableArray *_inventory;
}

- (NSMutableArray *)inventory {
    return _inventory;
}

- (void)setInventory:(NSMutableArray *)newInventory {
    _inventory = newInventory;
}

@end

Back in main.m, let’s assign our inventory variable to CarStore’s inventory property:

// main.m
#import <Foundation/Foundation.h>
#import "CarStore.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSMutableArray *inventory = [[NSMutableArray alloc] init];
        [inventory addObject:@"Honda Civic"];
        
        CarStore *superstore = [[CarStore alloc] init];
        [superstore setInventory:inventory];
        [inventory release];

        // Do some other stuff...

        // Try to access the property later on (error!)
        NSLog(@"%@", [superstore inventory]);
    }
    return 0;
}

The inventory property in the last line is a dangling pointer because the object was already released earlier in main.m. Right now, the superstore object has a weak reference to the array. To turn it into a strong reference, CarStore needs to claim ownership of the array in its setInventory: accessor:

// CarStore.m
- (void)setInventory:(NSMutableArray *)newInventory {
    _inventory = [newInventory retain];
}

This ensures the inventory object won’t be released while superstoreis using it. Notice that the retain method returns the object itself, which lets us perform the retain and assignment in a single line.

Unfortunately, this code creates another problem: the retain call isn’t balanced with a release, so we have another memory leak. As soon as we pass another value to setInventory:, we can’t access the old value, which means we can never free it. To fix this, setInventory: needs to call release on the old value:

// CarStore.m
- (void)setInventory:(NSMutableArray *)newInventory {
    if (_inventory == newInventory) {
        return;
    }
    NSMutableArray *oldValue = _inventory;
    _inventory = [newInventory retain];
    [oldValue release];
}

This is basically what the retain and the strong property attributes do. Obviously, using @property is much more convenient than creating these accessors on our own.

Memory management calls on the  inventory object

The above diagram visualizes all of the memory management calls on the inventory array that we created in main.m, along with their respective locations. As you can see, all of the alloc’s and retain’s are balanced with a release somewhere down the line, ensuring that the underlying memory will eventually be freed up.

The copy Method

An alternative to retain is the copy method, which creates a brand new instance of the object and increments the reference count on that, leaving the original unaffected. So, if you want to copy the inventoryarray instead of referring to the mutable one, you can change setInventory: to the following.

// CarStore.m
- (void)setInventory:(NSMutableArray *)newInventory {
    if (_inventory == newInventory) {
        return;
    }
    NSMutableArray *oldValue = _inventory;
    _inventory = [newInventory copy];
    [oldValue release];
}

You may also recall from The copy Attribute that this has the added perk of freezing mutable collections at the time of assignment. Some classes provide multiple copy methods (much like multiple initmethods), and it’s safe to assume that any method starting with copyhas the same behavior.

The autorelease method

Like release, the autorelease method relinquishes ownership of an object, but instead of destroying the object immediately, it defers the actual freeing of memory until later on in the program. This allows you to release objects when you are “supposed” to, while still keeping them around for others to use.

For example, consider a simple factory method that creates and returns a CarStore object:

// CarStore.h
+ (CarStore *)carStore;

Technically speaking, it’s the carStore method’s responsibility to release the object because the caller has no way of knowing that he owns the returned object. So, its implementation should return an autoreleased object, like so:

// CarStore.m
+ (CarStore *)carStore {
    CarStore *newStore = [[CarStore alloc] init];
    return [newStore autorelease];
}

This relinquishes ownership of the object immediately after creating it, but keeps it in memory long enough for the caller to interact with it. Specifically, it waits until the end of the nearest @autoreleasepool{}block, after which it calls a normal release method. This is why there’s always an @autoreleasepool{} surrounding the entire main() function—it makes sure all of the autoreleased objects are destroyed after the program is done executing.

All of those built-in factory methods like NSString’s stringWithFormat:and stringWithContentsOfFile: work the exact same way as our carStore method. Before ARC, this was a convenient convention, since it let you create objects without worrying about calling releasesomewhere down the road.

If you change the superstore constructor from alloc/init to the following, you won’t have to release it at the end of main().

// main.m
CarStore *superstore = [CarStore carStore];

In fact, you aren’t allowed to release the superstore instance now because you no longer own it—the carStore factory method does. It’s very important to avoid explicitly releasing autoreleased objects (otherwise, you’ll have a dangling pointer and a crashed program).

The dealloc Method

An object’s dealloc method is the opposite of its init method. It’s called right before the object is destroyed, giving you a chance to clean up any internal objects. This method is called automatically by the runtime—you should never try to call dealloc yourself.

In an MRR environment, the most common thing you need to do in adealloc method is release objects stored in instance variables. Think about what happens to our current CarStore when an instance is deallocated: its _inventory instance variable, which has been retained by the setter, never has the chance to be released. This is another form of memory leak. To fix this, all we have to do is add a custom deallocto CarStore.m:

// CarStore.m
- (void)dealloc {
    [_inventory release];
    [super dealloc];
}

Note that you should always call the superclass’s dealloc to make sure that all of the instance variables in parent classes are properly released. As a general rule, you want to keep custom dealloc methods as simple as possible, so you shouldn’t try to use them for logic that can be handled elsewhere.

MRR Summary

And that’s manual memory management in a nutshell. The key is to balance every allocretain, and copy with a release or autorelease, otherwise you’ll encounter either a dangling pointer or a memory leak at some point in your application.

Remember that this section only used Manual Retain Release to understand the internal workings of iOS and OS X memory management. In the real world, much of the above code is actually obsolete, though you might encounter it in older documentation. It’s very important to understand that explicitly claiming and relinquishing ownership like this has been completely superseded by Automatic Reference Counting.

Automatic Reference Counting

Now that you’ve got your head wrapped around manual memory management, you can forget all about it. Automatic Reference Counting works the exact same way as MRR, but it automatically inserts the appropriate memory-management methods for you. This is a big deal for Objective-C developers, as it lets them focus entirely on whattheir application needs to do rather than how it does it.

ARC takes the human error out of memory management with virtually no downside, so the only reason not to use it is when you’re interfacing with a legacy code base (however, ARC is, for the most part, backward compatible with MRR programs). The rest of this module explains the major changes between MRR and ARC.

Enabling ARC

First, let’s go ahead and turn ARC back on in the project’s Build Settingstab. Change the Automatic Reference Counting compiler option to Yes. Again, this is the default for all Xcode templates, and it’s what you should be using for all of your projects.

Turning on Automatic Reference Counting

No More Memory Methods

ARC works by analyzing your code to figure how what the ideal lifetime of each object should be, then inserts the necessary retain andrelease calls automatically. The algorithm requires complete control over the object-ownership in your entire program, which means you’re not allowed to manually call retainrelease, or autorelease.

The only memory-related methods you should ever find in an ARC program are alloc and copy. You can think of these as plain old constructors and ignore the whole object-ownership thing.

New Property Attributes

ARC introduces new @property attributes. You should use the strongattribute in place of retain, and weak in place of assign. All of these attributes are discussed in the Properties module.

The dealloc Method in ARC

Deallocation in ARC is also a little bit different. You don’t need to release instance variables as we did in The dealloc Method—ARC does this for you. In addition, the superclass’s dealloc is automatically called, so you don’t have to do that either.

For the most part, this means you’ll never need to include a customdealloc method. One of the few exceptions is when you’re using low-level memory allocation functions like malloc(). In this case, you’d still have to call free() in dealloc to avoid a memory leak.

Summary

For the most part, Automatic Reference Counting lets you completely forget about memory management. The idea is to focus on high-level functionality instead of the underlying memory management. The only thing you need to worry about are retain cycles, which was covered in the Properties module.

If you need a more detailed discussion about the nuances of ARC, please visit the Transitioning to ARC Release Notes.

By now, you should know almost everything you need to know about Objective-C. The only thing we haven’t covered are the basic data types provided by both C and the Foundation Framework. The next module introduces all of the standard types, from numbers to strings, arrays, dictionaries, and even dates.

Mailing List

Sign up for my low-volume mailing list to find out when new content is released. Next up is a comprehensive Swift tutorial planned for late January.

Email Address:

You’ll only receive emails when new tutorials are released, and your contact information will never be shared with third parties. Click here to unsubscribe.

标题SpringBoot智能在线预约挂号系统研究AI更换标题第1章引言介绍智能在线预约挂号系统的研究背景、意义、国内外研究现状及论文创新点。1.1研究背景与意义阐述智能在线预约挂号系统对提升医疗服务效率的重要性。1.2国内外研究现状分析国内外智能在线预约挂号系统的研究与应用情况。1.3研究方法及创新点概述本文采用的技术路线、研究方法及主要创新点。第2章相关理论总结智能在线预约挂号系统相关理论,包括系统架构、开发技术等。2.1系统架构设计理论介绍系统架构设计的基本原则和常用方法。2.2SpringBoot开发框架理论阐述SpringBoot框架的特点、优势及其在系统开发中的应用。2.3数据库设计与管理理论介绍数据库设计原则、数据模型及数据库管理系统。2.4网络安全与数据保护理论讨论网络安全威胁、数据保护技术及其在系统中的应用。第3章SpringBoot智能在线预约挂号系统设计详细介绍系统的设计方案,包括功能模块划分、数据库设计等。3.1系统功能模块设计划分系统功能模块,如用户管理、挂号管理、医生排班等。3.2数据库设计与实现设计数据库表结构,确定字段类型、主键及外键关系。3.3用户界面设计设计用户友好的界面,提升用户体验。3.4系统安全设计阐述系统安全策略,包括用户认证、数据加密等。第4章系统实现与测试介绍系统的实现过程,包括编码、测试及优化等。4.1系统编码实现采用SpringBoot框架进行系统编码实现。4.2系统测试方法介绍系统测试的方法、步骤及测试用例设计。4.3系统性能测试与分析对系统进行性能测试,分析测试结果并提出优化建议。4.4系统优化与改进根据测试结果对系统进行优化和改进,提升系统性能。第5章研究结果呈现系统实现后的效果,包括功能实现、性能提升等。5.1系统功能实现效果展示系统各功能模块的实现效果,如挂号成功界面等。5.2系统性能提升效果对比优化前后的系统性能
在金融行业中,对信用风险的判断是核心环节之一,其结果对机构的信贷政策和风险控制策略有直接影响。本文将围绕如何借助机器学习方法,尤其是Sklearn工具包,建立用于判断信用状况的预测系统。文中将涵盖逻辑回归、支持向量机等常见方法,并通过实际操作流程进行说明。 一、机器学习基本概念 机器学习属于人工智能的子领域,其基本理念是通过数据自动学习规律,而非依赖人工设定规则。在信贷分析中,该技术可用于挖掘历史数据中的潜在规律,进而对未来的信用表现进行预测。 二、Sklearn工具包概述 Sklearn(Scikit-learn)是Python语言中广泛使用的机器学习模块,提供多种数据处理和建模功能。它简化了数据清洗、特征提取、模型构建、验证与优化等流程,是数据科学项目中的常用工具。 三、逻辑回归模型 逻辑回归是一种常用于分类任务的线性模型,特别适用于二类问题。在信用评估中,该模型可用于判断借款人是否可能违约。其通过逻辑函数将输出映射为0到1之间的概率值,从而表示违约的可能性。 四、支持向量机模型 支持向量机是一种用于监督学习的算法,适用于数据维度高、样本量小的情况。在信用分析中,该方法能够通过寻找最佳分割面,区分违约与非违约客户。通过选用不同核函数,可应对复杂的非线性关系,提升预测精度。 五、数据预处理步骤 在建模前,需对原始数据进行清理与转换,包括处理缺失值、识别异常点、标准化数值、筛选有效特征等。对于信用评分,常见的输入变量包括收入水平、负债比例、信用历史记录、职业稳定性等。预处理有助于减少噪声干扰,增强模型的适应性。 六、模型构建与验证 借助Sklearn,可以将数据集划分为训练集和测试集,并通过交叉验证调整参数以提升模型性能。常用评估指标包括准确率、召回率、F1值以及AUC-ROC曲线。在处理不平衡数据时,更应关注模型的召回率与特异性。 七、集成学习方法 为提升模型预测能力,可采用集成策略,如结合多个模型的预测结果。这有助于降低单一模型的偏差与方差,增强整体预测的稳定性与准确性。 综上,基于机器学习的信用评估系统可通过Sklearn中的多种算法,结合合理的数据处理与模型优化,实现对借款人信用状况的精准判断。在实际应用中,需持续调整模型以适应市场变化,保障预测结果的长期有效性。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值