你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> iOS---自動釋放池

iOS---自動釋放池

編輯:IOS開發綜合

自動釋放池

  • 在 mrc 的代碼中,沒有 weak,只有 assign
  • assign 修飾符號,對對象不做任何操作,只是簡單的記錄地址
  • weak 是 ARC 專有的,如果對象沒有其他任何對象做強引用,會被立即釋放!
  • weak 的效率非常差!
  • assign 會記錄住地址,對象釋放後,地址仍然保留,在 MRC 開發中,野指針錯誤非常頻繁
  • weak 安全性很好!一旦沒有強引用,自動將地址設置為 nil,OC中可以向 nil 發送任何消息都不會抱錯!

  • autorelease 作用就是延遲釋放,給對象添加延遲釋放的標記

    自動釋放對象 atuorelease

    自動釋放對象

    • 所有 "autorelease" 的對象,在出了作用域之後,會被自動添加到"最近創建的"自動釋放池中!
    • 在自動釋放池被銷毀或者耗盡的時候,會向池中所有對象發送 release 消息,釋放池中對象
    • 自動釋放池,在 ARC & MRC 程序中,同樣有效!

      MRC 下系統變量的測試

      聲明變量

      @property (nonatomic, assign) UIButton *btn;
      
      

      向控制器View中添加按鈕:

      // alloc / init - 應該出了作用域才會被釋放
          //
          self.btn = [[UIButton alloc] init];
          self.btn.frame = CGRectMake(40, 40, 100, 100);
          self.btn.backgroundColor = [UIColor orangeColor];
      
          [self.view addSubview:self.btn];
          NSLog(@"%@", self.view.subviews);
      
          // 類方法創建的 - 是 ‘autorelease’ 的
          // 出了作用域,會被添加到自動釋放池中
          // 自動釋放池被銷毀前,才釋放對象!
          self.btn = [UIButton buttonWithType:UIButtonTypeContactAdd];
          [self.view addSubview:self.btn];
          NSLog(@"%@", self.view.subviews);
      
          // ARC 上述演練不好使!以 weak 為主!
      
      

      運行結果:

      2015-03-25 10:31:20.055 09-自動釋放池[27279:718327] (
          "<_UILayoutGuide: 0x7fc5d0d346b0; frame = (0 0; 0 0); hidden = YES; layer = >",
          "<_UILayoutGuide: 0x7fc5d0e77950; frame = (0 0; 0 0); hidden = YES; layer = >",
          ">"
      )
      2015-03-25 10:31:20.063 09-自動釋放池[27279:718327] (
          "<_UILayoutGuide: 0x7fc5d0d346b0; frame = (0 0; 0 0); hidden = YES; layer = >",
          "<_UILayoutGuide: 0x7fc5d0e77950; frame = (0 0; 0 0); hidden = YES; layer = >",
          ">",
          ">"
      )
      
      

      可以看到,無論是alloc還是buttonWithType: 都是可以添加到控制器的View上的,也就意味著,創建出來的button並沒有立刻銷毀

      • assign 聲明的變量,在alloc後,對象並不會立刻釋放,除了作用域才會銷毀,即使是對象銷毀後,assign記錄的地址並不會清除,這就很容易造成野指針錯誤
      • 利用buttonWithType: 創建出來的,也不會立刻釋放,系統類方法創建出來的對象,是默認autorealease的,出了作用域,會自定添加到自動釋放池中,自動釋放池銷毀前,才會釋放對象,同樣assign聲明的變量會持有該對象的地址,一直不肯放~

        MRC下自定義對象的自動釋放

        Person.h

        
        @interface Person : NSObject
        
        + (instancetype)person;
        
        @end
        
        

        Person.m

        • 注意:這裡沒有添加autorelease
          @implementation Person
          
          + (instancetype)person {
              // autorelease 作用就是延遲釋放
              // 給對象添加延遲釋放的標記
              Person *p = [[Person alloc] init] ;
          
              return p;
          }
          
          - (void)dealloc {
              NSLog(@"我去了");
              [super dealloc];
          }
          
          @end
          
          

          聲明Persion變量:

          @property (nonatomic, assign) Person *p;
          
          

          測試Demo:

          - (void)viewDidLoad {
              [super viewDidLoad];
              // Do any additional setup after loading the view, typically from a nib.
              self.p = [Person person];
          
              NSLog(@"%@", self.p);
          }
          
          

          運行結果:

          2015-03-25 10:44:16.652 10-ARC自動釋放[27546:726446] 
          
          

          小結:

          在MRC下,如果提供的類方法沒有autorelease,那麼在調用的時候,用assign變量接收,仍然會打印出對象的地址,當然對象並沒有銷毀(dealloc沒有輸出)。

          將類方法帶上 autorelease

          Person *p = [[[Person alloc] init] autorelease];
          
          

          打印結果:

          2015-03-25 10:56:04.224 10-ARC自動釋放[27690:732059] 
          2015-03-25 10:56:04.238 10-ARC自動釋放[27690:732059] 我去了
          
          

          可以看到對象被釋放了,說明autorelease給對象添加了延遲釋放的標記,讓對象在自動是釋放池銷毀,或者出了離它最近的自動釋放池作用域的時候,自動釋放。

          ARC中的自動釋放

          變量的聲明:

          @property (nonatomic, weak) Person *p;
          
          @property (nonatomic, weak) UIButton *button;
          
          

          測試Demo:

          // ARC 中,如果給 weak 做 alloc / init 的設置數值,Xcode 會提示!
              self.p = [[Person alloc] init];
          
              // 對於 Xcode 編譯器而言,只要是類方法,就不會提示!
              // 如果使用 MRC 開發,所有 返回 instancetype 的類方法,返回的對象都是 autorelease 的!
              // 因為沒有強引用,會被立即釋放
              self.p = [Person person];
              // ARC中之所以為 null,是因為 weak,跟autorelease沒有關系!
              NSLog(@"%@", self.p);
          
              // 之所以會這樣是因為蘋果框架的底層是用 MRC 寫的,buttonWithType返回的對象是帶有 autorelease
              self.button = [UIButton buttonWithType:UIButtonTypeContactAdd];
              NSLog(@"%@", self.button);
          
          

          打印結果:

          2015-03-25 11:00:58.756 10-ARC自動釋放[27778:735123] (null)
          2015-03-25 11:00:58.769 10-ARC自動釋放[27778:735123] >
          
          

          小結

          • 如果普通對象的alloc,且對象是weak的,Xcode會自動提示警告,告訴你這個對象,一創建就會被銷毀啦
          • 但是如果你提供了一個類方法,快速生成。即使內部寫的是alloc,Xcode也不會警告你,但是你不能改變一創建就被銷毀的事實。
          • 蘋果自己提供的類方法創建的button,我們用weak接收,卻沒有銷毀,為啥呢,因為蘋果框架的底層是用MRC寫的,buttoWithType:返回的對象都是帶autorelease的,意味著會延遲銷毀,僅僅是被添加到自動釋放池而已,等待著出自動釋放池作用域或者自動釋放池銷毀。而不會立刻銷毀。

            一道經典面試題

            問題:以下代碼是否有問題?如果有,如何解決?

            for (long i = 0; i < largeNumber; ++i) {
                    @autoreleasepool {
                        NSString *str = [NSString stringWithFormat:@"hello - %ld", i];
                        str = [str uppercaseString];
                        str = [str stringByAppendingString:@" - world"];
                    }
                }
            
            

            答案:如果 largeNumber 非常大,會創建太多自動釋放的對象,有可能會把自動釋放池"撐滿" 提示:經常一次用戶交互,遠遠不止一個 for,在 for 的前後,會有很多的代碼,但是這個 for 會占用大量的自動釋放池空間

            解決方案

            引入自動釋放池,網絡上有兩種解決方案!

             1> 外面加自動釋放池(快):能夠保證for循環結束後,內部產生的自動釋放對象,都會被銷毀
                需要等到 for 結束後,才會釋放內存
             2> 裡面加自動釋放池(慢):能夠每一次 for 都釋放產生的自動釋放對象!
            
             提問:那種方式效率高!速度差不多!大多數測試內部比外部外!
             如果面試中問道:要說裡面的略快,“親測”!!!
            
             提示:在日常工作中!有的時候,真的會出現代碼內存飙升的情況!要知道解決辦法!
            
            

            設置largeNumner的數值:

            long largeNumber = 5 * 1000 * 1000;
            
            

            測試Demo:

            
            
            NSLog(@"內部 start");
                CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
                for (long i = 0; i < largeNumber; ++i) {
                    @autoreleasepool {
                        NSString *str = [NSString stringWithFormat:@"hello - %ld", i];
                        str = [str uppercaseString];
                        str = [str stringByAppendingString:@" - world"];
                    }
                }
                NSLog(@"end - %f", CFAbsoluteTimeGetCurrent() - start);
            
                [self answer1];
            
            

            answer1方法:

             NSLog(@"外面 start");
                CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
                @autoreleasepool {
                    for (long i = 0; i < largeNumber; ++i) {
            
                        NSString *str = [NSString stringWithFormat:@"hello - %ld", i];
                        str = [str uppercaseString];
                        str = [str stringByAppendingString:@" - world"];
                    }
                }
                NSLog(@"end - %f", CFAbsoluteTimeGetCurrent() - start);
            
            

            測試結果:

            2015-03-25 11:18:50.663 12-自動釋放池的面試題[28015:743360] 內部 start
            2015-03-25 11:19:03.996 12-自動釋放池的面試題[28015:743360] end - 13.332371
            2015-03-25 11:19:03.996 12-自動釋放池的面試題[28015:743360] 外面 start
            2015-03-25 11:19:18.227 12-自動釋放池的面試題[28015:743360] end - 14.230752
            
            

            由測試結果可以知道,對於500w次循環,內部、外部添加autorelease,速度其實差不多,內部略微快一點,但這是取決於編譯器。 但是,在外部添加autorelease會占用極大的內存(注意,這裡跟不加autorelease不一樣,不加autorelease開辟的內存是不會釋放的)。 開發中,通常在內部添加。

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved