美文网首页读书笔记iOS Developer程序员
kkbox-ios-dev笔记(三) - 内存管理/代理

kkbox-ios-dev笔记(三) - 内存管理/代理

作者: 百草纪 | 来源:发表于2017-02-26 15:00 被阅读31次

    内存管理(一)

    • 内存泄漏:该释放的对象, 没有被释放(已经不再使用的对象, 没有被释放)
    • 无效内存引用:内存已经被释放了,我们还强行调用。会报EXC_BAD_ACCESS错误。

    基本原则

    • 如果是initnewcopy这些方法产生出来的对象,用完就该调用release.
    • 如果是其他一般方法产生出来的对象,就会回调auto-release对象、或是singleton对象(稍晚会解释什么是singleton),就不需要另外调用release.

    而调用retain与release的时机包括:

    • 如果是在一般代码中用了某个对象,用完就要release或是auto-release
    • 如果是要将某个Objective-C对象,变成是另外一个对象的成员变量,就要将对象retain起来。但是delegate对象不该retain
    • 在一个对象被释放的时候,要同时释放自己的成员变量,也就是要在实现delloc的时候,释放自己的成员变量。
    • 要将某个对象设为另外一个对象的成员变量,需要写一组getter/setter

    Getter/SetterProperty语法

    • 基本数据类型

    @interface MyClass:NSObject
    {
    int number;
    }

    • (int)number;
    • (void)setNumber:(int)inNumber;
      @end
    > * 实现部分
    
    > ```swift
    - (int)number
    

    {
    return number;
    }

    • (void)setNumber:(int)inNumber
      {
      number = inNumber;
      }
    
    >* 如果是 OC 对象,我们则是要将原本成员变量已经指向的内存释放,然后将传入的对象`retain`起来。该写法并不安全
    
    >```swift
    - (id)myVar {
    
    return myVar;
    

    }

    • (void)setMyVar:(id)inMyVar
      {
      [myVar release];
      myVar = [inMyVar retain];
      }
    * 假如今天我们在开发中用到很多个线程,而在不同的线程中同时会用到`myVar`,在某某个线程中调用了`[myVar release]`之后,到`myVar`指定到`inMyVar`的位置之间,假如另外一个线程刚好用到了`myVar`,这时候`myVar`刚好指到了一个已経被释放的内存,这就造成了
    

    EXC_BAD_ACCESS错误.

    • 更安全的写法:加锁,让程序在调用setMyVar:的时候,不让其他线程调用myVar;另外一种简单的方法如下:
    • (void)setMyVar:(id)inMyVar
      {
      id tmp = myVar;
      myVar = [inMyVar retain];
      [tmp release];
      }
    
    * 上面的例子,用`property`语法可以写成:
    
    ```swift
    @interface MyClass:NSObject
    

    {
    id myVar;
    int number;
    }
    @property (retain, nonatomic) id myVar;
    @property (assign, nonatomic) int number;
    @end
    @implementation MyClass

    • (void)dealloc
      {
      [myVar release];
      [super dealloc];
      }
      @end
    **`myVar = nil`与`self.myVar = nil`的区别?**
    
    * 前者只是单纯的将`myVar`的指针指向`nil`,但是并没有释放原本所指向的内存位置,所以会造成内存泄漏,但后者却等同于调用`[self setMyVar:nil]`,会先释放`myVar`原本指向的位置,然后将`myVar`设成`nil`。
    

    内存管理(二)

    • ARC 是通过静态分析,在编译时决定应该要在代码的哪个地方加入retainrelease

    循环retain

    • 错误情况:
      1. 把代理设置为strong
      1. 某对象的某property是一个block,但是在这个block里面把对象自己给retain了一份。
      1. 使用timer的时候,到了dealloc的时候才停止timer
    • 假如我们在有一控制器,我们希望这个控制器可以定时更新,
      那么,我可能会使用+scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:方法建立timer对象,指定定时执行某个selector.要特别注意,在建立这个timer的时候,我指定给timertarget,也曾被timer retain一份,因此,我们想要在控制器在dealloc的时候,才停止timer就曾有问题:因为控制器已经被timer retain起来了,所以只要timer还在执行,控制器就不
      可能走到dealloc的地方。

    对象桥接

    • 什么是对象桥接:Foundation库里面的每一个对象,都有对应的 C 实现,这 C 的实现叫作Core Foundation,当我们在使用Core Foundation里面的 C 形态时,像CFStringCFArray等,我们可以让这些形态变成可以接受 ARC 的管理.这种让 C 形态也可以被当做 OC 对象,接受 ARC 管理的方式,就叫对象桥接。
    • 有三个关键字:__bridge__bridge_retained__bridge_transfer
    • __bridge:会把Core Foundation的 C 资料形态转换成 OC 对象,但是不会多做retainrelease
    • __bridge_retained:会把Core Foundation的 C 资料形态转换成 OC 对象,并且会做一次retain,但是之后必须由我们手动调用CFRelease,释放内存。
    • __bridge_transfer:会把Core Foundation转换成 OC 对象,并且会让 ARC 主动添加retainrelease
    • 但不一定每个Core Foundation型态都有办法转换成OC对象。详见苹果文档详细说明

    内存管理(三)

    • 略 (关于控制器的内存管理)

    代理

    Delegate属性应该要用Weak,而非strong

    • 原因是:需要设置代理对象的这个对象,往往是其代理对象的成员变量,A 的实例是 A 对象,是 B 的成员变量,可能已经被 B retain了一份,如果 A 又 retain了一次 B,就会出现循环 retain的问题 -- 已经被别人retain,又把别人retain一次。

    命名规范

    • 至少传入一个参数,就是代理调用者本身;往往以传入的类名开头,让我们可以辨别这是哪各类的代理方法。以代理UITableViewDelegate为例
    • - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

    我们曾经犯过的低级错误

    • 源代码
    @class MyClass;
    

    @protocol MyClassDelegate <NSObject>

    • (void)myClassWillBegin:(MyClass *)myClasss;
    • (void)myClassDidBegin:(MyClass *)myClasss;
    • (void)myClassWillStop:(MyClass *)myClasss;
    • (void)myClassDidStop:(MyClass *)myClasss;
      @end
      @interface MyClass : NSObject
      {
      id <MyClassDelegate> delegate;
      }
    • (void)begin;
    • (void)stop;
      @property (assign, nonatomic) id <MyClassDelegate> delegate;
      @end
      @implementation MyClass
    • (void)begin
      {
      [delegate myClassWillBegin:self];
      // Do something
      [delegate myClassDidBegin:self];
      }
    • (void)stop
      {
      [delegate myClassWillStop:self];
      // Do something
      [delegate myClassDidStop:self];
      }
      @synthesize delegate;
      @end
    * 问题:在`myClassWillBegin:`里面想要做一些检查,如果在某些条件下,这件事情不该跑起来,而应该停止,所以在`myClassWillBegin:`里面调用了`stop`。但这么做,并不会让这件事情结束,因为`begin`这个方法在对代理调用完`myClassWillBegin:`之后,程序还是会继续走下去,所以还是把`begin`整个做完了。
    
    * 优化后:
    
    ```swift
    @class MyClass;
    ####Delegate属性应该要用Weak,而非strong
    >* **原因是:**需要设置代理对象的这个对象,往往是其代理对象的成员变量,A 的实例是 A 对象,是 B 的成员变量,可能已经被 B `retain`了一份,如果 A 又 `retain`了一次 B,就会出现循环 `retain`的问题 -- 已经被别人`retain`,又把别人`retain`一次。
    >
    >####命名规范
    >* 至少传入一个参数,就是代理调用者本身;往往以传入的类名开头,让我们可以辨别这是哪各类的代理方法。以代理`UITableViewDelegate`为例
    >  * `- (void)tableView:(UITableView *)tableView
            didSelectRowAtIndexPath:(NSIndexPath *)indexPath`
    >
    >####我们曾经犯过的低级错误
    >>* 源代码
    >>
    >>```swift
    >>@class MyClass;
    @protocol MyClassDelegate <NSObject>
    - (void)myClassWillBegin:(MyClass *)myClasss;
    - (void)myClassDidBegin:(MyClass *)myClasss;
    - (void)myClassWillStop:(MyClass *)myClasss;
    - (void)myClassDidStop:(MyClass *)myClasss;
    @end
    @interface MyClass : NSObject
    {
        id <MyClassDelegate> delegate;
    }
    - (void)begin;
    - (void)stop;
    @property (assign, nonatomic) id <MyClassDelegate> delegate;
    @end
    @implementation MyClass
    - (void)begin
    {
        [delegate myClassWillBegin:self];
        // Do something
        [delegate myClassDidBegin:self];
    }
    - (void)stop
    {
        [delegate myClassWillStop:self];
        // Do something
        [delegate myClassDidStop:self];
    }
    @synthesize delegate;
    @end
    >>```
    >>* 问题:在`myClassWillBegin:`里面想要做一些检查,如果在某些条件下,这件事情不该跑起来,而应该停止,所以在`myClassWillBegin:`里面调用了`stop`。但这么做,并不会让这件事情结束,因为`begin`这个方法在对代理调用完`myClassWillBegin:`之后,程序还是会继续走下去,所以还是把`begin`整个做完了。
    >>
    >>* 优化后:
    >>
    >>```swift
    >>@class MyClass;
    @protocol MyClassDelegate <NSObject>
    
    • (BOOL)myClassShouldBegin:(MyClass *)myClasss;
    • (void)myClassDidBegin:(MyClass *)myClasss;
    • (BOOL)myClassShouldStop:(MyClass *)myClasss;
    • (void)myClassDidStop:(MyClass *)myClasss;
      @end
      @interface MyClass : NSObject
      {
      id <MyClassDelegate> delegate;
      }
    • (void)begin;
    • (void)stop;
      @property (assign, nonatomic) id <MyClassDelegate> delegate;
      @end
      @implementation MyClass
    • (void)begin
      {
      if (![delegate myClassShouldBegin:self]) {
      return;
      }
      // Do something
      [delegate myClassDidBegin:self];
      }
    • (void)stop
      {
      if (![delegate myClassShouldStop:self]) {
      return;
      }
      // Do something
      [delegate myClassDidStop:self];
      }
      @synthesize delegate;
      @end

    相关文章

      网友评论

        本文标题:kkbox-ios-dev笔记(三) - 内存管理/代理

        本文链接:https://www.haomeiwen.com/subject/byiowttx.html