新年之際,盤點一些APP開發技巧

來源:互聯網
上載者:User

標籤:

本文轉載至 http://www.cocoachina.com/ios/20150114/10912.html回顧過去一年發生在我們身邊的事情時,有一點不得不提:對蘋果開發人員來講,2014年是令人難以置信的一年。在這短短的一年中(有關APP的開發)發生了如此多的變化:在充滿吸引力的Swift面前,我們幾乎忘了之前是如何癡迷於Objective-C;以及充滿想象力的iOS 8和WatchKit,難以想象還有什麼API能與之相比。

NSHipster的慣例:請可愛的童鞋們,在新年的第一天,為大家展示你們(在開發中)常使用的技巧和方法。如今,隨著來自庫比蒂諾(Cupertino,蘋果總部,位於舊金山)和眾多開源社區的一系列API的湧現,媽媽再也不用擔心我們找不到有趣的東西來分享啦!

在此,感謝以下童鞋們所做的貢獻:

Colin Rofls, Cédric Luthi, Florent Pillet, Heath Borders, Joe Zobkiw, Jon Friskics, Justin Miller, Marcin Matczuk, Mikael Konradsson, Nolan O‘Brien, Robert Widmann, Sachin Palewar,Samuel Defago, Sebastian Wittenkamp, Vadim Shpakovski, and Zak

成員函數的提示
(來自 Robert Widmann)

在用靜態方式調用Swift類和結構中的成員函數時,通常使用以下格式:

Object->(參數)->Things

比如,你可以用以下兩種方式調用reverse():

12 [1,2,3,4].reverse( )Array.reverse([1,2,3,4])

用@()來封裝C字串
(來自 Samuel Defago)

事實上文字大部分時候是數字和字母的集合,使用C字串,尤其當我在使用運行時編碼的時候,我常常會忘記用UTF8編碼、以NULL結束:Objective-C字串封裝:

123 NSString *propertyAttributesString =    @(property_getAttributes(class_getProperty([NSObject class], "description")));// [email protected]"NSString",R,C

AmIBeingDebugged

Nolan O‘Brien在這篇Q&A技術文檔中讓我們注意到了AmIBeingDebugged函數方法:

使用延遲儲存屬性
(來自 Colin Rofls)

在開發過程中,應該避免使用Optionals類型,更不應該使用隱式解包optionals類型。你想聲明一個var變數卻不想給一個初始值?使用“lazy”吧,唯一要注意的就是:在你的屬性被賦值之前不要調用getter方法即可(童叟無欺!)

1 lazy var someModelStructure = ExpensiveClass()

假如你僅僅對這var變數調用set方法,而沒有調用getter方法的話,這個被lazy修飾的var變數不會被賦值。例如,用lazy修飾那些直到viewDidLoad時才需要初始化的views變數就會非常合適。

擷取Storyboard視圖容器裡的子視圖控制器
(來自 Vadim Shpakovski)

有一個比較方便的方法來擷取故事板視圖容器裡的子視圖控制器:

123456789101112131415161718192021222324 // 1. A property has the same name as a segue identifier in XIB@property (nonatomic) ChildViewController1 *childController1;@property (nonatomic) ChildViewController2 *childController2; // #pragma mark - UIViewController - (void)prepareForSegue:(UIStoryboardSegue *)segue                 sender:(id)sender{    [super prepareForSegue:segue sender:sender];     // 2. All known destination controllers assigned to properties    if ([self respondsToSelector:NSSelectorFromString(segue.identifier)]) {        [self setValue:segue.destinationViewController forKey:segue.identifier];    }} - (void)viewDidLoad {    [super viewDidLoad];     // 3. Controllers already available bc viewDidLoad is called after prepareForSegue    self.childController1.view.backgroundColor = [UIColor redColor];    self.childController2.view.backgroundColor = [UIColor blueColor];}

重複運行項目,不重複構建項目
(來自 Heath Borders)

假如你一直在不停地調試同一個問題,你可以在不重複構建的情況下運行你的APP,這樣:“Product>Perform Action>Run without Building” (快速鍵??R: Command + R)

快速擷取Playground資源
(來自 Jon Friskics)

Swift裡的所有Playground共用相同的資料目錄:/Users/HOME/Documents/Shared Playground Data

如果你喜歡使用很多Playgrounds,你將需要在上述共用目錄下為每個Playground建立對應的子目錄,來儲存每個Playground用到的資料;但是那之後你需要告訴每個Playground在哪兒可以擷取其對應的資料。下面是我常用的一個輔助解決方案:

123 func pathToFileInSharedSubfolder(file: String) -> String {    return XCPSharedDataDirectoryPath + "/" + NSProcessInfo.processInfo().processName + "/" + file}

processName屬性是Playground檔案的名字,因此只要你已經在Playground資料共用檔案目錄下以相同的名字建立了一個子目錄,那麼你可以很容易訪問這些資料,和讀取本地JSON資料一樣:

123 var jsonReadError:NSError?let jsonData = NSFileManager.defaultManager().contentsAtPath(pathToFileInSharedSubfolder("data.json"))!let jsonArray = NSJSONSerialization.JSONObjectWithData(jsonData, options: nil, error: &jsonReadError) as [AnyObject]

....或者 訪問本地圖片

12 let imageView = UIImageView()imageView.image = UIImage(contentsOfFile: pathToFileInSharedSubfolder("image.png"))

-----------------------------------------------------------------------

Please attention!本篇文章剩餘的部分來自Cédric Luthi大神的貢獻,他分享了一些比較有用的開發技巧和技術,這些內容足夠自成一篇,值得細細品讀。這裡再次感謝Cédric!

CocoaPods大揭秘

這兒有一個快速的方法來檢查APP裡用到的所有pods:

1 $ class-dump -C Pods_ /Applications/Squire.app | grep -o "Pods_\w+"

CREATE_INFOPLIST_SECTION_IN_BINARY

注意Xcode中為命令模式APP(command-line apps)設定的CREATE_INFOLIST_SECTION_IN_BINARY屬性。這比使用-sectcreate__TEXT__info_plist連結標誌位更加容易,前者還把已經編譯好的Info.plist檔案嵌入在二進位編碼中。

關於如何向蘋果提需求,它也給我們上了一課,這個特性需求早在2006年的 rdar://4722772 被提出,但直到7年後才被滿足。

(譯者註:言外之意是它是反面教材,應該更有技巧的提需求)

禁用 dylib鉤子
(來自 Sam Marshall)

Sam Marshall這個技巧可謂是走自己的路,讓駭客無路可走。

在你的“Other Linker Flags”裡加上下面這行:

1 -Wl,-sectcreate,__RESTRICT,__restrict,/dev/null

NSBundle -preferredLocalizations

某些時候,你需要知道APP當前使用的是什麼語言。通常,大家會使用NSLocal+preferredLanguages. 可惜的是這個方法不會告訴你APP實際呈現的文字語種。你僅僅會得到iOS系統裡“Settings->General->Language&Region->Preferred Language”列表中的選項,或者OSX系統裡“System Preferences->Language & Region->Preferred Languages”列表中的選項。想象一下:優先語言列表中只有{英語,法語},但你的APP僅使用德語;調用[[NSLocal preferredLanguages] firstObject]返回給你的是英語,而不是德語。

正確的方法是用[[NSBundle mainBundle] preferredLocalizations]方法。

蘋果的開發文檔是這樣說的:

一個包含了在bundle中本地化的語言ID的NSString對象的數組,裡面的字串排序是根據使用者的語言喜好設定和可使用的地理位置而來的。

NSBundle.h裡的備忘:

一個bundle中本地化的子集,重新排序到當前執行壞境的優先序列裡,main bundle的語言順序中最前面的是使用者希望在UI介面上看到的語種。

當然你也許需要調用這個方法:

1 NSLocal+canonicalLanguageIdentifierFromString:

來確保你使用的文字語種是規範的語種。

保護SDK標頭檔

如果你用dmg安裝Xcode,那麼看看這篇Joar Wingfors的文章,它講述了如何通過保留所有權來避免SDK標頭檔被意外修改:

1 $ sudo ditto /Volumes/Xcode/Xcode.app /Applications/Xcode.app

任意類型的執行個體變數檢測

為了達到逆向處理的目的,查詢對象的執行個體變數是一個常見可靠的途徑。通常調用對象valueForKey:方法就能達到這一目的,除了少數重寫了類方法+accessInstanceVariablesDirectly的類屏蔽了該操作。

下面是一個例子:當執行個體變數有一個為任意類型的屬性時,上述提到的操作無效

這是iOS6.1 SDK中MediaPlayer 架構的一段引用:

1234 @interface MPMoviePlayerController : NSObject {    void *_internal;    // 4 = 0x4    BOOL _readyForDisplay;  // 8 = 0x8}

因為 id internal=[moviePlayerController valueForKey:@”internal”] 無效,下面有一個笨辦法來取得這個變數:

1 id internal = *((const id*)(void*)((uintptr_t)moviePlayerController + sizeof(Class)));

注意!不要隨意調用這段代碼,因為ivar的布局可能改變(指標位移量計算可能出錯)。僅在逆向工程中使用!

NSDateFormatter +dateFormatFromTemplate:options:locale:

友情提示:假如你調用[NSDateFormatter setDateFormat],而沒有調用[NSDateFormatter dateFormatFromTemplate:options:local:],n那麼很可能出錯。

蘋果文檔:

123 + (NSString *)dateFormatFromTemplate:(NSString *)template                             options:(NSUInteger)opts                              locale:(NSLocale *)locale

不同地區有不同的日期格式。使用這個方法的目的:得到指定地區指定日期欄位的一個合適的格式(通常你可以通過currentLocal查看當前所屬地區)

下面這個例子給我們表現了英式英語和美式英語不同的日期格式:

1234567891011121314151617 NSLocale *usLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];NSLocale *gbLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_GB"]; NSString *dateFormat;NSString *dateComponents = @"yMMMMd"; dateFormat = [NSDateFormatter dateFormatFromTemplate:dateComponents options:0 locale:usLocale];NSLog(@"Date format for %@: %@",    [usLocale displayNameForKey:NSLocaleIdentifier value:[usLocale localeIdentifier]], dateFormat); dateFormat = [NSDateFormatter dateFormatFromTemplate:dateComponents options:0 locale:gbLocale];NSLog(@"Date format for %@: %@",    [gbLocale displayNameForKey:NSLocaleIdentifier value:[gbLocale localeIdentifier]], dateFormat); // Output:// Date format for English (United States): MMMM d, y// Date format for English (United Kingdom): d MMMM y

通過調試擷取內部常量

近期, Matthias Tretter在Twitter上問到:

有人知道在iOS8裡modal viewController presentation的預設動畫時間和跳轉方式嗎?

我們在UIKit的類庫中發現了這樣一個函數:[UITransitionView defaultDurationForTransition:],並在這個方法的位置加一個斷點:

1 (lldb) br set -n "+[UITransitionView defaultDurationForTransition:]"

模態顯示一個viewController,就會停在這個斷點,輸入finish執行該方法:

1 (lldb)finish

在defaultDurationForTransition:被執行時,你就能讀到結果(在xmm0寄存器裡)

12 (lldb) register read xmm0 --format float64    xmm0 = {0.4 0}

回複:預設動畫時間0.4s

DIY 弱關聯對象

不幸的是,關聯對象OBJC_ASSOCIATION_ASSIGN策略不支援引用計數為0的弱引用。幸運的是,你可以很容易實現它,你僅僅需要一個簡單的類,並在這個類裡弱引用一個對象:

12345678910111213141516 @interface WeakObjectContainter : NSObject@property (nonatomic, readonly, weak) id object;@end @implementation WeakObjectContainter- (instancetype)initWithObject:(id)object {    self = [super init];    if (!self) {        return nil;    }     self.object = object;     return self;}@end

然後,通過OBJC_ASSOCIATION_RETAIN(_NONATOMIC)關聯WeakObjectContainter:

1 objc_setAssociatedObject(self, &MyKey, [[WeakObjectContainter alloc] initWithObject:object], OBJC_ASSOCIATION_RETAIN_NONATOMIC);

    用object屬性指向這個所需的引用計數為0的弱引用對象。

1 id object = [objc_getAssociatedObject(self, &MyKey) object];

在這麼多日新月異的新技術推動下,我們將迎來一個充滿無限可能和機會的一年。在此,祝大家2015年新年快樂!

願編程路上,與君共勉。
(本文為CocoaChina組織翻譯,本譯文權利歸譯者所有,未經允許禁止轉載。)

新年之際,盤點一些APP開發技巧

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.