OC中的内存问题
常见的内存问题有以下几种:
1.野指针异常:访问没有所有权的内存,如果想要安全的访问,必须确保空间还在
2.内存泄露:空间使用完之后没有及时释放
3.过度释放:对同一块存储空间释放多次,立刻crash
4.内存溢出:所有存储空间被占用
管理内存的三种方式:
1.垃圾回收机制:程序员只需要开辟存储空间,系统会自动回收内存,Java采用该机制
2.MRC:手动引用计数机制,由开发人员开辟空间,手动添加影响引用计数增加或减少的方法,能够灵活的控制空间何时释放。
3.ARC:自动引用计数机制,是iOS5.0退出的,基于MRC,不需要程序员手动添加管理内存的代码,编译器会在合适的位置自动添加管理内存的代码。
MRC和ARC都是采用引用计数来管理对象内存的
// 引用计数机制:
iOS采用引用计数来管理内存,当你想拥有这个对象的时候,需要使该对象的引用计数+1,使用完这个对象的时候,使该对象的引用计数-1,当对象的引用计数为0的时候,表示没有任何对象对该对象持有,那么这时候系统会自动调用dealloc方法来回收该对象的存储空间。
// 影响引用计数的方法
1.使用引用计数+1的方法:alloc,retain,copy
2.使用引用计数-1的方法:release,autorelease
下面我们创建一个Person类,还有一个结婚协议(顺道说下协议的问题)
Person.h
#import <Foundation/Foundation.h>
#import "MarryProtocol.h" // 引入协议所在的.h文件 // 让Person类遵守MarryProtocol协议
@interface Person : NSObject <MarryProtocol, NSCopying> @property (nonatomic, retain) NSString *name;
@property (nonatomic, retain) NSString *gender; @end
Person.m
#import "Person.h" @implementation Person // 重写dealloc方法
// dealloc相当于临终遗言
- (void)dealloc
{ NSLog(@"该对象%@被销毁", self);
[super dealloc]; // 让父类回收存储空间,这句话通常写在最后
} - (NSString *)description
{
return [NSString stringWithFormat:@"name = %@, gender = %@", _name, _gender];
} // 实现协议中的copyWithZone:方法
//- (id)copyWithZone:(NSZone *)zone {
//
// // 伪拷贝:拷贝地址,相当于retain,引用计数+1
// return [self retain];
//
//} //- (id)copyWithZone:(NSZone *)zone {
//
// // 浅拷贝:对象开辟新的空间,但两个对象的实例变量指向同一块存储空间(zone是系统申请的内存池)
// Person *person = [[Person allocWithZone:zone] init];
// person.name = self.name;
// person.gender = self.gender;
//
// return person;
//} - (id)copyWithZone:(NSZone *)zone { // 深拷贝:对象开辟新的存储空间,并且对象的实例变量指向不同的存储空间(zone是系统申请的内存池)
Person *person = [[Person allocWithZone:zone] init];
person.name = [self.name mutableCopy];
person.gender = [self.gender mutableCopy];
return person;
} // 实现协议中的方法 // 不能家暴
- (void)noViolence {
NSLog(@"不能家暴");
} // 不能出轨
- (void)noDerail {
NSLog(@"不能出轨");
} // 不能抽大烟
- (void)noSmoking {
NSLog(@"不能抽烟");
} // 不能藏私房钱
- (void)noSelfMoney {
NSLog(@"不能藏私房钱");
} // 做家务(可实现也可以不实现)
- (void)doHousework {
NSLog(@"做家务");
} @end
MarryProtocol.h(结婚协议)
#import <Foundation/Foundation.h> // 定义一个协议:@protocol...@end
@protocol MarryProtocol <NSObject> // 定义方法 @required // 指定协议中的方法必须遵守
// 不能家暴
- (void)noViolence; // 不能出轨
- (void)noDerail; // 不能抽大烟
- (void)noSmoking; // 不能藏私房钱
- (void)noSelfMoney; @optional // 方法可以实现也可以不实现
// 做家务
- (void)doHousework; @end
main.m
#import <Foundation/Foundation.h>
#import "Person.h" int main(int argc, const char * argv[]) {
@autoreleasepool { // alloc的作用:开辟空间,让对象的引用计数由0变成1
Person *person = [[Person alloc] init]; // 查看对象的引用计数retainCount
// retainCount是MRC才有的机制,所以如果使用的话,需要将ARC转化为MRC
NSUInteger count = person.retainCount; //
NSLog(@"%lu", count); // retain 使对象的引用计数+1
[person retain]; //
NSLog(@"%lu", person.retainCount); // release 使对象的引用计数-1
[person release]; //
NSLog(@"%lu", person.retainCount); [person release]; // 0 当对象的引用计数为0,系统自动调用dealloc方法
person = nil; // 对象置为nil,防止野指针异常 // 如果对象已经被回收,当前代码无效
// 开启僵尸模式检查野指针异常
// 如果一个对象被销毁,这个对象就称为僵尸对象
NSLog(@"%lu", person.retainCount);
// 这句话默认情况下不报错,如果要报错,需要开启僵尸模式 // autorelease
Person *person1 = [[Person alloc] init]; //
Person *person2 = [person1 retain]; //
NSLog(@"person1 = %lu, person2 = %lu", person1.retainCount, person2.retainCount); Person *person3 = [person1 retain]; //
NSLog(@"person1 = %lu, person2 = %lu, person3 = %lu", person1.retainCount, person2.retainCount, person3.retainCount); [person3 release]; // 2 立刻-1
NSLog(@"person1 = %lu, person2 = %lu, person3 = %lu", person1.retainCount, person2.retainCount, person3.retainCount); [person2 autorelease]; // 在未来的某个时刻-1
NSLog(@"person1 = %lu, person2 = %lu, person3 = %lu", person1.retainCount, person2.retainCount, person3.retainCount); // 自动释放池
// 定义一个自动释放池
// 第一种形式
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSLog(@"%lu", pool.retainCount); Person *p1 = [[Person alloc] init]; //
NSLog(@"%lu", p1.retainCount); [p1 retain]; // [p1 autorelease]; //
NSLog(@"%lu", p1.retainCount); [pool release]; // 销毁自动释放池
NSLog(@"%lu", p1.retainCount); // 1 // 对象使用autorelease是在未来的某一个时刻让对象的引用计数-1,这个某一时刻指的是喷到自动释放池之后才会释放 // 第二种形式 (在iOS5.0之后推荐使用的)
@autoreleasepool {
Person *p2 = [[Person alloc] init]; //
p2.name = @"p2";
[p2 autorelease]; // Person *p3 = [[Person alloc] init];
p3.name = @"p3";
//[p3 autorelease];
} // 自动释放池的作用:自动释放池会在销毁之前检查内部有没有autorelease操作,如果有autorelease操作,让该对象的引用计数做一次-1操作 // 内存管理原则:
// 凡是使用alloc,retain让对象的引用计数+1,相应的就该使用release或者autorelease让对象的引用计数-1,也就是说增加的次数要和减少的次数相等,才能保证对象的引用计数最终为0,对象才会被销毁。 // 增加次数和减少次数不一致,会出现内存问题 // 1.内存泄露:增加的次数大于减少的次数
// 情况1
Person *per1 = [[Person alloc] init]; // 情况2
Person *per2 = [[Person alloc] init];
[per2 retain];
[per2 release]; // 情况3
Person *per3 = [[Person alloc] init];
per3 = nil;
[per3 release]; // 2.过度释放:增加的次数小于减少的次数
Person *q = [[Person alloc] init];
[q retain];
[q release];
[q release];
[q release]; // 过度释放 // 3.野指针异常:增加的次数等于减少的次数,还将继续访问
Person *x = [[Person alloc] init];
[x retain];
[x release];
[x release];
//x = nil; // 没有这一句,就会野指针异常
NSLog(@"%lu", x.retainCount); // 并不是所有的对象都能进行copy操作,只有遵循NSCopying协议并实现copyWithZone:方法的对象才能进行copy
Person *pe = [[Person alloc] init];
pe.name = @"hhh";
[pe retain];
NSLog(@"%lu", pe.retainCount);
Person *pe1 = [pe copy];
NSLog(@"%lu, %lu", pe.retainCount, pe1.retainCount);
NSLog(@"%p, %p", pe.name, pe1.name); // copy和mutableCopy
// copy出来的指针副本是不可变的
NSString *str1 = @"詹姆斯";
NSString *str2 = [str1 copy];
NSLog(@"str1 = %p, str2 = %p", str1, str2); NSMutableString *mStr1 = [str1 copy];
NSLog(@"mStr1 = %p", mStr1); // [mStr1 appendString:@"威武"];
// NSLog(@"%@", mStr1); // mutableCopy拷贝出来的内容是可变的
NSString *str3 = [str1 mutableCopy];
NSLog(@"str3 = %p, str1 = %p", str3, str1); NSMutableString *mStr2 = [str1 mutableCopy];
[mStr2 appendString:@"威武"];
NSLog(@"%@", mStr2); // NSArray *array = @[@"1", @"2", @"3"];
// NSMutableArray *mArray = [array mutableCopy]; // 深拷贝和浅拷贝
// 如果是深拷贝,那么成员对象的地址和之前的地址不同,如果是浅拷贝,成员变量的地址和之前的相同 }
return ;
}
OC中的内存问题的更多相关文章
- OC中的内存管理
一. 基本原理 1. 什么是内存管理 移动设备的内存极其有限,每个app所能占用的内存是有限制的 当app所占用的内存较多时,系统会发出内存警告,这时得回收一些不需要再使用的内存空间.比如回收一些不需 ...
- OC中最难的一部分内容:内存管理
OC中最难的一部分内容:内存管理为什么说他难呢?因为内存如果需要我们程序员去管理的话,那个难度肯定是很大的,如果是Java,垃圾回收器会把这份工作给做了,我们不需要关心,但是就是因为如此,Androi ...
- Unity游戏开发中的内存管理_资料
内存是手游的硬伤——Unity游戏Mono内存管理及泄漏http://wetest.qq.com/lab/view/135.html 深入浅出再谈Unity内存泄漏http://wetest.qq.c ...
- C++中的内存管理
在C++中也是少不了对内存的管理,在C++中只要有new的地方,在写代码的时候都要想着delete. new分配的时堆内存,在函数结束的时候不会自动释放,如果不delete我分配的堆内存,则会造成内存 ...
- Objective-C中的内存管理
在编程语言中是少不了对内存的管理的,内存对于计算机来说是宝贵的资源,所以对使用不到的资源进行回收是很有必要的.OC中使用引用计数和垃圾回收来管理内存,在OC中为每个对象分配一个引用计数器,当对象刚刚被 ...
- java中的继承与oc中的继承的区别
为什么要使用继承? 继承的好处: (1)抽取出了重复的代码,使代码更加灵活 (2)建立了类和类之间的联系 继承的缺点: 耦合性太强 OC中的继承 1.OC中不允许子类和父类拥有相同名称的成员变量名:( ...
- iOS-旧项目中手动内存管理(MRC)转ARC
在ARC之前,iOS内存管理无论对资深级还是菜鸟级开发者来说都是一件很头疼的事.我参 加过几个使用手动内存管理的项目,印象最深刻的是一个地图类应用,由于应用本身就非常耗内存,当时为了解决内存泄露问题, ...
- Oc中的数组
========================== 数组 ========================== 一.认识数组 oc中可以把NSObject对象的子类放到数组这个集合中,但是int.f ...
- OC中面向对象2
一. 定义OC的类和创建OC的对象 接下来就在OC中模拟现实生活中的情况,创建一辆车出来.首先要有一个车子类,然后再利用车子类创建车子对象 要描述OC中的类稍微麻烦一点,分2大步骤:类的声明.类的实现 ...
随机推荐
- Android应用安全之外部动态加载DEX文件风险
1. 外部动态加载DEX文件风险描述 Android 系统提供了一种类加载器DexClassLoader,其可以在运行时动态加载并解释执行包含在JAR或APK文件内的DEX文件.外部动态加载DEX文件 ...
- 通过IIS不能连接远程数据库的问题
近期遇到一个奇怪的问题:在调试MES程序时发现,如果连接的是远程的SQL SERVER数据库(通过了IIS),则提示连接失败,就是经常见到的数据库不允许远程连接的错误提示: 而且又测试了以下几种情况: ...
- transactional replication 的immediate_sync属性
在默认情况下,immediate_sync是关闭的,这个属性可以在创建publication时指定,也可以在创建完毕后修改. 如果immediate_sync为true, snapshot 文件和re ...
- No Dialect mapping for JDBC type: -9
由于项目中使用的是hibernate 4.35版本和sqlserver 2008数据库.所以,自定义方言时,需要和老版本做区别: public class MySQLServerDialect ext ...
- 队列的链式存储方式的实现(Java语言描述)
链队列的结构示意图: 先进先出. QueueInterface.java//操作方法接口 package 队列的实现; public interface QueueInterface { public ...
- 用emacs的org2blog组件写cnblogs博客 -- 环境配置及使用
Table of Contents 配置 使用 创建一篇博文并发布 更新一篇博文 删除一篇博文 待办 本文给出了一个安装.配置org2blog的方法,实现在emacs中书写blog文章.并发布到cnb ...
- IOS开发UI基础 UIAlertView的属性
UIAlertView1.Title获取或设置UIAlertView上的标题. 2.Message获取或设置UIAlertView上的消息 UIAlertView *alertView = [[UIA ...
- SpringMVC基础——@ModelAttribute和@SessionAttribute
一.@ModelAttribute 注解 对方法标注 @ModelAttribute 注解,在调用各个目标方法前都会去调用 @ModelAttribute 标记的注解.本质上来说,允许我们在调用目标方 ...
- Node.js爬虫抓取数据 -- HTML 实体编码处理办法
cheerio DOM化并解析的时候 1.假如使用了 .text()方法,则一般不会有html实体编码的问题出现 2.如果使用了 .html()方法,则很多情况下(多数是非英文的时候)都会出现,这时, ...
- App.Config详解
App.Config详解 应用程序配置文件是标准的 XML 文件,XML 标记和属性是区分大小写的.它是可以按需要更改的,开发人员可以使用配置文件来更改设置,而不必重编译应用程序.配置文件的根节点是c ...