拷贝与内存管理

一、关于深拷贝和浅拷贝的总结

理解

  • 本质上我认为区别在于复制是是指针复制(浅拷贝)还是复制到新的地址上(深拷贝)

实际应用(yltest中有具体代码)

*  大体上会区分为对象和容器两个概念,对象的copy是浅拷贝,mutablecopy是深拷贝。
  - 容器包含对象的拷贝,无论是copy,还是mutablecopy都是浅拷贝,要想实现对象的深拷贝,必须自己提供拷贝方法
  - 非容器不可变对象:NSString
  
   ```

    1. 对于非容器不可变对象的copy为浅拷贝,mutableCopy为深拷贝
    2. 浅拷贝获得的对象地址和原对象地址一致, 返回的对象为不可变对象
      3. 深拷贝返回新的内存地址,返回对象为可变对象
    
   ```
   
  - 非容器可变对象: NSMutableString
  
  ```
   对于可变对象的的拷贝,无论是copy还是mutabelCopy,都为深拷贝,且拷贝后返回的对象也是可变的
  ```
  - 容器类不可变对象: NSArray

  ```
  1.容器类不可变对象 copy只是指针copy ,mutableCopy,容器内是地址重新copy是深拷贝
  2.但是容器内的对象无论是可变与不可变对象都是浅拷贝
     
  ```
  - 容器类可变对象: NSMutableArray

    ```
  1.容器类可变对象 copy和mutableCopy是深拷贝
  2.但是容器内的对象无论是可变与不可变对象都是浅拷贝
     
  ```
小结:copy: 对于可变对象为深拷贝,对于不可变对象为浅拷贝; mutableCopy:始终是深拷贝,所谓拷贝,本质上是持有指针(无论是新的地址还是原来的地址);此外对可变容器赋值时,如果没有使用copy进行赋值,者修改其中一个内容时,其他内容会被修改(思考方向可以从引用指针和引用指针计数上开始)。不可变容器则不受影响.

自定义类对象的深浅拷贝

 在OC中不是所有的类都支持拷贝,只有遵循才支持copy,只有遵循才支持mutableCopy。如果没有遵循,拷贝时会直接Crash。
 遵守协议以后,无论是copy还是mutableCopy,对于对象而言都是深拷贝;对对象内的属性重新赋值,不会改变其他对象的属性的值,因为赋值本质上来说是赋予新对象的属性指针
 
 ###关于属性得补充
 
 ```
 @property (nonatomic ,strong) NSArray *array;
 
    -(void)propertyTest {
NSArray *array = @[ @1, @2, @3, @4 ];
NSMutableArray *mutableArray = [[NSMutableArray alloc] initWithArray:array];
self.array = mutableArray;
NSLog(@"array :%@, mutableArray: %@,array object_p: %p,object_p: %p",self.array ,mutableArray,self.array ,mutableArray);
[mutableArray removeAllObjects];
NSLog(@"array :%@, mutableArray: %@,array object_p: %p,object_p: %p",self.array ,mutableArray,self.array ,mutableArray);

}

 ```
 
 结果
 
 ```
 2018-03-02 11:57:28.028222+0800 YLAudioFrequecy[1446:2312615] array :(
1,
2,
3,
4

), mutableArray: (
1,
2,
3,
4
),array object_p: 0x144d8d150,object_p: 0x144d8d150
2018-03-02 11:57:28.028532+0800 YLAudioFrequecy[1446:2312615] array :(
), mutableArray: (
),array object_p: 0x144d8d150,object_p: 0x144d8d150
```
如果没有使用[mutableArray copy](深拷贝);而直接使用self.array = mutableArray;因为实际上array是被赋值了mutable属性;
移除时值会被消除;

二、内存泄漏的一些想法

  总的来说,我认为内存泄漏的原因是由于内存在ARC环境下没有被系统自动释放(如循环引用)

记录如何调试内存泄漏的blog

内存之定时器

  我们都知道定时器使用完毕时需要将其停止并滞空,但**置空定时器**方法到底何时调用呢?在当前类的dealloc方法中吗?并不是,若将置空定时器方法调用在dealloc方法中会产生如下问题,**当前类销毁执行dealloc的前提是定时器需要停止并滞空**,**而定时器停止并滞空的时机在当前类调用dealloc方法时**,这样就造成了互相等待的场景,从而内存一直无法释放。因此需要注意**置空定时器**的调用时机从而避免内存无法释放,可以将**置空定时器**方法外漏,在外部调用即可。

内存之block

   Block的内存泄漏主要是由于循环引用;防止内存泄漏就是要防止对象之间引用的闭环出现;
   一般采用的方法是让其中一个对象持有另一个对象的弱引用,例如:
   
   ```
   __weak typeof(self) weakself = self; 
   ```

内存泄漏之delegate

   ```
   @property (nonatomic, weak) id<****delegate> delegate;
   
   ```
   比较值得注意的是swift中的协议要继承与class的,这时候可以用weak来修饰

特别之非OC对象

   CGImageRef类型变量非OC对象,其需要手动执行释放操作

CGImageRelease(ref),否则会造成大量的内存泄漏导致程序崩溃。其他的对
于CoreFoundation框架下的某些对象或变量需要手动释放、C语言代码中的
malloc等需要对应free等都需要注意。

地图之内存泄漏

   若项目中使用地图相关类,一定要检测内存情况,因为地图是比较耗费

App内存的,因此在根据文档实现某地图相关功能的同时,我们需要注意内存
的正确释放,大体需要注意的有需在使用完毕时将地图、代理等滞空为nil,注
意地图中标注(大头针)的复用,并且在使用完毕时清空标注数组等。

循环数量巨大时内存管理

   ```

   for (int i = 0; i < 100000; i++) {

    @autoreleasepool {

        NSString *string = @"Abc";

        string = [string lowercaseString];

        string = [string stringByAppendingString:@"xyz"];

        NSLog(@"%@", string);

    }

}

   ```

在循环中创建自己的autoReleasePool,及时释放占用内存大的临时变量,减少内存占用峰值。

三、Xcode中将图片放入assets.xcassets和直接拖入的区别

  • 在mainBundle里面Xcode会生成一个Assets.car文件,将我们放在Images.xcassets的图片打包在里面。这里会有压缩,但是直接拖入(相当于直接将图片放入了mainBundle里面)的不会有压缩,所以程序打包出来会大一点
  • 无论是通过imageNamed:来加载图片,还是直接在Storyboard的UIImageView里面设置图片,并且无论图片是jpg格式还是png格式,都不需要写后缀名;而拖入的图片:如果在Storyboard的UIImageView设置图片,那么需要明确地写上后缀名。(无论是.png还是.jpg都要写)在使用imageNamed:加载图片时,如果是.png格式,则不需要使用后缀名;如果是.jpg格式,则必须要写上后缀名。
  • 放在Images.xcassets的图片不能通过imagesWithContentsOfFile:来加载。(因为这个方法相当于是去mainBundle里面找图片,但是这些图片都被打包进了Assets.car文件)
  • 存放在.xcassets更方便与资源管理,例如更换图片时并需要修改名称,只需要简单的替换图片资源即可。
  • 无需为不同像素的图片分别明明,系统会自动排列并且自动选择相应的图片。

你可能感兴趣的:(拷贝与内存管理)