———————————————————————————————————————————

NSString中的内存管理问题



由于autoreleasepool的存在,对于内存管理就会很复杂,retainCount 不能作为调试内存时的依据。

所以一般来说NS开头的类(或者说系统自己内部提供的类)基本上不需要我们做太多的内存管理,因为我们很难检测出来。



比如:



NSString *str=[[NSString alloc]initWithString:@"123123"];

        

        NSLog(@"str retainCount=%tu",[str retainCount]);



输出的结果是:str retainCount=18446744073709551615



这里的值不是乱码,而是很大,简单的release一下根本不会使这个值改变(一般一点不会改变),所以我们不要对这种系统中原有的类型进行内存操作。



if(retainCount>0)

{

      [str release];

}



这样很容易死循环。





———————————————————————————————————————————

autorelease 基本使用



(1)自动释放池 及 autorelease介绍



自动释放池:

①在iOS程序运行过程中,会创建无数个池子,这些池子都是以栈结构(先进后出)存在的

②当一个对象调用autorelease时,会将这个对象放到位于栈顶的释放池中



(2)自动释放池的创建方式



iOS 5.0 以后



@autoreleasepool

{  //开始代表创建自动释放池

………

}  //结束代表销毁自动释放池



(3)autorelease的原理



实际上autorelease只是把对release的调用延迟了,对于每一个autorelease,系统只是把该object放入了当前的autorelease pool中,当该pool释放时,该pool中所有的object会被一起调用release。





代码:



#import <Foundation/Foundation.h>



@interface Car : NSObject

-(void)run;

@end



@implementation Car

-(void)run

{

    NSLog(@"Car run!");

}

- (void)dealloc

{

    NSLog(@"Car dealloc!");

    [super dealloc];

}

@end



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

//    创建一个自动释放池

    @autoreleasepool {

        Car *car=[[[Car alloc]init]autorelease];//首先要用autorelease,那就一定得将ARC关掉

        

        [car run];

        [car run];

        [car run];

        

//        单单是创建对象,如果调用autorelease,那么程序结束的时候会自动释放每一个对象

        

//        如果是在其中retain了对象,记住,如果retain就要release一下,否则是无法释放内存的。

//        [car retain];

//        [car release];     

        

    }//执行的此处的时候,会对释放池中的每一个对象进行一次release操作

    return 0;

}





———————————————————————————————————————————

自动释放池的 使用误区/嵌套/注意事项



#import <Foundation/Foundation.h>



@interface Car : NSObject

-(void)run;

@end



@implementation Car

-(void)run

{

    NSLog(@"Car run!");

}

- (void)dealloc

{

    NSLog(@"Car dealloc!");

    [super dealloc];

}

@end



void test1()

{

    Car *car2=[[[Car alloc]init]autorelease];//此时car2不能释放,因为autorelease没有在autorelease的代码块中调用

    

    Car *car3=[[Car alloc]init];

    

    @autoreleasepool {//自动释放池代码块

        

        //        误区①:并不是将对象放到自动释放池代码块内部它就能自动释放的,只有手动调用autorelease方法以后,才能把对象加入到自动释放池

        //        Car *car1=[[Car alloc]init];

        //        [car1 run];

        //        上面的代码是无法释放car1的

        

        

        //        误区②:如果调用了autorelease,但是这个调用的语句并没有放到自动释放池代码块中,那么也无法将对象加入到自动释放池

        [car3 autorelease];//car3可以被释放

    }

}



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

    

//    自动释放池的嵌套

    Car *car1=[[Car alloc]init];

    [car1 run];

    [car1 retain];//如果要在此处加上retain,让car1的retainCount变为2,那么就要在@autoreleasepool {//自动释放池1

        @autoreleasepool {//自动释放池2

            @autoreleasepool {//自动释放池3

                [car1 autorelease];

            }//显然在这里执行了 [p release]; 的操作

//            [car1 autorelease];

//            但是有一点 [car1 autorelease]; 不可以过多执行,如果retainCount为0了还执行的话,那么就会出错了~

        }

    }

    return 0;

}



自动释放池嵌套在内存中的实现看下图:

★注意:

①自动释放池不能放占用内存较大的对象,也不要将大量循环放到同一个@autorelease之间,这样会造成内存峰值的上升



②@autorelease {

    //如果不写这里的自动释放池代码块,则在ARC机制下无法释放内存

}

所以必须加这句话,不要将编译器自动生成的这句话删掉



③[car1 autorelease]; 之后,就不要再 [car1 release]; 了,也不要 [car1 autorelease];   ,这样都是多余的,会报错,上面也提到了。所以说要么使用自动释放池,要么使用手动内存管理。





———————————————————————————————————————————

autorelease的应用场景——利用autorelease建立一个快速创建对象的方法



首先,快速创建实例对象的方法通常是一个类方法,以小写的类名命名。下面展示两种快速创建实例对象的方法,无参和有参。



声明:     

+(instancetype)bigCar;  //无参数的



实现:   

+(instancetype)bigCar

{

           return [[[self alloc]init]autorelease];

}





或者是   



声明:     

+(instancetype)bigCarWithSpeed:(int)speed;  //含参数的



实现:    

+(instancetype)bigCarWithSpeed:(int)speed

{

    return [[[self alloc]initWithSpeed:speed]autorelease];

}



但是写成上面这样的有参数的快速创建实例对象的格式,需要重写父类的构造方法(自定义构造方法),如下:



-(instancetype)initWithSpeed:(int)speed

{

    if(self=[super init])

    {

        _speed=speed;

    }

    return self;  //提醒一下,return self ;  要写在if大括号的外面~

}





代码:



#import <Foundation/Foundation.h>



@interface Car : NSObject

-(void)run;

+(instancetype)car;

@end



@implementation Car

-(void)run

{

    NSLog(@"Car run!");

}



- (void)dealloc

{

    NSLog(@"Car dealloc!");

    [super dealloc];

}





//方法①

//+(Car *)car

//{

//    return [[[Car alloc]init]autorelease];

//}



//方法①是不可取的,因为如果有一个BigCar类继承Car类,我们在main中作了如下操作

//BigCar *bigcar1=[BigCar car];

//[bigcar1 run];

//显然我们方法①中是用Car开辟的内存空间,故创建出来的还是Car的实例对象。所以无法调用BigCar中的run方法,调用的还是Car的(父类的)





//方法②

//+(id)car

//{

//    return [[[self alloc]init]autorelease];

//}



//方法②中,我们将调用创建内存空间和初始化的类设置为self,也就是当前类。将返回值类型设置为id类型,这样的话我们就可以利用父类的快速创建实例对象的方法去创建子类的实例对象了。但是还是有些不足,我们再看下面这个例子。

//NSString *str=[BigCar car]; 我们用BigCar类去快速创建了一个实例变量,返回值是BigCar类型的,但是我们是将返回值赋给了str,str明明是NSString类型的,自然会出错。但是!这里在编译的时候检测不出来,也没有警告,所以这里还是不合适,需要改进





//方法③

+(instancetype)car

{

    return [[[self alloc]init]autorelease];//这里是self,也就是这里用的是当前类创建实例对象

}

//最后我们完善了最后的方法③,这个方法是将返回值类型设置为instancetype类型,这样相对于方法②而言优点就是能在编译的时候判断两边的类型是否一致,遇到上面的情况,就会在编译的时候发出警告了!

//所以,我们以后快速创建实例对象的时候要用方法③

@end



@interface BigCar : Car



@end



@implementation BigCar

-(void)run

{

    NSLog(@"BigCar run!");

}



- (void)dealloc

{

    NSLog(@"BigCar dealloc!");

    [super dealloc];

}

@end



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

    @autoreleasepool {

//        先将ARC关闭

//        Car *car1=[[[Car alloc]init]autorelease];

//        [car1 run];

        

//        我们要利用autorelease建立一个快速创建实例对象的方法,就是要代替上面 Car *car1=[[[Car alloc]init]autorelease];  这句话

        

        BigCar *bigcar1=[BigCar car];

        [bigcar1 run];

    }

    return 0;

}





快速创建实例对象的方法(代码2——这部分对上面的代码做了优化,将   自定义构造方法   和   快速创建实例对象   的方法都做了传值处理,熟练书写的过程)



代码2:



#import <Foundation/Foundation.h>



@interface Person : NSObject

@property NSString *name;

@property int age;



-(instancetype)initWithName:(NSString *)name andWithAge:(int)age;



+(instancetype)person:(int)age;



@end



@implementation Person



-(instancetype)initWithName:(NSString *)name andWithAge:(int)age;

{

    if(self=[super init])

    {

        _name=name;

        _age=age;

    }

    return self;

}



+(instancetype)person:(int)age

{

    return [[self alloc]initWithName:@"wang" andWithAge:age];

//name 和 age 接收参数的不同点就是age是传进来的,是我们在main中赋值的,传进来的age直接赋给_age。而name是我们在 快速创建实例对象的方法 中进行赋值的,直接将赋好的 wang 传递给_name,所以说两个赋值的方式只是赋值的位置不同而已。

//不管哪种形式,我们都要知道,我们写的这个快速创建实例对象的方法在内部是调用自定义构造方法的!然后在自定义的构造方法中返回创建好的实例对象。

}



@end



@interface Student : Person



@end



@implementation Student



@end



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

    @autoreleasepool {

        Person *p=[Person person:12];

        NSLog(@"person.name=%@,person.age=%d",p.name,p.age);

        

        Student *stu=[Student person:12];

        NSLog(@"student.name=%@,p.age=%d",stu.name,stu.age);

        

    }

    return 0;

}





———————————————————————————————————————————

版权声明:本文为博主原创文章,未经博主允许不得转载。

Objective-C 【autorelease基本使用】的更多相关文章

  1. Automake

    Automake是用来根据Makefile.am生成Makefile.in的工具 标准Makefile目标 'make all' Build programs, libraries, document ...

  2. Objective C中的ARC的修饰符的使用---- 学习笔记九

    #import <Foundation/Foundation.h> @interface Test : NSObject /** * 默认的就是__strong,这里只是做示范,实际使用时 ...

  3. 理解autorelease

    如果你能够真正的理解autorelease,那么你才是理解了Objective c的内存管理.Autorelease实际上只是把对release的调用延迟了,对于每一个Autorelease,系统只是 ...

  4. Objective C ARC 使用及原理

    手把手教你ARC ,里面介绍了ARC的一些特性, 还有将非ARC工程转换成ARC工程的方法 ARC 苹果官方文档 下面用我自己的话介绍一下ARC,并将看文档过程中的疑问和答案写下来.下面有些是翻译,但 ...

  5. iOS开发——项目实战总结&带你看看Objective-C的精髓

    带你看看Objective-C的精髓 1:接口与实现 @interface...@end @implementation...@end @class 接口(头文件) 实现文件 向前引用 注:类别通过增 ...

  6. Objective C内存管理之理解autorelease------面试题

    Objective C内存管理之理解autorelease   Autorelease实际上只是把对release的调用延迟了,对于每一个Autorelease,系统只是把该Object放入了当前的A ...

  7. 【转】对cocos2d 之autorelease\ratain\release的理解

    原文链接:http://blog.sina.com.cn/s/blog_4057ab6201018y4y.html Objective C内存管理进阶(二):理解autorelease: http:/ ...

  8. iOS完全自学手册——[三]Objective-C语言速成,利用Objective-C创建自己的对象

    1.前言 上一篇已经介绍了App Delegate.View Controller的基本概念,除此之外,分别利用storyboard和纯代码创建了第一个Xcode的工程,并对不同方式搭建项目进行了比较 ...

  9. 什么时候应该使用Autorelease Pool

    csdn首发:http://blog.csdn.net/guijiewan/article/details/46470285 Objective c使用ARC之后,一般都不需要再手动调用retain, ...

  10. iOS开发核心语言Objective C —— 全部知识点总结

    本分享是面向有意向从事iOS开发的伙伴及苹果产品的发烧友,亦或是已经从事了iOS的开发人员,想进一步提升者.假设您对iOS开发有极高的兴趣,能够与我一起探讨iOS开发.一起学习,共同进步.假设您是零基 ...

随机推荐

  1. hdu 5286 How far away ? tarjan/lca

    How far away ? Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pi ...

  2. leanchat-android

    Original: https://github.com/lzwjava/leanchat-android Backup: https://github.com/eltld/leanchat-andr ...

  3. 20条IPTables防火墙规则用法!

    导读 管理网络流量是系统管理员必需处理的最棘手工作之一,我们必需规定连接系统的用户满足防火墙的传入和传出要求,以最大限度保证系统免受攻击.很多用户把 Linux 中的 IPTables 当成一个防火墙 ...

  4. linux shell “(())” 双括号运算符使用

    导读 在刚开始学习inux shell脚本编程时候,对于它的 四则运算以及逻辑运算.估计很多朋友都感觉比较难以接受.特变逻辑运算符"[]"使用时候,必须保证运算符与算数 之间有空格 ...

  5. Cows

    Farmer John's cows have discovered that the clover growing along the ridge of the hill (which we can ...

  6. Comparison method violates its general contract

    生产环境出现的错误排查,错误log如下 java.lang.IllegalArgumentException: Comparison method violates its general contr ...

  7. C++重载自增/减操作符

    作为类成员使用. 前缀是先加/减1,再取值:后缀是先取值,再加/减1. 前缀是左值,返回引用:后缀是右值,返回值. 后缀多一个int参数进行区分,用时编译器会传个没用的0作实参. 在后缀实现中调用前缀 ...

  8. Android 高级UI设计笔记20:RecyclerView 的详解之RecyclerView添加Item点击事件

    1. 引言: RecyclerView侧重的是布局的灵活性,虽说可以替代ListView但是连基本的点击事件都没有,这篇文章就来详细讲解如何为RecyclerView的item添加点击事件,顺便复习一 ...

  9. Ultra Edit常用正则表达式

    一.怎样可以删除包含特殊字符的行? 你可以用正则表示式全部替换命令替换行中包含的字符.要执行这个操作,你应该先进行查找: 查找: %*YOUR STRING*^p 替换为: (随便什么文字) 帮助文件 ...

  10. 【Android 界面效果32】ImageView中XML属性src和background的区别

    background会根据ImageView组件给定的长宽进行拉伸,而src就存放的是原图的大小,不会进行拉伸. src是图片内容(前景),bg是背景,可以同时使用. 此外:scaleType只对sr ...