IOS解決循環(huán)引用

雖然目前IOS都普遍使用了ARC開發(fā)荡短,但是還是有一些情況下是必然存在循環(huán)引用的来吩,為了更好的說明循環(huán)引用。先以MRC的代碼來描述一些內(nèi)存關(guān)系了讨。

@interface ViewController ()

@end

@implementation ViewController

  • (void)open{
    //av 引用計數(shù) 1
    AViewController *av = [[AViewController alloc] init];
    // presentViewController的時候 AViewController引用計數(shù) 2
    [self presentViewController:av animated:YES completion:^{
    }];
    // 這里釋放一次 AViewController 引用計數(shù) 1
    [av release];
    }

  • (void)addButton{
    UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
    button.backgroundColor = [UIColor redColor];
    button.frame = CGRectMake(100, 100, 100, 100);
    [button addTarget:self action:@selector(open) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:button];
    }

  • (void)viewDidLoad {
    [super viewDidLoad];
    [self addButton];
    }

@end

這里我們看到 AViewController *av 的引用計數(shù)是1, 以下是 AViewController 的代碼

@implementation AViewController

  • (void)back{
    // AViewController 引用計數(shù)-1 (正常情況的話會形成 0)
    [self dismissViewControllerAnimated:YES completion:^{
    }];
    }

  • (void)viewDidLoad {
    [super viewDidLoad];
    }
    @end

正常邏輯下 當(dāng)我們 調(diào)用 back 的時候 這個時候 AViewController 會因?yàn)?dismissViewControllerAnimated 使得引用計數(shù)變成0内颗,然后完成正常的內(nèi)存釋放钧排。

這個時候我們?nèi)胍粋€ BClass 先看 BClass的定義
@protocol BClassDelegate <NSObject>

  • (void)finished;
    @end
    @interface BClass : NSObject
    @property (nonatomic, retain) id delegate;
  • (void)startAnimation;
    @end

注意 delegate 是 retain 關(guān)鍵點(diǎn)來了。這種設(shè)計擺明了跟其他 委托模式不一樣(參考UITableView 的 delegate 是assign[因?yàn)槟壳笆荕RC 所以不是weak])均澳。

這個時候 在 AViewController 使用 BClass卖氨。
@interface AViewController ()
@property (nonatomic, retain) BClass *animation;
@end

  • (void)userBClass{
    //tempAnimation 引用計數(shù) 1
    _animation = [[BClass alloc] init];
    //關(guān)鍵點(diǎn)(這里 AViewController 引用計數(shù)變成了 2)
    _animation.delegate = self;
    }

  • (void)viewDidLoad {
    [super viewDidLoad];
    [self userBClass];
    }

為了降低復(fù)雜度 我們先只考慮AViewController 的內(nèi)存釋放,先暫時放下 animation的內(nèi)存釋放問題负懦。
這里重點(diǎn)情況是 _animation.delegate = self; 這里會使得AViewController 引用計數(shù)變成2筒捺。
看下 BClass 源碼

@implementation BClass

  • (void)dealloc{
    NSLog(@"%s", func);
    [_delegate release];
    _delegate = nil;
    [super dealloc];
    }
  • (void)startAnimation{
    }
    // 這里傳進(jìn)來的 adelegate 是 AViewController
  • (void)setDelegate:(id)adelegate{
    [_delegate release];
    [adelegate retain]; //關(guān)鍵的一部使得這個引用計數(shù) + 1 AViewController(引用計數(shù)變成2)
    _delegate = adelegate;
    }
    @end

關(guān)鍵點(diǎn)是因?yàn)?delegate 設(shè)計成 retain類型,所以 _animation.delegate = self; 這句代碼使得 AViewController 變成了2,
這個時候 dismissViewControllerAnimated 引用計數(shù)減1, 但是 AViewController 引用計數(shù)沒有變成0纸厉,所以AViewController內(nèi)存沒有釋放系吭,這就造成內(nèi)存泄漏。到這里給出完整BClass的代碼

@interface AViewController ()
@property (nonatomic, retain) BClass *animation;
@end

@implementation AViewController

  • (void)dealloc
    {
    [_animation release];
    _animation = nil;
    [super dealloc];
    }

  • (void)back{
    // AViewController 引用計數(shù)-1 (正常情況的話會形成 0)
    [self dismissViewControllerAnimated:YES completion:^{
    }];
    }

  • (void)userBClass{
    //tempAnimation 引用計數(shù) 1
    _animation = [[BClass alloc] init];
    //關(guān)鍵點(diǎn)(這里 AViewController 引用計數(shù)變成了 2)
    _animation.delegate = self;
    }

這里因?yàn)?back的時候 我們沒有辦法使得AViewController引用計數(shù)變成0颗品,所以沒有調(diào)用AViewController的dealloc肯尺,所以無法
觸發(fā) [_animation release]; 進(jìn)而使得 BClass也沒有被釋放內(nèi)存。這里就是循環(huán)引用了躯枢。
解決方案可以這樣考慮则吟,只要在調(diào)用back之前我們能夠使得 AViewController的引用計數(shù)由2變成1就可以了。
這個時候改寫back

  • (void)back{
    // 先使得AViewController 引用計數(shù)-1
    [_animation.delegate release];
    // AViewController 引用計數(shù)-1 (正常情況的話會形成 0)
    [self dismissViewControllerAnimated:YES completion:^{
    }];
    }
    這樣就可以在調(diào)用back的時候 我們的AViewController引用計數(shù)減了2次,這種寫法非常丑陋锄蹂,雖然能夠解決問題氓仲,但是不夠方便。
    參考網(wǎng)絡(luò)上有一種通過Proxy來處理這種循環(huán)引用的 先看下TestProxy的定義
    @interface TestProxy : NSProxy
    @property (nonatomic, assign) id target;
    @end

@implementation TestProxy

  • (void)dealloc{
    [super dealloc];
    }

  • (NSMethodSignature*)methodSignatureForSelector:(SEL)selector
    {
    NSMethodSignature *signature = [_target methodSignatureForSelector:selector];
    return signature;
    }

  • (void)forwardInvocation:(NSInvocation *)invocation{
    SEL sel = invocation.selector;
    if (_target) {
    if ([_target respondsToSelector:sel]) {
    [invocation invokeWithTarget:_target];
    }
    }
    }

@end

這里可以先忽略 methodSignatureForSelector 和 forwardInvocation 先重點(diǎn)放在內(nèi)存泄漏上得糜。
引入TestProxy后敬扛,改寫AViewController里面的userBClass和dealloc,back

  • (void)dealloc
    {
    [_animation release];
    [_proxy release];
    [super dealloc];
    }

  • (void)back{
    [self dismissViewControllerAnimated:YES completion:^{
    }];
    }

  • (void)userBClass{
    //_animation 引用計數(shù)1
    _animation = [[BClass alloc] init];
    // _proxy 引用計數(shù)1
    _proxy = [TestProxy alloc];
    // _proxy 引用計數(shù)2
    _animation.delegate = _proxy;
    }

重點(diǎn)是 _animation.delegate = _proxy 這里并沒有引起AViewController 引用計數(shù)的改變朝抖,所以 back的時候 AViewController能正常釋放內(nèi)存啥箭。所以 back觸發(fā)的時候 會進(jìn)入 dealloc 這個時候 [_animation release]; 會使得_animation引用計數(shù)變成了0。
到這里我們能夠正常釋放了 AViewController和BClass了治宣。剩下只有能解決NSProxy正常釋放那么就能夠解決所有的內(nèi)存泄漏問題了急侥。 目前 proxy引用計數(shù)是2砌滞,參考代碼可以知道 AViewController 的 dealloc 有一次 [_proxy release], BClass里面有一個
[_delegate release], BClass里面的delegate就是_proxy 所以 TestProxy的引用計數(shù)變成0,最終 AViewController, BClass 和 TestProxy 出現(xiàn)的3個類都成功完成內(nèi)存釋放坏怪。

對比原來非常粗暴的back函數(shù)里面調(diào)用release來解決內(nèi)存問題贝润,另一種通過引入 TestProxy 把釋放內(nèi)存的時機(jī)放到了 AViewController 的 dealloc。一般情況建議大家這樣引用TestProxy, 因?yàn)檫@樣寫法不需要考慮內(nèi)存釋放的時機(jī)的陕悬。同理NSTimer這種也可以引入Proxy,然后在 dealloc 調(diào)用 invalidate即可按傅。

總結(jié):循環(huán)引用計數(shù)的關(guān)鍵點(diǎn)在于 setDelegate方法捉超, aaa.delegate = self; aaa.delegate = nil 。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末唯绍,一起剝皮案震驚了整個濱河市拼岳,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌况芒,老刑警劉巖惜纸,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異绝骚,居然都是意外死亡耐版,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進(jìn)店門压汪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來粪牲,“玉大人,你說我怎么就攤上這事止剖∠傺簦” “怎么了?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵穿香,是天一觀的道長亭引。 經(jīng)常有香客問我,道長皮获,這世上最難降的妖魔是什么焙蚓? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮洒宝,結(jié)果婚禮上主届,老公的妹妹穿的比我還像新娘。我一直安慰自己待德,他們只是感情好君丁,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著将宪,像睡著了一般绘闷。 火紅的嫁衣襯著肌膚如雪橡庞。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天印蔗,我揣著相機(jī)與錄音扒最,去河邊找鬼。 笑死华嘹,一個胖子當(dāng)著我的面吹牛吧趣,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播耙厚,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼强挫,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了薛躬?” 一聲冷哼從身側(cè)響起俯渤,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎型宝,沒想到半個月后八匠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡趴酣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年梨树,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片岖寞。...
    茶點(diǎn)故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡劝萤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出慎璧,到底是詐尸還是另有隱情床嫌,我是刑警寧澤,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布胸私,位于F島的核電站厌处,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏岁疼。R本人自食惡果不足惜阔涉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望捷绒。 院中可真熱鬧瑰排,春花似錦、人聲如沸暖侨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽字逗。三九已至京郑,卻和暖如春宅广,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背些举。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工跟狱, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人户魏。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓驶臊,卻偏偏與公主長得像,于是被迫代替她去往敵國和親叼丑。 傳聞我的和親對象是個殘疾皇子关翎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評論 2 348

推薦閱讀更多精彩內(nèi)容

  • *面試心聲:其實(shí)這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個offer,總結(jié)起來就是把...
    Dove_iOS閱讀 27,129評論 30 470
  • 內(nèi)存管理 簡述OC中內(nèi)存管理機(jī)制。與retain配對使用的方法是dealloc還是release幢码,為什么笤休?需要與a...
    丶逐漸閱讀 1,950評論 1 16
  • 1.OC里用到集合類是什么尖飞? 基本類型為:NSArray症副,NSSet以及NSDictionary 可變類型為:NS...
    輕皺眉頭淺憂思閱讀 1,366評論 0 3
  • 37.cocoa內(nèi)存管理規(guī)則 1)當(dāng)你使用new,alloc或copy方法創(chuàng)建一個對象時政基,該對象的保留計數(shù)器值為1...
    如風(fēng)家的秘密閱讀 835評論 0 4
  • 一片云贞铣,一陣風(fēng),一抹殘陽沮明。 一道河辕坝,一座橋,一處人家荐健。 別問我的腳步走的多快酱畅, 只等你來的時間有多久。 一支筆江场,一...
    睿陽公子閱讀 461評論 0 4