[WindowsStore][C#] 關於Memory Leaks 的debugging 筆記

一直以來,Windows Store App由於有GC (Garbage Collection)的關係,所以之前寫的App也都不容易有memory leak的產生.
最近剛好遇到這樣的問題,也好好的把一些相關的工具都學習了一下.再次筆記一下

Debugging Memory Leaks之前,你必須了解

  • 所謂的memory leak在使用者能夠看到的永遠是記憶體被用完crash(或是app 收起來).但是就像是水管漏水一樣, 積水的地方絕對不是漏水的地方.

  • 所以面對memory leak 的問題,第一件事情就是不斷地簡化復現(reproduce)的步驟.

  • 由於有GC,如果你重複三到五次的動作發現有記憶體長大的情形,絕對不要貿然認為你找到關鍵的步驟.必須要超過10~15次讓系統把記憶體收回去.

    • 通常如果是Frame 的記憶體回收,可能5~10次會發生,WinRT 的部分可能會更久

    • 也可以跑一些其他大記憶體的App讓GC強迫產生

    • 或是放一段時間,讓GC動作.

    • 試著可以加上GC.Collect()的程式碼讓系統快一點回收,不過通常而言產生的效果相當有限.

  • 找到了memory leak的部分的時候,千萬不要選擇使用 Dispose或是 Null 某些component,因為這樣都不是正確的方式.必須要回過頭來去尋找為何該記憶體無法被正常的釋放.

關於尋找Memory Leak的Tools

這邊要註解一下,大部份的C#或是.NET的memory dump tool 都只能先把關於 managed code 的部分可以先列印出來
如果memory leak 出現在WINRT或是自己寫的unmanaged code這樣就比較難去尋找了. 

  • NP .NET Profiler tool

    • 這個工具還不錯,只要先指定好之後.做好一次的動作就用snapshot 抓下現在的記憶體狀況,最後再把狀況dump出來就可以了

    • 心得:

      • 比起等等會提到的PerfView算是比較好用,當然東西會少一點.不過使用上相當直覺,相當好用.
  • PerfView (說明網頁)

    • 這是微軟的工具,一樣只能提managed code的memory dump但是資訊更多還可以trace到Windows System Symbol.

    • 使用方法也相當簡單,先dump一次後.開始執行會產生memory leak 的動作然後執行第二次dump. 打開原始跟第二次的report就可以diff然後觀看結果.

    • 心得:

      • 比較建議使用這個畢竟是微軟提供的工具,資訊也比較多.不過在使用的時候如果有用debugger attach 會更慢.

如何Debugging Memory Leaks

  • 尋找正確的復現步驟,任何bug都是如此但是對於Windows Store(Mobile) system 而言,需要注意以下狀況

    • 如果發現做一次的步驟,記憶體沒有任何的增長,代表你的方式無法復現.反之則不然

    • 做出一次步驟發現記憶體的增長,不要以為找到復現的步驟.恐怕只是系統還未回收記憶體,建議要做到記憶體爆掉App收掉.

  • 這裡先列出一些在managed code裡面容易發生memory leak的部分:

    • UIElement AddHandler/RemoveHandler

      • 根據許多的資料(還有這裡)上來說GC應該會處理這樣的Handler,但是透過我實地拿PerfView的結果卻是會產生memory leak. 還是得注意一下.
    • Delegate的處理  += 記得要 -= 回去

      • 這個是一定會出事情的,千萬要注意你的 OnNavigationTo/OnNavigationFrom 有沒有成對的 += 與 -= 
    • 利用PerfvIew 查詢reference count是否有任何的異常產生(也就是查詢到爆量或是超過自己想像的ref cnt)

  • 利用PerfView 來觀察的部分,這邊有一些小技巧可以去觀察,驗證的方法:

    • [觀察]使用Diff方法,一開始先記錄下來後,做完疑似memory leak 的步驟之後,再記錄下來.

    • [驗證]可以把修改前的跟修改後的比對,不過這裡建議用修改前去diff 修改後~雖然數字出來會是正數但是這樣比較容易分析…

參考文章:

[iOS]繪圖函式CorePlot設定與使用

CorePlot 是一個相當好用來畫各種條狀圖,折線圖與圓餅圖的library. 要在手機上畫出美美的條狀圖,圓餅圖與長方圖?真的只能靠這套Library了.
以下整理一些在使用上可能會遇到的問題 

遇到的問題:

使用了好用的CorePlot也必須要了解使用最基本的方式來畫圖
以下是一些基本跟UIView 有關的部分,順便複習一下

  • 關於UIView
  • UIView Constructor 有兩種
      • (id)initWithCoder:(NSCoder *)aDecoder  
      • (id)initWithFrame:(CGRect)frame
    • initWithCoder 是給 StoryBoard 上面的UIView 使用,另外一個通常是自定frame大小的時候會用到.
    • 參數定義使用property與synthesize
  • 要畫Label 與畫出有顏色的自定UIView 可以用以下方式

          // 畫出UILable  
          CGRect frame = CGRectMake(20, 45, 140, 21);
          UILabel *label = [[UILabel alloc] initWithFrame:frame];
          [self.view addSubview:label];
          [label setText:@"Number of sides:"];
        
          // 自定UIView但是是個塗滿的黑筐
          UIView *lineView = [[UIView alloc] initWithFrame:CGRectMake(0, 200, self.view.bounds.size.width, 30)];
        
          lineView.backgroundColor = [UIColorblackColor];
          [self.view addSubview:lineView];
    
  • 參考:
  • 設定動畫 Animation
    • 主要是利用 UIView 的transform 裡面主要有三種 (可以達到放大,縮小,旋轉與Translation)
      • CGAffineTransformMakeScale
      • CGAffineTransformMakeRotation  
      • CGAffineTransformMakeTranslation
    • 詳細內容如下:

        -(IBAction)startScaleTransform:(id)sender
        {
            self.view.transform = CGAffineTransformMakeScale(2,2);
            [UIViewbeginAnimations:nilcontext:NULL];
            [UIViewsetAnimationDuration:1];
            self.view.transform = CGAffineTransformMakeScale(1,1);
            self.view.alpha = 1.0;
            [UIViewcommitAnimations];
        }
      

參考文章:

[iOS] 上架App到Apple去review前會遇到的一些問題-- 使用 XCode Organizer 來遞交App到Store

聽從一些前輩的建議,決定還是把已經寫到一半卻累到半死的iOS App來上架
這裡記錄一下上架之前會遇到的一些問題: 

首先要先連到 iTune Connect 去新增一個App(當然你需要開發者帳號,也就是付每年$99的費用)
詳細流程參考: http://www.minwt.com/ios/4726.html 

關於新增資料上面有一些翻譯可以參考: http://www.csdtn.net/article/2011-01-07/289703

資料都新增完了,就會使用Application Loader 去上傳你寫好的App,不過這裡我發生幾次錯誤,分享給大家.

使用 XCode Organizer 來遞交App到Store

詳細方式如下:

  • 先到 Xcode 裏面Project Property —> [General] —> 把 Bundle Identifier 抄下來.記得把[team] 也先選擇到你的開發者帳號

    • ex: com.XXX.youAppName
  • iTune Connect 新增App申請,詳細流程可以參考這裡.注意 Bundle ID要跟XCode裡面的相同(就算寫錯了~可以之後修改)

  • Apple iOS Developer 網站的相關處理:

    • [Certificates, Identifiers & Profiles] —>[Identifiers]—> 新增一個 iOS App ID

      • 記得這裡Bundle Identifier 要跟你Xcode設定裡面一樣

        • ex: com.XXX.youAppName

    • [Certificates, Identifiers & Profiles] —> [Provisioning Profiles] —> 新增一個[Distribution]的 Provisioning

      • 這裡的App ID要使用剛剛申請的,這裡最好是一對一的mapping 比較不會有問題.
    • 下載 Provisioning 並且點兩下安裝

  • 到Xcode 準備打包上傳

    • 先到Project Property —>[Build Setting] 搜尋 [PROVISIONING PROFILE] —> 將它改成你剛剛下載的Provisioning

    • 把device 從模擬器或是手機切換到 [iOS Device]

    • [Product] —> [Archive]

      • 如還是有出現問題請參照以上得問題解答
    • 這時候就可以做App Validation 跟 Distribute 

大致流程就是這樣~接下來我的App就等著Apple 審核~~也祝福大家都成功啦…. 

[iOS] 關於時間NSDate的相關處理

昨天把NSDate的一些處理稍微研究了一下,有一些處理方式可以整理一下:

1. 比較時間 (NSDate comparison) 

<code style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;font-family:Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif;white-space:inherit;"><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;color:#00008b;" class="kwd">if</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">([</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pln">date1 compare</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">:</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pln">date2</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">]</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">==</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;color:#2b91af;" class="typ">NSOrderedDescending</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">)<br></br></span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">{<br></br></span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;color:#2b91af;" class="typ"> NSLog</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">(@</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;color:#800000;" class="str">"date1 is later than date2"</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">);<br></br></span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">}<br></br></span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;color:#00008b;" class="kwd">else</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;color:#00008b;" class="kwd">if</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">([</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pln">date1 compare</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">:</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pln">date2</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">]</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">==</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;color:#2b91af;" class="typ">NSOrderedAscending</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">)<br></br></span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">{<br></br></span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;color:#2b91af;" class="typ"> NSLog</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">(@</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;color:#800000;" class="str">"date1 is earlier than date2"</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">);<br></br></span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">}<br></br></span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;color:#00008b;" class="kwd">else</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">{<br></br></span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;color:#2b91af;" class="typ"> NSLog</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">(@</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;color:#800000;" class="str">"dates are the same"</span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">);<br></br></span><span style="margin:0;padding:0;border:0;font-size:13.63636302948px;vertical-align:baseline;background-color:transparent;" class="pun">}</span></code>

可以拿來比較時間的先後,在NSDate大概算是最方便的.

參考: http://stackoverflow.com/questions/5965044/how-to-compare-two-nsdates-which-is-more-recent

**
**

2. 時區的轉換

使用NSDate拿來的時間都是GMT要顯示的時候得轉成當地時區(台灣是TST)

-(NSString) transferGMT2TST: (NSDate)inGMT :(NSString*) newTZ

{

    NSDateFormatter *dateFormatter = [[NSDateFormatteralloc] init];

    dateFormatter.dateFormat = @”yyyy-MM-dd’T’HH:mm”;

    

    NSTimeZone *nTZ = [NSTimeZonetimeZoneWithAbbreviation:newTZ];

    [dateFormatter setTimeZone:nTZ];

    return [dateFormatter stringFromDate:inGMT];

}

 

  1. 也可以把NSDate當成一個時間比對或是timer,可以先設定一個未來的時間,然後做時間的比較.

 

繼續研究….

[iOS] 關於一些網路上SDK的學習整理心得

整理一下最近的關於iOS一些SDK學習心得

#import “CoreData+MagicalRecord.h”

  * 建立CoreData資料庫


  * AppDelegate 加入     [MagicalRecordsetupCoreDataStackWithStoreNamed:@"CoreData.sqlite”];


  * 搞定



* 詳細流程參考[這篇文章](http://www.cnblogs.com/mybkn/p/3328183.html)
  • Inappsettingskit (http://www.inappsettingskit.com/)

    • 設定App的設定通常都是使用Settings Bundle (參考: 這裡).如果要在app裡面更改設定就得自己都出整個頁面與設定控制,這個SDK可以快速的幫助你建制起來.

    • 流程也簡單到不行:

      • 把InAppSettingsKit資料夾加入你的專案

      • IASKAppSettingsViewController *IASKAppSettingView;

 if(IASKAppSettingView == nil)

    {

        IASKAppSettingView = [[IASKAppSettingsViewControlleralloc] init];

        IASKAppSettingView.delegate = self;

    }

    UINavigationController *aNavController = [[UINavigationControlleralloc] initWithRootViewController:IASKAppSettingView];

    //[viewController setShowCreditsFooter:NO];   // Uncomment to not display InAppSettingsKit credits for creators.

    // But we encourage you not to uncomment. Thank you!

    IASKAppSettingView.showDoneButton = YES;

    [self  presentViewController:aNavController animated:YEScompletion:nil];

    //   [self SwitchPage:PAGE_SETTING];

* 中文的介紹文章: [在這裏](http://blog.csdn.net/artwebs/article/details/8295937) 

我是參考這篇文章去找出相關的SDK,把這些SDK都玩過一次之後,又想去翻動我的程式了.一直改來改去~感覺很難上架了~~

[WindowsStore] 兩個產品共用同一份程式碼 (Shared source code with different store app with different name)

起因:
主要是因為在Windows Store 上面,需要產生兩個相同的內容的產品
但是版號與產品名稱卻不能相同.

做法:

  • 原本做法

    • 複製一個相同的目錄 (原本稱為ㄓ ProjA 複製出來為 ProjB)

    • 修改 csproj 把每個檔案的鏈結改到原本的 檔案

    • 在搬移的過程中,會出現以下的錯誤

      • Could not find **.xbf in target folder
    • 查詢過後發現,XAML 的檔案無法去link具有上一層目錄的檔案架構 ( ….XXX)

      • 但是卻可以去link “XXX “
  • 解決方法:

    • 僅僅複製把ProjB 的需要的檔案到ProjA的目錄下

      • AssemblyInfo.cs (注意要改名)

      • XXX.csproj

      • XXX_StoreKey.pfx

      • XXX_TemporaryKey.pfx

      • Package_StoreAssociation.xml (注意要改名)

      • Package.appxmanifest(注意要改名)

    • 這樣下來是有點醜,因為 bin/obj 會共用~這個之後會再仔細觀察是否有任何問題

    • 此外需要改兩個東西

      • 在 csproj 裡面
      • 在 Package.appxmanifest與Package_StoreAssociation.xml 內
    • 這樣就可以產生兩個一樣內容的app 在你的桌面

參考:

http://stackoverflow.com/questions/8162179/how-do-i-install-two-versions-of-my-metro-app