內存泄露監測

2019獨角獸企業重金招聘Python工程師標準>>> hot3.png

iOS 內存泄露監測
144  作者 謝謝生活 已關注
2017.05.19 17:38* 字數 4235 閱讀 209評論 0喜歡 6
iOS可能存在的內存泄露:block 循環引用。當一個對象有一個block屬性,而block屬性又引用這個對象本身那么要造成循環引用。這個時候就用___weak聲明下對象,用對象的弱引用指針。
頭文件相互包含。那么先在.h文件用前向引用聲明,@class(類名);然后在.m文件導入#import " AHMessageCell"(類頭文件)
移除通知 [[NSNotificationCenter defaultCenter]removeObserver:self];、
移除NSTimer[_timer invalidate];_timer = nil;
移除觀察者
//添加觀察者[self addObserver:<#(nonnull NSObject *)#> forKeyPath:<#(nonnull NSString *)#> options:<#(NSKeyValueObservingOptions)#> context:<#(nullable void *)#>]
//移除觀察者[self removeObserver:<#(nonnull NSObject *)#> forKeyPath:<#(nonnull NSString *)#>];
timer,觀察者,通知的移除。一般的開發者都是放到dealloc中,但是這樣不能保證一定能夠移除成功。可以更加實際情況移除,可以在viewWillAppear中添加,viewWillDisappear中移除,也可以強制移除。iOS內存泄露測試:可以用xcode自帶instrument工具,如:leaks、Analyze、allocation,也可以用第三方工具。一: leaks打開Xcode7自帶的Instruments
打開Instruments
按上面操作,build成功后跳出Instruments工具,選擇Leaks選項選擇之后界面如下圖:打開leaks
到這里之后,我們前期的準備工作做完啦,下面開始正式的測試!1.選中Xcode先把程序(command + R)運行起來2.再選中Xcode,按快捷鍵(command + control + i)運行起來,此時Leaks已經跑起來了3.由于Leaks是動態監測,所以我們需要手動操作APP,一邊操作,一邊觀察Leaks的變化,當出現紅色叉時,就監測到了內存泄露,點擊右上角的第二個,進行暫停檢測(也可繼續檢測,當多個時暫停,一次處理了多個).如圖所示:4.下面就是定位修改了,此時選中有紅色柱子的Leaks,下面有個"田"字方格,點開,選中Call Tree顯示如下圖界面找到內存泄露位置
5.下面就是最關鍵的一步,在這個界面的右下角有若干選框,選中Invert Call Tree 和Hide System Libraries,(紅圈范圍內)顯示如下:監測回調函數
到這里就算基本完成啦,這里顯示的就是內存泄露代碼部分,那么現在還差一步:定位!6.選中顯示的若干條中的一條,雙擊,會自動跳到內存泄露代碼處,如圖所示
查看回調函數
7.找到了內存泄露的地方,那么我們就可以修改即可。二:Analyze—靜態分析顧名思義,靜態分析不需要運行程序,就能檢查到存在內存泄露的地方。使用方法:打開Xcode,command + shift + B;或者Xcode - Product - Analyze;
常見的三種泄露情形:
(1)創建了一個對象,但是并沒有使用。Xcode提示信息: Value Stored to 'number' is never read 。翻譯一下:存儲在'number'里的值從未被讀取過。
(2)創建了一個(指針可變的)對象,且初始化了,但是初始化的值一直沒讀取過。Xcode提示信息: Value Stored to 'str' during its initialization is never read
(3)調用了讓某個對象引用計數加1的函數,但沒有調用相應讓其引用計數減1的函數。Xcode提示信息: Potential leak of an object stored into 'subImageRef' 。 翻譯一下:subImageRef對象的內存單元有潛在的泄露風險。
貼上Demo代碼:
/*** 情 形 一:創建了一個對象,但是并沒有使用。* 提示信息:Value Stored to 'number' is never read* 翻譯一下:存儲在'number'里的值從未被讀取過,*/
- (void)leakOne {NSString *str1 = [NSString string];NSNumber *number;number = @(str1.length);/*說我們沒有讀取過它,那就讀取一下,比如打開下面這句代碼,對它發送class消息,就不再會有這個提示了。當然最好的方法還是將有關number的代碼都刪掉,因為,你只對number賦值,又不使用,那干嘛創建出來呢。這是一個比較常見和典型的錯誤,也很容易檢查出來*/// [number class];
}/*** 情 形 二:創建了一個(指針可變的)對象,且初始化了,但是初始化的值一直沒讀取過。* 提示信息:Value Stored to 'str' during its initialization is never read*/
- (void)leakTwo {NSString *str = [NSString string]; // 創建并初始化str,此時已經有一個內存單元保存str初始化的值// NSString *str; // 這樣就內存不泄露,因為str是可變的,只需要先聲明就行。// printf("str前 = %p\n",str);str = @"ceshi";             // str被改變了,指向了"ceshi"所在的地址,指針改變了,但之前保存初始化值的內存空間還未釋放,保存str初始化值的內存單元泄露了。// printf("str后 = %p\n",str); // 指針改變了[str class];// 再舉兩個例子,同理NSArray *arr = [NSArray array];// printf("arr前 = %p\n",arr);// NSArray *arr;            // 這樣就內存不泄露arr = @[@"1",@"2"];// printf("arr后 = %p\n",arr); // 指針改變了[arr class];CGRect rect = self.view.frame;// CGRect rect = CGRectZero; // 這樣就內存不泄露rect = CGRectMake(0, 0, 0, 0);NSLog(@"rect = %@",NSStringFromCGRect(rect));
}/*** 情 形 三:調用了讓某個對象引用計數加1的函數,但沒有調用相應讓其引用計數減1的函數。* 提示信息:Potential leak of an object stored into 'subImageRef'* 翻譯一下:subImageRef對象的內存單元有潛在的泄露風險*/
- (void)leakThree {CGRect rect = CGRectMake(0, 0, 50, 50);UIImage *image;CGImageRef subImageRef = CGImageCreateWithImageInRect(image.CGImage, rect); // subImageRef 引用計數 + 1;UIImage* smallImage = [UIImage imageWithCGImage:subImageRef];// 應該調用對應的函數,讓subImageRef的引用計數減1,就不會泄露了// CGImageRelease(subImageRef);[smallImage class];UIGraphicsEndImageContext();
}
監測結果:可能存在內存泄露的地方
三:allocation使用這個時候我們通過Allocation可以進行內存分析,將Xcode切換為Release狀態,通過Product→Profile(Cmd+i)找到Allocations:
代開allocation
1.紅色的按鈕是表示停止和啟動應用程序,不要理解成了暫停,Objective-C所有的對象都是在堆上分配的,記得勾選一下All Heap Allocations:
開始監測
2.點擊All Heap Allocation,勾選Call Tree,同時不查看系統的函數庫:
監測回調函數
3.具體方法占用的內存,可以逐級點開,效果如下:
內存占用
以上是常規的Allocations使用,關于第二張圖的有框中的幾個選項可以解釋一下:
Separate by Thread: 每個線程應該分開考慮,考慮到應用程序中GCD的存在;
Invert Call Tree: 從上倒下跟蹤堆棧,這意味著你看到的表中的方法,將已從第0幀開始取樣,利用棧的先進后出的特性,我們可以在棧頂看到最近調用的函數;
Hide System Libraries: 勾選此項會顯示app的代碼,這是非常有用的;
Flatten Recursion: 遞歸函數, 每個堆棧跟蹤一個條目;左側有幾個比較有用的選項:
All Objects Created
Created & Still Living
Created & Destroyed
內存監測
4.Allocation 分析技巧
通過以上方法可以對應用的整體內存使用情況有所了解,但內存不合理使用導致的內存警告往往是部分代碼或視圖導致的,我們往往要關注于某段時間或操作過程中內存的分配和使用情況,Allocation提供了這種功能。
比如在進入一個視圖前或操作前,我們在Allocation面板左側點擊Mark Generation,這時候會產生Generation A節點,顯示內存當前的情況:
比較測出內存泄露點
我們可以在進入視圖后再點一次Mark Generation,在視圖退出后再點一次Mark,這樣三次產生的 Generation分別記錄了進入前、進入后、關閉后,再最后一個Generation應該內存被合理釋放,否則就代表了在這個視圖或操作中有泄漏或不合理的地方。
以上只是Allocation的基本運用,設計出一套使用Allocation來合理測試的方案是比較復雜的,后續慢慢介紹。四:MLeaksFinderMLeaksFinder 提供了內存泄露檢測更好的解決方案。只需要引入 MLeaksFinder,就可以自動在 App 運行過程檢測到內存泄露的對象并立即提醒,無需打開額外的工具,也無需為了檢測內存泄露而一個個場景去重復地操作。MLeaksFinder 目前能自動檢測 UIViewController 和 UIView 對象的內存泄露,而且也可以擴展以檢測其它類型的對象。
MLeaksFinder 的使用很簡單,參照 https://github.com/Zepo/MLeaksFinder,基本上就是把 MLeaksFinder 目錄下的文件添加到你的項目中,就可以在運行時(debug 模式下)幫助你檢測項目里的內存泄露了,無需修改任何業務邏輯代碼,而且只在 debug 下開啟,完全不影響你的 release 包。
當發生內存泄露時,MLeaksFinder 會中斷言,并準確的告訴你哪個對象泄露了。這里設計為中斷言而不是打日志讓程序繼續跑,是因為很多人不會去看日志,斷言則能強制開發者注意到并去修改,而不是犯拖延癥。
中斷言時,控制臺會有如下提示,View-ViewController stack 從上往下看,該 stack 告訴你,MyTableViewController 的 UITableView 的 subview UITableViewWrapperView 的 subview MyTableViewCell 沒被釋放。而且,這里我們可以肯定的是 MyTableViewController,UITableView,UITableViewWrapperView 這三個已經成功釋放了。
* Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Possibly Memory Leak.In case that MyTableViewCell should not be dealloced, override -willDealloc in MyTableViewCell by returning NO.View-ViewController stack: ( MyTableViewController, UITableView, UITableViewWrapperView, MyTableViewCell)'從 MLeaksFinder 的使用方法可以看出,MLeaksFinder 具備以下優點:
使用簡單,不侵入業務邏輯代碼,不用打開 Instrument
不需要額外的操作,你只需開發你的業務邏輯,在你運行調試時就能幫你檢測
內存泄露發現及時,更改完代碼后一運行即能發現(這點很重要,你馬上就能意識到哪里寫錯了)
精準,能準確地告訴你哪個對象沒被釋放原理(http://wereadteam.github.io/2016/02/22/MLeaksFinder/?from=singlemessage&isappinstalled=0#u539F_u7406)
MLeaksFinder 一開始從 UIViewController 入手。我們知道,當一個 UIViewController 被 pop 或 dismiss 后,該 UIViewController 包括它的 view,view 的 subviews 等等將很快被釋放(除非你把它設計成單例,或者持有它的強引用,但一般很少這樣做)。于是,我們只需在一個 ViewController 被 pop 或 dismiss 一小段時間后,看看該 UIViewController,它的 view,view 的 subviews 等等是否還存在。
具體的方法是,為基類 NSObject 添加一個方法 -willDealloc
方法,該方法的作用是,先用一個弱指針指向 self,并在一小段時間(3秒)后,通過這個弱指針調用 -assertNotDealloc,而 -assertNotDealloc 主要作用是直接中斷言。- (BOOL)willDealloc { __weak id weakSelf = self; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [weakSelf assertNotDealloc]; }); return YES;}- (void)assertNotDealloc { NSAssert(NO, @“”);}
這樣,當我們認為某個對象應該要被釋放了,在釋放前調用這個方法,如果3秒后它被釋放成功,weakSelf 就指向 nil,不會調用到 -assertNotDealloc
方法,也就不會中斷言,如果它沒被釋放(泄露了),-assertNotDealloc
就會被調用中斷言。這樣,當一個 UIViewController 被 pop 或 dismiss 時(我們認為它應該要被釋放了),我們遍歷該 UIViewController 上的所有 view,依次調 -willDealloc,若3秒后沒被釋放,就會中斷言。在這里,有幾個問題需要解決:
不入侵開發代碼
這里使用了 AOP 技術,hook 掉 UIViewController 和 UINavigationController 的 pop 跟 dismiss 方法,關于如何 hook,請參考 Method Swizzling。遍歷相關對象
在實際項目中,我們發現有時候一個 UIViewController 被釋放了,但它的 view 沒被釋放,或者一個 UIView 被釋放了,但它的某個 subview 沒被釋放。這種內存泄露的情況很常見,因此,我們有必要遍歷基于 UIViewController 的整棵 View-ViewController 樹。我們通過 UIViewController 的 presentedViewController 和 view 屬性,UIView 的 subviews 屬性等遞歸遍歷。對于某些 ViewController,如 UINavigationController,UISplitViewController 等,我們還需要遍歷 viewControllers 屬性。構建堆棧信息
需要構建 View-ViewController stack 信息以告訴開發者是哪個對象沒被釋放。在遞歸遍歷 View-ViewController 樹時,子節點的 stack 信息由父節點的 stack 信息加上子結點信息即可。例外機制
對于有些 ViewController,在被 pop 或 dismiss 后,不會被釋放(比如單例),因此需要提供機制讓開發者指定哪個對象不會被釋放,這里可以通過重載上面的 -willDealloc
方法,直接 return NO 即可。特殊情況
對于某些特殊情況,釋放的時機不大一樣(比如系統手勢返回時,在劃到一半時 hold 住,雖然已被 pop,但這時還不會被釋放,ViewController 要等到完全 disappear 后才釋放),需要做特殊處理,具體的特殊處理視具體情況而定。系統View
某些系統的私有 View,不會被釋放(可能是系統 bug 或者是系統出于某些原因故意這樣做的,這里就不去深究了),因此需要建立白名單手動擴展
MLeaksFinder目前只檢測 ViewController 跟 View 對象。為此,MLeaksFinder 提供了一個手動擴展的機制,你可以從 UIViewController 跟 UIView 出發,去檢測其它類型的對象的內存泄露。如下所示,我們可以檢測 UIViewController 底下的 View、Model:- (BOOL)willDealloc { if (![super willDealloc]) { return NO; } MLCheck(self.viewModel); return YES;}
這里的原理跟上面的是一樣的,宏 MLCheck() 做的事就是為傳進來的對象建立 View-ViewController stack 信息,并對傳進來的對象調用 -willDealloc
方法。五:faceBook提供的內存泄露自動化測試:FBRetainCycleDetector、FBAllocationTracker、FBMemoryProfiler。讓這工具真正閃光的是,在工程師內部構建的時候,它會連續的、自動的運行。
客戶端部分自動化是簡單的。我們在定時器上運行循環引用檢測器,定期掃描內存去尋找循環引用,雖然這不是完全沒有問題。當我們第一次運行分析器的時候,我們意識到它不足以很快的掃描整個內存空間。當它開始檢測的時候,我們需要給它提供一組候選對象。
為了更有效的解決這個問題,我們開發了FBAllocationTracker。這個工具會主動跟蹤NSObject
子類的創建和釋放。它可以以一個很小的性能開銷來獲取任何類的任何實例。
對于客戶端的自動化,只要在NSTimer
上使用FBRetainCycleDetector,再用FBAllocationTracker來抓取實例來配合跟蹤就行。
現在,讓我們來仔細看看后臺會發生什么。
循環引用可以包含任何數量的對象。一個壞的連接會導致很多環的時候,這就復雜了。在環中,A→B是一個壞連接,創建了兩個環:A-B-C-D 和 A-B-C-E。
這有兩個問題:
我們不想給一個壞連接導致的兩個循環引用分別標記。
我們不想給可能代表兩個問題的兩個循環引用一起標記,即使它們共享一個連接。所以我們需要給循環引用定義簇組(clusters),鑒于這些啟發,我們寫了個算法來找到這些問題。
在給定的時間收集所有的環。
對于每一個環,提取Facebook特定的類名。
對于每一個環,找到包含在環內的被報告的最小的環。
依據上面的最小環,將環添加到組中。
只報告最小環。最后一部分是找出誰第一時間偶然引入了循環引用。我們可以通過環中的”git/hg責任”的部分代碼來猜測最近的變化所導致的問題。最后一個接觸這個代碼的人將會收到修復代碼的任務。
整個系統如下:手動性能分析
雖然自動化有助于簡化發現循環引用的過程,降低人員的消耗,手動性能分析依然有它的用武之地。我們創建的另一個工具允許任何人查看內存使用,甚至不需要把他的手機插到電腦上。
FBMemoryProfiler可以很容易的添加到任何應用程序,可以讓你手動配置構建文件,可以讓你在應用程序內運行循環應用檢測。它會借用FBAllocationTracker和FBRetainCycleDetector來實現此功能。生成(Generations)
FBMemoryProfiler的一個很偉大的特性是“生成追蹤(generation tracking)”,類似于蘋果的Instruments的生成追蹤。生成只是簡單的在兩次標記之間拍攝所有仍然活著的對象的快照。
使用FBMemoryProfiler的界面,我們可以標記生成,例如,分配三個對象。然后我們標記另一個生成,之后繼續分配對象。第一個生成包含我們一開始的三個對象。如果任意一個對象被釋放了,它會從我們第二個生成中移除。當我們有一個重復的任務,我們認為可能會內存泄露的時候,生成追蹤是很有用的,例如,導航View Controller的進出。在每次開始我們的任務的時候,我們標記一個生成,然后,對之后的每個生成進行調查。如果一個對象不應該活這么長時間,我們可以在FBMemoryProfiler界面清楚地看到。
Check Out
無論你的應用程序是大是小,功能是多是少,好的工程師都應有好的內存管理。在這些工具的幫助之下,我們可以更簡單的找到并修復這些內存泄露,所以我們可以花費更少的時間去手動處理,這樣就可以有更多的時間去編寫更好的代碼。我們也希望你可以發現它們是有用的。在Github上check out下來吧。FBRetainCycleDetector, FBAllocationTracker 和 FBMemoryProfiler。

?

轉載于:https://my.oschina.net/u/2562364/blog/906566

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/456424.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/456424.shtml
英文地址,請注明出處:http://en.pswp.cn/news/456424.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

玩Azkaban跳過的坑

文章目錄一號坑&#xff1a;啟動Azkaban報錯&#xff1a;User xml file conf/azkaban-users.xml doesnt exist.二號坑&#xff1a;報錯&#xff1a;failed SslSocketConnector0.0.0.0:8443: java.io.FileNotFoundException: /home/hadoop/app/azkaban/azkaban-web-2.5.0/bin/ke…

兩種解除禁止右鍵、選中、復制的方法

我在網上找的 兩種解除禁止右鍵、選中、復制的方法 1、直接存到書簽點擊即可 javascript:(function(){var docdocument;var bddoc.body;bd.onselectstartbd.oncopybd.onpastebd.onkeydownbd.oncontextmenubd.onmousemovebd.onselectstartbd.ondragstartdoc.onselectstartdoc.o…

刪除節點removeChild()

http://www.imooc.com/code/1700 刪除節點removeChild() removeChild() 方法從子節點列表中刪除某個節點。如刪除成功&#xff0c;此方法可返回被刪除的節點&#xff0c;如失敗&#xff0c;則返回 NULL。 語法: nodeObject.removeChild(node) 參數: node &#xff1a;必需&…

機器學習自主解決安全威脅離我們還有多遠?

曾經聽見不止一次這樣的問題&#xff1a; “機器學習會替代基于人工經驗規則的安全解決方案么&#xff1f;”把這個問題放在去年來看&#xff0c;我們已經得到了非常多的討論甚至是一些已經實際應用的解決方案&#xff0c;對于人工智能在安全以及其它各種對數據進行價值挖掘的場…

Linux執行定時任務(crontab)遇到的坑

文章目錄前言&#xff1a;1、建立定時任務的兩種方式1.1、crontab -e1.2、vi /etc/ crontab2、兩種方法的區別2.1、用戶級2.2、系統級3、解決辦法前言&#xff1a; 之前第一次要在生產環境部署定時任務&#xff0c;無奈的是&#xff0c;博主對定時任務這塊還是個小白&#xff…

Vue:解決[Vue warn]: Failed to resolve directive: modle (found in Anonymous)

解決問題 [Vue warn]: Failed to resolve directive: modle (found in <ComponentA>) console.error(("[Vue warn]: " msg trace)); 原因是 我把model 寫成了 modle 這類錯誤一般是單詞寫錯了 (found in <Anonymous>) 解決思路

Oracle樹查詢及相關函數

Oracle樹查詢的最重要的就是select...start with... connect by ...prior 語法了。依托于該語法&#xff0c;我們可以將一個表形結構的中以樹的順序列出來。在下面列述了Oracle中樹型查詢的常用查詢方式以及經常使用的與樹查詢相關的Oracle特性函數等&#xff0c;在這里只涉及到…

Mysql常用函數總結

文章目錄前言&#xff1a;1、日期相關函數1.1、mysql獲取未來、現在、過去的時間&#xff1a;DATE_SUB&#xff08;&#xff09;、DATE_ADD()1.2、格式化日期&#xff1a;date_format&#xff08;&#xff09;1.3、MySQL 日期、時間相減函數&#xff1a;datediff(date1,date2),…

一行Python代碼制作動態二維碼

目錄 1、普通二維碼 2、藝術二維碼 3、動態二維碼 在GitHub上發現了一個比較有意思的項目&#xff0c;只需要一行Python代碼就可以快捷方便生成普通二維碼、藝術二維碼(黑白/彩色)和動態GIF二維碼。 GitHub網站參加&#xff1a;https://github.com/sylnsfar/qrcode 用法比…

Vue常用經典開源項目匯總參考-海量

Vue常用經典開源項目匯總參考-海量 Vue是什么&#xff1f; Vue.js&#xff08;讀音 /vju?/, 類似于 view&#xff09; 是一套構建用戶界面的 漸進式框架。與其他重量級框架不同的是&#xff0c;Vue 采用自底向上增量開發的設計。Vue 的核心庫只關注視圖層&#xff0c;并且非常…

鼠標移入視頻播放,鼠標移出播放停止,恢復到原來狀態

<!doctype html> <html lang"en"> <head><meta charset"UTF-8"><title>鼠標移入視頻播放&#xff0c;鼠標移出播放停止&#xff0c;恢復到原來狀態</title><link rel"shortcut icon" href"http://f…

Pycharm常用高效技巧總結

文章目錄1、PyCharm如何自動生成函數注釋2、pycharm運行程序時在Python console窗口中運行3、Pycharm在創建py文件時,如何自動添加文件頭注釋4、Pycharm配置遠程調試5、pycharm同一目錄下無法import明明已經存在的.py文件1、PyCharm如何自動生成函數注釋 一般在函數def()行下敲…

EntityFramework中常用的數據刪除方式

最近在學EF&#xff0c;目前了解到刪除操作有三種方式&#xff0c; 第一&#xff0c;官方推薦的先查詢數據&#xff0c;再根據查詢的對象&#xff0c;刪除對象。 這是第一種&#xff0c;官方推薦 第二&#xff0c;自己創建一個對象&#xff0c;然后附加&#xff0c;然后刪除。 …

Elasticsearch的前后臺運行與停止(tar包方式)

備注&#xff1a;在生產環境中&#xff0c;往往一般用后臺來運行。jps查看。 1、ES的前臺運行 [hadoopdjt002 elasticsearch-2.4.3]$ pwd/usr/local/elasticsearch/elasticsearch-2.4.3[hadoopdjt002 elasticsearch-2.4.3]$ bin/elasticsearch 2、ES的后臺運行 [hadoopdjt002 e…

解決pycharm運行Flask指定ip、端口更改無效

后來查了一下官網文檔&#xff0c;原來Flask 1.0 版本不再支持之前的FLASK_ENV 環境變量了。 Prior to Flask 1.0 the FLASK_ENV environment variable was not supported and you needed to enable debug mode by exporting FLASK_DEBUG1. This can still be used to control…

Freeswitch總結大全

文章目錄1、Freeswitch安裝2、Freeswitch中文文檔3、Freeswitch的event socket event list的中文簡介4、freeswitch之sip協議的注冊、呼叫、掛斷流程5、Freeswitch之mod_cdr_csv6、一款第三方收費的mod_vad&#xff08;看介紹挺不錯的&#xff0c;有做語音交互的童鞋可以看下&a…

Android中SimpleAdapter的使用—自定義列表

本人初學Android&#xff0c;今天研究到Adapter這塊感覺挺有意思的&#xff0c;寫了個自定義列表進行測試 首先我們新建一個layout列表布局文件&#xff0c;具體布局可以自己設定。 下面貼上我的自定義布局文件代碼 1 <?xml version"1.0" encoding"utf-8&qu…

Module 的語法

Module 的語法 概述嚴格模式export 命令import 命令模塊的整體加載export default 命令export 與 import 的復合寫法模塊的繼承跨模塊常量import()概述 歷史上&#xff0c;JavaScript 一直沒有模塊&#xff08;module&#xff09;體系&#xff0c;無法將一個大程序拆分成互相依…

解決:SyntaxError: Non-UTF-8 code starting with '\xe6' in file

pycharm加注釋報錯SyntaxError: Non-UTF-8 code starting with \xe6 in file 處理 代碼最上面加上編碼格式 #coding:utf-8

Freeswitch之ASR(語音識別)總結大全

文章目錄1、使用Pocket Sphinx進行英文語音識別2、PocketSphinx語音識別系統語言模型的訓練和聲學模型的改進3、PocketSphinx語音識別系統的編譯、安裝和使用4、FS之play_and_detect_speech模塊5、一些開源的語音識別軟件6、某大神寫的一系列干貨7、語音識別——基于深度學習的…