首页 > 代码库 > Objective-c的内存管理

Objective-c的内存管理

IOS设备的内存很有限,所以在OC中内存管理至关重要,虽然现在有ARC机制(自动管理内存),但是我们还是需要了解和清楚OC的内存管理。非OC对象是不需要进行内存管理的,因为非OC对象一般放在栈里面(栈内存会被系统自动回收),OC对象是放在堆里面的。

1.oc中对象创建

?
1
2
3
Person *p = [Person new];
//或者
Person *p1 = [[Person alloc] init];

 这行代码做了三件事:

①分配内存空间,存储对象  ②初始化成员变量 ③返回对象的指针地址

2.oc是如何对内存管理的呢

①每个对象在完成创建的同时,内部会自动创建一个引用计数器,这个计数器是系统回收对象的唯一依据,当它的引用计数器retainCount =0的时候,系统就会毫不犹豫的回收掉这个对象。

②[对象 retain]   retainCount+1,返回self

③[对象 release] retainCount - 1

④@autoreleasepool{} 会对自动释放池中的对象都做一次release操作。

⑤ARC机制的实现细节是编译器会自动在适当的地方插入适当的retain,release,autorelease语句,让程序员更关注App业务。

?需要注意的是: release并不代表销毁\回收对象, 仅仅是计数器-1

3.内存管理的原则(配对原则)

只要出现了new,alloc,retain,copy就一定配对出现一个release,autorelease

4.写一个setter方法用于完成@property (nonatomic,retain)NSString *name

?
1
2
3
4
5
6
7
8
9
10
11
12
13
- (void)setName:(NSString *)name:
{
    if (_name != name)
    {
        //relese旧值
        [_name release];//[nil release];
        
        //retain新值
        _name  = [name retain];
        //或者这么写
        //_name  = [name copy];
    }
}

 5.dealloc方法

那么我们怎么知道一个对象被销毁了呢,这时我们要重写dealloc方法

?
1
2
3
4
5
- (void)dealloc
{
    NSLog("对象被销毁了"); 
    [super dealloc];
}
?一旦重写了dealloc方法, 就必须调用[super dealloc],并且放在最后面调用
 6.野指针/空指针
这里介绍几个概念
①僵尸对象—已经被销毁的对象
②野指针—指向僵尸对象的指针。给野指针发消息会报EXC_BAD_ACCESS错误
③空指针—没有指向存储空间的指针(里面存储的是nil,也就是0).给空指针发消息是没有任何反应的。
7.关闭ARC功能
现在xcode新建的项目都是ARC,那么要想手动调用retain、release等方法 , 就必须关闭ARC功能
8.ARC机制中还有内存泄漏吗?
有人可能会有疑惑,ARC机制这么强悍,那么使用了ARC机制后,程序还会不会有内存泄漏呢?
答案是有的,ARC机制也是会用到内存管理的。下面我举个例子:
?
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
// 定义一个强引用的属性
@property (nonatomic, strong) UIViewController *vc;
//懒加载
- (UIViewController *)vc
{
    if (_vc == nil) {
     _vc = [[UIViewController alloc] init];
        _vc.view.backgroundColor = [UIColor redColor];
        UIButton *back = [UIButton buttonWithType:UIButtonTypeContactAdd];
        [_vc.view addSubview:back];
        back.center = CGPointMake(100, 300);
        [back addTarget:self action:@selector(btnClick) forControlEvents:UIControlEventTouchUpInside];
    }
    return _vc;
}
- (void)btnClick
{
    [self dismissViewControllerAnimated:YES completion:nil];
}
#pragma mark - Table view data source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return 10;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    //1.定义标识
    static NSString *ID = @"测试";
    //2.通过标识去缓存池中寻找可循环利用的cell
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
    //3.如果没有可循环利用的cell
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
    }
    NSString *text = [NSString stringWithFormat:@"%d--%d",indexPath.row,indexPath.row];
    cell.textLabel.text = text;
    return cell;
}
 
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [self presentViewController:self.vc animated:YES completion:nil];
}

初看代码,可能会觉得没什么问题.

我们在第六行和第十五行打两个断点来运行程序,观察一下_vc的内存地址。

当你第二次点击cell的时候,执行单步运行,会发现,直接从19行跳到23行了,说明了这是_vc不是空,是有值的。这说明了_vc并没有被销毁当我们点击+号按钮的时候,跟我们的预期可能有点不同,我们大概想的是,每次它都给我创建一个新的_vc。要是还是有童鞋不明白我在讲什么,说只要能modal出来_vc不就行了,那我就说具体一点,假如我们要点击每行cell ,modal出来的控制器不同呢,问题就在这。
那么为什么会出现这种情况呢?
这其实就是 内存泄漏!!!
self.vc是我们强引用的,在懒加载的方法中,对它进行初始化,所以它的retainCount +1,然后当
执行[self presentViewController:self.vc animated:YES completion:nil]这个方法后,它的retainCount又加了+1,然后执行[self dismissViewControllerAnimated:YES completion:nil]这个方法后,它的retainCount-1,最后它的retainCount是1.这就是强引用的作用。
那么我们如何解决呢?—安全释放
安全释放指当我们如果你确定当前作用于中的对象已经不会再被使用了,通常我们会把不在使用的指针变量赋值为nil
?
1
2
3
4
//这个方法我们就要再完善下<br>- (void)btnClick
{
    [self dismissViewControllerAnimated:YES completion:nil];<br>   self.vc = nil;
}
安全释放也是防止野指针操作的一种办法。
希望对大家有帮助。