iOS開發入門 ☞ OC語言·筆記五

来源:http://www.cnblogs.com/bossren/archive/2017/02/22/6428254.html
-Advertisement-
Play Games

MRC — 手動管理記憶體 1.1 記憶體引用平衡原則 1) 如果使用alloc,new開頭,或者是copy(複製一個對象)來創建一個對象,意味著你擁有這個對象的所有權。這個對象的引用計數器初始值為1(也有可能>1)。 2) 如果你擁有這個對象的所有權,在不使用此對象時,就有責任向對象發送release ...


MRC 手動管理記憶體

1.1 記憶體引用平衡原則

1) 如果使用alloc,new開頭,或者是copy(複製一個對象)來創建一個對象,意味著你擁有這個對象的所有權。這個對象的引用計數器初始值為1(也有可能>1)

2) 如果你擁有這個對象的所有權,在不使用此對象時,就有責任向對象發送release消息。(誰創建了對象,誰就有責任release這個對象)

3) 如果並不擁有一個對象的所有權,而想要使用這個對象,為了防止你在使用此對象期間,對象被別人釋放掉,需要向對象發送retain消息,以保持對象。此時可以認為,你也擁有了這個對象所有權。

4) 當你使用完retain過的對象後,有責任release一下這個對象。

(誰retain了一個對象,誰就有責任release這個對象)

       配對出現:(+1、-1 ==>平衡) 

我們創建的對象不用了,就release;我們retain的對象不用了,就release

    記憶體管理的原則就是有加就有減。也就是說, 一次allocnew對應一次release, 一次retain對應一次release。

1.2 自動釋放池(autoreleasepool)

通過自動釋放池來管理對象,只需要一個自動釋放池,可以管理很多對象,當自動釋放池結束的時候,會自動向池中的每個對象都發送release消息。

1) 如果一個對象創建後,不能馬上釋放它,但又不得不盡到釋放對象的責任,此時可以將對象放入自動釋放池,延遲對象的釋放時機。比如絕大部分工廠方法都是如此。工廠方法中的對象是方法中創建的,按理來說應該由工廠方法內部釋放,但工廠方法的功能決定了這個對象不能馬上釋放,此時應該將對象放入自動釋放池。

2) 當自動釋放池結束時,會向池中的所有對象發送release消息,如果此時,池中的對象的引用計數器是1,那麼,對象會被釋放掉。

3) 如何開始和結束一個自動釋放池呢?

//自動釋放池,用於回收對象的存儲空間。

@autoreleasepool{ //開始創建一個自動釋放池

  ……  ……

} //結束自動釋放池銷毀了, 給自動釋放池中所有的對象發送一條release消息

      還有一種性能低下,被淘汰的自動釋放池創建方式(瞭解)

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];//開始一個自動釋放池

   ……  ……

[pool drain];//結束一個自動釋放池

4) 無論一個對象是否在自動釋放池,只要這個對象是由別人創建的,你要使用此對象,就得retain,用完此對象,就得release,即使對象在自動釋放池,也依然如此。

5) 實際開發中,合理使用自動釋放池來避免記憶體使用出現峰值。

如果App出現的記憶體使用的峰值,此時才考慮是否是由於大量使用工廠方法造成的,是否需要使用自動釋放池解決問題。要合理使用自動釋放池,大量使用會消耗系統資源。

6) autorelease方法:在自動釋放池中使用,目的是將對象添加到自動釋放池中。自動釋放池銷毀時會對池中對象作release操作。(延遲release)

@autoreleasepool {

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

        

}

  • autorelease方法與release方法的區別:這兩個方法都能把對象的引用計數器減1。
    • release是一個精確的減1,對對象的操作只能在release之前進行,如果是在之後,就會出現野指針錯誤;
    • autorelease是一個不精確的引用計數器減1,當給對象發送autorelease消息時,對象就會被放到自動釋放池中,自動釋放池銷毀時會給池中的所有對象發送release消息,使得所有對象的計數器減1,所以本質上autorelease還是會調用release。
  • 常見錯誤:

// 銷毀自動釋放池的時候 要對person再執行release操作的話 會報野指針錯誤

@autoreleasepool {

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

[person release];

// 對象執行兩次autorelease意味著自動釋放池銷毀的時候 對象會執行兩次release操作 會報野指針錯誤

@autoreleasepool {

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

1.3 setter方法的記憶體管理(應用場景:兩個類是聚合關係)

當一個方法傳入一個對象後,如果需要將這個對象用實例變數等手段保存起來持續使用時,需要做以下事:

1) 先將此對象的引用計數器加1(retain)

2) 再將原來實例變數指向的對象的引用計數器減1(release)

3) 最後將傳入的對象地址保存到實例變數中

4) retain的對象通常需要在dealloc方法中release.

 

只要一個對象想使用房間,就需要對這個房間的引用計數器+1

只要一個對象不想使用房間,就需要對這個房間的引用技術器-1

      

經常會被問到的問題:

1) 前面3個順序可否顛倒?

能不能先release原來的對象,再賦值,最後retain新對象? 一般可以,但不建議

       

2) dealloc方法中,可不可以使用self.屬性 = nil;的方式釋放屬性所指向的對象? ok

可以的,self.屬性 相當於調用上面的setter方法(該方法中有向對象發release

    Demo:setter方法的記憶體管理

     

    

1.4 property修飾符

1) nonatomic 非原子性操作 (安全性低,但效率高。iOS實際開發中99%用這個,以確保性能)

atomic 原子性操作 (安全性高,但很耗費資源)

註意,預設是原子性的(atomic),所以在定義屬性時一定要寫上nonatomic

2) assign, retain, copy

    assign  是預設值,僅做賦值。不會解決記憶體問題(即不會retain,也不會release)。在MRC中可用於對象類型和非對象類型。

    retain 只能用於對象類型的屬性,會解決記憶體問題(生成的setter方法會自動加入retain,release等記憶體操作代碼)

    copy 一些特殊對象類型,如果不希望和別人共用一個對象,用copy會自動創建一個新的對象。

      有些屬性在傳入後,需要拷貝一份,以防止傳入的對象內容被修改後,影響到我的對象。

(並不是所有的對象都能拷貝,只有遵守<NSCopying>協議,實現了協議中CopyWithZone方法的這種對象才擁有拷貝的功能)

3) readonly/readwrite

readwrite 是預設的,編譯器生成gettersetter方法

readonly 只讀,編譯器只生成getter方法

  屬性訪問器的類型:

  

註:聲明屬性預設情況下,並沒有解決記憶體問題

當使用 @property(retain)引用數據類型,幫我們解決了setter使用中的記憶體問題,但dealloc中的release操作,我們自己來做。

1.5  MRC中的迴圈引用(兩個對象互相引用(即互相包含)時,要一強一弱!)

  如果A對象要擁有B對象, 而B對象又要擁有A對象, 此時會形成迴圈retain

  如何解決這個問題:讓其中一方不要做retain操作即可!

Demo:

   

ARC 自動管理記憶體

1.1 概念

Automatic Reference Counting  自動引用計數

基於MRC, 在MRC下,對象的引用計數是由程式員負責的。在ARC下,對象的引用計數工作由編譯器來完成。

1.2 ARC的工作原理

在ARC下,堆記憶體空間的管理依然使用引用計數器的方式。只是ARC下的引用計數器的加減工作不再由程式員來做,而是由編譯器來做。

編譯器在編譯程式期間,會在程式恰當的位置自動加入對對象的retain,release和autorelease的操作。

註意:ARC是 編譯期語法或編譯器特性(即可以理解為是Xcode的一個功能),而不是運行時特性。

1.3 怎麼用

  程式員不要對對象的引用計數器進行操作,編譯器會幫我們做:

    1)  在ARC下,不要在程式中調用retain, release, autorelease方法。

    2)  在ARC下,不要重寫retain, release, autorelease方法。

    3)  在ARC下,不要在dealloc方法中調用父類的dealloc方法。實際上,ARC下,dealloc方法基本沒有用了。

      總之,一切與記憶體操作相關的東西都由ARC自動完成。

1.4 ARC的判斷原則:(即系統怎麼判斷對象是否要釋放)

  • 自動管理記憶體的判斷原則:只要還有一個強指針變數指向對象,對象就會保持在記憶體中。
  • 強指針(強引用):
    • 預設情況下所有的指針變數都是強指針
    • __strong修飾的指針
    • 例如:

TRPerson *p1 = [[TRPerson alloc]init];

__strong TRPerson *p2 = [[TRPerson alloc]init];

 

  • 弱指針(弱引用)
    • __weak修飾的指針
    • 例如:

__weak TRPerson *p = [[TRPerson alloc]init];

 

  • 舉例:

//ARC的判斷準則:只要沒有強指針指向對象,對象就會釋放

TRPerson *p1 = [[TRPerson alloc]init];

__strong TRPerson *p2 = [[TRPerson alloc]init];

__weak TRPerson *p3 = p2;

p2 = nil;//p2改變了指向,此時就沒有強指針指向對象,對象就會釋放

}//出了大括弧,局部變數p1就釋放,此時就沒有強指針指向對象,對象就會釋放

        單個對象的記憶體管理:如果一個對象不再使用了,就把指向對象的強指針置為nil,對象就會釋放。

  • 註意:
    • 當使用ARC的時候,暫時忘記“引用計數器”,因為判斷標準變了。
    • 在實際開發中,千萬不要使用一個弱指針來保存一個剛剛創建的對象。

{

//p是弱指針,對象會被立即釋放

__weak TRPerson *p = [[TRPerson alloc]init];//剛創建就被釋放!

}

 

1.5 ARC中多個對象的記憶體管理:

  • ARCMRC一樣,想擁有某個對象必須用強指針保存對象,但是不需要在dealloc方法中release

@class TRDog;

@interface TRPerson : NSObject

 

//MRC下寫法

@property (nonatomic, retain) TRDog *dog;

//ARC下寫法

@property (nonatomic, strong) TRDog *dog;

 

@end

MRC

A對象想使用B對象, 需要對B對象進行一次retain

A對象不用B對象了, 需要對B對象進行一次release

即,property的時候進行retain, dealloc的時候進行release

 

ARC

A對象想使用B對象, 那麼就需要用一個強指針指向B對象(即用strong,表示強引用)

// ARC中保存一個對象用strong, 相當於MRC中的retain

@property (nonatomic, strong) Dog *dog;

A對象不用B對象了, 什麼都不需要做, 編譯器會自動幫我們做

 

1.6 ARC下迴圈引用問題

  迴圈引用:指兩個對象相互強引用了對方,即retain了對方,從而導致誰也釋放不了誰的記憶體泄露問題。

  • ARC和MRC一樣,如果A擁有B,B也擁有A,那麼必須一方使用弱指針
  • 兩個強指針互相引用,兩個空間就會永不釋放!所以必須要一強一弱。

@class TRDog;

@interface TRPerson : NSObject

//MRC寫法

//@property (nonatomic, retain) TRDog *dog;

//ARC寫法

@property (nonatomic, strong) TRDog *dog;

@end

 

@interface TRDog : NSObject

//錯誤寫法,迴圈引用會導致記憶體泄露

//@property(nonatomic, strong)TRPerson *owner;

 

//正確寫法,當如果保存的是對象類型建議使用weak

//@property(nonatomic, assign)TRPerson *owner;

@property (nonatomic, weak) TRPerson *owner;

@end

  • ARC下迴圈引用的案例:

    

1.7 如果在ARC下定義屬性的記憶體特質(attribute)

  • 在MRC下, 與記憶體相關的屬性特質有:assign, retain, copy
  • 在ARC下, 與記憶體相關的屬性特質有:

(1)strong  強引用 

  類似於retain,引用時候會引用計數+1。

strong 案例:

@property (nonatomic, strong) NSString *str1;

@property (nonatomic, strong) NSString *str2;

self.str1 = @"Hello World";

self.str2 = self.str1;

self.str1 = nil;

NSLog(@"str2 = %@", self.str2);

結果是:str2 = Hello World

(2)weak   弱引用

  • 類似於assign,不會改變引用計數,只做簡單的賦值。
  • weakassign的區別:
    • assign 僅做賦值(預設值),不會解決記憶體問題。在MRC中可用於對象類型和非對象類型。
    • weak只用於ARC下的對象類型(即帶*號的指針類型)。weak比assign更完全,會自動把野指針置空。
    • assign一般在ARC下用於基本數據類型(即不帶*號的類型,包括id類型)
    • weak 的特點:
      • 聲明為weak的指針,指針指向的地址一旦被釋放,這些指針都將被賦值為nil。這樣的好處能有效的防止野指針。
      • 特定情況下,如果記憶體的釋放會出現問題,經常使用weak來解決,比如,最常見的問題是"記憶體迴圈引用"

weak 案例:

@property (nonatomic, strong) NSString *str1;

@property (nonatomic, weak) NSString *str2;

self.str1 = @"Hello World";

self.str2 = self.str1;

self.str1 = nil;

NSLog(@"str2 = %@", self.str2);

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 代理設計模式 Demo 一、讓別人幫我做事 —— 基本版 二、讓別人幫我做事 —— 引入協議 三、讓別人幫我做事 —— 規範完整版 ...
  • 由OpenDigg 出品的安卓開源項目周報第九期來啦。我們的安卓開源周報集合了OpenDigg一周來新收錄的優質的安卓開源項目,方便安卓開發人員便捷的找到自己需要的項目工具。 ...
  • 前言: 實現非同步下載apk文件 並 安裝。(進度條對話框顯示下載進度的展現方式) 涉及技術點: 1、ProgressDialog 進度條對話框 用於顯示下載進度 2、AsyncTask 非同步任務的使用 耗時操作不能再主線程中進行 安卓開發_淺談AsyncTask 3、File 文件相關操作 將文件的 ...
  • 工具 常見的反編譯工具我已經放到開源中國了,大家可以自行下載。 http://git.oschina.net/zyj1609/AndroidReverseProject 博客 這裡引用郭神的大作: Android安全攻防戰,反編譯與混淆技術完全解析(上) Android安全攻防戰,反編譯與混淆技術完 ...
  • block(代碼塊) 1.1 什麼是block block是iOS中一種比較特殊的數據類型。(可以定義變數、做形參或返回值) block是蘋果官方特別推薦使用的數據類型,應用場景比較廣泛 動畫 多線程 集合遍歷 網路請求回調 動畫 多線程 集合遍歷 網路請求回調 在多線程和網路也使用得相當頻繁,一般 ...
  • 分類 Category 分類只能給類擴充方法,不能添加屬性! Category有很多種翻譯:分類 / 類別 / 類目(一般叫分類) 分類是一個類的補充,是類的一部分。 Category的作用: 可以在不修改原來類的基礎上,為這個類擴充一些方法。(使用繼承也可以,在子類中擴充父類沒有的方法) 一個龐大 ...
  • 面向對象的三大特性: 封裝, 繼承, 多態 1. 封裝 1.1 基本概念 將零散的東西組合起來。 廣義上封裝指:將代碼封裝成函數,將實例變數和方法封裝成類,將類封裝成框架.... 面向對象中的封裝指:封裝屬性和方法放在一個對象中,只給外界公開訪問的介面,而且把具體實現隱藏起來。 1.2 封裝的好處 ...
  • 關於 Android 手機橫豎屏切換時 Activity 的生命周期問題,網上有很多相似的文章,大多數都是說明在豎屏切換橫屏時 Activity 會重啟一次,而在橫屏切換豎屏時 Activity 會重啟兩次。 我本身不太理解這樣設計的意義,並且覺得新版本會解決這個問題,所以做了一項測試,測試環境為 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...