zoukankan      html  css  js  c++  java
  • Objective

    在前面, 我们知道了OC的内存管理以及@property的参数, 其实还有一个叫做循环引用, 其实所谓的循环引用很简单, 就是两个类中, 你包含我, 我包含你, 这个就叫循环引用, 比如下面这个例子:


    #import <Foundation/Foundation.h>
    #import "Car.h"
    
    @interface Person : NSObject
    
    @property (nonatomic, retain) Car *car;
    
    @end
    

    #import <Foundation/Foundation.h>
    #import "Person.h"
    
    @interface Car : NSObject
    
    @property (nonatomic, retain)Person *person;
    
    @end
    






    这样子就是循环引用, 但这个例子里的循环引用是有性能问题的, 需要改进, 我们来看看改进之后的例子:

    #import <Foundation/Foundation.h>
    
    @class Car;
    
    @interface Person : NSObject
    
    @property (nonatomic, retain) Car *car;
    
    @end
    

    #import <Foundation/Foundation.h>
    
    @class Person;
    
    @interface Car : NSObject
    
    @property (nonatomic, retain)Person *person;
    
    @end
    

    为什么这么做呢? 我们来回想一下, #import的作用就是引用头文件, 如果在我们平常的简单编程里面, 直接这么做是没问题的, 但如果在几十个几百个类中循环引用, 一旦某个被循环引用的类修改了一点点, 那么全部引用它的类就要重新再引用, 就会造成性能问题, 所以我们这里使用@class.



    @class的作用仅仅只是告诉编译器XXX是一个类, 我们可以使用, 不像#import那样直接引用, 但@class也并不是完美的, 如果我们使用手动引用计数, 需要在.m文件里release对象的话, 那么就需要使用到#import来辅助一下, 比如:

    #import "Person.h"
    #import "Car.h"
    
    @implementation Person
    
    - (void)dealloc
    {
        [_car release];
        [super dealloc];
    }
    
    @end
    

    #import "Car.h"
    #import "Person.h"
    
    @implementation Car
    
    - (void)dealloc
    {
        [_person release];
        [super dealloc];
    }
    
    @end


    那么@class#import的区别在哪里? 我们来看看区别:

    #improt 方法会包含被引用类的所有信息, 包括被引用类的变量和方法;
    @class 方式只是告诉编译器在X.h文件中X *x只是类的声明, 具体这个类里面有什么信息, 这里不需要知道, 等实现文件中真正要用到时, 才会真正去查看X类中信息




    总结
    1. 如果有上百个头文件都#import了同一个文件, 或者这些文件一次被#import, 那么一旦最开始的文件稍有改动, 后面引用到这个文件的所有类都需要重新编译一边, 这样的效率可想而知, 而相对来讲, 使用@class方式就不会出现这种问题了.


    2. 在.m实现文件中, 如果需要引用到被引用类的实例变量或者方法时, 还需要使用#import方法引入被引用类.





    再回头看看我们的例子, 我们会发现, 该例子会有循环retain, 什么是循环retain呢? 其实循环retain一般只会出现在循环引用上, 当两个类相互引用并且使用retain的时候, 那么谁也不会被释放, 这样子就会造成内存泄漏, 比如:

    #import <Foundation/Foundation.h>
    #import "Person.h"
    #import "Car.h"
    
    int main(int argc, const char * argv[])
    {
        Person *p = [[Person alloc] init];
        Car *c = [[Car alloc] init];
        
        p.car = c;
        c.person = p;
        
        NSLog(@"p = %ld", [p retainCount]);
        NSLog(@"c = %ld", [c retainCount]);
        
        [c release];
        [p release];
        
        return 0;
    }

    打印出来的结果:

    2015-01-27 12:23:23.885 07.循环引用[6274:661345] p = 2
    2015-01-27 12:23:23.886 07.循环引用[6274:661345] c = 2
    



    有人会想到, 简单啦, 直接写两次release就可以啦, 没错是可以这么做, 但这么做不符合apple的内存管理原则, 我们之前说过内存管理的原则是一次alloc一次release, 所以该做法不可取.


    解决办法其实也很简单, 只要把其中一个类改成assign就可以解决了, 比如:

    #import <Foundation/Foundation.h>
    
    @class Car;
    
    @interface Person : NSObject
    
    @property (nonatomic, assign) Car *car;
    
    @end

    #import <Foundation/Foundation.h>
    
    @class Person;
    
    @interface Car : NSObject
    
    @property (nonatomic, retain)Person *person;
    
    @end



    在看看main()函数打印出来的结果:

    2015-01-27 12:27:19.038 07.循环引用[6316:663154] p = 2
    2015-01-27 12:27:19.039 07.循环引用[6316:663154] c = 1
    2015-01-27 12:27:19.039 07.循环引用[6316:663154] Car被释放了
    2015-01-27 12:27:19.039 07.循环引用[6316:663154] Person被释放了
    



    两端互相引用造成内存泄漏的解决方案: 把其中一端的retain改成assign.





    好了, 这次我们就讲到这里, 下次我们继续~~

  • 相关阅读:
    锐捷 ac ap 连接 记录
    锐捷 Fat/Fit Ap切换
    qualcomm lk gpio
    git patch 使用
    qualcomm batch 烧录脚本
    Cisco无线控制器配置Radius
    hostapd作为radius服务器
    Android N: jack server failed
    win10: This file can't be opened
    2. 特征工程之特征选择
  • 原文地址:https://www.cnblogs.com/iOSCain/p/4282823.html
Copyright © 2011-2022 走看看