1. 程式人生 > >ARC 下 C++/OC 混編計數器的問題

ARC 下 C++/OC 混編計數器的問題

ARC 模式下,object-C 編寫的程式碼的記憶體管理都交給了自動引用計數器了,不用我們自己再去操心記憶體的管理,但使用 Core Foundation 框架或者是和 C++ 混編時,程式碼的記憶體還是需要我們自己去管理的,這時候就需要注意了!

Core Foundation 主要是由不透明型別(opaque type),即 C 結構體組成,語言層面並不支援繼承和多型,而 Core Foundation能夠製造出物件具有層次結構的假象是因為它的根“類” CFType 物件的指標 CFTypeRefvoid * 型別的。

Core Foundation 記憶體管理方式:

  • 如果建立或者複製物件,你就是所有者,比如以 create
    或者 copy 開頭的函式,那呼叫者就是返回物件的所有者。
  • 如果沒有建立或者複製物件,你不是所有者。這是如果不想讓物件被銷燬,就必須呼叫 CFRetain 來成為所有者。
  • 如果你是物件的所有者,就必須在用完物件後呼叫 CFRelease

C++object-C 混編時,C++ 程式碼的記憶體管理方式和 Core Foundation 類似,在使用完 C++ 物件後,必須先 delete 掉該物件,然後置為 NULL 。

先來看一段程式碼:

- (instancetype)init
    {
        self = [super init];
        if (self
) { _listener = new KGCorePlayerListener(self); //1 [self setListener:_listener]; } return self; }

init 函式裡面呼叫了 C++ 函式,並把 self 作為形參傳遞過去,執行完函式後發現 self 的計數器變為 2 了!很奇怪,斷點看了一下

retainCount

查看了 1 的實現,發現函式將傳過去的 self 給 capture 了。查閱了資料,發現了這個

SemanticsOfInit

然後就以為在 init 方法族裡面,將 self 作為引數傳遞給其他函式使用的都會導致 return a retained object

,後來我發現我錯了!

  • 錯誤1:並不是將 self 作為形參傳遞給其他類就會導致計數器 +1,除非接收類 capture 了 self 才會導致 retainCount +1;不 capture 的話是不會 +1 的。
KGCorePlayerListener::KGCorePlayerListener( id<KGCorePlayerListenerProtocol> manager)
: mManager( manager )
{
     //將傳進來的 self 賦值給成員變數 manager 了,計數器 +1 
}
  • 錯誤2:當把 self 作為引數傳遞時並不是只在 init 方法族裡面才會導致計數器 +1,其他方法也一樣會。

針對以上出現的問題,當你想銷燬該例項時(以 KGTingPlayerManager 為例),簡單的將 _tingPlayerManager = nil 是並不會導致呼叫到 dealloc 方法,置為 nil 後計數器變為 1 。

解決辦法的思路無非就是將 retainCount 降到 0

方法一:

先釋放掉之前被 capture 的 self ,最後再將 _tingPlayerManager 置為 nil ,最傳統也是最實用的方法,本來的寫法也應該是這樣的。

-(void)destroy {

[self setListener:nil];
if (_listener) {
    delete _listener;
    _listener = 0;
}
}

方法二:

利用 Clang 的編譯屬性 __attribute((ns_consumed)) 關於這個屬性的介紹看下面

ns_consumed

ConsumedParameters

所以在定義函式或者方法時,可以為該函式的引數指定 __attribute((ns_consumed)) 屬性,靜態分析器會在函式呼叫結束的時候傳送一個 relese 訊息,所以在返回給呼叫者的時候計數器並不會 + 1 。

有一點需要特別注意的是,它是有按照 passing an argument 還是 receiving an argument 來分別對計數器進行操作的。

修改程式碼如下:

KGCorePlayerListener::KGCorePlayerListener( id<KGCorePlayerListenerProtocol> __attribute((ns_consumed)) manager)
: mManager( manager )
{
}

然後再測試一下,發現 retainCount 並沒有變為 2 。
這裡寫圖片描述

When receiving such an argument, ARC releases the argument at the end of the function, subject to the usual optimizations for local values.

使用這個屬性還要考慮到 ARC 和非 ARC 下是否會有記憶體洩露的問題,Example

Important note when using Garbage Collection: Note that the analyzer essentially ignores this attribute when code is compiled to use Objective-C garbage collection. This is because the release message does nothing when using GC. If the underlying function/method uses something like CFRelease to decrement the reference count, consider using the cf_consumed attribute instead.

關於記憶體管理部分,Clang 定義了很多編譯屬性,如:

#define NS_RETURNS_RETAINED attribute((ns_returns_retained))

#define NS_RETURNS_NOT_RETAINED attribute((ns_returns_not_retained))

#define NS_RETURNS_INNER_POINTER attribute((objc_returns_inner_pointer))

更多的可以參照 NSObjCRuntime.h 標頭檔案的定義。鑑於平時編碼也很少需要用到 Clang 的編譯屬性,所以除非特殊情況,否則還是不要隨便使用 Clang 的編譯屬性為好。

參考連結:

拓展連結:

相關推薦

ARC C++/OC 計數器的問題

ARC 模式下,object-C 編寫的程式碼的記憶體管理都交給了自動引用計數器了,不用我們自己再去操心記憶體的管理,但使用 Core Foundation 框架或者是和 C++ 混編時,程式碼的記憶體還是需要我們自己去管理的,這時候就需要注意了! Core

iOS C++/OC

先說題外話,文章標題其實起的不好,在iOS的開發中,Apple建立的庫基本都是用Objective-C寫的,所以在這裡的C++指的其實是Objective-C++。首先,最最最要緊的事情,不是程式碼而是編譯器選項,在做混合編譯之前一定要把編譯器的Compile Sources As選項改為Objective

iOS 靜態類庫 打包 C,C++檔案及和OC

iOS 靜態類庫 編譯 C,C++ 我們都知道,OC 原生支援C, 在 建立的 OC類的 .m 裡面,可以直接編寫C的程式碼; 同樣 Xcode 也支援 OC ,C++的混編,此時,我們通常把OC建立的 .m 檔案,手動修改為 .mm 檔案以支援 oc c++的混編

iOS開發時OCC中,strcpy導致的記憶體溢位、野指標

在最近的專案開發中,由於需要使用C語言的演算法供給OC專案呼叫,所以研究了一下OC與C的混編及.a庫的相關生成。而在混編的過程中,C語言的演算法都能正常呼叫了,但是被一個問題困擾了很長一段時間,就是在

Fortran與C

動態鏈接 調用程序 運行 vc++ clu 出現一次 可執行 裏的 lock \(Fortran\) 作為用於科學計算的一種編譯型語言積累了大量數值計算的庫,但對於現代編程來說, \(Fortran\) 無 \(GUI\)庫 是其一大短板。本文就\(Fortran\) 與

Python和C|C++的(二):利用Cython進行

cde uil 有時 當前 class def 將在 python 混編 還能夠使用Cython來實現混編 1 下載Cython。用python setup.py install進行安裝 2 一個實例 ① 創建helloworld文件夾創建hellowor

Swift-OC

一.建立橋接標頭檔案: 1.首先需要建立一個空的.h檔案,命名規則為:專案名-Bridging-Header; 2.在build settings 中找到 swift compiler Code Generation, 找到 Objective-c bridger Header ,填寫上剛才建立

CPP檔案和C檔案和將sqlite3加入自己的c++工程

今天嘗試將使用sqlite3資料庫,直接使用sqlite3的原始碼,得到sqlite3.c和sqlite3.h。 我想將他們加入到我的cpp工程裡面 所以我新建了一個mysqlite3.cpp檔案,在裡面呼叫了sqlite3的函式。 下面來說明我遇到的問題及解決方法 一共有兩種編譯方

CSharp與C/CPP技術簡要說明

CSharp與C/CPP混編 (mixing coding with CSharp and C/CPP)混合程式設計技術,是一種在工程上廣泛存在,並且已經發展了很多年的“古老”技術,常見的混合程式設計,有Java/C的程式設計,也有今天要提到的CSharp/C的組合形式。說到底,我們使用混合程式設計技術,是因

iOS8開發~Swift(五)Swift與OC

一、概要 首先看《The Swift Programming Language》中提到“Swift’s compatibility with Objective-C lets you create a project that contains files written

iOS開發之swift與OC出現的坑,oc中不能對swift的代理進行呼叫,不能訪問swift中的代理,swift中的回撥方法

1. swift與oc混編譯具體怎麼實現,這兒我就不重複講出了,網上有大把的人講解。 2. 在swift與OC混編的編譯環境下, oc類不能訪問swift建立類中的代理? 解決方法如下: 在代理的頭部加上 @objc(代理名字),這樣就在外部就可以訪問了,如下圖。 然

MATLAB/C語言的第一步,在MATLAB R2017b中生成mex檔案

Matlab在科學計算方面的優越性使其成為科研人員的必備軟體之一,搭載了大量複雜工具箱,日益優雅的程式碼編輯器讓MATLAB R2017b徹底替代了之前的先輩版本。然而MATLAB畢竟是一個商業化的數學軟體,在大型演算法的編寫和執行上都存在諸多缺點。因此一些大神們更喜歡用C

ubuntuc++中base64解碼測試和圖片解碼測試

全棧工程師開發手冊 (作者:欒鵬) 字元陣列的base64編解碼 base64.h #include <string> std::string base64_encode(unsigned char const* , unsigned in

swift與OC(建立工程)

原創文章轉載請註明出處。 背景 Swift語言自推出以來蘋果官方就開始努力開始推這門新語言。就在15年WWDC上更是把Swift進行了開源,瞬間star就開始不斷飆升,截止今天(16年1月25)已經達到26059個star【原始碼請點選這裡】

ubuntu16.04MATLAB和C++(基於ROS,不含VS類別的編譯器)

如題,真的是一個很艱辛的過程,在windows下的話,在VS下的話,一切問題都不是問題。 但是,現在需要在ubuntu下的ROS中實現c++和MATLAB混編,就很雞肋。 ------------------------------------------------------------

OCC++ 導致的問題

object c cos alt idt tails blog fun 所有 src 最近項目中用到 std::function. 在導入頭文件的時候,發現問題總是報頭文件無法找到。但是我通過xcode 跳轉都能夠跳轉到對應的文件了 #include <strin

詳解swift和OC以及C語言的(不看後悔!)

前言:        Swift 語言出來後,可能新的專案直接使用swift來開發,但可能在過程中會遇到一些情況,某些已用OC寫好的類或封裝好的模組,不想再在swift 中再寫一次,或者有一些第三方使用OC寫的,沒有swift版本,怎麼辦?那就使用混編。這個在IOS8

Objective-C: ARC和Non-ARC的問題

假設有個framework是按照non-ARC編譯的,並且有個類方法建立並返回一個物件: +(TestMe *)fetch {     TestMe *obj = [[[TestMe alloc] init] autorelease];     return obj; }

資料結構--佇列-泛型OC&C++-泛型程式設計

在這篇文章裡, 您可以學習到: 資料結構簡介資料結構的邏輯結構和物理結構佇列OC和C++在Xcode中的混編泛型程式設計思想泛型程式設計實現迴圈佇列和連結串列佇列部落格中使用的圖片均來自網路 一.資料結構簡介 資料結構是計算機儲存、組織資料的方式。資料結構是指相互之

OCC++

先說題外話,文章標題其實起的不好,在iOS的開發中,Apple建立的庫基本都是用Objective-C寫的,所以在這裡的C++指的其實是Objective-C++。首先,最最最要緊的事情,不是程式碼而是編譯器選項,在做混合編譯之前一定要把編譯器的Compile Sources As選項改為Objective