內(nèi)存優(yōu)化實(shí)踐-2019

簡(jiǎn)介

項(xiàng)目開發(fā)完,提交測(cè)試之后精刷,解bug之余舀寓,看看內(nèi)存相關(guān)的內(nèi)容。

靜態(tài)檢查

這個(gè)推薦做一下完慧,非常簡(jiǎn)單。Product =》Analyse剩失。
這里可以檢查出一些低級(jí)錯(cuò)誤屈尼,比如viewWillAppear缺少super等。一般情況下拴孤,這里查出的問(wèn)題最好都能解決一下脾歧。

Swift

工程里用到了一個(gè)Swift寫的第三方庫(kù),在用Instrument的時(shí)候發(fā)現(xiàn)跑不起來(lái)演熟。輸出的信息有This copy of libswiftCore.dylib requires an OS version prior to 12.2.0這樣的一句話鞭执。解決方法是,在Build Setting =》Runpath Search Path標(biāo)簽下添加/usr/lib/swift
https://stackoverflow.com/questions/55361057/this-copy-of-libswiftcore-dylib-requires-an-os-version-prior-to-12-2-0

image.png

NSURLSession

工程中直接使用了NSURLSession進(jìn)行網(wǎng)絡(luò)傳輸芒粹。由于NSURLSession對(duì)于其delegate是強(qiáng)引用兄纺,所以容易造成引用循環(huán)。需要調(diào)用其invalidateAndCancel或者finishTasksAndInvalidate方法化漆,進(jìn)行釋放估脆。

解決方案,一種是全局使用一個(gè)NSURLSession進(jìn)行網(wǎng)絡(luò)傳輸座云。
NSURLSession時(shí)需要注意一個(gè)內(nèi)存泄漏問(wèn)題
https://www.cnblogs.com/lys-iOS-study/p/5684454.html

另外一種方法就是在網(wǎng)絡(luò)完成之后疙赠,主動(dòng)釋放。
關(guān)于NSURLSession內(nèi)存泄露解決方案

當(dāng)然疙教,這個(gè)只有在自己寫網(wǎng)絡(luò)傳輸?shù)臅r(shí)候才需要注意棺聊,如果用AFNetworking之類的第三方庫(kù),這些問(wèn)題已經(jīng)考慮到了贞谓,不需要再操心限佩。

Block

blockObject-C中比較普遍。如果block中包含self裸弦,很容易造成引用循環(huán)祟同。當(dāng)然,只是“很容易”理疙,而不是"一定"

這個(gè)就是會(huì)造成引用循環(huán)的例子晕城,需要處理

    kWeakSelf(self)
    // 左邊的關(guān)閉按鈕
    self.bankSelectView.leftButtonBlock = ^{
        kStrongSelf(self)
        self.viewIndex = KJTPayViewIndexPay;
    };

這個(gè)其實(shí)是不會(huì)造成引用循環(huán)的。不過(guò)處理一下窖贤,也沒什么關(guān)系砖顷。

// 從網(wǎng)絡(luò)取數(shù)據(jù)
- (void)fetchData {
    kWeakSelf(self)
    [KJTRequestApi getMainPageInfo:^(KJTMainPageInfoModel * _Nonnull pageInfo) {
        kStrongSelf(self)
        self.errorView.hidden = YES;
    } failureBlock:^(NSString * _Nullable message) {
        kStrongSelf(self)
        self.errorView.hidden = NO;
    }];
}

所以贰锁,簡(jiǎn)單起見,只要block里面包含了self滤蝠,就用weak self ; strong self對(duì)處理一下豌熄,反正也沒有什么副作用。

RACObserve

這個(gè)可以用來(lái)監(jiān)聽屬性值的改變物咳,然后在隨后的subscribeNext中進(jìn)行相應(yīng)锣险。比如下面的例子

[RACObserve(self.viewModel, title) subscribeNext:^(NSString * title)     {
        self.title = title;
}];

由于RACObserve宏隱含了對(duì)self的引用,所以览闰,上面的代碼其實(shí)已經(jīng)造成了循環(huán)引用芯肤。

#define RACObserve(TARGET, KEYPATH) \\
({ \\
_Pragma("clang diagnostic push") \\
_Pragma("clang diagnostic ignored \\"-Wreceiver-is-weak\\"") \\
__weak id target_ = (TARGET); \\
[target_ rac_valuesForKeyPath:@keypath(TARGET, KEYPATH) observer:self]; \\
_Pragma("clang diagnostic pop") \\
})

所以,這里要打破引用循環(huán)压鉴,可以這樣:

@weakify(self);
[RACObserve(self.viewModel, title) subscribeNext:^(NSString * title) {
    @strongify(self);
    self.title = title;
}];

ReactiveCocoa之RAC內(nèi)存管理

更進(jìn)一步: 在有subscribeNext的地方都用上weak self崖咨,比如像下面這樣的代碼:

@weakify(self);
[self.viewModel.titleSignal subscribeNext:^(NSString * title) {
    @strongify(self);
    self.title = title;
}];

RACSubject

RACSubject被稱為熱信號(hào),這個(gè)難以理解晴弃。我更愿意把它類比為一種消息發(fā)送和監(jiān)聽處理的一種比較方便的使用方式

image.png

這個(gè)用起來(lái)還是比較方便的掩幢,特別是網(wǎng)絡(luò)訪問(wèn)逊拍,只要?jiǎng)?chuàng)建成功上鞠,失敗兩個(gè)不同的RACSubject就可以了。

不過(guò)要注意的是芯丧,如果忘了[subject sendCompleted];這句芍阎,很可能會(huì)帶來(lái)內(nèi)存泄漏。所以缨恒,為了保險(xiǎn)起見谴咸,[subject sendNext:@1];都跟上這么一句話,就好多了骗露。詳細(xì)信息可以參考下面這篇文章:

ReactiveCocoa中潛在的內(nèi)存泄漏及解決方案

嵌套的block

在項(xiàng)目中岭佳,還遇到一種情況:首先是接口1的網(wǎng)絡(luò)訪問(wèn),這里有一個(gè)異步block萧锉;然后珊随,彈出一個(gè)對(duì)話框,等待用戶輸入柿隙,這里有一個(gè)異步block叶洞;最后,調(diào)用接口2的網(wǎng)絡(luò)接口禀崖,這里有一個(gè)異步block衩辟。這種異步block嵌套多次的情況,斷點(diǎn)調(diào)試波附,最后能出來(lái)艺晴,不過(guò)要等好久昼钻。所以,這種時(shí)候封寞,也加上weak self换吧,出來(lái)的速度就快多了。

@weakify(self);
[Newwork sucess:^(void) {
    @strongify(self);
    [InputDialog sucess:^(NSString * title) {
        self.title = title;
        [newwork sucess:^(void) {
            NSLog(@"done");
        }]
    }];
}];

簡(jiǎn)述@weakify钥星、@strongify

dealloc

在基類控制器中添加如下代碼:

- (void)dealloc {
    NSLog(@"%@退出了沾瓦。",NSStringFromClass([self class]));
}

其實(shí),產(chǎn)品運(yùn)行起來(lái)后谦炒,都是一個(gè)Controller在跳轉(zhuǎn)贯莺,其他的類,比如view宁改,button之類的缕探,并不能獨(dú)立存在。所以还蹲,一般來(lái)說(shuō)爹耗,只要Controller能正常退出,就不會(huì)有內(nèi)存泄漏谜喊。這是檢測(cè)內(nèi)存是否泄漏的一個(gè)比較簡(jiǎn)單的方法潭兽。

單例

網(wǎng)上雖然有關(guān)于單例的消除相關(guān)文章,但是斗遏,實(shí)際執(zhí)行下來(lái)山卦,發(fā)現(xiàn)dealloc并不能真正執(zhí)行。比如:

+ (void)tearDown{

  sInstance=nil;

  onceToken=0l;

}

所以诵次,單例并不能真正執(zhí)行账蓉。
不過(guò),既然是單例逾一,常駐內(nèi)存也是可以的铸本。并且,只有一個(gè)實(shí)例遵堵,所以箱玷,并不會(huì)造成內(nèi)存泄漏。
不過(guò)鄙早,有一種情況需要注意汪茧,如果把Controller賦值給某個(gè)單例強(qiáng)引用屬性,那么這個(gè)Controller就退不出來(lái)了限番。不過(guò)舱污,這種情況內(nèi)存占用是有限的,并不會(huì)造成內(nèi)存泄漏弥虐。下次再進(jìn)的話扩灯,Controller會(huì)被替換媚赖,不會(huì)疊加,占用的內(nèi)存是有限的珠插。
解決這個(gè)問(wèn)題的方法是單例在把Controller作為屬性的時(shí)候惧磺,修飾符用weak,不要用strong就可以了捻撑。

// 輸入Controller
@property (weak, nonatomic) UIViewController *inputVc;

Leaks

Xcode自帶的Leaks一般被認(rèn)為是查內(nèi)存泄漏的好工具磨隘。以前也用過(guò),雖然不熟練顾患,不過(guò)按照網(wǎng)上的介紹文章番捂,還是能夠找出幾個(gè)內(nèi)存泄漏的地方的。當(dāng)時(shí)江解,現(xiàn)在的10.2.1版本的XCode设预,Leaks幾乎沒什么用。工程跑起來(lái)犁河,就停止首頁(yè)不動(dòng)鳖枕,也能報(bào)出很多的紅叉,并且一會(huì)兒就自動(dòng)退出了桨螺,說(shuō)是遇到了error宾符,但是卻找不到有效的錯(cuò)誤信息。這讓人感覺到彭谁,蘋果是越來(lái)越差吸奴,越來(lái)越?jīng)]前途。XCode改得越來(lái)越難用缠局。

image.png

以前雖然感覺也很差,不過(guò)至少還有點(diǎn)作用考润,反正用得也不多狭园,湊合著也行。現(xiàn)在的糊治,真是太差了唱矛。庫(kù)克還真是個(gè)蠢貨。

網(wǎng)上的文章也很多井辜,不過(guò)幾乎沒有實(shí)用的绎谦,這實(shí)在是Leaks這工具本身做得實(shí)在太差,太難用了粥脚。

IOS-Instrument-Leaks
iOS Instruments之Leaks
iOS性能優(yōu)化之內(nèi)存管理:Analyze窃肠、Leaks、Allocations的使用和案例代碼

聚焦ViewController

ViewController能夠退出刷允,那么內(nèi)存泄漏的問(wèn)題就不大冤留。

如果ViewController作為其他成員的屬性碧囊,那么就應(yīng)該用weak修飾。特別是用子ViewController的時(shí)候纤怒,在父ViewController應(yīng)該作為weak類型的屬性成員存在糯而。因?yàn)楦缸?code>ViewController難免要相互引用。

// child controller
@property (weak, nonatomic) KJTPasswordLoginChildViewController *passwordController;
@property (weak, nonatomic) KJTCodeLoginChildViewController *codeController;

騰訊基于Facebook的庫(kù)泊窘,二次開發(fā)了一個(gè)內(nèi)存泄漏檢測(cè)的工具熄驼,非常好用,強(qiáng)烈推薦
MLeaksFinder
iOS開發(fā)中內(nèi)存泄漏檢測(cè)工具--MLeaksFinder

備注:內(nèi)存檢測(cè)工具烘豹,就不要加入正式版本中了谜洽。可以拉一個(gè)分支吴叶,加入阐虚。用的時(shí)候,這個(gè)分支只運(yùn)行蚌卤,不修改实束。修改都在開發(fā)分支中進(jìn)行,開發(fā)完后逊彭,再和到這個(gè)檢測(cè)分支咸灿,跑一下。如果發(fā)現(xiàn)內(nèi)存泄漏侮叮,就在開發(fā)分支上改避矢,改完后再把改動(dòng)合過(guò)來(lái)。

image.png

如果有Controller釋放不了囊榜,會(huì)彈框提示审胸,然后再去查找代碼,就相對(duì)容易點(diǎn)了卸勺。這個(gè)和在Controllerdealloc函數(shù)中打log的功能是一樣的砂沛,不過(guò)這個(gè)更直觀。推薦使用曙求。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末碍庵,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子悟狱,更是在濱河造成了極大的恐慌静浴,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,718評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挤渐,死亡現(xiàn)場(chǎng)離奇詭異苹享,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)挣菲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門富稻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)掷邦,“玉大人,你說(shuō)我怎么就攤上這事椭赋「Ц冢” “怎么了?”我有些...
    開封第一講書人閱讀 158,207評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵哪怔,是天一觀的道長(zhǎng)宣蔚。 經(jīng)常有香客問(wèn)我,道長(zhǎng)认境,這世上最難降的妖魔是什么胚委? 我笑而不...
    開封第一講書人閱讀 56,755評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮叉信,結(jié)果婚禮上亩冬,老公的妹妹穿的比我還像新娘。我一直安慰自己硼身,他們只是感情好硅急,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著佳遂,像睡著了一般营袜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上丑罪,一...
    開封第一講書人閱讀 50,050評(píng)論 1 291
  • 那天荚板,我揣著相機(jī)與錄音,去河邊找鬼吩屹。 笑死跪另,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的祟峦。 我是一名探鬼主播罚斗,決...
    沈念sama閱讀 39,136評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼宅楞!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起袱吆,我...
    開封第一講書人閱讀 37,882評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤厌衙,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后绞绒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體婶希,經(jīng)...
    沈念sama閱讀 44,330評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評(píng)論 2 327
  • 正文 我和宋清朗相戀三年蓬衡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了喻杈。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片彤枢。...
    茶點(diǎn)故事閱讀 38,789評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖筒饰,靈堂內(nèi)的尸體忽然破棺而出缴啡,到底是詐尸還是另有隱情,我是刑警寧澤瓷们,帶...
    沈念sama閱讀 34,477評(píng)論 4 333
  • 正文 年R本政府宣布业栅,位于F島的核電站,受9級(jí)特大地震影響谬晕,放射性物質(zhì)發(fā)生泄漏碘裕。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評(píng)論 3 317
  • 文/蒙蒙 一攒钳、第九天 我趴在偏房一處隱蔽的房頂上張望帮孔。 院中可真熱鬧,春花似錦不撑、人聲如沸文兢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)禽作。三九已至,卻和暖如春揩页,著一層夾襖步出監(jiān)牢的瞬間旷偿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工爆侣, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留萍程,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,598評(píng)論 2 362
  • 正文 我出身青樓兔仰,卻偏偏與公主長(zhǎng)得像茫负,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子乎赴,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評(píng)論 2 351