OC语言之autorelease基本使用

本文详细介绍了Objective-C中的autorelease机制及其原理,包括autorelease的基本概念、自动释放池的创建与使用,以及在ARC环境下如何处理autorelease。文中通过实例代码展示了autorelease的工作流程,并对比了手动内存管理和ARC的优缺点。同时,提到了在使用autorelease时的注意事项,如避免过度释放和正确使用自动释放池。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

# autorelease基本使用

##1.autorelease基本概念

- autorelease是一种支持引用计数的内存管理方式,只要给对象发送一条autorelease消息,会将对象放到一个自动释放池中,当自动释放池被销毁时,会对池子里面的`所有对象做一次release操作`

    +> 注意,这里只是发送release消息,如果当时的引用计数(reference-counted)依然不为0,则该对象依然不会被释放。

- autorelease方法会返回对象本身

```

Person *p = [Person new];

p = [p autorelease];

```

- 调用完autorelease方法后,对象的计数器不变

```

Person *p = [Person new];

p = [p autorelease];

NSLog(@"count = %lu", [pretainCount]); // 1

```

- autorelease的好处

    +不用再关心对象释放的时间

    +不用再关心什么时候调用release

- autorelease的原理

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

---

##2.自动释放池

- 创建自动释放池格式:

- iOS 5.0前

```

NSAutoreleasePool *pool =[[NSAutoreleasePool alloc] init]; // 创建自动释放池

[pool release]; // [pool drain]; 销毁自动释放池

```

- iOS 5.0 开始

```

@autoreleasepool

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

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

```

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

- 当一个对象调用autorelease方法时,会将这个对象放到栈顶的释放池

---

##3.autorelease基本使用

```

NSAutoreleasePool *autoreleasePool = [[NSAutoreleasePoolalloc] init];

Person *p = [[[Person alloc] init]autorelease];

[autoreleasePool drain];

```

@autoreleasepool

{ // 创建一个自动释放池

       Person *p = [[Person new] autorelease];

} // 销毁自动释放池(会给池子中所有对象发送一条release消息)

```

# ARC基本概念

##1.什么是ARC

- Automatic Reference Counting,自动引用计数,即ARC,可以说是WWDC2011和iOS5所引入 的最大的变革和最激动人心的变化。ARC是新的LLVM 3.0编译器的一项特性,使用ARC,可以说一 举解决了广大iOS开发者所憎恨的手动内存管理的麻烦。

    +>手动管理内存, 可以简称MRC (Manual Reference Counting)

- 在工程中使用ARC非常简单:只需要像往常那样编写代码,只不过永远不写`retain,release和autorelease`三个关键字就好~这是ARC的基本原则。

 

- 当ARC开启时,编译器将自动在代码合适的地方插入retain, release和autorelease,而作为程序猿,完全不需要担心编译器会做错(除非开发者自己错用ARC了)。

---

##2.ARC的注意点和优点

- ARC的注意点

    +ARC是编译器特性,而不是运行时特性

    +ARC不是其它语言中的垃圾回收, 有着本质区别

- ARC的优点

    +完全消除了手动管理内存的烦琐, 让程序猿更加专注于app的业务

    +基本上能够避免内存泄露

    +有时还能更加快速,因为编译器还可以执行某些优化

---

##3.ARC的判断原则

- ARC的判断原则

    +只要还有一个强指针变量指向对象,对象就会保持在内存中

- 强指针

    +默认所有指针变量都是强指针

    +被__strong修饰的指针

```

 Person *p1 = [[Person alloc] init];

 __strong Person *p2 = [[Person alloc] init];

```

- 弱指针

    +被__weak修饰的指针

```

__weak Person *p = [[Person alloc] init];

```

- > 注意:当使用ARC的时候,暂时忘记“引用计数器”,因为判断标准变了。

---

# ARC快速入门

##1.ARC机制判断

- OS5以后,创建项目默认的都是ARC

![](http://7xj0kx.com1.z0.glb.clouddn.com/Snip20150625_2.png)

- ARC机制下有几个明显的标志:

   +  不允许调用对象的 release方法

   +  不允许调用 autorelease方法

   +   再重写父类的dealloc方法时,不能再调用 [superdealloc];

---

##2.ARC快速使用

```

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

   // 不用写release, main函数执行完毕后p会被自动释放

   Person *p = [[Person alloc] init];

   return 0;

}

```

# ARC下的内存管理

##1.ARC下单对象内存管理

- 局部变量释放对象随之被释放

```

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

  @autoreleasepool {

       Person *p = [[Person alloc] init];

    }// 执行到这一行局部变量p释放

   // 由于没有强指针指向对象, 所以对象也释放

   return 0;

}

```

- 清空指针对象随之被释放

```

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

   @autoreleasepool{

       Person *p = [[Person alloc] init];

       p = nil; // 执行到这一行, 由于没有强指针指向对象, 所以对象被释放

    }

   return 0;

}

```

- 默认清空所有指针都是强指针

```

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

  @autoreleasepool {

       // p1和p2都是强指针

       Person *p1 = [[Person alloc] init];

       __strong Person *p2 = [[Person alloc] init];

    }

   return 0;

}

```

- 弱指针需要明确说明

    +注意: 千万不要使用弱指针保存新创建的对象

```

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

  @autoreleasepool {

       // p是弱指针, 对象会被立即释放

       __weak Person *p1 = [[Person alloc] init];

    }

   return 0;

}

```

##2.ARC下多对象内存管理

- ARC和MRC一样, 想拥有某个对象必须用强指针保存对象, 但是不需要在dealloc方法中release

```

@interface Person : NSObject

 

// MRC写法

//@property (nonatomic, retain) Dog *dog;

 

// ARC写法

@property (nonatomic, strong) Dog *dog;

 

@end

```

##3.ARC下循环引用问题

- ARC和MRC一样, 如果A拥有B, B也拥有A, 那么必须一方使用弱指针

```

@interface Person : NSObject

//@property (nonatomic, retain) Dog *dog;

@property (nonatomic, strong) Dog *dog;

@end

 

@interface Dog : NSObject

// 错误写法, 循环引用会导致内存泄露

//@property (nonatomic, strong) Person*owner;

 

// 正确写法, 当如果保存对象建议使用weak

//@property (nonatomic, assign) Person*owner;

@property (nonatomic, weak) Person *owner;

@end

```

##4.ARC下@property参数

- strong : 用于OC对象, 相当于MRC中的retain

- weak : 用于OC对象, 相当于MRC中的assign

- assign : 用于基本数据类型, 跟MRC中的assign一样

---

# Category注意事项

##1.分类的使用注意事项

- 分类只能增加方法, 不能增加成员变量

```

@interface Person (NJ)

{

//   错误写法

//   int _age;

}

- (void)eat;

@end

```

- 分类中写property只会生成方法声明

```

@interface Person (NJ)

// 只会生成getter/setter方法的声明, 不会生成实现和私有成员变量

@property (nonatomic, assign) int age;

@end

```

- 分类可以访问原来类中的成员变量

```

@interface Person : NSObject

{

   int _no;

}

@end

 

@implementation Person (NJ)

- (void)say

{

   NSLog(@"%s", __func__);

   // 可以访问原有类中得成员变量

   NSLog(@"no = %i", _no);

}

@end

```

- 如果分类和原来类出现同名的方法, 优先调用分类中的方法, 原来类中的方法会被忽略

```

@implementation Person

- (void)sleep

{

   NSLog(@"%s", __func__);

}

@end

 

@implementation Person (NJ)

- (void)sleep

{

   NSLog(@"%s", __func__);

}

@end

 

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

    Person *p = [[Person alloc] init];

   [p sleep];

   return 0;

}

输出结果:

-[Person(NJ) sleep]

```

##2.分类的编译的顺序

- 多个分类中有同名方法,则执行最后编译的文件方法(注意开发中千万不要这么干)

```

@implementation Person

- (void)sleep

{

   NSLog(@"%s", __func__);

}

@end

 

@implementation Person (NJ)

- (void)sleep

{

   NSLog(@"%s", __func__);

}

@end

 

@implementation Person (MJ)

- (void)sleep

{

   NSLog(@"%s", __func__);

}

@end

 

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

   Person *p = [[Person alloc] init];

   [p sleep];

   return 0;

}

输出结果:

-[Person(MJ) sleep]

```

![](http://7xj0kx.com1.z0.glb.clouddn.com/Snip20150625_10.png)

- 方法调用的优先级(从高到低)

    +分类(最后参与编译的分类优先)

    +原来类

    +父类

---

# Block注意事项

##1.Block注意事项

- 在block内部可以访问block外部的变量

```

int a = 10;

void (^myBlock)() = ^{

   NSLog(@"a = %i", a);

    }

myBlock();

输出结果: 10

```

- block内部也可以定义和block外部的同名的变量(局部变量),此时局部变量会暂时屏蔽外部

```

int a = 10;

void (^myBlock)() = ^{

   int a = 50;

   NSLog(@"a = %i", a);

    }

myBlock();

输出结果: 50

```

- 默认情况下, Block内部不能修改外面的局部变量

```

int b = 5;

void (^myBlock)() = ^{

    b= 20; // 报错

   NSLog(@"b = %i", b);

   };

myBlock();

```

- Block内部可以修改使用__block修饰的局部变量

```

 __block int b = 5;

void (^myBlock)() = ^{

    b= 20;

   NSLog(@"b = %i", b);

   };

myBlock();

输出结果: 20

```

# ARC和MRC兼容和转换

##1.ARC模式下如何兼容非ARC的类

- 转变为非ARC -fno-objc-arc

- 转变为ARC的,-f-objc-arc (不常用)

![](http://7xj0kx.com1.z0.glb.clouddn.com/Snip20150625_5.png)

---

##2.如何将MRC转换为ARC

![](http://7xj0kx.com1.z0.glb.clouddn.com/Snip20150625_7.png)

---

# autorelease注意事项

##1.autorelease使用注意

- 并不是放到自动释放池代码中,都会自动加入到自动释放池

```

 @autoreleasepool {

   // 因为没有调用 autorelease 方法,所以对象没有加入到自动释放池

   Person *p = [[Person alloc] init];

   [p run];

}

```

- 在自动释放池的外部发送autorelease不会被加入到自动释放池中

    +autorelease是一个方法,只有在自动释放池中调用才有效。

```

 @autoreleasepool {

 }

 // 没有与之对应的自动释放池,只有在自动释放池中调用autorelease才会放到释放池

 Person *p = [[[Person alloc] init]autorelease];

 [prun];

 // 正确写法

 @autoreleasepool {

   Person *p = [[[Person alloc] init] autorelease];

 }

 // 正确写法

 Person *p = [[Person alloc] init];

 @autoreleasepool {

   [p autorelease];

 }

```

- 自动释放池的嵌套使用

    +自动释放池是以栈的形式存在

    +由于栈只有一个入口, 所以调用autorelease会将对象放到栈顶的自动释放池

    +>栈顶就是离调用autorelease方法最近的自动释放池

```

@autoreleasepool { // 栈底自动释放池

       @autoreleasepool {

           @autoreleasepool { // 栈顶自动释放池

                Person *p = [[[Person alloc]init] autorelease];

           }

           Person *p = [[[Person alloc] init] autorelease];

       }

    }

```

- 自动释放池中不适宜放占用内存比较大的对象

    +尽量避免对大内存使用该方法,对于这种延迟释放机制,还是尽量少用

    +不要把大量循环操作放到同一个 @autoreleasepool 之间,这样会造成内存峰值的上升

```

// 内存暴涨

   @autoreleasepool {

       for (int i = 0; i < 99999; ++i) {

           Person *p = [[[Person alloc] init] autorelease];

       }

    }

```

// 内存不会暴涨

 for(int i = 0; i < 99999; ++i) {

       @autoreleasepool {

           Person *p = [[[Person alloc] init] autorelease];

       }

    }

```

##2.autorelease错误用法

- 不要连续调用autorelease

```

 @autoreleasepool {

 // 错误写法, 过度释放

   Person *p = [[[[Person alloc] init] autorelease] autorelease];

 }

```

- 调用autorelease后又调用release

```

 @autoreleasepool {

   Person *p = [[[Person alloc] init] autorelease];

   [p release]; // 错误写法, 过度释放

 }

```

# Block基本概念

##1.什么是Block

- Block是iOS中一种比较特殊的数据类型

- Block是苹果官方特别推荐使用的数据类型, 应用场景比较广泛

    +动画

    +多线程

    +集合遍历

    +网络请求回调

- Block的作用

    +用来保存某一段代码, 可以在恰当的时间再取出来调用

    +功能类似于函数和方法

---

##2.block的格式

- Block的定义格式

```

返回值类型 (^block变量名)(形参列表) = ^(形参列表) {

};

```

![](http://7xj0kx.com1.z0.glb.clouddn.com/Snip20150625_11.png)

- block最简单形式

```

void (^block名)() = ^{代码块;}

例如:

void (^myBlock)() = ^{ NSLog(@"李南江");};

```

- block带有参数的block的定义和使用

```

void (^block名称)(参数列表)

= ^ (参数列表) { // 代码实现; }

例如:

void (^myBlock)(int) = ^(int num){NSLog(@"num = %i", num); };

```

- 带有参数和返回值的block

```

返回类型 (^block名称)(参数列表)

= ^ (参数列表) { // 代码实现; }

例如:

int (^myBlock)(int, int) = ^(int num1, intnum2){ return num1 + num2; };

```

- 调用Block保存的代码

```

block变量名(实参);

```

# Category基本概念

##1.什么是Category

- Category有很多种翻译: 分类 \ 类别 \ 类目 (一般叫分类)

- Category是OC特有的语法, 其他语言没有的语法

- Category的作用

    +可以在不修改原来类的基础上, 为这个类扩充一些方法

    + 一个庞大的类可以分模块开发

    +一个庞大的类可以由多个人来编写,更有利于团队合作

---

##2.Category的格式

- 在.h文件中声明类别

    +1)新添加的方法必须写在 @interface 与 @end之间

    +2)ClassName 现有类的类名(要为哪个类扩展方法)            + 3)CategoryName 待声明的类别名称

    +4)NewMethod 新添加的方法

```

@interface ClassName (CategoryName)

NewMethod; //在类别中添加方法

//不允许在类别中添加变量

@end

```

    +>注意: 1)不允许在声明类别的时候定义变量

- 在.m文件中实现类别:

    +1)新方法的实现必须写在@ implementation与@end之间

    +2)ClassName 现有类的类名

    +3)CategoryName 待声明的类别名称

    +4)NewMethod 新添加的方法的实现

```

@implementation ClassName(CategoryName)

NewMethod

... ...

@end

```

- 使用Xcode创建分类

![](http://7xj0kx.com1.z0.glb.clouddn.com/Snip20150625_8.png)

![](http://7xj0kx.com1.z0.glb.clouddn.com/Snip20150625_9.png)

---

# 类扩展(Class Extension)

##1.什么是类扩展

- 延展类别又称为扩展(Extendsion),Extension是Category的一个特例

- 可以为某个类扩充一些私有的成员变量和方法

    +写在.m文件中

    +英文名是Class Extension

---

##2.类扩展书写格式

```

@interface 类名 ()

@end

```

- > 对比分类, 就少了一个分类名称,因此也有人称它为”匿名分类”

# typedef和Block

##本小节知识点:

##1.函数指针回顾

- 函数指针使用

```

int sum(int value1, int value2)

{

   return value1 + value2;

}

int minus(int value1, int value2)

{

   return value1 - value2;

}

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

   int (*sumP) (int, int) = sum;

   int res = sumP(10, 20);

   NSLog(@"res = %i", res);

 

   int (*minusP) (int , int) = minus;

   res = minusP(10, 20);

   NSLog(@"res = %i", res);

   return 0;

}

```

- 函数指针别名

```

typedef int (*calculate) (int, int);

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

   calculate sumP = sum;

   int res = sumP(10, 20);

   NSLog(@"res = %i", res);

   calculate minusP = minus;

   res = minusP(10, 20);

   NSLog(@"res = %i", res);

   return 0;

}

```

##2.block和typedef

- block使用

```

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

   int (^sumBlock) (int, int) = ^(int value1, int value2){

       return value1 + value2;

   };

   int res = sumBlock(10 , 20);

   NSLog(@"res = %i", res);

 

   int (^minusBlock) (int, int) = ^(int value1, int value2){

       return value1 - value2;

   };

   res = minusBlock(10 , 20);

   NSLog(@"res = %i", res);

    return 0;

}

```

- block别名

```

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

   calculateBlock sumBlock = ^(int value1, int value2){

       return value1 + value2;

   };

   int res = sumBlock(10, 20);

   NSLog(@"res = %i", res);

   calculateBlock minusBlock = ^(int value1, int value2){

       return value1 - value2;

   };

   res = minusBlock(10, 20);

   NSLog(@"res = %i", res);

 

   return 0;

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值