iOS 问题整理07----内存管理

1.对于block,理解,mrc和arc下有什么区别,使用注意事项

  • mrc 下栈上的 block,在 arc 中并且符合一定条件的情况下会自动被拷贝到堆上
  • block 内部如果使用了 __block 修饰的局部变量,在 mrc 下 block 不会对这个变量产生强引用,在 arc 下 block 会对这个变量产生强引用。block 从栈上拷贝到堆上的时候,如果是在 mrc 下,那么 block 结构体中的 bref 结构部不会从栈上拷贝到堆上,所以 bref 不会持有变量;如果是在 arc 下,那么 bref 也会跟着一起拷贝到堆上,所以 bref 会强引用变量。
  • arc 下,解决循环引用可以使用 __weak,__unsafe__unretained,或者使用__block 并且在块内将变量置 nil,当代码块内的代码执行完的时候,block 就不会再持有局部变量了。如果是在 mrc 下,可以使用 __unsafe__unretained、__block 解决循环引用的问题。

2.使用CADisplayLink、NSTimer有什么注意点?

  • 当定时器添加到 runloop 中的时候,runloop 会持有定时器,定时器会持有 target,就会导致 target 不释放。
    • block,在内部使用 weakSelf
    • NSProxy,使用消息转发
  • NSTimer 与 CADisplayLink 依赖与 RunLoop,如果 RunLoop 的任务过于繁重,可能导致 NSTimer 不准时
    • 使用 GCD 的定时器

3. 介绍一下内存的几大区域

从低地址到高地址依次是:

  • 代码段
  • 数据段(常量段):字符串、全局变量和静态变量(未初始化的、已初始化的)
  • 堆区:自己 alloc、calloc、malloc 出来的对象
  • 栈区:函数的开销,比如局部变量。(栈区的地址是从高往低)
  • 内核区

4. 对mrc和arc的理解。ARC都帮我们做了什么。讲一下你对 iOS 内存管理的理解。ARC 的本质(阿里二面)。ARC 都帮我们做了什么?

  • iOS 是使用引用计数来管理 OC 对象内存的,当引用计数为 0 的时候对象的内存就会被释放。
  • 在 mrc 下我们需要手动地管理引用计数。使用 retain 让对象的引用计数加一,使用 release 让对象的引用计数减一。
  • 使用 arc 时,LLVM 编译器会自动在合适的时候往我们的代码里添加 release 或者 autorelease 和 retain 等,我们不需要再写 retain 或者 release了。运行时会在 dealloc 的时候把 weak 修饰的指针自动置 nil。

5. 引用计数是怎么存储的?

  • 在 64bit 中,引用计数可以直接存储在 isa 里面,也可能存储在 SideTable 类中
struct SideTable {
	sipinlock_t slock;
	RefcountMap refcnts; //存放着对象引用计数的散列表,以对象的地址为key
	weak_table_t weak_table; //散列表;key 是对象的地址,value 是所有弱引用了这个对象的指针的数组
}
复制代码

6. @property 的本质是什么?

  • 自动帮我们生成成员变量、setter 与 getter 方法的声明和实现

7. property 的常用修饰词有哪些?

  • 原子性:atomic/nonatomic
  • 读写权限:readwrite/readonly
  • 内存管理:strong、copy、assign、unsafe_unretained、weak
  • 方法名:getter=<name> 、setter=<name>

8. 如果属性完全不加修饰词入weak,atomic,系统会怎么处理

  • property 默认使用的是 atomic、readwrite、assign

9. weak 和 assign 的区别?

  • assign 可以修饰基本类型和对象,weak 只能修饰对象
  • 被 weak 修饰的指针会在对象被释放的时候自动置 nil

10. 对于strong,weak,atomic等等理解

  • atomic 是属性的原子性,给系统自动生成的 setter、getter 方法加锁
  • 被 strong 修饰的属性在指向对象的时候,对象的引用计数加一
  • 被 weak 修饰的属性在指向对象的时候,不会持有对象,对象销毁的时候属性自动置nil

11. 使用 atomic 一定是线程安全的吗?

  • 使用 atomic 就是给系统自动生成的 setter、getter 加锁,它可以保证数据的完整性,但是并不能保证数据是准确的。比如 A 线程的写操作结束后,B 线程又开始写,这时候 A 线程读操作就会等到 B 线程的写操作结束后再做,那么最后 A 线程的读操作读到的是 B 线程写进去的数据。
  • atomic 也不能保证整个对象是线程安全的。比如 A 线程在进行读操作之前,对象被 C 线程 release 了,那么就会崩溃。再比如,一个可变的数组在多个线程下使用 addObjectAtIndex:,这就是不安全的,这也和 setter 与 getter 没什么关系。

12. __block vs __weak

  • 使用 __block 的目的是在代码块中修改局部变量。使用 __block 后,变量会被包装成 bref 结构体被捕获进 Block 结构体,当 Blcok 拷贝到堆上的时候,在 arc 中 bref 也会被拷贝到堆上,在 mrc 中 bref 不会被拷贝到堆上,所以在 arc 中使用 __block 还要注意解决循环引用的问题,而在 mac 中使用 __block 不会引起循环引用的问题。
  • 使用 __weak 的目的是在 arc 中解决 Block 的循环引用的问题。Block 捕获局部变量时,如果变量是被 __weak 修饰的,捕获进 Block 结构体的时候,也会被 weak 修饰,当 Block 拷贝到堆上的时候,捕获进来的这个变量引用计数不会增加。

13. weak 属性需要在 dealloc 中置 nil 吗?

  • 不需要,weak 修饰的变量会在对象被释放后自动置 nil

14. IBOutlet 连出来的属兔属性为什么可以被设置成 weak?

  • xib 有一个数组持有着这个对象

15. 怎么使用 copy 关键字?

  • copy 的目的
    • 产生一个副本对象,跟源对象互不影响
    • 修改了源对象,不会影响副本对象
    • 修改了副本对象,不会影响源对象
  • 常用 copy 的对象: NSString、NSArray、NSDictionary
  • iOS 提供了两个拷贝方法:copy、mutableCopy
    • copy:产生不可变副本
    • mutableCopy:产生可变副本
NSString *str1 = @"123"; //这是一个在常量区的不可变字符串
NSString *str2 = [str1 copy];//还是这个字符串。不采用taggedPointer技术
NSString *str3 = [str1 mutableCopy];//把它拷贝成可变的字符串,并且是拷贝到堆上去了。
复制代码
NSMutableString *str1 = [NSMutableString stringWithFormat:@"123"];//在堆上的一个可变字符串
NSString *str2 = [str1 copy];//拷贝成不可变字符串。会采用taggedPointer技术,比较短的话就是在常量区的taggedPointer,长一点的话会变成在堆上的字符串。
NSString * str3 - [str1 mutableCopy];//拷贝成一个新的、可变的字符串。在堆上的,所以它会是不一样的。
复制代码

16. 深复制和浅复制指的是什么?对于深拷贝和浅拷贝的理解

  • 深拷贝
    • 内容拷贝,产生了新的对象
    • 比如拷贝堆上的对象
  • 浅拷贝
    • 指针拷贝,没用产生新的对象
    • 比如 copy 常量区的字符串。NSString *str = [@"123" copy];
  • mutableCopy 一定是深拷贝。 copy 不一定是浅拷贝。

17. 如何重写带 copy 关键字的 setter?

- (void)setKey:(id)key 
{
	if (key != _key) {
		//[_key release]; //MRC
		_key = [key copy];
	}
}
复制代码

18. 如何让自己的的类用copy修饰符?

  • 自定义的类要想使用 copy 方法,必须得先遵守 NSCopying 协议
  • 实现 - (id)copyWithZone:(NSZone *)zone 方法
- (id)copyWithZone:(NSZone *)zone 
{
    Person *person = [[Person allocWithZone:zone] init];
	person.age = self.age;
	person.weight = self.weight;
	return person;
}
复制代码

19. 这种写法会出现什么问题:@property(copy)NSMutableArray *array;?

  • 使用 self.array 对 _array 进行赋值之后,会 copy 一个不可变数组赋值给它,当调用 NSMutableArray 中的方法时,会报错。

20. @property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?

  • 因为它们有对应子类,根据里氏替换原则,父类指针可以指向子类对象,所以可以给他们赋值一个可变的对象,通过改变这个对象就改了属性的值。使用 copy 是为了保证属性指向的对象的值是不能被修改的。

21. Objective-C 中的 copy 方法(阿里)

  • 拷贝的目的是产生一个副本,它们修改的时候互不影响。
  • NSString、NSArray、NSDictionary 等这些类有对应的可变类型,所以它们的拷贝有 copy 和 mutableCopy
    • copy 一定产生一个不可变对象
    • mutableCopy 一定产生一个不可变对象
    • 不可变对象使用 copy ,copy 出来的时候也是一个不可变的,拷贝的目的是拷贝之后它们修改的时候互不影响,既然它们都不能修改,那就没有产生新对象的必要了。所以,只会拷贝一个新的指针指向这个不可变对象。这是浅拷贝。
    • 拷贝的时候产生了新的对象,这就是深拷贝
  • 自定义类要想使用 copy 方法,需要先遵守 NSCopying 协议,实现 copyWithZone:(NSZone *)zone 方法。

22. weak 的实现原理是什么?

  • 对象的弱引用关系会存入 SideTable 里面的弱引用散列表中,这个列表中的 key 是对象的地址,value 是所有弱引用了这个对象的的指针的数组。当对象调用 release 的时候,会判断引用计数是否为零,如果为零的话就会调用 dealloc 方法,然后就会通过对象的地址在 SideTable 的弱引用表中找到这个数组,遍历这个数组对数组里边的指针清空置 nil。

23. 谈谈对自动释放池的理解。autoreleasepool 的使用场景和原理。Autoreleasepool 什么时候释放,在什么场景下使用?(阿里)

  • AutoreleasePool 本质上是一个结构体,里面有 push 和 pop 函数,这两个函数时对 AutoreleasePoolPage 的封装,所以 AutoreleasePool 没有单独的结构,是由若干个 AutoreleasePoolPage 以双链表的形式组成的,自动释放机制的核心在于 AutoreleasePoolPage 这个类。
  • 每一个 AutoreleasePoolPage 对象占 4096 个字节。出了自己结构体所占的内存,都用来存放的是 autorelease 对象的地址。
  • 当创建 @autoreleasepool{} 时,会调用 push,会往里面存入一个哨兵对象。
  • 在 autoreleasepool{} 中创建对象的时候就会向 next 指向的地址中写入新创建的这个对象的地址。
  • 当 @autoreleasepool{} 结束的时候会调用 pop 的,这时从最新写入的地址开始,向前找,依次向它们指向的对象发送 release 消息,直到找到前一个哨兵对象。(可能跨了好几页)
  • 使用场景:当有大量临时变量的时候,避免内存峰值过高
NSArray *urls = <# An array of file URLs #>;
for (NSURL *url in urls) {
    @autoreleasepool {
        NSError *error;
        NSString *fileContents = [NSString stringWithContentsOfURL:url
                                        encoding:NSUTF8StringEncoding
                                         error:&error];
   	}
}
复制代码

24. 自动释放池在mrc和arc区别

25. 多层自动释放池嵌套的对象在哪一层释放

  • 最里面的一层先 release。因为它先出大括号,先调用 AutoreleasePool 的 pop 方法。

25. 方法里有局部对象,出了方法后会立即释放吗?

  • 在 arc 下,如果编译器帮我们添加的代码是在出方法前 [obj release],那么就是出了方法会立即释放;如果编译器帮我们添加的代码是在创建的时候添加 autorelease,那么就不是立即 release 的。它会在 runloop 休眠前进行 release。

26. 你在项目中是怎么优化内存的?

  • 使用 instruments 的 LEAKS 或者 第三方工具比如 LeakFinder 来检测内存泄漏,然后进行处理
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值