Objective-C笔记

记录一些最近使用OC时涉及到的内容,算是巩固和总结。

ARC内存管理

Cocoa使用引用计数(RC)方法来管理对象的生命周期,以达到内存管理的目的。

引用计数(RC)

对于内存中每个对象,都有一个整数与其相对应,这个数字就是它的引用计数(RC)。

当某段代码需要访问一个对象时,代码就将该对象的引用计数器值加1,表示“我要访问该对象”;当这段代码结束对该对象访问时,将对象的引用计数器减1,表示它不再访问该对象。当引用计数的值为0时,表示不再有代码访问该对象,此时该对象将被销毁,其占用的内存会被系统回收以便重用。

当使用alloc、new方法或者通过copy消息创建一个对象时,对象的引用计数值被设置为1,要增加对象的引用计数的值,可以给对象发送一条retain消息。要减少的话,可以给对象发送一条release消息。

当一个对象因其引用计数值归0而即将被销毁时,Objective-C会自动向对象发送一条dealloc消息。你可以在自己的对象中重写dealloc方法,这样就能释放掉已经分配的全部相关资源。一定不要直接调用dealloc方法,Objective-C会在需要销毁对象时自动调用它。

要获得一个对象的引用计数的值,可以发送retainCount消息。

autorelease pool

我们可以很容易地知道何时创建了一个对象,但常常很难确定在何时何处不再使用该对象而将其释放。
Cocoa中的自动释放池很好地解决了这一问题。NSObject类提供有以下方法:

1
- (id) autorelease;

该方法预先设定了一条会在未来某个时间发送的release消息,其返回值是接收这条消息的对象。这一特性与retain消息采用了相同的技术,使嵌套调用更加容易。当给一个对象发送
autorelease消息时,实际上是将该对象添加到了自动释放池中。当自动释放池被销毁时,会向该池中的所有对象发送relase消息。

其实有两种方法可以创建一个自动释放池:

  • 使用 @autoreleasepool 关键字
  • 通过NSAutoreleasePool 对象

在Foundation库中可以使用@autoreleasepool,大括号作用范围内所有的代码都会放进这个自动释放池内:

1
2
3
4
5

@autoreleasepool
{
//...
}

如果使用NSAutoreleasePool对象,需要手动创建和释放对象。当创建了一个自动释放池之后,该对象会自动成为当前激活的(active)自动释放池。当该自动释放池对象被销毁时,会释放池中的所有对象:

1
2
3
4
5
6
7

NSAutoreleasePool *pool;
pool = [NSAutoreleasePool new];

//...

[pool release];

Cocoa内存管理规则

Cocoa内存管理的规则如下:

  • 当你使用new、alloc或copy方法创建一个对象时,该对象的保留计数器的值为1。当不再使用该对象时,你应该向该对象发送一条release或autorelease消息。这样,该对象将在其使用寿命结束时被销毁。

  • 当你通过其他方法获得一个对象时,假设该对象的保留计数器的值为1,而且已经被设置为自动释放,那么你不需要执行任何操作来确保该对象得到清理。如果你打算在一段时间内拥有该对象,则需要保留它并确保在操作完成时释放它。如果你保留了某个对象,就需要(最终)释放或自动释放该对象。必须保持retain方法和release方法的使用次数相等。

当拥有一个对象时,只需要判断该对象的获得途径(怎样获得的),以及将要拥有这个对象多长时间:

获得途径 临时对象 拥有对象
alloc/new/copy 不再使用时释放对象(Release) 在dealloc中释放对象(Release)
其他方法 不需要执行任何操作 获得对象时保留(Retain) 在dealloc中释放对象

[NSMutableArray arrayWithCapacity][NSColor blueColor]等都属于上表中的“其他方法”。

自动引用计数

自动引用计数(ARC)是苹果公司的内存管理解决方案。

ARC是在编译时进行工作的,它在代码中插入了合适的retainrelease语句,就好像是你自己手动写好了所有的内存管理代码。不过,是编译器替你完成了内存管理的工作,程序在运行的时候。retainrelease会像往常一样发挥作用。系统不会知道也不会关心这些命令是你写的还是编译器写的。
ARC是一个可选的功能,你必须明确地启用或禁用它。

ARC只对可保留对象指针(retainable object pointers,ROPs)有效。可保留对象指针主要有以下三种:

  1. 代码块指针
  2. Objective-C对象指针
  3. 通过_attribute__((NSObject))类型定义的指针

在代码中使用ARC,需要满足以下三个条件:

  1. 能够确定哪些对象需要进行内存管理
  2. 能够表明如何去管理对象
  3. 有可行的办法传递对象的所有权

代理传值

现有两个UIViewController,分别名为UIViewControllerFirst和UIViewControllerSecond,在First中点击某按钮后会创建Second并跳转。此时很容易实现由First向Second的传值(以及函数调用)。如果需要从Second向First传值(或函数调用),有很多种方法,如可以使用代理的方式,主要步骤为:

  1. 在Second中声明协议,协议中包含一个传递值的方法
  2. Second中声明代理
  3. First需要实现Second中的协议
  4. 将First设置为Second中的代理
  5. 在Second中调用代理实现的方法

UIViewControllerFirst.h:

1
2
3
4
5
6
7
#import <UIKit/UIKit.h>

@interface UIViewControllerFirst : UIViewController

@property (nonatomic, copy) NSArray *assets;

@end

UIViewControllerFirst.m:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#import "UIViewControllerFirst.h"

@interface UIViewControllerFirst ()
<UIViewControllerSecondDelegate>
@end

@implementation UIViewControllerFirst

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

- (IBAction)btnSelectPressed:(id)sender
{
UIViewControllerSecond *view = [UIViewControllerSecond new];

[view setDelegate:self];


[self.navigationController pushViewController:view animated:YES];
}


- (void)passAssets:(NSArray *)assets
{
self.assets = assets;
}

@end

UIViewControllerSecond.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
#import <UIKit/UIKit.h>

@protocol UIViewControllerSecondDelegate <NSObject>
- (void)passAssets:(NSArray *)assets;
@end

@interface UIViewControllerSecond : UIViewController

@property (nonatomic, copy) NSArray *assets;

@property (weak, nonatomic) id <UIViewControllerSecondDelegate> delegate;

@end

UIViewControllerSecond.m:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#import "UIViewControllerSecond.h"

@implementation UIViewControllerSecond

- (void)viewDidLoad
{
[super viewDidLoad];
}

- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[self.navigationController setToolbarHidden:YES animated:YES];

if ([_delegate respondsToSelector:@selector(passAssets:)]) {
[_delegate passAssets:self.assets];
}
}

@end

单例模式

1
2
3
4
5
@interface UploadManager : NSObject

+(UploadManager*)defaultManager;

@end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

#import "UploadManager.h"

@implementation UploadManager

static UploadManager *defaultManager = nil;


+ (UploadManager*)defaultManager
{
static dispatch_once_t token;
dispatch_once(&token, ^{
if(defaultManager == nil)
{
defaultManager = [[self alloc] init];
}
});
return defaultManager;
}


+ (id)allocWithZone:(struct _NSZone *)zone
{
static dispatch_once_t token;
dispatch_once(&token, ^{
if(defaultManager == nil)
{
defaultManager = [super allocWithZone:zone];
}
});
return defaultManager;
}


- (id)copy
{
return self;
}


- (id)mutableCopy
{
return self;
}


- (instancetype)init
{
self = [super init];
if(self)
{
// init your singleton here

}
return self;
}

@end

REFERENCE

http://www.cnblogs.com/andyque/archive/2011/08/03/2125728.html

Objective-C基础教程

https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmPractical.html

https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/EncapsulatingData/EncapsulatingData.html

http://www.jianshu.com/p/4a1d1921284b