iOS底层系列之<49>--内存管理<5>copy、weak、autorelease

1、copy关键字

  • copy,不可变拷贝,产生不可变副本
  • mutableCopy,可变拷贝,产生可变副本
NSString *str1 = [NSString stringWithFormat:@"test"];
NSString *str2 = [str1 copy];
NSMutableString *str3 = [str1 mutableCopy];

指针指向情况:
在这里插入图片描述

2、深拷贝、浅拷贝

深拷贝:内容拷贝,有产生新的对象
浅拷贝:指针拷贝,没有产生新对象

 NSMutableString *str1 = [NSString stringWithFormat:@"test"];
 NSString *str2 = [str1 copy]; // 类型不同,产生新对象,深拷贝
NSMutableArray * arr1 = [[NSMutableArray alloc] initWithObjects:@"b",@"ab", nil];
        
NSArray *arr2 = [arr1 copy]; // 产生新类型的对象,深拷贝
        
NSMutableArray *arr3 = [arr1 mutableCopy]; // 深拷贝

总结:
不可变对象(NSString,NSArray,NSDictionary),调用copy,都是浅拷贝;
对象只要是调用mutableCopy,都是深拷贝;

3、使用

下面这句代码有什么问题?为什么?

@property (copy, nonatomic) NSMutableArray *array;

问题:使用copy修饰的,不应该是可变对象,因为底层生成的set方法会使用copy,返回的对象就是不可变对象了。
底层set方法如下:

- (void)setArray:(NSMutableArray *)array {
    if (_array != array) {
        [_array release];
        _array = [array copy];
    }
}

一般都用strong修饰:
@property (strong, nonatomic) NSMutableArray *array;

4、自定义对象的copy

1、遵守NSCopying协议

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Dog : NSObject<NSCopying>
@property (assign, nonatomic) int age;
@property (assign, nonatomic) double weight;
@end

NS_ASSUME_NONNULL_END

2、需要实现- (id)copyWithZone:(NSZone *)zone方法

#import "Dog.h"

@implementation Dog

- (id)copyWithZone:(NSZone *)zone {
    
    Dog *dog = [[Dog allocWithZone:zone] init];
    dog.age = self.age;
    dog.weight = self.weight;
    
    return dog;
}

- (NSString *)description {
    return [NSString stringWithFormat:@"age = %d weight = %f", self.age, self.weight];
}

@end

5、补充weak原理

weak指针实现原理
将弱引用对象存到一个哈希表里面,到时候当这个对象要销毁的时候,就从这个哈希表里面取出这个对象,将这个对象置为nil

ARC:
LLVM + runtime
1.LLVM自动给代码生成retain,release,autorelease操作;
2.弱引用的指针通过runtime进行操作

6、补充autorelease

这个关键字是在MRC下使用
自动释放池的主要底层数据结构是:__AtAutoreleasePool、AutoreleasePoolPage
调用了autorelease的对象最终都是通过AutoreleasePoolPage对象来管理的

1.每个AutoreleasePoolPage对象占用4096个字节内存,除了用来存放它内部的成员变量,剩下的空间用来存放autorelease对象地址
2.所有的AutoreleasePoolPage对象通过双向链表的形式连接在一起
在这里插入图片描述
每个autorelease内部都是先push,然后执行完再pop

extern void _objc_autoreleasePoolPrint(void); // 打印release情况

int main(int argc, const char * argv[]) {
    @autoreleasepool { // r1 = push()
        
        NSLog(@"111");
        @autoreleasepool { // r2 = push()
            Dog *d1 = [[[Dog alloc] init] autorelease];
            Dog *d2 = [[[Dog alloc] init] autorelease];
            
            NSLog(@"22222");
            _objc_autoreleasePoolPrint();
            
            @autoreleasepool { // r3 = push()
                Dog *d3 = [[[Dog alloc] init] autorelease];
                Dog *d4 = [[[Dog alloc] init] autorelease];
                
                NSLog(@"3333");
                _objc_autoreleasePoolPrint();
                
                @autoreleasepool { // r4 = push()
                    Dog *d5 = [[[Dog alloc] init] autorelease];
                    Dog *d6 = [[[Dog alloc] init] autorelease];
                    for (int i = 0; i < 1000; i ++) {
                        Dog *d6 = [[[Dog alloc] init] autorelease];
                    }
                    NSLog(@"444");
                    _objc_autoreleasePoolPrint();
                } // r4 = pop()
                NSLog(@"555");
                _objc_autoreleasePoolPrint();
            } // r3 = pop()
        } // r2 = pop()
       
        NSLog(@"22");
    }  // r1 = pop()
    return 0;
}

autorelease的调用时机:
所处的Runloop即将休眠的时候调用

1、ARC下,局部变量一执行完毕,就会释放,因为编译器会自动添加释放方法

- (void)viewDidLoad {
    [super viewDidLoad];
    Person *p = [[Person alloc] init];
   //  [p release] //这一行,编译器会自动加上 
}

2、MRC下,局部变量一执行完,不一定会马上释放,会在Runloop即将休眠的时候释放

- (void)viewDidLoad {
    [super viewDidLoad];
    Person *p = [[[Person alloc] init] autorelease];
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值