在此,感謝以下童鞋們所做的貢獻:
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、Zak。
成員函式的使用技巧(來自Robert Widmann)
在用靜態方式呼叫Swift類和結構中的成員函式時,通常使用以下格式:
Object->(引數)->Things
比如,你可以用以下兩種方式呼叫reverse():
[1,2,3,4].reverse( ) Array.reverse([1,2,3,4])用@()來封裝C字串(來自Samuel Defago)
事實上文字大部分時候是數字和字母的集合,使用C字串,尤其當我在使用執行時編碼的時候,我常常會忘記用UTF8編碼、以NULL結束:Objective-C字串封裝:
NSString *propertyAttributesString = @(property_getAttributes(class_getProperty([NSObject class], "description"))); // T@"NSString",R,C
AmIBeingDebugged
Nolan O'Brien在這篇Q&A技術文件中讓我們注意到了AmIBeingDebugged函式方法:
#include <assert.h> #include <stdbool.h> #include <sys/types.h> #include <unistd.h> #include <sys/sysctl.h> static Bool AmIBeingDebugged(void) { int mib[4]; struct kinfo_proc info; size_t size = sizeof(info); info.kp_proc.p_flag = 0; mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_PID; mib[3] = getpid(); sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0); return (info.kp_proc.p_flag & P_TRACED) != 0; }
使用延遲儲存屬性(來自Colin Rofls)
在開發過程中,應該避免使用Optionals型別,更不應該使用隱式解包optionals型別。你想宣告一個var變數卻不想給一個初始值?使用“lazy”吧,唯一要注意的就是:在你的屬性被賦值之前不要呼叫getter方法即可(童叟無欺!)
lazy var someModelStructure = ExpensiveClass()
假如你僅僅對這var變數呼叫set方法,而沒有呼叫getter方法的話,這個被lazy修飾的var變數不會被賦值。例如,用lazy修飾那些直到viewDidLoad時才需要初始化的views變數就會非常合適。
獲取Storyboard檢視容器裡的子檢視控制器(來自Vadim Shpakovski)
有一個比較方便的方法來獲取故事板檢視容器裡的子檢視控制器:
// 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”
快速獲取Playground資源(來自Jon Friskics)
Swift裡的所有Playground共享相同的資料目錄:/Users/HOME/Documents/Shared Playground Data
如果你喜歡使用很多Playgrounds,你將需要在上述共享目錄下為每個Playground新建對應的子目錄,來儲存每個Playground用到的資料;但是那之後你需要告訴每個Playground在哪兒可以獲取其對應的資料。下面是我常用的一個輔助解決方法:
func pathToFileInSharedSubfolder(file: String) -> String { return XCPSharedDataDirectoryPath + "/" + NSProcessInfo.processInfo().processName + "/" + file }
processName屬性是Playground檔案的名字,因此只要你已經在Playground資料共享檔案目錄下以相同的名字新建了一個子目錄,那麼你可以很容易訪問這些資料,和讀取本地JSON資料一樣:
var jsonReadError:NSError? let jsonData = NSFileManager.defaultManager().contentsAtPath(pathToFileInSharedSubfolder("data.json"))! let jsonArray = NSJSONSerialization.JSONObjectWithData(jsonData, options: nil, error: &jsonReadError) as [AnyObject]
....或者訪問本地圖片
let imageView = UIImageView() imageView.image = UIImage(contentsOfFile: pathToFileInSharedSubfolder("image.png"))
Please attention!本篇文章剩餘的部分來自Cédric Luthi大神的貢獻,他分享了一些比較有用的開發技巧和技術,這些內容足夠自成一篇,值得細細品讀。這裡再次感謝Cédric!
CocoaPods大揭祕
這兒有一個快速的方法來檢查APP裡用到的所有pods:
[ ubbcodeplace_9 ]nbsp;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”里加上下面這行:
-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介面上看到的語種。
當然你也許需要呼叫這個方法:
NSLocal+canonicalLanguageIdentifierFromString:來確保你使用的文字語種是規範的語種。
保護SDK標頭檔案
如果你用dmg安裝Xcode,那麼看看這篇Joar Wingfors的文章,它講述瞭如何通過保留所有權來避免SDK標頭檔案被意外修改:
[ ubbcodeplace_12 ]nbsp;sudo ditto /Volumes/Xcode/Xcode.app /Applications/Xcode.app
任意型別的例項變數檢測
為了達到逆向處理的目的,查詢物件的例項變數是一個常見可靠的途徑。通常呼叫物件valueForKey:方法就能達到這一目的,除了少數重寫了類方法+accessInstanceVariablesDirectly的類遮蔽了該操作。
下面是一個例子:當例項變數有一個為任意型別的屬性時,上述提到的操作無效
這是iOS6.1 SDK中MediaPlayer 框架的一段引用:
@interface MPMoviePlayerController : NSObject { void *_internal; // 4 = 0x4 BOOL _readyForDisplay; // 8 = 0x8 }
因為 id internal=[moviePlayerController valueForKey:@”internal”] 無效,下面有一個笨辦法來取得這個變數:
id internal = *((const id*)(void*)((uintptr_t)moviePlayerController + sizeof(Class)));
注意!不要隨意呼叫這段程式碼,因為ivar的佈局可能改變(指標偏移量計算可能出錯)。僅在逆向工程中使用!
NSDateFormatter +dateFormatFromTemplate:options:locale:
友情提示:假如你呼叫[NSDateFormatter setDateFormat],而沒有呼叫[NSDateFormatter dateFormatFromTemplate:options:local:],n那麼很可能出錯。
蘋果文件:
+ (NSString *)dateFormatFromTemplate:(NSString *)template options:(NSUInteger)opts locale:(NSLocale *)locale不同地區有不同的日期格式。使用這個方法的目的:得到指定地區指定日期欄位的一個合適的格式(通常你可以通過currentLocal檢視當前所屬地區)
下面這個例子給我們表現了英式英語和美式英語不同的日期格式:
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:],並在這個方法的位置加一個斷點:
(lldb) br set -n "+[UITransitionView defaultDurationForTransition:]"
模態顯示一個viewController,就會停在這個斷點,輸入finish執行該方法:
(lldb)finish
在defaultDurationForTransition:被執行時,你就能讀到結果(在xmm0暫存器裡)
(lldb) register read xmm0 --format float64 xmm0 = {0.4 0}
回覆:預設動畫時間0.4s
DIY 弱關聯物件
不幸的是,關聯物件OBJC_ASSOCIATION_ASSIGN策略不支援引用計數為0的弱引用。幸運的是,你可以很容易實現它,你僅僅需要一個簡單的類,並在這個類裡弱引用一個物件:
@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:
objc_setAssociatedObject(self, &MyKey, [[WeakObjectContainter alloc] initWithObject:object], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
用object屬性指向這個所需的引用計數為0的弱引用物件。
id object = [objc_getAssociatedObject(self, &MyKey) object];
英文文章來源:NSHipster,譯文出自:CocoaChina
相關閱讀
評論(1)