Understanding Automatic Reference Counting in Objective-C

http://longweekendmobile.com/2011/09/07/objc-automatic-reference-counting-in-xcode-explained/


Automatic Reference Counting (ARC) largely removes the burden of manual memory management, not to mention the chore of tracking down bugs caused by leaking or over-released objects! Despite its awesomeness, ARC does not let you ignore memory management altogether.

This post covers the following key aspects of ARC to help you get up and running quickly.

  1. Reference Counted Memory: Quick Revision
  2. How Automatic Reference Counting Works
  3. Enabling ARC in Your Project
  4. New Rules Enforced by ARC
  5. ARC Qualifiers – Declared Properties
  6. ARC Qualifiers – Regular Variables
  7. Migrating Existing Projects to ARC
  8. Including Code that is not ARC Compliant
  9. Should I Use ARC?

What Has Changed?

ARC Related changes to Xcode 4.2

In the time before ARC, you had to manually retain/release/autorelease objects to ensure they would “stick around” for as long as you needed them. Forgetting to send retain to an object, or releasing it too many times would cause your app to leak memory or crash.

In Xcode 4.2, in addition to syntax checking as you type, the new Apple LLVM compiler makes it possible to offload the burden of manual memory management to the compiler, introspecting your code to decide when to release objects. Apple’s documentation describes ARC as follows:

“Automatic Reference Counting (ARC) is a compiler-level feature that simplifies the process of managing object lifetimes (memory management) in Cocoa applications.”

This feature makes the memory management trivial most of the time, but you still need to take some responsibility for how your classes manage references to other objects.

Let’s start at the beginning …

Reference Counted Memory: Quick Review

Manually managed, reference counted memory works in iOS as follows: when creating an object using alloc/init (or other similar method), the object is returned with a retainCount of 1, meaning you havetaken ownership of the object.

NSObject *obj = [[NSObject alloc] init];
// do some stuff
[obj release];

In between alloc/init’ing an object (i.e. taking ownership) and releasing an object (i.e. relinquishing ownership), you can do with it as you wish, safe in the knowledge the object will not be deallocated whilst still in use.

Similarly, by adding an object to an autorelease pool, your object will stick around when needed and be deallocated sometime in the future when no longer needed.

-(NSObject*) someMethod {
  NSObject *obj = [[[NSObject alloc] init] autorelease];
  return obj; // will be deallocated by autorelease pool later
}

How Automatic Reference Counting Works

Most programmers new to iOS have trouble getting their heads around reference counted memory. ARC is a pre-compilation step that adds retain/release/autorelease statements to your code for you.

This is not Garbage Collection, and reference counted memory has not disappeared, it has simply been automated. It may sound like a bit of an after thought, but considering how many features in Objective-C are implemented by pre-processing source files before compiling, ARC is really par for the course.

When writing code with ARC enabled your code will look like this:

NSObject *obj = [[NSObject alloc] init];
// do some stuff

The ARC pre-compilation step will auto-magically turn it into this:

NSObject *obj = [[NSObject alloc] init];
// do some stuff
[obj release]; // **Added by ARC**

The diagram below (from Apple’s documentation) seems to imply that writing operative code takes almost as long to write as retain/release logic. Whilst this is not true for experienced Objective-C coders, it is probably a conservative estimate if you are new to Objective-C. Also, when they do pop-up, memory problems can be a PITA to track down.

Source: Programming With ARC Release Notes

Enabling ARC in Your Project

To enable ARC simply set the Objective-C Automatic Reference Counting option in your Xcode project’s Build Settings to YES. Behind the scenes this sets the -fobjc-arc compiler flag that enables ARC.

New Rules Enforced by ARC

There are a few rules you need to abide by in order to compile with ARC enabled.

1. Alloc/Init Objects

As described above, creating objects is done as before, however you must not make anyretain/release/autorelease/retainCount calls. You also cannot be sneaky by calling their selectors indirectly: use of @selector(retain) and @selector(release) are prohibited.

2. Dealloc Methods

Generally these will be created for you. You must not make a dealloc call directly. However you can still create a custom dealloc method if you need to release resources other than instance variables. When creating a custom dealloc method, do not call the [super dealloc] method. This will be done for you and is enforced by the compiler.

3. Declared Properties

Before ARC, we told the compiler how to memory manage declared public properties using theassign/retain/copy parameters with the @property directive. These parameters are no longer used in ARC. Instead we have the weak/strong parameters to tell the compiler how we want our properties treated.

4. Object Pointers in C Structures

This is also a taboo. The docs suggest storing them in a class instead of a struct. This makes sense since they would otherwise be unknown to ARC. It might cause extra some migration headaches. However, ARC can be disabled on a file by file basis. See the section on “Including Code that is not ARC Compliant” below.

5. Casual Casting Between id and void*

Casting between id and void* data types is frequently done when handing objects between Core Foundation’s C library functions and Foundation Kit’s Objective-C library methods. This is known asToll Free Bridging.

With ARC you must provide hints/qualifiers to tell the compiler when CF objects are moving into and out of its control for memory management. These qualifiers include __bridge, __bridge_retain and__bridge_transfer. Also you still need to call CFRetain and CFRelease to memory manage Core Foundation objects.

This is a more advanced topic, so if you don’t know what CF objects are, don’t worry for now.

6. @autoreleasepool instead of NSAutoReleasePool

ARC compliant code must not use NSAutoReleasePool objects, instead use the @autoreleasepool{}blocks. A good example is in the main.m file of any ARC compliant/migrated project.

int main(int argc, char *argv[])
{
  @autoreleasepool {
    return UIApplicationMain(argc, argv, nil, NSStringFromClass([ExampleAppDelegate class]));
  }
}

7. Other Gotchas

Zone based memory is gone (apparently it’s not in the runtime anymore either); you cannot use NSAllocateObject or NSDeallocateObject.

ARC Qualifiers – Declared Properties

As programmers, we are used to making decisions like whether to make something a variable or a constant, locally or globally defined, etc. So too, we must decide how our properties relate to other objects. We use the strong/weak qualifiers to notify the compiler of these relationships.

Strong References

A strong reference is a reference to an object that stops it from being deallocated. In other words it creates a owner relationship. Whereas previously you would do this:

// Non-ARC Compliant Declaration
@property(retain) NSObject *obj;

Under ARC we do the following to ensure a class instance takes an ownership interest a referenced object (i.e. so it cannot be deallocated until the owner is).

// ARC Compliant Declaration
@property(strong) NSObject *obj;

Weak References

A weak reference is a reference to an object that does not stop it from being deallocated. In other words, it does not create an owner relationship. Whereas previously you would do this:

// Non-ARC Compliant Declaration
@property(assign) NSObject *parentObj;

Under ARC you would do the following to ensure you do not “own” the object being referred to (e.g. typically a child object should not own its parent, so we would use a weak reference).

// ARC Compliant Declaration
@property(weak) NSObject *parentObj;

ARC Qualifiers – Regular Variables

Variable Qualifiers

The above examples illustrate declaring how our declared properties should be managed. For regular variables we have:

__strong
__weak
__unsafe_unretained
__autoreleasing

Generally speaking, these extra qualifiers don’t need to be used very often. You might first encounter these qualifiers and others when using the migration tool. For new projects however, you generally you won’t need them and will mostly use strong/weak with your declared properties.

  • __strong – is the default so you don’t need to type it. This means any object created usingalloc/init is retained for the lifetime of its current scope. The “current scope” usually means the braces in which the variable is declared (i.e. a method, for loop, if block, etc…)
  • __weak – means the object can be destroyed at anytime. This is only useful if the object is somehow strongly referenced somewhere else. When destroyed, a variable with __weak is set tonil.
  • __unsafe_unretained – is just like __weak but the poiner is not set to nil when the object is deallocated. Instead the pointer is left dangling (i.e. it no longer points to anything useful).
  • __autoreleasing, not to be confused with calling autorelease on an object before returning it from a method, this is used for passing objects by reference, for example when passing NSError objects by reference such as [myObject performOperationWithError:&tmp];

Source: LLVM Clang Objective-C Automatic Reference Counting Documentation

NB: We have found @property declarations using ‘retain’ with ARC enabled (i.e. instead of ‘strong’) do not cause compiler warnings and yield the same compiled result. This could change in future so stick to ‘strong’ for clarity.

Migrating Existing Projects to ARC

Xcode 4.2 provides a conversion tool that migrates existing code to ARC, and helps you manually convert code that cannot be automatically migrated.

1. Open your non-ARC compliant project and go to Edit > Refactor > Convert to Objective-C ARC.

2. Select the build targets and contained files to convert (you exclude files in laters steps too)

3. Run Precheck and press next.

NB: When you press next the LLVM compiler will build the project in order to analyse it. If your project has any errors, you cannot proceed to the next step. If you are opening a project from a previous Xcode version for the first time, remember to Clean.

4. Inspect the proposed changes and choose to include/exclude any files. Then press save.

NB: You will also be notified of any files that cannot be migrated. Not all files (including existing libraries) need to be migrated. ARC works on a file by file basis, so see the section below on how to exclude them at compile time.

5. The migration tool will automagically set the compiler flag enabling ARC to on. You can confirm this in your project’s build settings (search for ‘reference counting’).

Including Code that is not ARC Compliant

According to Apple’s documentation: “ARC interoperates with manual reference counting code on a per-file basis. If you want to continue using manual reference counting for some files, you can do so.”

This means some files can use ARC and some files can be spared from it’s magical grasp. Here are the steps for bulk excluding files from ARC at compile time. At the time of writing, many popular libraries are not ARC ready, to get around this follow these steps:

  1. Click on your Project in the Xcode project tree
  2. Click on the Target
  3. Select the Build Phases tab
  4. Expand the Compile Sources section
  5. Select one or more files you want to exclude from ARC
  6. Press the return key
  7. Type -fno-objc-arc
  8. Press the return key again
  9. Each file selected now has a -fno-objc-arc compiler flag set and will be excluded from ARC

Should I Use ARC?

If you are new to Objective-C, you will certainly welcome it. There is more than enough to learn at first without worrying about reference counted memory. Once you start using existing libraries though, you are sure to experience pain until you get accustomed to explicitly excluding them from ARC pre-compilation.

If you’re already coding happily without ARC, you’re probably thinking “I don’t need it!” This may be the right answer for you — for now. Most popular libraries haven’t been converted to ARC and it does not play well with Core Foundation classes. There are specific limitations when using CF classes listed in Apple’s documentation, and a bunch of extra qualifiers are needed to make Toll Free Bridging work when migrating code.

From what I have seen and read, ARC is ready to use right now. However, it is probably best left to new projects until you get familiar with it. Whilst ARC is backwards compatible with iOS 4.0 onwards, weak references are only supported in iOS 5.0 onwards — possibly another reason not to migrate everything just yet (there are work arounds to this, see the “Resources” section at the bottom of this post).

Performance wise, some early reports claim ARC can make your projects faster – possibly due to less reliance on autorelease. However, you can also do that yourself by “coding better” and using retain/release more judiciously. But that’s just the point: ARC should always choose the most optimised approach for your code automatically.

Right now there is some pain involved in moving to ARC, but not a lot. At Long Weekend we will leave it for new projects at first, but this is Apple’s “new” recommended approach, so you can be sure future design decisions will build on ARC and move further away from manual reference counting.

ARC Resources:

Other iOS Resources

If you are looking to learn more about iOS development, you might be interested in our post on the resources we used. You might also like some of our other blog articles on development.





翻译



自动引用计数(Automatic Reference Counting, ARC)把压在程序员们肩头的管理内存的重担卸除了不少,更不用说让跟踪内存泄漏那样的烦心事也少了很多。不过,虽然ARC很棒,我们仍然不能完全把内存管理这回事儿抛在脑后。

这篇文章将要讨论以下方面的问题,帮助大家快速进入ARC的世界。

  1. 内存的引用计数: 快速复习
  2. ARC的工作原理
  3. 在工程中开启ARC
  4. ARC施加的新规则
  5. ARC限定符 - 声明的属性
  6. ARC限定符 - 常规变量
  7. 移植到ARC
  8. 引入不兼容ARC的代码
  9. 我该用ARC吗?
AlfredCheung
AlfredCheung
翻译于 1年前

1人顶

 翻译的不错哦!

发生了什么事?

ARC Related changes to Xcode 4.2

在ARC出现以前,程序员们只能靠retain/relese/autorelease来确保对象们恰好“坚持”到被需要的那一刻。如果忘了retain,或者多次release某个对象,程序就会发生内存泄漏的问题,甚至直接崩溃。

在Xcode 4.2中,除了语法检查外,Apple的新LLVM编译器还将内存管理的苦差事接了过来,它会检查代码,决定何时释放对象。Apple的文档里是这么定义ARC的:

“自动引用计数(ARC)是一个编译器级的功能,它能简化Cocoa应用中对象生命周期管理(内存管理)的流程。”

ARC使内存管理在大部分时候变得如同小事一桩,但我们仍要在决定自己的类如何管理其它对象的引用时承担一些责任。

那么,让我们正式开始吧……

AlfredCheung
AlfredCheung
翻译于 1年前

0人顶

 翻译的不错哦!

其它翻译版本(1)

引用计数: 快速复习

手工管理、引用计数式的内存管理在iOS中是这样工作的: 当使用alloc/init(或其它类似方法)创建对象时,随同对象返回的,还有个retainCount,其值为1,表明我们获得了这个对象的所有权。

1 NSObject *obj = [[NSObject alloc] init];
2 // do some stuff
3 [obj release];
在对象的alloc/init和release(即放弃对象的所有权)之间,我们可以任意处理它,这很安全,因为系统是无法回收正在使用中的对象的。

将对象加入到自动释放池也是类似,对象会一直存在,直到未来的某个时间我们不再需要它,才会被系统回收。

1 -(NSObject*) someMethod {
2   NSObject *obj = [[[NSObject alloc] init] autorelease];
3   return obj; // will be deallocated by autorelease pool later
4 }
AlfredCheung
AlfredCheung
翻译于 1年前

1人顶

 翻译的不错哦!

其它翻译版本(1)

ARC的工作原理

大多数新的iOS程序员都会在引用计数这问题上遇到理解障碍。ARC则是一个编译前的步骤,它为我们的代码自动加上retain/release/autorelease语句。

ARC并不是垃圾收集,而且,引用计数也没有消失,只是变成自动而已。听起来像是事后追加的这么一个功能,不过,只要我们想一想Objective-C有多少功能是通过对源文件的预处理来实现的,就不会这么想了。

当采用ARC后,代码只要这样写:

1 NSObject *obj = [[NSObject alloc] init];
2 // do some stuff
ARC会自动将它变成:
1 NSObject *obj = [[NSObject alloc] init];
2 // do some stuff
3 [obj release]; // **Added by ARC**

从下图(来自Apple官方文档)看起来,好像retain/release的数量快赶上真正有用的代码了。当然,这肯定不是熟手的情况,不过可以看成是对新手的保守估计。这些代码跑起来,要跟踪某个内存问题真的是会搞死人。

来源: Programming With ARC Release Notes

AlfredCheung
AlfredCheung
翻译于 1年前

0人顶

 翻译的不错哦!

在工程中开启ARC

如果想开启ARC,只要在工程的Build Settings中设置ARC为YES。在幕后,实际上是设置了-fobj-arc的编译器标识。


ARC施加的新规则

如果想用ARC,必须服从一些新规则。

1. 对象的Alloc/Init

创建对象的方法跟以前一样,但你一定不能调用retain/release/autorelease/retainCount。也不能通过selector偷偷地调用它们: 禁止使用@selector(retain)和@selector(release)。

AlfredCheung
AlfredCheung
翻译于 1年前

0人顶

 翻译的不错哦!

2. dealloc方法

ARC为自动为你调用,一定不能直接调用dealloc。不过,如果你需要释放实例变量以外的资源,还是可以创建自定义的dealloc方法。但在这个方法里,不要调用[super dealloc]。因为ARC会帮你调。

3. 声明的属性

在ARC之前,我们是用@property指令中的assign/retain/copy参数来告诉编译器,如何管理这些属性的内存。用了ARC之后,这些参数就作废了,改用weak/strong这两个参数。

AlfredCheung
AlfredCheung
翻译于 1年前

0人顶

 翻译的不错哦!

4. C结构中的对象指针

同样禁止使用。文档里建议不要把它们放在结构了,改放到类里去。否则ARC就不认识它们了。可能会出现一些移植上的问题。不过,ARC是可以以文件为单位来关闭的。参考下文的“引入不兼容ARC的代码”。

5. id与void*之间的临时互转

当我们在Core Foundation的C函数和Foundation Kit的Objective-C方法间传递对象时,常常需要进行id和void*两个类型的互转。叫做免费桥接(Toll Free Bridging)

如果使用ARC,必须在CF对象进入和脱离ARC的控制时,用提示/限定符来告知编译器。限定符有__bridge、__bridge_retain和__bridge_transfer。另外,仍需要用CFRetain和CFRelease来管理Core Foundation的对象。

这一块已经比较高深了,如果你不清楚CF对象是什么,也不需要太烦恼。

AlfredCheung
AlfredCheung
翻译于 1年前

0人顶

 翻译的不错哦!

6. 以@autoreleasepool代替NSAutoReleasePool

兼容ARC的代码不能再使用NSAutoReleasePool对象,而要改用@autoreleasepool{}块。一个很好的例子:

1 int main(int argc, char *argv[])
2 {
3   @autoreleasepool {
4     returnUIApplicationMain(argc, argv, nil, NSStringFromClass([ExampleAppDelegate class]));
5   }
6 }

7. 其它

基于Zone的内存已经没了(在运行时里也没了)。不能再使用NSAllocateObject和NSDeallocateObject。

AlfredCheung
AlfredCheung
翻译于 1年前

0人顶

 翻译的不错哦!

ARC限定符 - 声明的属性

身为程序员,习惯于做出一些决定,例如把某个量声明为变量还是常量、本地还是全局,等等。因此,在这里,我们也要决定某个属性与其它属性的关系。我们用strong/weak来把这一关系告诉编译器。

强引用

强引用是对某对象的引用,并且能阻止它被回收。换句话说,强引用创建了一个所有关系。在ARC之前,我们这么写:

1 // Non-ARC Compliant Declaration
2 @property(retain) NSObject *obj;

在ARC下,我们需要这么写,以确保当前实例获得被引用对象的所有权(主人不被回收,它也不能被回收)。

1 // ARC Compliant Declaration
2 @property(strong) NSObject *obj;

弱引用

引用是对某对象的引用,但不能阻止它被回收换句话说,弱引用并不会创建所有关系。在ARC之前,我们这么写:


1 // Non-ARC Compliant Declaration
2 @property(assign) NSObject *parentObj;
在ARC下,我们需要这么写,以确保当前实例没有获得被引用对象的所有权(一般来说,子对象不应该拥有父对象,这时可以用弱引用)。
1 // ARC Compliant Declaration
2 @property(weak) NSObject *parentObj;
AlfredCheung
AlfredCheung
翻译于 1年前

0人顶

 翻译的不错哦!

ARC限定符 - 常规变量

上一节是说明如何管理属性。对于常规变量,则有:

1 __strong
2 __weak
3 __unsafe_unretained
4 __autoreleasing
一般来说,我们不太需要使用上面这些限定符。在使用移植工具的时候可能会看到那么几个,但新工程基本上不需要。
  • __strong: 默认限定符,不需要显式指定。表示任何用alloc/init创建的对象在当前范围的生命期内得以保留。“当前范围”是指变量声明语句所在的两个大括号之间(方法、循环、块,等等)。
  • __weak: 表示对象可以随时被摧毁。只有当它被其它对象强引用时才有用。__weak变量在摧毁时,被设为nil。
  • __unsafe_unretained: 与__weak类似,但在摧毁时,不设为nil,保留原值(不再指向有效的东西)。
  • __autoreleasing: 不要与autorelease搞混,它用于通过引用传递对象,比如,通过引用传递NSError对象: [myObject performOperationWithError:&tmp]

来源http://clang.llvm.org/docs/AutomaticReferenceCounting.html#ownership

: 我们发现在ARC下,@property中使用“retain”时(而不是“strong”),编译器并不会报错,而且能生成同样结果。但以后可能会变,所以还是用“strong”吧。

AlfredCheung
AlfredCheung
翻译于 1年前

0人顶

 翻译的不错哦!

移植到ARC

Xcode 4.2提供了一个移植ARC的工具,还可以帮你将无法自动移植的代码手工转换过去。

1. 打开不兼容ARC的工程,进入Edit > Refactor > Convert to Objective-C ARC。


2. 选择需要转换的构建目标和文件(在后续步骤排除不需要转换的文件)


3. 运行预查,按下一步。


注: 按下一步后,LLVM编译器会开始构建,以便对工程进行分析。如果有错误,是无法进入下一步的。如果是第一次打开低版本Xcode建立的工程,请先执行清理。

AlfredCheung
AlfredCheung
翻译于 1年前

0人顶

 翻译的不错哦!

其它翻译版本(1)

4. 检查一下工具建议的修改,并选择是否要排除某个文件。然后按下保存。

注: 如果有文件无法移植,工具也会告诉你。并不是所有文件都需要移植(包括库)。ARC是以文件为基础生效的,可以参考下文,看编译时如何把不需要开启ARC的文件排除在外。

5. 工具会自动设置编译器的标识,开启ARC。可以查看工程的Build Settings确认这一点。

AlfredCheung
AlfredCheung
翻译于 1年前

0人顶

 翻译的不错哦!

引入不兼容ARC的代码

Apple的文档说,“ARC能以文件为基础,与采用手工引用计数的代码进行交互。你可以在部分文件上使用手工引用计数。”

它的意思是说,我们可以让一部分文件用ARC,另一部分文件不用。下面是将文件批量排除在ARC之外的步骤。在我写下这篇文章的时候,还有许多流行的库都没有用ARC,为了解决这个问题,请按照下面的步骤做:

  1. 在Xcode的工程树上,点击你自己的工程
  2. 点击Target
  3. 选择Build Phases标签
  4. 展开Compile Sources
  5. 选择需要排除在ARC外的文件
  6. 按下回车
  7. 输入-fno-objc-arc
  8. 再按下回车
  9. 现在,你选中的文件都有了-fno-objc-arc编译器标识,会被排除在ARC之外
AlfredCheung
AlfredCheung
翻译于 1年前

0人顶

 翻译的不错哦!

我该用ARC吗?

如果你是Objective-C的新手,肯定会欢迎ARC。一开始,有太多的东西要学习,有了ARC就不用担心手工计数的问题。随着你开始接触一些已有的库,就会经历一些痛苦(译者注: 目前的第三方库很多不兼容ARC),但只要习惯了将它们排除在ARC之外,就没什么问题了。

如果你不是新手,在没有ARC的年代已经玩的很high,那么可能会觉得“我干嘛要用它!”对你来说,这可能是正确的答案——就目前而言。因为,大多数流行的库都还没转到ARC下,而且ARC对Core Foundation的支持也不好。使用CF类的时候有一些限制,而且,移植代码的时候,为了让免费桥接生效,还需要加上一大堆限定符。

AlfredCheung
AlfredCheung
翻译于 1年前

0人顶

 翻译的不错哦!

在我看来,目前ARC已经是可以使用的状态了。不过,除非你对它很熟悉,否则还是先用在新工程里会比较好。虽然ARC兼容iOS 4.0以上,但弱引用只在iOS 5.0以上才支持,所以现在还是不要把所有东西都移植过去(有一些相关的文章,请参考最后的“资源”一节)

至于性能方面,早前有报告指出用ARC之后速度会变快,我想可能是由于减少对自动释放的依赖的缘故。虽然这完全可以通过改进代码、更好地使用retain/release来实现,但我觉得重点在于,ARC总是会自动帮你选择最优的方法。

AlfredCheung
AlfredCheung
翻译于 1年前

0人顶

 翻译的不错哦!

目前为止,还是有很多令人苦恼的东西被移到ARC,但还不是全部。在长周末后我们会离开一段时间以投入到新的项目,但是这是苹果一个“新的”推荐手段,所以你可以肯定以后的设计决策会继续强大ARC并且让大家离开使用手动的引用计数

ARC资源



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值