object c内存管理(2)

Linux C 内存管理 提到C语言,我们知道C语言和其他高级语言的最大的区别就是C语言是要操作内存的!      内存的使用时程序设计中需要考虑的重要因素之一,这不仅由于系统内存是有限的(尤其在嵌入式系统中),而且内存分配也会直接影响到程序的效率。因此,我们要对C语言中的内存管理,有个系统的了解。     在C语言中,定义了4个内存区间:代码区;全局变量和静态变量区;局部变量区即栈区;动态存储区,即堆区;具体如下: 阅读详情

转自:http://vinceyuan.cnblogs.com

 

 

前言

初学objectice-C的朋友都有一个困惑,总觉得对objective-C的内存管理机制琢磨不透,程序经常内存泄漏或莫名其妙的崩溃。我在这里总结了自己对objective-C内存管理机制的研究成果和经验,写了这么一个由浅入深的教程。希望对大家有所帮助,也欢迎大家一起探讨。

此文涉及的内存管理是针对于继承于NSObject的Class。


 

 

一 基本原理

 

Objective-C的内存管理机制与.Net/Java那种全自动的垃圾回收机制是不同的,它本质上还是C语言中的手动管理方式,只不过稍微加了一些自动方法。

1. Objective-C的对象生成于堆之上,生成之后,需要一个指针来指向它。

ClassA *obj1 = [[ClassA alloc] init];

2. Objective-C的对象在使用完成之后不会自动销毁,需要执行dealloc来释放空间(销毁),否则内存泄露。

[obj1 dealloc];

这带来了一个问题。下面代码中obj2是否需要调用dealloc?

ClassA *obj1 = [[ClassA alloc] init];

ClassA *obj2 = obj1;

[obj1 hello]; //输出hello

[obj1 dealloc];

[obj2 hello]; //能够执行这一行和下一行吗

[obj2 dealloc];

不能,因为obj1和obj2只是指针,它们指向同一个对象,[obj1 dealloc]已经销毁这个对象了,不能再调用[obj2 hello]和[obj2 dealloc]。obj2实际上是个无效指针。

如何避免无效指针?请看下一条。

3. Objective-C采用了引用计数(ref count或者retain count)。对象的内部保存一个数字,表示被引用的次数。例如,某个对象被两个指针所指向(引用)那么它的retain count为2。需要销毁对象的时候,不直接调用dealloc,而是调用release。release会让retain count减1,只有retain count等于0,系统才会调用dealloc真正销毁这个对象。

ClassA *obj1 = [[ClassA alloc] init]; //对象生成时,retain count = 1

[obj1 release]; //release使retain count减1,retain count = 0,dealloc自动被调用,对象被销毁

我们回头看看刚刚那个无效指针的问题,把dealloc改成release解决了吗?

ClassA *obj1 = [[ClassA alloc] init]; //retain count = 1

ClassA *obj2 = obj1; //retain count = 1

[obj1 hello]; //输出hello

[obj1 release]; //retain count = 0,对象被销毁

[obj2 hello];

[obj2 release];

[obj1 release]之后,obj2依然是个无效指针。问题依然没有解决。解决方法见下一条。

4. Objective-C指针赋值时,retain count不会自动增加,需要手动retain。

ClassA *obj1 = [[ClassA alloc] init]; //retain count = 1

ClassA *obj2 = obj1; //retain count = 1

[obj2 retain]; //retain count = 2

[obj1 hello]; //输出hello

[obj1 release]; //retain count = 2 – 1 = 1

[obj2 hello]; //输出hello

[obj2 release]; //retain count = 0,对象被销毁

问题解决!注意,如果没有调用[obj2 release],这个对象的retain count始终为1,不会被销毁,内存泄露。(1-4可以参考附件中的示例程序memman-no-pool.m)

这样的确不会内存泄露,但似乎有点麻烦,有没有简单点的方法?见下一条。

5. Objective-C中引入了autorelease pool(自动释放对象池),在遵守一些规则的情况下,可以自动释放对象。(autorelease pool依然不是.Net/Java那种全自动的垃圾回收机制)

5.1 新生成的对象,只要调用autorelease就行了,无需再调用release!

ClassA *obj1 = [[[ClassA alloc] init] autorelease]; //retain count = 1 但无需调用release

5.2 对于存在指针赋值的情况,代码与前面类似。

ClassA *obj1 = [[[ClassA alloc] init] autorelease]; //retain count = 1

ClassA *obj2 = obj1; //retain count = 1

[obj2 retain]; //retain count = 2

[obj1 hello]; //输出hello

//对于obj1,无需调用(实际上不能调用)release

[obj2 hello]; //输出hello

[obj2 release]; //retain count = 2-1 = 1

细心的读者肯定能发现这个对象没有被销毁,何时销毁呢?谁去销毁它?(可以参考附件中的示例程序memman-with-pool.m)请看下一条。

6. autorelease pool原理剖析。(其实很简单的,一定要坚持看下去,否则还是不能理解Objective-C的内存管理机制。)

6.1 autorelease pool不是天生的,需要手动创立。只不过在新建一个iphone项目时,xcode会自动帮你写好。autorelease pool的真名是NSAutoreleasePool。

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

6.2 NSAutoreleasePool内部包含一个数组(NSMutableArray),用来保存声明为autorelease的所有对象。如果一个对象声明为autorelease,系统所做的工作就是把这个对象加入到这个数组中去。

ClassA *obj1 = [[[ClassA alloc] init] autorelease]; //retain count = 1,把此对象加入autorelease pool中

6.3 NSAutoreleasePool自身在销毁的时候,会遍历一遍这个数组,release数组中的每个成员。如果此时数组中成员的retain count为1,那么release之后,retain count为0,对象正式被销毁。如果此时数组中成员的retain count大于1,那么release之后,retain count大于0,此对象依然没有被销毁,内存泄露。

6.4 默认只有一个autorelease pool,通常类似于下面这个例子。

int main (int argc, const char *argv[])

{

NSAutoreleasePool *pool;

pool = [[NSAutoreleasePool alloc] init];

// do something

[pool release];

return (0);

} // main

所有标记为autorelease的对象都只有在这个pool销毁时才被销毁。如果你有大量的对象标记为autorelease,这显然不能很好的利用内存,在iphone这种内存受限的程序中是很容易造成内存不足的。例如:

int main (int argc, const char *argv[])

{

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

int i, j;

for (i = 0; i < 100; i++ )

{

for (j = 0; j < 100000; j++ )

[NSString stringWithFormat:@"1234567890"];//产生的对象是autorelease的。

}

[pool release];

return (0);

} // main

(可以参考附件中的示例程序memman-many-objs-one-pool.m,运行时通过监控工具可以发现使用的内存在急剧增加,直到pool销毁时才被释放)你需要考虑下一条。

7. Objective-C程序中可以嵌套创建多个autorelease pool。在需要大量创建局部变量的时候,可以创建内嵌的autorelease pool来及时释放内存。(感谢网友hhyytt和neogui的提醒,某些情况下,系统会自动创建autorelease pool, 请参见第四章)

int main (int argc, const char *argv[])

{

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

int i, j;

for (i = 0; i < 100; i++ )

{

NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];

for (j = 0; j < 100000; j++ )

[NSString stringWithFormat:@"1234567890"];//产生的对象是autorelease的。

[loopPool release];

}

[pool release];

return (0);

} // main

二 口诀与范式

1 口诀。

1.1  谁创建,谁释放(类似于“谁污染,谁治理”)。如果你通过alloc、new或copy来创建一个对象,那么你必须调用release或autorelease。换句话说,不是你创建的,就不用你去释放。
例如,你在一个函数中alloc生成了一个对象,且这个对象只在这个函数中被使用,那么你必须在这个函数中调用release或autorelease。如果你在一个class的某个方法中alloc一个成员对象,且没有调用autorelease,那么你需要在这个类的dealloc方法中调用release;如果调用了autorelease,那么在dealloc方法中什么都不需要做。

1.2  除了alloc、new或copy之外的方法创建的对象都被声明了autorelease。

1.3  谁retain,谁release。只要你调用了retain,无论这个对象是如何生成的,你都要调用release。有时候你的代码中明明没有retain,可是系统会在默认实现中加入retain。不知道为什么苹果公司的文档没有强调这个非常重要的一点,请参考范式2.7和第三章。

2    范式。
范式就是模板,就是依葫芦画瓢。由于不同人有不同的理解和习惯,我总结的范式不一定适合所有人,但我能保证照着这样做不会出问题。

2.1  创建一个对象。

ClassA *obj1 = [[ClassA alloc] init];

2.2  创建一个autorelease的对象。

ClassA *obj1 = [[[ClassA alloc] init] autorelease];

2.3  Release一个对象后,立即把指针清空。(顺便说一句,release一个空指针是合法的,但不会发生任何事情)

[obj1 release];

obj1 = nil;

2.4  指针赋值给另一个指针。

ClassA *obj2 = obj1;

[obj2 retain];

//do something

[obj2 release];

obj2 = nil;

2.5  在一个函数中创建并返回对象,需要把这个对象设置为autorelease

ClassA *Func1()

{

ClassA *obj = [[[ClassA alloc]init]autorelease];

return obj;

}

2.6  在子类的dealloc方法中调用基类的dealloc方法

-(void) dealloc

{

…

[super dealloc];

}

2.7  在一个class中创建和使用property。

2.7.1 声明一个成员变量。

ClassB *objB;

2.7.2 声明property,加上retain参数。

@property (retain) ClassB* objB;

2.7.3 定义property。(property的默认实现请看第三章)

@synthesize objB;

2.7.4 除了dealloc方法以外,始终用.操作符的方式来调用property。

self.objB 或者objA.objB

2.7.5 在dealloc方法中release这个成员变量。

[objB release];

示例代码如下(详细代码请参考附件中的memman-property.m,你需要特别留意对象是在何时被销毁的。):

@interface ClassA : NSObject

{

ClassB* objB;

}

 

@property (retain) ClassB* objB;

@end

 

@implementation ClassA

@synthesize objB;

-(void) dealloc

{

[objB release];

[super dealloc];

}

@end

2.7.6 给这个property赋值时,有手动release和autorelease两种方式。

void funcNoAutorelease()

{

ClassB *objB1 = [[ClassB alloc]init];

ClassA *objA = [[ClassA alloc]init];

objA.objB = objB1;

[objB1 release];

[objA release];

}

 

void funcAutorelease()

{

ClassB *objB1 = [[[ClassB alloc]init] autorelease];

ClassA *objA = [[[ClassA alloc]init] autorelease];

objA.objB = objB1;

}

 

三 @property (retain)和@synthesize的默认实现

在这里解释一下@property (retain) ClassB* objB;和@synthesize objB;背后到底发生了什么(retain property的默认实现)。property实际上是getter和setter,针对有retain参数的property,背后的实现如下

@interface ClassA : NSObject
{
ClassB *objB;
}
-(ClassB *) getObjB;
-(void) setObjB:(ClassB *) value;
@end

@implementation ClassA
-(ClassB*) getObjB
{
return objB;
}

-(void) setObjB:(ClassB*) value
{
if (objB != value)
{
[objB release];
objB = [value retain];
}
}
在setObjB中,如果新设定的值和原值不同的话,必须要把原值对象release一次,这样才能保证retain count是正确的。
由于我们在class内部retain了一次(虽然是默认实现的),所以我们要在dealloc方法中release这个成员变量。
-(void) dealloc
{
[objB release];
[super dealloc];
}


ROS2 DDS中间件(图文并茂+超详细) DDS 的目标场景在端上,致力于使端上的应用可以更容易的进行通信,由于它的完全分布式架构,可以方便的将端上的应用拆分为多进程架构,从而提高端的容错能力。从产品定位角度来讲,它和 Chromium 中的 mojo 都是端上的通信中间件,只不过 mojo 不强调分布式,内部集成了更多的特定于端上的机制,比如句柄的传输,消息的缓存等。但由于 mojo 生于 Chromium,目前官方并没有把它独立出来,所以用起来比较不方便。而 DDS 或许可以在简单的场景下作为 mojo 的替代品。 阅读详情

相关推荐

自监督学习与上下文赌博机融合:视觉决策中的协同优化与实战指南

在机器学习领域,自监督学习(Self-Supervised Learning)通过设计前置任务从无标注数据中学习通用表征,已成为解决标注数据稀缺问题的关键技术。其核心原理是利用数据自身的结构生成监督信号,训练编码器提取鲁棒特征。上下文赌博机(Contextual Bandits)作为强化学习的简化分支,专注于在给定上下文信息下做出单步最优决策,平衡探索与利用,广泛应用于推荐系统、广告投放等场景。将两者结合,构建端到端的协同训练框架,能让视觉表征的优化直接服务于最终的决策目标,同时利用自监督任务作为正则化项提

weixin_33721344的博客 520

Object-c2.0内存管理

详解Objective-C 2.0 关于Objective-C内存管理规则是本文要介绍的内容,不多说,先来看内容。Objective-C 2.0增加了一些新的东西,包括属性和垃圾回收。那么,我们在学习Objective-C2.0之前,最好应该先了解,从前是什么样的,为什么Objective-C 2.0要增加这些支持。 这一切都跟Cocoa内存的管理规则有关系,我们知道,Objective-C中所

追梦 1512

INMP441麦克风与ESP32的I2S音频采集实战指南

本文提供了INMP441数字麦克风与ESP32开发板通过I2S接口进行高质量音频采集的完整实战指南。详细讲解了硬件连接、软件配置、数据读取及基础处理,并分享了常见的避坑技巧与性能优化建议,帮助开发者快速构建物联网音频应用。

weixin_28396629的博客 598

iphone(object-c) 内存管理(2) 内存管理策略

  现在搞iphone开发,一直不是很懂object-c的内存管理机制,看到apple的官方文档写的不错而又没有找到翻译的文章。于是自己在学习它的过程中就顺便把它翻译了,自己的英语不是太好,文字组织能力那就更菜了,读的蹩脚之处还望大家指出,我好在以后的翻译过程中好好改正。第一次翻译,欢迎拍砖,可不要把我拍死了呀!!! 文章中带有LPSTUDY的字样表明是我个人的理解,可能会有不对的地方,敬请指...

字节跳动 内推找我 2726

SNN系列|编码篇(2)时序编码

Tempotral Coding 选择编码方法是要根据编码对象以及模型如何处理编码信息进行选择,如下是一种时序信息的编码对比: Threshold-based encoding 又称为tempotral contrast,该方法可以用于编解码一段序列,当差值超过阈值时发放脉冲,阈值是根据编码的序列自适应变化的。 使用该编码方法时,实值序列最好是沿时间变化的,因为在根据差分决定是否发放脉冲后,脉冲的顺序与差分顺序有关。 Time-to-first spike coding 该编码方法将信息用神经元首次

Tianlong Lee的博客 1万+

攀藤PMS系列传感器STM32驱动开发与数据解析实战

本文详细介绍了攀藤PMS系列传感器与STM32微控制器的驱动开发与数据解析实战,涵盖硬件连接、数据协议解析、驱动源码编写及数据处理优化。重点解析了传感器数据帧结构,提供了完整的STM32驱动实现代码,并分享了实际应用中的问题排查与性能优化经验,帮助开发者快速实现高精度颗粒物浓度检测。

milk8的博客 348

IOS开发基础Object-C(08)—OC内存管理(2)-对象之间的内存管理

前几天,我们学习了内存管理的基本知识,了解了内存管理的基本原理。那么,今天我们来学习一下对象之间的内存管理,看看对象之间是如何进行内存管理的。首先,我们新建两个类:Student和Book类,在Student类中声明一个Book对象 Student.h #import #import "Book.h" @interface Student : NSObject {

崔小花的专栏 2607

GAN模型中的常见变体有哪些,如DCGAN、WGAN和CGAN等?

生成对抗网络(GAN)是一种强大的深度学习模型,它由生成器和判别器两个网络组成,通过对抗训练的方式实现生成数据的能力。随着研究的发展,出现了许多GAN的变体,每个变体都有其独特的改进和特点。

huidhsu的博客 2262

Object_c的retain机制

Object-c使用引用计数的方式管理内存。当我们使用一个指针指向一块内存的时候,应该对这块内存做retain操作,那么引用计数加一。当我们不再使用这个指针指向这块内存,应该对这块内存做release操作,那么引用计数减一。这样可以使引用计数值一直保持指向这块内存的指针数量。retainCount返回当前内存的引用计数值(即是有多少个指针指向该块内存)。当内存被开辟后,默认的引用计数为1 。

doublefly87jjyy的专栏 886

告别串口升级!手把手教你为STM32H750的W25Q32制作MDK下载算法(QSPI篇)

本文详细介绍了为STM32H750的W25Q32制作MDK下载算法(QSPI篇)的完整流程,帮助开发者告别低效的串口烧录方式。通过定制下载算法,可实现20倍以上的烧录速度提升,支持完整的调试功能,显著提高开发效率。文章涵盖环境准备、核心算法实现、性能优化及常见问题解决方案,适合嵌入式开发者参考。

weixin_30735745的博客 403

MYSQL、ORACLE、PostgreSQL数据库对象层次及权限管理对比

本文为出于自己扩展、比较、图形化的思维路径自行总结归纳,可能有些细节不太准确,欢迎指正。MySQL、Oracle、PostgreSQL关系型数据库都有管理员用户、用户、权限管理、表函数索引等数据库对象,schema。但在数据库对象隶属层次上有差异和相同的地方。

rachel_2016的博客 1490

Object-c基础知识之内存管理(二)

Object-c 之内存管理总结  *引用计数和对象所有权  *点语法的内存管理  *自动释放池  *ARC和垃圾回收机制  *内存管理常见的错误  *内存管理的经典问题  **/

liuxb1223的专栏 979

PCB模块化设计24——DCDC电源模块PCB布局布线设计规范

PCB模块化设计24——DCDC电源模块PCB布局布线设计规范

qq_31444421的博客 2万+

Object-C 内存管理

  原文学习记录来自:文顶顶博客园   一、内存管理基本原理 1、管理范围:        任何继承NSObject的对象 (对其他的基本数据类型无效)       本质原因:     对象和其他数据类型在系统中的存储空间不一样(其他局部变量主要存储于栈中,而对象存储于堆中)     当代码块结束时这个代码块中涉及的所有局部变量会被回收,指向对象的指针也被回收,此时对象已经没有指针...

qq_28596071的博客 822

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

【C++】C/C++内存管理

本文总共写了7000字,从C/C++内存分布、C语言中内存管理方式、等6个方面,去系统地带大家一起探索C/C++内存管理方式,也了解到了内存泄漏的危害以及如何防止内存泄漏,希望大家读后能够有所收获!

qq_62662919的博客 4138

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

object c 内存管理

   看了下官方文档的关于object c 的内存管理,总结下:     在iphone中object c  中的内存管理是通过计数器来管理的,每个对象内部都有一个计数器.新建一个对象,或者这个对象被其他对象引用,多会使计数器加1.Retain 增加计数器值 release 减少计数器值.当计数器为0时对象就dealloc自己.    在object c中你生成的一个对象那么你就有责任

dongpoyezi的专栏 597

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

Object C 内存管理

1.内存管理         ·内存管理是关于如何管理对象生命周期的编程原则 ·OC的内存管理只针对于对象,基本数据类型不需要管理内存 ·当以一个对象没有再被使用时,应该从内存中释放掉 ·所以的对象都有一个计数器,叫做引用计数(retainCount),表示该对象当前被多少“人”在使用。 ·当引用计数为0时,系统会销毁该对象 2.对象的基本结构    

Focus Now 1072

C语言——内存管理

C语言中,内存管理需要对静态和动态内存分配,静态分配在编译时确定,而动态分配(如malloc, calloc, realloc)则在运行时进行,需程手动管理,包括适时释放(用free)以避免内存泄漏。同时,了解栈、堆、数据区、代码区等内存区域的特性和用途,对于有效管理内存至关重要。

脑壳有点痒,好像要长脑子了! 4547

探索C嘎嘎:内存管理

●new的原理1.调用operator new函数申请空间。2.在申请空间的同时调用构造函数,完成对象的构造。●delete的原理1.调用operator delete函数释放空间。2.在释放空间的同时调用析构函数,完成对象的析构。●new[T]的原理1.调用operator new[]函数申请空间,而operator new[]实则是调用了T次operator new来进行申请空间。2.在申请的空间调用了T次构造函数●delete[T]的原理。

effort123_的博客 2879

C语言中如何进行内存管理

C语言是一种强大而灵活的编程语言,但与其他高级语言不同,它要求程序员自己负责内存的管理。正确的内存管理对于程序的性能和稳定性至关重要。

kevin_blog 2474

「C系列」C 内存管理

频繁地分配和释放小块内存可能会导致内存碎片化,使得虽然总内存量充足,但无法找到足够大的连续内存块来满足新的分配请求。会尝试找到一个足够大的内存块,并将原始数据复制到新的内存块中,然后返回新的内存块的指针。:如果你忘记释放分配的内存,就会导致内存泄漏。不同的是,它分配的内存会被初始化为零,并且你需要指定元素的数量和每个元素的大小,而不是总的字节数。函数用于动态分配指定字节大小的内存块,并返回一个指向该内存块的指针。释放同一块内存两次,或者释放一个已经被释放的内存块,程序可能会崩溃或产生不可预测的行为。

日常笔记/总结/系列知识梳理 1869

object c内存管理转载

iPhone/Mac Objective-C内存管理教程和原理剖析此文版权归作者Vince Yuan (vince.yuan#gmail.com)所有。欢迎非营利性转载,转载时必须包含原始链接http://vinceyuan.cnblogs.com/,且必须包含此版权声明的完整内容。版本 1.1 发表于2010-03-08前言初学objectice-C的朋友都有一个困惑,总觉得对objective-C的内存管理机制琢磨不透,程序经常内存泄漏或莫名其妙的崩溃。我在这里总结了自己对objective-C内存管理

我是一只笨鸟 4442

OC_浅谈Object-C的内存管理机制

和C++一样通过内存操作获得的空间在使用完毕后需要释放内存否则就会造成内存泄露,接下来就让我们一起看看OC的内存管理机制; 首先我们知道oc的内存管理分为两部分:1、自动内存管理。2、手动内存管理。好,接下来我们就分别从这两个方面来探讨oc的内 存管理机制 在介绍具体具体的管理方法前,我先讨论一些概念性的问题: 1、对象:什么是对象? 标准定义:对象就是客观世界存在的人、事、物体等实体在

Core__Java的专栏 919

object c内存管理机制

object c内存是通过引用计数来管理的

VCwenqinglin的专栏 718

【C语言】内存管理

动态内存分配malloc、calloc;重新调整内存的大小realloc;释放内存free

v13111329954的博客 2156

iOS---Object-C之内存管理机制

概述在程序运行过程中需要创建大量的对象,Object-C中对象是存储在堆中的,系统不会自动释放堆中的内存(基本类型数据是由系统自己管理的,放在栈上)。如果一个对象创建后并没有得到及时的释放则就会占用大量的内存。其他高级语言入C#、Java等都是通过垃圾回收(GC)来解决的,但是在OC中并没有类似的垃圾回收机制,因此OC的内存管理需要由开发人员手动处理。1.引用计数器 2.属性参数 3.自动释放池

C_Lluckying的博客 992
上一篇: object c内存管理(1)
下一篇: object c内存管理(3)
whichapple
博客等级 码龄16年 2粉丝 4原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值