iOS秋招复习——底层基础

本文深入探讨了iOS中Objective-C的内存管理机制,包括MRC与ARC的区别,以及如何通过强弱引用避免循环引用问题。文章详细解释了引用计数的概念,并提供了代码示例,帮助读者理解不同情况下对象的生命周期。

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

iOS底层基础

1.iOS的内存管理

OC采用引用计数器对内存进行管理,当一个对象的引用计数(retainCount)为0,则被释放。
Objective C的引用计数理解起来很容易,当一个对象被持有的时候计数加一,不再被持有的时候引用计数减一,当引用计数为零的时候,说明这个对象已经无用了,则将其释放。
OC的内存机制可以简单概括为:谁持有(retain)谁释放(release)。retain引用计数+1,release反之。

采用引用计数的方式来管理内存,分MRC(手动引用计数)和ARC(自动引用计数)两种,其中ARC是基于MRC的.
在MRC中, 每当我们使用alloc,new,retain,copy使对象的引用计数增加,都要对应的使用release或者autorelease来使对象的引用计数减少,增加和减少的次数要相等.

内存管理的思考方式:

  1. 自己生成的对象,自己持有
  2. 非自己生成的对象,自己也能持有
  3. 不再需要自己持有对象时释放
  4. 非自己持有的对象无法释放

(1) 自己生成的对象,自己持有

在iOS内存管理中有四个关键字,alloc、new、copy、mutableCopy,自身使用这些关键字产生对象,那么自身就持有了对象

// 使用了alloc分配了内存,obj指向了对象,该对象本身引用计数为1,不需要retain
 id obj = [[NSObject alloc] init]; 
// 使用了new分配了内存,objc指向了对象,该对象本身引用计数为1,不需要retain
 id obj = [NSObject new];

(2) 非自己生成的对象,自己也能持有

// NSMutableArray通过类方法array产生了对象(并没有使用alloc、new、copy、mutableCopt来产生对象),因此该对象不属于obj自身产生的
 // 因此,需要使用retain方法让对象计数器+1,从而obj可以持有该对象(尽管该对象不是他产生的) 
id obj = [NSMutableArray array];
 [obj retain];

(3) 不再需要自己持有对象时释放

id obj = [NSMutableArray array];  
[obj retain];
// 当obj不在需要持有的对象,那么,obj应该发送release消息
[obj release];

(4) 无法释放非自己持有的对象

// 1. 释放一个已经释放的对象 
id obj = [[NSObject alloc] init];
 // 释放对象 
[obj release]; 
// 释放了对象还进行释放
[obj release]; // 应用程序崩溃
//2. 释放一个不属于自己的对象 
id obj1 = [obj object]; //取得的对象存在,但自己不持有对象
// obj1没有进行retain操作而进行release操作,使得obj持有对象释放,造成了野指针错误 
[obj1 release];

在Objective C中,有三种类型是ARC适用的:

1.block
2.objective 对象,id, Class, NSError*等
3.由attribute((NSObject))标记的类型。

像double* ,CFStringRef等不是ARC适用的,仍然需要手动管理内存。
以CF开头的(Core Foundation)的对象往往需要手动管理内存。

2.iOS的强弱引用

2.1强弱引用的区别

简单说,强引用持有对象,而弱引用不持有对象。
具体看代码?

	__strong NSObject *obj1=[[NSObject alloc] init];
    
    __strong NSObject *obj2=obj1;
    
    NSLog(@"%@,%@",obj1,obj2);
    
    obj1=nil;
    
    NSLog(@"%@,%@",obj1,obj2);

    //输出 :
    //<NSObject: 0x7fef53708b80>,<NSObject: 0x7fef53708b80>
    //(null),<NSObject: 0x7fef53708b80>

将obj2声明改为__weak

    __strong NSObject *obj1=[[NSObject alloc] init];
    
    __weak NSObject *obj2=obj1;
    
    NSLog(@"%@,%@",obj1,obj2);
    
    obj1=nil;
    
    NSLog(@"%@,%@",obj1,obj2);

    //输出 :
    //<NSObject: 0x7fef53708b80>,<NSObject: 0x7fef53708b80>
    //(null),(null)

因为strong会使retaincount+1,所以第一部分代码retaincount是2,obj1置为nil之后retaincount-1,所以retaincount还剩1,因此不影响obj2;
而weak不会retaincount+1,所以整个代码段的retaincount一直是1,因此obj置为nil之后retaincount-1,所以retaincount为0,内存也跟着释放了,因此obj2也为空。
(这里提下weak和MRC时代的assign的区别,两者都可以用于弱引用,但是内存释放后使用weak会将对象置nil,而assign不会,会造成野指针,现在assign一般只用在基础类型))

2.2 iOS循环强引用

平时一般都是用strong,在造成循环引用的时候才会使用weak
当两个不同的对象各有一个强引用指向对方时就造成循环引用,会导致对象无法释放,例如我们常用的delegate,见图:
循环强引用
这就是我们在申明delegate时都是用weak的原因(MRC的话是用assign)

@property (nonatomic, weak) id<Delegate>delegate

block的使用也会照成循环引用,比如当一个对象持有block,而该block又持有该对象时,类似下面的伪代码会照成循环引用:

[self block:^{
        
    self.value=1;
}];

应该改为

__weak typeof(self) weakself=self;
[self block:^{
        
    weakself.value=1;
}];

注意:只有该对象持有block,而block里的代码块又持有该对象时才需要用到weak。:只有该对象持有block,而block里的代码块又持有该对象时才需要用到weak。

[UIView animateWithDuration:0.2 animations:^{
       
    self.value=1;
}];

这种就不会造成循环引用。
luhh

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值