Sean's Note: 2015

2015年12月30日 星期三

Default highlight behavior of ListView when clicking item

如果 adapter 裡的 view item 都不能觸發點擊事件的話:
android:clickable="false"
android:focusable="false"
點選每一行就會有 default(透明灰的) 的 highlight,
如果有設  setOnItemLongClickListener,就會有長按的效果。
反之,如果有 CheckBox 或 EditText,其屬性又沒有設成 false 的話,
就不會有 default 的效果。

2015年11月12日 星期四

How to check ProGuard obfuscation has worked?

用 Android Studio build 完 APK 檔之後,
在 C:\{Project}\{Application Project}\build\outputs\mapping\release 下會生成四個檔案:
  • dump.txt
    描述了 APK 檔裡所有 class 檔案的架構。
  • mapping.txt
    class, method, field 混淆前後的名稱。 
  • seeds.txt
    沒有被混淆的  class, method, field 名稱。 
  • usage.txt
    被 ProGuard 拿掉的 class, method, field。

How to use proguard for library project(module)?

發現如果在 application project 跟 library project 裡都有定義

buildTypes {
      release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
      }
}

就會有一堆 cannot find symbol class 的 errors。
解決的方法有兩種:
  1. 把 library project 的 proguard-rules.pro 裡的定義都搬到 application project 的 proguard-rules.pro,再把 library project 的 minifyEnabled 設為 false。
  2. 另一種就是把 library project 的 proguardFiles 改成 consumerProguardFiles 就行了 (minifyEnabled 也不須設定,minifyEnabled 是對應 proguardFiles 的)。
consumerProguardFiles 的定義是:
ProGuard rule files to be included in the published AAR.
These proguard rule files will then be used by any application project that consumes the AAR (if ProGuard is enabled).
This allows AAR to specify shrinking or obfuscation exclude rules.
This is only valid for Library project. This is ignored in Application project.

Ref:
  1. http://stackoverflow.com/questions/30820915/android-studio-proguard-handling-in-multi-library-projects
  2. http://google.github.io/android-gradle-dsl/current/com.android.build.gradle.internal.dsl.BuildType.html


2015年11月5日 星期四

How to access data/data folder in Android device without root?

透過 adb 指令可以備份資料到當前的目錄下:
adb backup -noapk com.your.packagename
裝置上就會跳出一個視窗問你要不要備份,並輸入密碼,建議是不要輸入密碼會比較方便。
另外,備份的檔案為壓縮過 .ab 檔,必須還得用 tool Android Backup Extractor 解開,
然後再輸入以下指令解壓縮:
java -jar abe.jar unpack backup.ab backup.tar <password>
解完就可以看到 Preference 的 XML 檔案和 DB,
其他 Private Folder 的內容好像看不到。

2015年10月29日 星期四

Material Design

What is material?

Android 從 5.0 開始,引進了 Material Design 的概念,Material 即為物質之意,
而 Material 所處的環境是一個 3D 的空間。利用光和影 (Light and shadow) 的效果,
可以使 UI 元件看起來更為立體 (圖1)。

圖1. 圖片來源: Google Design

Material properties

Google 對 Material 的屬性提出了幾點規定:
  1. Material 應保持著 1dp 的厚度(高度)。
  2. Material 的陰影效果(Shadow) 應隨其高度而反映。
  3. Input events 不能橫跨 material 與 material 之間。
  4. 兩個 material 不能在同一高度上重疊。
  5. Material 可以改變其大小與形狀 (x, y 軸),但僅限於在原本的高度(z 軸)上。
  6. Material 不能被對摺 (fold)。
  7. 高度上的改變,通常是與使用者的互動所造成的。
  8. 更多詳見 Material properties

Elevation and shadows

Google 也對不同的 UI 元件定義的不同的高度。詳見 Elevation and shadows


2015年10月28日 星期三

A tool for reverse engineering Android apk files

由於 APK 檔裡的 AndroidManifest 已經組譯過,看到的是亂碼,
可以用第三方的工具來反組譯,就看的到了~
http://ibotpeaches.github.io/Apktool/

2015年10月16日 星期五

To get how many methods used in an APK.

GitHub 上有熱心的大大寫出了一個 tool (jar),可以用來查看 APK 用了多少 methods。
不過有點小麻煩的是得把 project 抓下來,自己 build 出 dex-method-counts.jar。
我把它  build 好放在 Google Drive 上方便懶人們直接使用:
https://drive.google.com/file/d/0B2EwSdT6jFo8ZWs2MlhxVzZwaU0/view?usp=sharing

Check following link for details:
https://github.com/mihaip/dex-method-counts

更有人把它進一步整成了 Android Studio 的 plugin:
https://github.com/KeepSafe/dexcount-gradle-plugin

2015年9月14日 星期一

Can I package Android resources to jar?

當然我們可以不用 jar,讓其他人直接 import 我們的 library project,
可是這樣程式碼不就被看光光了嗎?
但當我們的 library 有自己的 resources,而且想 build 出 jar 檔給其他人用時該怎麼辦?
我可以把 resources 也包進去嗎?
The answer is No!
但有其他替代的方法來解決這樣的問題。
畫了幾張圖來說明:
(一樣一開始準備一個要 build 出 jar 檔的 "myLib" project)
問題在於 Android 的每個 project 會產生自己的 R 檔,
在編譯的時候會自動把程式碼裡的 R.xxx 轉換成 id,
但 "myLib" 引用到的 id 不在於 jar 檔裡也不在於 apk 檔裡,
所以得把 "myLib" 用到的 resources 給搬到 "myAppTestbed" 主要應用程式的 project,
這樣 apk 檔裡就會有這個 id,然後再動態的去引用這些 resources。
Android 提供了一個 API Context.getResources().getIdentifier() 
可以在 runtime 的時候取得 resources。
流程如圖1 所示。

圖1. 

圖2. 
但是把 resources 都搬到 main project 也不是很好,一來開發 main project 的人
得手動搬一次,二來弄髒了 main project 的 resource folder。
所以我們可以參考 google-play-services_lib 的作法,
再另建一個 lib project "myLibWrapper"(名字可能取的不太好,大家可以自行決定),
這個 "myLibWrapper" 的主要功能有三個:
  1. 引用 myLib.jar。
  2. 存放 myLib.jar 所需的 resources。
  3. 讓 main project 引用。
這樣一來開發 main project 的人,只要 import "myLibWrapper",
並設定 dependencies 的關係就好,雖然迂迴了一下,但是簡單又乾淨。
詳細如圖3 的流程所示。
(當然弄成 aar 更簡單,不過這又是另一個議題了)

圖3.

2015年8月18日 星期二

Writing DPI-Aware Desktop and Win32 Applications

在 Windows 上,依據 DPI 的設定可將應用程式分為三類:

Not DPI-aware Applications

沒有設定 DPI-aware 的應用程式,皆以 96 DPI 做 rendering。
系統的 Desktop Window Manager (DWM) 會自動縮放 (virtualizes and scales) 這些應用程式。


System-DPI Aware Application

從 Windows Vista 開始到 Windows 8,應用程式可以依據系統 DPI 的設定,自己去做最佳化,而不是透過 DWM 來做。但如果是在不同 DPI 的螢幕間移動,DWM 還是會自動縮放。 


Per Monitor-DPI Aware Applications

從 Windows 8.1 開始,多了這個類別。當使用者改變了 DPI 設定或在不同 DPI 的螢幕間移動時,這些應用程式將會動態縮放。DWM 同樣不會去自動縮放這些應用程式。

DPI Virtualization and Scaling 

從 Window Vista 開始,引進了新功能  DPI virtualization,這項功能會將沒有設定 DPI-aware 的應用程式,自動縮放其文字和其他 UI 元件。這樣應用程式的字在 high DPI 的螢幕上就不會看起來特別的小。而從 Window 8.1 開始,延展了 DPI virtualization 的功能,DPI 從系統層級變成螢幕層級。


Supporting Dynamic DPI Changes

那應用程式該做哪些事情來支援 DPI 的動態設定呢?
  1. Set the DPI Awareness Level
    可以透過 manifest 檔或 SetProcessDpiAwareness API 來做設定。
  2. Get the DPI for the current monitor
    呼叫 GetDPIForMonitor API 來取得值。
  3. Listen for DPI changes
    聆聽 WM_DPICHANGED 這個 windows message。
  4. Respond to DPI changes
    收到上述訊息後,改變視窗大小或縮放 UI 元件等等。

Ref : Writing DPI-Aware Desktop and Win32 Applications

2015年8月4日 星期二

[sqlite3] Auto-commit will be inefficient for multiple SQL statements.

我們通常會用下列的程式碼來建立 DB 的連線,並指明是 auto-commit 的。

# Set isolation_level=None to use auto-commit
conn = sqlite3.connect('example.db', isolation_level=None)

來實驗看看執行 10000 次 INSERT 的敘述,

for i in range(0, 10000):
    conn.execute("INSERT INTO stocks VALUES ('2006-01-05','BUY','RHAT',100,35.14)")

居然要 657 秒!! 
因為每一筆 INSERT 都是一筆 Transaction,太花時間了,
所以如果我們只建立一筆 Transaction,
然後把 Statements 都塞到同一個 Transaction 再執行:

conn.execute("BEGIN IMMEDIATE TRANSACTION");
# Insert a row of data
for i in range(0, 10000):
    conn.execute("INSERT INTO stocks VALUES ('2006-01-05','BUY','RHAT',100,35.14)")
conn.execute("COMMIT TRANSACTION");

結論: 發現只要 0.17 秒,差了快 400 倍啊!!

2015年7月30日 星期四

Some notes for Fragment's usage

Subclass Fragment

繼承 Fragment 時,要提供 Public empty constructor,否則會出現下列錯誤訊息:

android.support.v4.app.Fragment$InstantiationException: Unable to instantiate fragment 省略: make sure class name exists, is public, and has an empty constructor that is public ...

詳見: http://blog.csdn.net/xplee0576/article/details/43057633

還有 subclass of fragment 如果是 non-static inner class,既使提供了 Public empty
constructor,仍會出現錯誤而且還會造成 Memory Leak(Implicit reference to Activity),
必須是 static class 或 non-inner class。

Fragments Communication

Fragments 的溝通可以分成下列三種情況:
  • Activity to Fragment
    1. Activity 建立 bundle 物件 並透過 Fragment.setArguments(bundle) 來傳遞;而fragment 則透過 Bundle bundle = getArguments() 來接收。
    2. 在 Fragment 中建立 public method 來讓 Activity 引用。
      EX: getSupportFragmentManager().findFragmentById(R.id.fragment).publicMethod(args)
  • Fragment to Activity
    1. 利用 interface(listener) 的方法,讓 Activity 實作 listener。
    2. 在 Activity 建立 public method 讓 fragments 引用。
  • Fragment to Fragment 
    1. 利用上述兩種方式來實現。

How to draw radius background to Button?

在 drawable 資料夾裡,新增一個 xml,定義 shapre 物件和 radius 屬性,範例如下:

// bg_selector_radius_button.xml
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
     <selector>
      <item android:state_pressed="true">
        <shape 
          android:shape="rectangle">
          <corners android:radius="2dp" />
          <solid android:color="#ffff00" />
        </shape>
      </item>
      <item  android:state_enabled="false">
        <shape 
          android:shape="rectangle">
          <corners android:radius="2dp" />
          <solid android:color="#999999" />
        </shape>
      </item>
      <item>
        <shape 
          android:shape="rectangle">
          <corners android:radius="2dp" />
          <solid android:color="#ff0000" />
        </shape>
      </item>
     </selector>
    </item>
</layer-list>

2015年7月22日 星期三

[Android Studio] Code Completion Case Sensitive Setting

Android Studio 的 Code Completion 預設是 Case sensitive 的,
更改設定的方法:
Settings(or Preferences in mac) -> Editor -> Code Completion -> Case sensitive completion -> None

[Android Studio 1.2.2] Debugger stuck

最近開始改用 Android Studio,卻發現 debug 的時候常常卡住,
但 Android Studio 本身並沒有 hang, 後來發現網路上有很多人 report Google 這支 issue,
其中 issue 172523 裡有 Google 人員提到暫時的 workaround 方法:

"For anyone else who might encounter this issue, here is a summary:

The issue shows up in one of two ways: Studio will be responsive, but the debugger will be stuck at either "Collecting Data.." or "Waiting for last debugger command to complete..". This happens on both Dalivk and ART, so all versions of the platform are affected. The issue is more prevalent with Studio 1.2, but exists on all versions of Studio.

The correct fix for this issue is in the platform. The next version of M preview is likely to have this fix (in progress CL here: https://android-review.googlesource.com/#/c/152715/)

Until then we have some workarounds which reduce the probability of hitting this issue. So if you encounter this issue, you can try one of the following:

1. Change your breakpoint to only suspend the thread where it is hit rather than all threads. See comment #82 for more info on how to do this. The next release of Studio 1.2 and Studio 1.3 will be make this the default. (https://android-review.googlesource.com/#/c/152715/)

2. You can turn off various settings in the debugger that invoke methods: These include:
  a) inline debugging (https://www.jetbrains.com/idea/help/inline-debugging.html)
  b) "Enable 'toString()' object view" (Settings | Debugger | Data Views | Java)
  c) "Enable alternative view for Collections classes" (Settings | Debugger | Data Views | Java)

The 2nd option is more severe (it limits the amount of automation the debugger does for you), so we are not enabling that by default. However, if you still see the issue after changing the suspend policy to thread only, then unfortunately, you'll have to do the steps in 2 as well.

Finally, if you still see the issue after both, then that would be a new bug. Please file a new bug with a test case.

Thanks everyone for your patience and your help in providing us with repro cases and stack traces."


結論是 2.a 的 workaround 似乎可行。

2015年6月18日 星期四

Set maximal text length of EditText

In XML:
android:maxLength="20"

In Code:
// EditText 並沒有直接提供 setMaxLength() 這類的方法,所以只能透過 setFilters 來達成
int maxLength = 20;
editText.setFilters(new InputFilter[] {new InputFilter.LengthFilter(maxLength)});


2015年6月14日 星期日

[讀書筆記] Refactoring - CH9 - 10

Chapter 9. Simplifying Conditional Expressions

Decompose Conditional
把複雜的 conditional expressions 寫成回傳布林值的函式也許會更清楚。(視情況而定)

Consolidate Conditional Expression
把多個有相同結果的判斷式,利用 Ors 和 Ands 寫成一個判斷式。(視情況而定)

Consolidate Duplicate Conditional Fragments
簡而言之,如果 if 和 else 裡有部分同樣的邏輯,何不把它搬到外面呢?

Remoce Control Flag
有些人會用 control flag 來決定是否終止 while 或 for 迴圈,何不直接用 break 和 return 呢?但對於多層迴圈,control flag 還是蠻好用的。

Replace Nested Conditional with Guard Clauses
把巢層的判斷式改寫成單層的判斷式。(看範例比較清楚)

Remove Condition with Polymorphism
這取決於有沒有要把 Object type 改寫成 subclass。

Introduce Null Object
可能得要多寫好幾個 Null classes...

Introduce Assertion
使用 assertion 可以即使反應 programmer 使用函式時的錯誤。


Chapter 10. Making Method Calls Simpler

Rename Method
簡單又常用,沒什麼好說的。

Add Parameter/Remove Parameter
...

Separate Query from Modifier
範例舉得好像不太好。

Parameterize Method
如果有幾個函式有著相同的邏輯依賴於某些變數,可以寫成一個帶有參數的函式。

Replace Parameter with Explicit Method
根本就不會想寫出課本舉的第一個範例,第二個 factory method 的範例倒是可以參考。

Preserve Whole Object
如果有個函式傳遞某個 object 的多個成員變數當作參數,不如傳遞整個 object 吧。

Replace Parameter with Method
a = funcA(); funcB(a); 可以的話,考慮直接在 funcB 裡,直接呼叫 funcA。

Introduce Parameter Object
如果有一些函式的參數列都一樣,隱含著我們可以為此建立一個 object 來持有這些資料,然後 Preserve Whole Object。

Remove Setting Method
封裝成員變數,public to private + accessors。如果一個成員變數是 immutable 的,就不要提供 setting method。

Hide Method
如果函式沒要給其它人用,就把它設成 private 吧。

Replace Constructor with Factory Method
...

Encapsulate Downcast
return Object 讓 caller 去做 cast,不如 cast 完再 return 回去。

Replace Error Code with Exception
...

Replace Exception with Test
...

2015年6月1日 星期一

Android Dashboards 2015.6.1

Android 官網公布了六月的各版本使用率圖表,看來目前 Android 4.4 還是佔大宗。

圖片來源: http://developer.android.com/about/dashboards/index.html



2015年5月13日 星期三

Using StateListDrawable

想要在 View 上呈現 Click 的效果(顏色的變化),用 XML 來實作,只要在 drawable
檔裡定義 selector 屬性,然後將該 drawable 設給該 View 的 android:background
即可。範例如下:
  
// color_selector_view_bg
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
    <item 
        android:state_pressed="true"
        android:drawable="@color/grey"
         >
    </item>
    <item 
     android:drawable="@color/white"
         >
    </item>
</selector>
  
<view>
  android:id="@+id/view_header" 
  android:layout_height="wrap_content" 
  android:layout_width="wrap_content">
  android:background="@drawable/color_selector_view_bg"
</view>

但想要用程式碼來實作的話,就稍微麻煩一些些,得用到 StateListDrawable 這個類別,
來設定個別狀態所對應的 drawable。加入的順序不同,行為也不同,越廣義的狀態要
晚加入,例如 state_enabled 就應該要最後加入。
以下是範例:
  
StateListDrawable stateListDrawable = new StateListDrawable();
stateListDrawable.addState(new int[]{android.R.attr.state_pressed}, new ColorDrawable(colorGrey));
stateListDrawable.addState(new int[]{android.R.attr.state_selected}, new ColorDrawable(colorWhite));
stateListDrawable.addState(new int[]{android.R.attr.state_enabled}, new ColorDrawable(colorwhite));
view.setBackground(stateListDrawable);

另外要注意,每個 view 不能共用 StateListDrawable,尤其是應用在 ListView 和
GridView 上,否則按下 A view,B view 也跟者改變了。

Show error on EditText to inform user

現在才發現原來 EditText 有個 showError() 的方法,

可以提示使用者輸入錯誤或未輸入的提示。

Ref: http://www.technotalkative.com/android-show-error-in-edittext/

2015年4月9日 星期四

Calculate UILabel for proper height

在多國語言中,最常見發現的問題就是字串 truncated,所以在拉 UILabel 時,
高就不能設死,如果設太小,落落長的語言如西文和德文,就會 truncated,
設太多又會讓簡短的繁簡中留有太多空白,這時就可以用計算的方式來設得剛剛好。
NSString 底下有個方法可以用來計算:

- boundingRectWithSize:options:attributes:context:

+ (CGFloat)getMaximalHeightForText:(NSString *)text withFont:(UIFont *)font withWidth:(CGFloat)width {
    CGRect frame = [text boundingRectWithSize:CGSizeMake(width, CGFLOAT_MAX) // Give CGFLOAT_MAX for height to calculate desired height.
                                      options:(NSLineBreakByWordWrapping | NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading) 
                                   attributes:@{NSFontAttributeName:font}
                                      context:nil];
    return frame.size.height+5; // 5 is added for compensation.
}

2015年4月7日 星期二

[讀書筆記] Refactoring - Chapter7 - 8

Chapter 7. Moving Features Between Objects

Move Method
如果一個在 Class A 的函式 M 用到了更多 Class B  的變數或方法,那是不是可以考慮將函式 M 移至 Class B?

Move Field
與 Move Method 同理。

Extract Class
檢視一個 Class 是否可以利用 Move Method 和 Move Field 在拆分為多個 Class。

Inline Class
與 Extract Class 反之。

Hide Delegate
Client 想取得 Class A 的 delegate Class B 的某資訊時,不需將 Class B 完全揭露給 Client,可以在 Class A 新增一個函式將資訊回傳即可。

Remove Middle Man
與 Hide Delegate 反之,如果上述新增的方法太多,就直接揭露吧。

Introduce Foreign Method
當在使用一個無法更動的 Server Class 時,考慮在 Client 裡新增一個以 Server Class 為參數的函式吧。

Introduce Local Extension
當有太多的 Foreign Method 時可考慮 subclass 或 wrapper Server Class。Wrapper 比較麻煩的是得寫出原 Server Class 所提供的函式。

Chapter 8. Organizing Data

Self Encapsulate Field
對於 Class 自身的成員變數,可不可以直接存取,還是要透過 accessors,有兩方學派的人馬各持己見。如果 Class 本身有可能被 subclass,且 Subclass 對於成員變數有不同的定義或計算方法,accessors 仍然是解決的方法,此外,直接存取也無妨。

Replace Data Value with Object
當資料可能有其他子資料或行為時,考慮把它寫成物件吧。

Change Value to Reference
EX: 把原本 Order 與 Customer 的組合關係(Composition),改成一般的結合關係(Associations),同名的 Custom 應該只有一個 Object。

Change Reference to Value
EX: 把原本 Company 與 Currency 的結合關係(Associations),改成組合關係(Composition)。我們不在乎兩個 "USD" Currency 是不是同一個 Object,只要知道他們相等就好。

Replace Array with Object
Array 應該是拿來放有相同型態的物件集合,如果要放不一樣的,不如使用物件吧。

Duplicate Observed Data 
Observer pattern 

Change Unidirectional Association to Bidirectional 


Change Bidirectional  Association to Unidirectional


Replace Magic Number with Symbolic Constant
這還蠻常用的,提高可讀性。

Encapsulate Field
封裝成員變數,public to private + accessors。

Encapsulate Collection
add + remove - setter method。

Replace Record with Data Class
封裝成員變數,public to private + accessors

Replace Type Code with Class
實用。

Replace Type Code with Subclass
如果 type code 影響了 class 的行為,考慮繼承吧。

Replace Type Code with State/Strategy
詳見範例。

Replace Subclass with Fields 
用 Factory Method 來取代 constructor,詳見範例。

2015年3月27日 星期五

[iOS8] 隱私權設定

iOS 的隱私權,譬如說“相機”,一但被應用程式請求過,
在“設定”->“隱私權”-> “相機”的列表就會出現該應用程式,
而下次應用程式就無法在請求權限,使用者只能到設定去更改,
即使重新安裝也一樣。若開發時,想要測試,可以到“設定”->“一般”->“重置”->
“重置定位服務與隱私權”去重置設定,這樣應用程式就可以再次請求使用者。

2015年3月12日 星期四

[讀書筆記] Refactoring - Chapter2 - 6

Chapter 2. Principles in Refactoring

Refactoring (noun): a change made to the internal structure of software to make it
easier to understand and cheaper to modify without changing its observable behavior.

Refactor (verb): to restructure software by applying a series of refactorings without
changing its observable behavior.


The Rule of Three: Three strikes and you refactor. 
開始做第一件事情的時候,就毫無顧忌的直接做吧。當開始做第二件相似的事情,瞥一下重複的地方,但還是一樣照做。直到第三次又做相似的事情,就重構吧!

Chapter 3. Bad Smells in Code

看完第六章再來看這章可能會比較清楚。

Duplicate Code

Long Method
當發現有註解(Comments)在說明一區甚至是一行的程式碼時,可以考慮將其取出,另寫成函式(Extract Method),配上清楚的函式名稱,也許會更清楚易懂。

Large Class
當一個類別極為龐大時,通常也隱含的許多變數的存在,當一個類別有許多變數,那也有極大的可能出現重複的程式碼,此時我們可以用 Extract Class 或 Extract Subclass 來解決問題 。

Long Parameter List


Chapter 6. Composing Method

前面的章節落落長說了一堆,終於來到比較有重點的一章。

Extract Method
如同其名,大概也是最常用到的 Refactoring 方法。可分成三種情形:
1. 沒有區域變數 - 直接就把一段程式碼搬到另一個函式 void newMethod()。
2. 有區域變數,且其值不因新函式而改變  - 把區域變數當成新函式的參數 void newMethod(int param1, ...)。
3.1. 有區域變數,且其值因新函式而改變,且供後段程式碼所用 - 把區域變數當成新函式的回傳結果 int newMethod()。
3.2. 有區域變數,且其值因新函式而改變,但不為後段程式碼所用 - 把區域變數當成新函式的區域變數。

Inline Method
剛好與 Extract Method 相反,當一個函式裡的程式碼,簡單到一看就知道在做什麼時,可以直接把函式拿掉,而把程式碼搬進 Caller 裡。

Inline Temp
這好像比較少遇到,例子中是說如果有一暫存變數只被簡單的 expression 賦值了一次,然後就當作結果回傳,這個暫存變數可以被省略,直接回傳 expression 。

Replace Temp with Query
如果有一暫存變數只被賦值了一次,可以用一個函式將其取代,結果雖然會發現新的函式因為取代了暫存變數而被引用了多次,感覺上會有效率上的問題(因為重複計算了多次),但繼續 refactor 下去時,有時會有意想不到的發現(程式碼變得更乾淨清楚了)。

Introduce Explaining Variable
這可以用來提高條件判斷式的可讀性,如果一個條件判斷式裡有很多條複雜的邏輯判斷,我們也許可以將其分別取出,用幾個命名清楚的布林變數來取代。也適用於複雜的 expression。不過有時也會考量用 Extract Method。

Split Temporary Variable
簡單來說就是不要讓一變數重複賦值,如果有不同目的,那就在宣告一個新的變數吧!

Remove Assignments to Parameters
不要讓函式裡的參數再次賦值,這並不會引響外部的結果,又容易造成混淆,定義一個新的變數吧!

Replace Method with Method Object
有些很長的函式難以用 Extract Method 來 Refactor 時,就可以用此法。建立一個 Class 來取代原函式,原 Class、參數和區域變數成為新 Class 的成員變數。

Substitute Algorithm
砍掉重練!(誤)



2015年2月17日 星期二

initWithNibName 和 loadNibNamed 方法的區別

initWithNibNameloadNibNamed 都可以用來建立 Custom ViewController,
// 延遲載入,用到時才會值
MyViewController *viewController = [[MyViewController alloc] initWithNibName:@"MyViewController" bundle:nil];

MyViewController *viewController = [[[NSBundle mainBundle] loadNibNamed:@"MyViewController" owner:self options:nil] objectAtIndex:0];

但是在 NIB 上的設定會有不同:
initWithNibName 的 XIB 需要在 Placeholders 設置 File's Owner 的 Custom Class,
View Controller 則不須設置 Custom Class。而且拉的元件是 UIView。
反之,用 loadNibNamed 的 XIB 不能在 Placeholders 設置 File's Owner 的 Custom
Class,而 View Controller 則須設置 Custom Class。而且拉的元件是 UIViewController。
若兩者誤用,就會跑出 setValue:forUndefinedKey 的錯誤。

2015年2月11日 星期三

[讀書筆記] Effective Objective-C - Chapter4

4. Protocols and Categories

Item 23: Use Delegate and Data Source Protocols for Interobject Communication 

  1. A delegate property will always be defined using the weak attribute.
  2. If required, implement the bitfield structure approach to cache which methods a delegate responds to from the protocol. 

Item 24: Use Categories to Break Class Implementation into Manageable Segments

  1. Use categories to split a class implementation into more manageable fragments.
  2. Create a category called Private to hide implementation detail of methods that should be considered as private.

Item 25: Always Prefix Category Names on Third-Party Classes

Item 26: Avoid Properties in Categories

  1. Can use associated objects instead, but not good enough.

Item 27: Use the Class-Continuation Category to Hide Implementation Detail

Item 28: Use a Protocol to Provide Anonymous Objects 

2015年2月10日 星期二

[讀書筆記] Effective Objective-C - Chapter3

3. Interface and API Design

Item 15: Use Prefix Names to Avoid Namespace Clashes

  1. Choose a class prefix that befits your company, application, or both. Then stick with that prefix throughout.
  2. If you use a third-party library as a dependency of your own library, consider prefixing its names with your prefix. 

Item 16: Have a Designated Initializer

  1. Throw an exception in initializers overridden from superclasses that should not be used in the subclass. 

Item 17: Implement the description Method

  1. -(NSString*)description,   -(NSString*)debugDescription

Item 18: Prefer Immutable Objects

Item 19: Use Clear and Consistent Naming

  1. Verbose but clear naming of methods is more acceptable in Objective-C than in other languages.
  2. Avoid abbreviations, such as str, and instead use full names, such as string.

Item 20: Prefix Private Method Names

  1. Suggestion: Use "p_" as prefix for private methods.
  2. Avoid using single "_" as the method prefix, since this is reserved by Apple.

Item 21: Understand the Objective-C Error Model

  1. Any objects that should be released at the end of a scope in which an exception is thrown will not be released. (-fobjc-arc-exceptions)

Item 22: Understand the NSCopying Protocol

  1. To support copying: 
    1. Implement NSCopying protocol. 
    2. Override -(id)copyWithZone:(NSZone*)zone or -(id)mutableCopyWithZone:(NSZone*)zone 
  2. If your object has mutable and immutable variants, implement both the NSCopying and NSMutableCopying protocols.

Core Data framework

Managed Objects and Contexts

可以把 managed objects contexts 想像成是智慧型的便條紙,
當從 persistent store 獲取資料時,這些資料的副本就會記錄在便條紙上,
並形成 object graph,這些副本可以被修改,直到儲存後 persistent store
也才會真的改變。
Model objects(詳見 MVC Pattern) 在 Core Data framework 中即是
managed objects,所有的 managed objects 都須向 managed object context
註冊,不管新增或移除 objects 都是透過 context。
每個 Application 可以有多個 managed object context 。

參考: http://www.cocoanetics.com/2012/07/multi-context-coredata/

Fetch Requests

可以建立 fetch requests(NSFetchRequest) 向 managed object context 擷取資料,
由於所有的 managed objects 都須向 managed object context 註冊,所以這些回
傳的 objects 將自動與請求的 context 註冊。

Persistent Store Coordinator

介於 managed objects contexts 和 persistent object stores 的是 persistent store
coordinator(圖1),

圖1. (來源: iOS Developer Library)