iOS攻防——(四)class-dump-与-Dumpdecrypted-使用
1 class dump
class dump 是一個用于檢查保存在 Mach-O 文件中的 objective-c 運行時信息的工具,攻防中最常用、實用的命令行工具。
1.1 class dump 好玩在哪?
class dump 絕對可以滿足你的好奇心。你可以通過 class dump :
1.2 Download
當前版本: 3.5 (64 bit Intel) 需要 Mac OS X 10.8 或更高版本
class-dump-3.5.dmg class-dump-3.5.tar.gz class-dump-3.5.tar.bz2
1.3 Use
下載好后,雙擊dmg文件,將其中的 class-dump 文件放到/usr/local/sbin 目錄下,然后就可以在命令行中使用了。
> date: 2016-08-05 10:33:00原文地址
點擊這里
這幾天部門的前輩再用RAC的時候問到一個問題,RACCommand在RAC中具體的作用和起到的功能,到底應該如何應用它。
關于RAC的使用文章非常多,但是大多僅限于介紹和基本的使用方法,很少介紹RAC究竟應該如何優雅的嵌入到項目中。
在查閱資料的時候發現了此篇博文,寫的非常細致,所以做了一次搬運工。
另,妹子我的英文屬于渣渣系列,所以有什么翻譯不當,請一定要指教。
Code
文章中所有代碼在這里
RACCommand是你的新伙伴嗎?
RACCommand是ReactiveCocoa最精華的部分之一,它可以讓你在開發中節約大量的時間并讓你的iOS或者OS X app有更強的魯棒性。
我見過不少剛接觸ReactiveCocoa(后文將簡寫為RAC),還不能完全理解RACCommand是如何工作又不知何時應該使用RACCommand的同學。所以我認為這個小介紹將會很實用,可以給他們帶來一些啟發。官方文檔并沒有給出多少如何使用RACCommand的Examples,但是RACCommand頭文件的介紹還是很不錯的,不過這對剛開始用RAC的同學來說還是太難理解了。
RACCommand類是用于表示一些操作的執行。通常,是由于UI上的一些事件觸發了RACCommand的執行。比如當用戶按了一個按鈕,如果對應RACCommand實例可以被執行,就會執行相應的操作。這使得它很容易和UI進行綁定,同時可以保證當RACCommand處于not enabled時RACCommand實例的操作不會被執行。當Command可以執行時,常做的方式是把allowsconcuuent的屬性設置為NO,這可以保證Command已經執行完成后不會被重復執行。Command執行的結果是一個RACSignal,因此你可以調用next:、completed:、或者error:。后面將會展示具體使用方式。
Example App
我們假設我們正在設計一個簡單的app,其功能是讓用戶訂閱一個郵件。最簡單的方式是,用一個UITextField和一個UIButton。當用戶輸入email并且點擊按鈕的時候,email地址將會傳給某個web服務。看起來很簡單,但是我們應該確保用戶有最好的體驗。如果用戶按了兩次按鈕?``如何處理請求出錯?``如果email不合法?``RACCommand可以幫助我們處理這些情況。在這篇文章中將一步步完善這個小app以此來討論一些概念和工作原理。
可以從這里獲得源碼。
從一個非常簡單的ViewController可以很好的實踐MVVM模式。
- (void)bindWithViewModel {RAC(self.viewModel, email) = self.emailTextField.rac_textSignal;self.subscribeButton.rac_command = self.viewModel.subscribeCommand;RAC(self.statusLabel, text) = RACObserve(self.viewModel, statusMessage); } 復制代碼在上面的方法(在viewDidLoad中調用),在View和ViewModel中建立了綁定關系。下面是ViewModel的定義:
@interface SubscribeViewModel : NSObject@property(nonatomic, strong) RACCommand *subscribeCommand;// write to this property @property(nonatomic, strong) NSString *email;// read from this property @property(nonatomic, strong) NSString *statusMessage;@end 復制代碼如上所示,一個暴露出的RACCommand屬性。另外兩個是字符串屬性,它們和View的兩個屬性綁定在一起。ViewModel的完整實現如下:
static NSString *const kSubscribeURL = @"http://reactivetest.apiary.io/subscribers";@interface SubscribeViewModel () @property(nonatomic, strong) RACSignal *emailValidSignal; @end@implementation SubscribeViewModel- (id)init {self = [super init];if (self) {[self mapSubscribeCommandStateToStatusMessage];}return self; }- (void)mapSubscribeCommandStateToStatusMessage {RACSignal *startedMessageSource = [self.subscribeCommand.executionSignals map:^id(RACSignal *subscribeSignal) {return NSLocalizedString(@"Sending request...", nil);}];RACSignal *completedMessageSource = [self.subscribeCommand.executionSignals flattenMap:^RACStream *(RACSignal *subscribeSignal) {return [[[subscribeSignal materialize] filter:^BOOL(RACEvent *event) {return event.eventType == RACEventTypeCompleted;}] map:^id(id value) {return NSLocalizedString(@"Thanks", nil);}];}];RACSignal *failedMessageSource = [[self.subscribeCommand.errors subscribeOn:[RACScheduler mainThreadScheduler]] map:^id(NSError *error) {return NSLocalizedString(@"Error :(", nil);}];RAC(self, statusMessage) = [RACSignal merge:@[startedMessageSource, completedMessageSource, failedMessageSource]]; }- (RACCommand *)subscribeCommand {if (!_subscribeCommand) {NSString *email = self.email;_subscribeCommand = [[RACCommand alloc] initWithEnabled:self.emailValidSignal signalBlock:^RACSignal *(id input) {return [SubscribeViewModel postEmail:email];}];}return _subscribeCommand; }+ (RACSignal *)postEmail:(NSString *)email {AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];manager.requestSerializer = [AFJSONRequestSerializer new];NSDictionary *body = @{@"email": email ?: @""};return [[[manager rac_POST:kSubscribeURL parameters:body] logError] replayLazily]; }- (RACSignal *)emailValidSignal {if (!_emailValidSignal) {_emailValidSignal = [RACObserve(self, email) map:^id(NSString *email) {return @([email isValidEmail]);}];}return _emailValidSignal; }@end 復制代碼這看起來真是一大坨~看還是從小的地方來看吧。我們真正感興趣RACCommandRACCommand創建部分是以下代碼:
- (RACCommand *)subscribeCommand {if (!_subscribeCommand) {NSString *email = self.email;_subscribeCommand = [[RACCommand alloc] initWithEnabled:self.emailValidSignal signalBlock:^RACSignal *(id input) {return [SubscribeViewModel postEmail:email];}];}return _subscribeCommand; } 復制代碼Command通過一個enabledSignal參數來初始化。這個Signal可以指示Command是否可以被執行。在我們本次的用例中Command應該在用戶合法輸入email時允許被執行。self.emailValidSignal就是用來在email發生變化發送NO或者YES指示的。
signalBlock參數在Command需要執行時被調用。block應該返回一個signal。當我們設置allowsConcurrentExecution為NO,Command將會看守這個signal并且在本次執行未完成前不允許任何新的執行。
由于本次用例中的Command來自于按鈕的rac_command(在UIButtton+RACCommandSupport分類中定義),根據Command是否可以被執行,按鈕會自動切換enabled和disabled狀態。
當然,Command會在按鈕被用戶點擊的時候自動執行。我們可以通過RACCommand自由的實現這一切。如果你需要手動執行你可以調用-[RACCommand execute:],參數是可選的,你可以傳遞nil。我們的用例里不需要參數,不過這里的參數通常會十分有用(按鈕可以將自己當做-execute:的參數傳入)。-execute:方法也是一個你可以監控執行狀態的地方,你可以這樣寫:
[[self.viewModel.subscribeCommand execute:nil] subscribeCompleted:^{NSLog(@"The command executed"); }]; 復制代碼在我們的用例中按鈕為我們調用Command的執行(所以我們不需要手動調用-execute:),所以在Command執行時,為了及時更新UI,我們需要監聽Command的另一個屬性。有幾個讓人迷惑的地方,RACCommand的executionSignals屬性是一個每當Commands開始執行時就發送next:的Signal。問題在于Signal由Command創建,所以Signal中還有一層Signal。每次Command開始執行的時候, 我們會在ViewModel中通過mapSubscribeCommandStateToStatusMessage方法里面獲取到一個信號。同時在這個信號里面返回了一個字符串:
RACSignal *startedMessageSource = [self.subscribeCommand.executionSignals map:^id(RACSignal *subscribeSignal) {return NSLocalizedString(@"Sending request...", nil); }]; 復制代碼假如我們想用更函數的方式,來在Command執行完成后都能獲取string,我們需要做更多的工作:
RACSignal *completedMessageSource = [self.subscribeCommand.executionSignals flattenMap:^RACStream *(RACSignal *subscribeSignal) {return [[[subscribeSignal materialize] filter:^BOOL(RACEvent *event) {return event.eventType == RACEventTypeCompleted;}] map:^id(id value) {return NSLocalizedString(@"Thanks", nil);}]; }]; 復制代碼當Command執行時,flattenMap:方法調用一個帶subscribeSignal參數的block。這個block返回一個新的Signal并且它的值會被傳遞到下一個返回信號。materialize操作符讓我們捕獲到一個RACEvent(例如 next: complete 和 error:都是RACEvent的實例)。我們可以在信號完成之后過濾這些event并且映射成一個string。這些解釋讓你暈了嗎,不過你可以去看一下flattenMap:和materialize的文檔以助于你的理解。
我們可以用另一種不同但更容易理解的方式來實現:
@weakify(self); [self.subscribeCommand.executionSignals subscribeNext:^(RACSignal *subscribeSignal) {[subscribeSignal subscribeCompleted:^{@strongify(self);self.statusMessage = @"Thanks";}]; }]; 復制代碼但是我并不喜歡上面的寫法,因為這樣會block中的操作會更多并且會更多的在block中使用到self。所以在這里還使用了@weakify和@strongify(在libextobjc中定義)避免循環retain。
關于executionSignals屬性,有一個重要的細節。在這里的Signal所發送的event不包含error,所以對于那些有特殊errors屬性
RACSignal *failedMessageSource = [[self.subscribeCommand.errors subscribeOn:[RACScheduler mainThreadScheduler]] map:^id(NSError *error) {return NSLocalizedString(@"Error :(", nil); }]; 復制代碼如果我們有三個帶有狀態消息的Signal,我們可以將他們合并成一個信號并綁定到ViewModel的一個statusMessage屬性 (statusMessage綁定ViewController的statusLabel.text)。
RAC(self, statusMessage) = [RACSignal merge:@[startedMessageSource, completedMessageSource, failedMessageSource]]; 復制代碼那么以上是一個RACCommand在iOS app 開發中的一個example。我相信這種實現邏輯比使用UITextFieldDelegate有更多的優點,能在屬性和變量中體現更多的狀態。
其他有趣的RACCommand使用細節
RACCommand有一個executing屬性,實際上它是一個當execute:時會發送YES,終止時發送NO的信號。在訂閱信號時這個信號將會發送它的當前值,如果你只需要獲取當前值而不需要獲得信號,你可以通過以下方式:
BOOL commandIsExecuting = [[command.executing first] boolValue]; 復制代碼enabled屬性也是一個發送YES和NO的信號。當Command通過發送NO的enabledSignal信號創建,或者如果信號在執行并且allowsConcurrentExecutions 為 NO,enabled就會發送NO。
-execute:方法會自動訂閱原始Signal并且廣播它。這意味著你不需要去訂閱-execute:返回的信號,但是如果你訂閱了也不需要擔心它會被執行兩次。
有什么問題都可以在博文后面留言,或者微博上私信我,或者郵件我 coderfish@163.com。
博主是 iOS 妹子一枚。
希望大家一起進步。
我的微博:小魚周凌宇
date: 2016-08-05 10:33:00
原文地址
點擊這里
這幾天部門的前輩再用RAC的時候問到一個問題,RACCommand在RAC中具體的作用和起到的功能,到底應該如何應用它。
關于RAC的使用文章非常多,但是大多僅限于介紹和基本的使用方法,很少介紹RAC究竟應該如何優雅的嵌入到項目中。
在查閱資料的時候發現了此篇博文,寫的非常細致,所以做了一次搬運工。
另,妹子我的英文屬于渣渣系列,所以有什么翻譯不當,請一定要指教。
Code
文章中所有代碼在這里
RACCommand是你的新伙伴嗎?
RACCommand是ReactiveCocoa最精華的部分之一,它可以讓你在開發中節約大量的時間并讓你的iOS或者OS X app有更強的魯棒性。
我見過不少剛接觸ReactiveCocoa(后文將簡寫為RAC),還不能完全理解RACCommand是如何工作又不知何時應該使用RACCommand的同學。所以我認為這個小介紹將會很實用,可以給他們帶來一些啟發。官方文檔并沒有給出多少如何使用RACCommand的Examples,但是RACCommand頭文件的介紹還是很不錯的,不過這對剛開始用RAC的同學來說還是太難理解了。
RACCommand類是用于表示一些操作的執行。通常,是由于UI上的一些事件觸發了RACCommand的執行。比如當用戶按了一個按鈕,如果對應RACCommand實例可以被執行,就會執行相應的操作。這使得它很容易和UI進行綁定,同時可以保證當RACCommand處于not enabled時RACCommand實例的操作不會被執行。當Command可以執行時,常做的方式是把allowsconcuuent的屬性設置為NO,這可以保證Command已經執行完成后不會被重復執行。Command執行的結果是一個RACSignal,因此你可以調用next:、completed:、或者error:。后面將會展示具體使用方式。
Example App
我們假設我們正在設計一個簡單的app,其功能是讓用戶訂閱一個郵件。最簡單的方式是,用一個UITextField和一個UIButton。當用戶輸入email并且點擊按鈕的時候,email地址將會傳給某個web服務。看起來很簡單,但是我們應該確保用戶有最好的體驗。如果用戶按了兩次按鈕?``如何處理請求出錯?``如果email不合法?``RACCommand可以幫助我們處理這些情況。在這篇文章中將一步步完善這個小app以此來討論一些概念和工作原理。
[站外圖片上傳中...(image-8ef4eb-1511406632743)]
可以從這里獲得源碼。
從一個非常簡單的ViewController可以很好的實踐MVVM模式。
- (void)bindWithViewModel {RAC(self.viewModel, email) = self.emailTextField.rac_textSignal;self.subscribeButton.rac_command = self.viewModel.subscribeCommand;RAC(self.statusLabel, text) = RACObserve(self.viewModel, statusMessage); } 復制代碼在上面的方法(在viewDidLoad中調用),在View和ViewModel中建立了綁定關系。下面是ViewModel的定義:
@interface SubscribeViewModel : NSObject@property(nonatomic, strong) RACCommand *subscribeCommand;// write to this property @property(nonatomic, strong) NSString *email;// read from this property @property(nonatomic, strong) NSString *statusMessage;@end 復制代碼如上所示,一個暴露出的RACCommand屬性。另外兩個是字符串屬性,它們和View的兩個屬性綁定在一起。ViewModel的完整實現如下:
static NSString *const kSubscribeURL = @"http://reactivetest.apiary.io/subscribers";@interface SubscribeViewModel () @property(nonatomic, strong) RACSignal *emailValidSignal; @end@implementation SubscribeViewModel- (id)init {self = [super init];if (self) {[self mapSubscribeCommandStateToStatusMessage];}return self; }- (void)mapSubscribeCommandStateToStatusMessage {RACSignal *startedMessageSource = [self.subscribeCommand.executionSignals map:^id(RACSignal *subscribeSignal) {return NSLocalizedString(@"Sending request...", nil);}];RACSignal *completedMessageSource = [self.subscribeCommand.executionSignals flattenMap:^RACStream *(RACSignal *subscribeSignal) {return [[[subscribeSignal materialize] filter:^BOOL(RACEvent *event) {return event.eventType == RACEventTypeCompleted;}] map:^id(id value) {return NSLocalizedString(@"Thanks", nil);}];}];RACSignal *failedMessageSource = [[self.subscribeCommand.errors subscribeOn:[RACScheduler mainThreadScheduler]] map:^id(NSError *error) {return NSLocalizedString(@"Error :(", nil);}];RAC(self, statusMessage) = [RACSignal merge:@[startedMessageSource, completedMessageSource, failedMessageSource]]; }- (RACCommand *)subscribeCommand {if (!_subscribeCommand) {NSString *email = self.email;_subscribeCommand = [[RACCommand alloc] initWithEnabled:self.emailValidSignal signalBlock:^RACSignal *(id input) {return [SubscribeViewModel postEmail:email];}];}return _subscribeCommand; }+ (RACSignal *)postEmail:(NSString *)email {AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];manager.requestSerializer = [AFJSONRequestSerializer new];NSDictionary *body = @{@"email": email ?: @""};return [[[manager rac_POST:kSubscribeURL parameters:body] logError] replayLazily]; }- (RACSignal *)emailValidSignal {if (!_emailValidSignal) {_emailValidSignal = [RACObserve(self, email) map:^id(NSString *email) {return @([email isValidEmail]);}];}return _emailValidSignal; }@end 復制代碼這看起來真是一大坨~看還是從小的地方來看吧。我們真正感興趣RACCommandRACCommand創建部分是以下代碼:
- (RACCommand *)subscribeCommand {if (!_subscribeCommand) {NSString *email = self.email;_subscribeCommand = [[RACCommand alloc] initWithEnabled:self.emailValidSignal signalBlock:^RACSignal *(id input) {return [SubscribeViewModel postEmail:email];}];}return _subscribeCommand; } 復制代碼Command通過一個enabledSignal參數來初始化。這個Signal可以指示Command是否可以被執行。在我們本次的用例中Command應該在用戶合法輸入email時允許被執行。self.emailValidSignal就是用來在email發生變化發送NO或者YES指示的。
signalBlock參數在Command需要執行時被調用。block應該返回一個signal。當我們設置allowsConcurrentExecution為NO,Command將會看守這個signal并且在本次執行未完成前不允許任何新的執行。
由于本次用例中的Command來自于按鈕的rac_command(在UIButtton+RACCommandSupport分類中定義),根據Command是否可以被執行,按鈕會自動切換enabled和disabled狀態。
當然,Command會在按鈕被用戶點擊的時候自動執行。我們可以通過RACCommand自由的實現這一切。如果你需要手動執行你可以調用-[RACCommand execute:],參數是可選的,你可以傳遞nil。我們的用例里不需要參數,不過這里的參數通常會十分有用(按鈕可以將自己當做-execute:的參數傳入)。-execute:方法也是一個你可以監控執行狀態的地方,你可以這樣寫:
[[self.viewModel.subscribeCommand execute:nil] subscribeCompleted:^{NSLog(@"The command executed"); }]; 復制代碼在我們的用例中按鈕為我們調用Command的執行(所以我們不需要手動調用-execute:),所以在Command執行時,為了及時更新UI,我們需要監聽Command的另一個屬性。有幾個讓人迷惑的地方,RACCommand的executionSignals屬性是一個每當Commands開始執行時就發送next:的Signal。問題在于Signal由Command創建,所以Signal中還有一層Signal。每次Command開始執行的時候, 我們會在ViewModel中通過mapSubscribeCommandStateToStatusMessage方法里面獲取到一個信號。同時在這個信號里面返回了一個字符串:
RACSignal *startedMessageSource = [self.subscribeCommand.executionSignals map:^id(RACSignal *subscribeSignal) {return NSLocalizedString(@"Sending request...", nil); }]; 復制代碼假如我們想用更函數的方式,來在Command執行完成后都能獲取string,我們需要做更多的工作:
RACSignal *completedMessageSource = [self.subscribeCommand.executionSignals flattenMap:^RACStream *(RACSignal *subscribeSignal) {return [[[subscribeSignal materialize] filter:^BOOL(RACEvent *event) {return event.eventType == RACEventTypeCompleted;}] map:^id(id value) {return NSLocalizedString(@"Thanks", nil);}]; }]; 復制代碼當Command執行時,flattenMap:方法調用一個帶subscribeSignal參數的block。這個block返回一個新的Signal并且它的值會被傳遞到下一個返回信號。materialize操作符讓我們捕獲到一個RACEvent(例如 next: complete 和 error:都是RACEvent的實例)。我們可以在信號完成之后過濾這些event并且映射成一個string。這些解釋讓你暈了嗎,不過你可以去看一下flattenMap:和materialize的文檔以助于你的理解。
我們可以用另一種不同但更容易理解的方式來實現:
@weakify(self); [self.subscribeCommand.executionSignals subscribeNext:^(RACSignal *subscribeSignal) {[subscribeSignal subscribeCompleted:^{@strongify(self);self.statusMessage = @"Thanks";}]; }]; 復制代碼但是我并不喜歡上面的寫法,因為這樣會block中的操作會更多并且會更多的在block中使用到self。所以在這里還使用了@weakify和@strongify(在libextobjc中定義)避免循環retain。
關于executionSignals屬性,有一個重要的細節。在這里的Signal所發送的event不包含error,所以對于那些有特殊errors屬性
RACSignal *failedMessageSource = [[self.subscribeCommand.errors subscribeOn:[RACScheduler mainThreadScheduler]] map:^id(NSError *error) {return NSLocalizedString(@"Error :(", nil); }]; 復制代碼如果我們有三個帶有狀態消息的Signal,我們可以將他們合并成一個信號并綁定到ViewModel的一個statusMessage屬性 (statusMessage綁定ViewController的statusLabel.text)。
RAC(self, statusMessage) = [RACSignal merge:@[startedMessageSource, completedMessageSource, failedMessageSource]]; 復制代碼那么以上是一個RACCommand在iOS app 開發中的一個example。我相信這種實現邏輯比使用UITextFieldDelegate有更多的優點,能在屬性和變量中體現更多的狀態。
其他有趣的RACCommand使用細節
RACCommand有一個executing屬性,實際上它是一個當execute:時會發送YES,終止時發送NO的信號。在訂閱信號時這個信號將會發送它的當前值,如果你只需要獲取當前值而不需要獲得信號,你可以通過以下方式:
BOOL commandIsExecuting = [[command.executing first] boolValue]; 復制代碼enabled屬性也是一個發送YES和NO的信號。當Command通過發送NO的enabledSignal信號創建,或者如果信號在執行并且allowsConcurrentExecutions 為 NO,enabled就會發送NO。
-execute:方法會自動訂閱原始Signal并且廣播它。這意味著你不需要去訂閱-execute:返回的信號,但是如果你訂閱了也不需要擔心它會被執行兩次。
有什么問題都可以在博文后面留言,或者微博上私信我,或者郵件我 coderfish@163.com。
博主是 iOS 妹子一枚。
希望大家一起進步。
我的微博:小魚周凌宇
官方用法指南:
class-dump 3.5 (64 bit) Usage: class-dump [options] <mach-o-file>where options are:-a show instance variable offsets-A show implementation addresses--arch <arch> choose a specific architecture from a universal binary (ppc, ppc64, i386, x86_64)-C <regex> only display classes matching regular expression-f <str> find string in method name-H generate header files in current directory, or directory specified with -o-I sort classes, categories, and protocols by inheritance (overrides -s)-o <dir> output directory used for -H-r recursively expand frameworks and fixed VM shared libraries-s sort classes and categories by name-S sort methods by name-t suppress header in output, for testing--list-arches list the arches in the file, then exit--sdk-ios specify iOS SDK version (will look in /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS<version>.sdk--sdk-mac specify Mac OS X version (will look in /Developer/SDKs/MacOSX<version>.sdk--sdk-root specify the full SDK root path (or use --sdk-ios/--sdk-mac for a shortcut) 復制代碼簡單的舉例:
class-dump -H /Applications/Calculator.app -o ~/Desktop/dump/Calculate-dump 復制代碼/Applications/Calculator.app 是 Mac 上計算器應用的路徑。 ~/Desktop/dump/Calculate-dump 是指定的存放 dump 出來頭文件的文件夾路徑。
執行完成后可以看到指定的保存目錄下已經有 dump 出來的頭文件了:
> date: 2016-08-05 10:33:00原文地址
點擊這里
這幾天部門的前輩再用RAC的時候問到一個問題,RACCommand在RAC中具體的作用和起到的功能,到底應該如何應用它。
關于RAC的使用文章非常多,但是大多僅限于介紹和基本的使用方法,很少介紹RAC究竟應該如何優雅的嵌入到項目中。
在查閱資料的時候發現了此篇博文,寫的非常細致,所以做了一次搬運工。
另,妹子我的英文屬于渣渣系列,所以有什么翻譯不當,請一定要指教。
Code
文章中所有代碼在這里
RACCommand是你的新伙伴嗎?
RACCommand是ReactiveCocoa最精華的部分之一,它可以讓你在開發中節約大量的時間并讓你的iOS或者OS X app有更強的魯棒性。
我見過不少剛接觸ReactiveCocoa(后文將簡寫為RAC),還不能完全理解RACCommand是如何工作又不知何時應該使用RACCommand的同學。所以我認為這個小介紹將會很實用,可以給他們帶來一些啟發。官方文檔并沒有給出多少如何使用RACCommand的Examples,但是RACCommand頭文件的介紹還是很不錯的,不過這對剛開始用RAC的同學來說還是太難理解了。
RACCommand類是用于表示一些操作的執行。通常,是由于UI上的一些事件觸發了RACCommand的執行。比如當用戶按了一個按鈕,如果對應RACCommand實例可以被執行,就會執行相應的操作。這使得它很容易和UI進行綁定,同時可以保證當RACCommand處于not enabled時RACCommand實例的操作不會被執行。當Command可以執行時,常做的方式是把allowsconcuuent的屬性設置為NO,這可以保證Command已經執行完成后不會被重復執行。Command執行的結果是一個RACSignal,因此你可以調用next:、completed:、或者error:。后面將會展示具體使用方式。
Example App
我們假設我們正在設計一個簡單的app,其功能是讓用戶訂閱一個郵件。最簡單的方式是,用一個UITextField和一個UIButton。當用戶輸入email并且點擊按鈕的時候,email地址將會傳給某個web服務。看起來很簡單,但是我們應該確保用戶有最好的體驗。如果用戶按了兩次按鈕?``如何處理請求出錯?``如果email不合法?``RACCommand可以幫助我們處理這些情況。在這篇文章中將一步步完善這個小app以此來討論一些概念和工作原理。
[站外圖片上傳中...(image-8ef4eb-1511406632743)]
可以從這里獲得源碼。
從一個非常簡單的ViewController可以很好的實踐MVVM模式。
- (void)bindWithViewModel {RAC(self.viewModel, email) = self.emailTextField.rac_textSignal;self.subscribeButton.rac_command = self.viewModel.subscribeCommand;RAC(self.statusLabel, text) = RACObserve(self.viewModel, statusMessage); } 復制代碼在上面的方法(在viewDidLoad中調用),在View和ViewModel中建立了綁定關系。下面是ViewModel的定義:
@interface SubscribeViewModel : NSObject@property(nonatomic, strong) RACCommand *subscribeCommand;// write to this property @property(nonatomic, strong) NSString *email;// read from this property @property(nonatomic, strong) NSString *statusMessage;@end 復制代碼如上所示,一個暴露出的RACCommand屬性。另外兩個是字符串屬性,它們和View的兩個屬性綁定在一起。ViewModel的完整實現如下:
static NSString *const kSubscribeURL = @"http://reactivetest.apiary.io/subscribers";@interface SubscribeViewModel () @property(nonatomic, strong) RACSignal *emailValidSignal; @end@implementation SubscribeViewModel- (id)init {self = [super init];if (self) {[self mapSubscribeCommandStateToStatusMessage];}return self; }- (void)mapSubscribeCommandStateToStatusMessage {RACSignal *startedMessageSource = [self.subscribeCommand.executionSignals map:^id(RACSignal *subscribeSignal) {return NSLocalizedString(@"Sending request...", nil);}];RACSignal *completedMessageSource = [self.subscribeCommand.executionSignals flattenMap:^RACStream *(RACSignal *subscribeSignal) {return [[[subscribeSignal materialize] filter:^BOOL(RACEvent *event) {return event.eventType == RACEventTypeCompleted;}] map:^id(id value) {return NSLocalizedString(@"Thanks", nil);}];}];RACSignal *failedMessageSource = [[self.subscribeCommand.errors subscribeOn:[RACScheduler mainThreadScheduler]] map:^id(NSError *error) {return NSLocalizedString(@"Error :(", nil);}];RAC(self, statusMessage) = [RACSignal merge:@[startedMessageSource, completedMessageSource, failedMessageSource]]; }- (RACCommand *)subscribeCommand {if (!_subscribeCommand) {NSString *email = self.email;_subscribeCommand = [[RACCommand alloc] initWithEnabled:self.emailValidSignal signalBlock:^RACSignal *(id input) {return [SubscribeViewModel postEmail:email];}];}return _subscribeCommand; }+ (RACSignal *)postEmail:(NSString *)email {AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];manager.requestSerializer = [AFJSONRequestSerializer new];NSDictionary *body = @{@"email": email ?: @""};return [[[manager rac_POST:kSubscribeURL parameters:body] logError] replayLazily]; }- (RACSignal *)emailValidSignal {if (!_emailValidSignal) {_emailValidSignal = [RACObserve(self, email) map:^id(NSString *email) {return @([email isValidEmail]);}];}return _emailValidSignal; }@end 復制代碼這看起來真是一大坨~看還是從小的地方來看吧。我們真正感興趣RACCommandRACCommand創建部分是以下代碼:
- (RACCommand *)subscribeCommand {if (!_subscribeCommand) {NSString *email = self.email;_subscribeCommand = [[RACCommand alloc] initWithEnabled:self.emailValidSignal signalBlock:^RACSignal *(id input) {return [SubscribeViewModel postEmail:email];}];}return _subscribeCommand; } 復制代碼Command通過一個enabledSignal參數來初始化。這個Signal可以指示Command是否可以被執行。在我們本次的用例中Command應該在用戶合法輸入email時允許被執行。self.emailValidSignal就是用來在email發生變化發送NO或者YES指示的。
signalBlock參數在Command需要執行時被調用。block應該返回一個signal。當我們設置allowsConcurrentExecution為NO,Command將會看守這個signal并且在本次執行未完成前不允許任何新的執行。
由于本次用例中的Command來自于按鈕的rac_command(在UIButtton+RACCommandSupport分類中定義),根據Command是否可以被執行,按鈕會自動切換enabled和disabled狀態。
當然,Command會在按鈕被用戶點擊的時候自動執行。我們可以通過RACCommand自由的實現這一切。如果你需要手動執行你可以調用-[RACCommand execute:],參數是可選的,你可以傳遞nil。我們的用例里不需要參數,不過這里的參數通常會十分有用(按鈕可以將自己當做-execute:的參數傳入)。-execute:方法也是一個你可以監控執行狀態的地方,你可以這樣寫:
[[self.viewModel.subscribeCommand execute:nil] subscribeCompleted:^{NSLog(@"The command executed"); }]; 復制代碼在我們的用例中按鈕為我們調用Command的執行(所以我們不需要手動調用-execute:),所以在Command執行時,為了及時更新UI,我們需要監聽Command的另一個屬性。有幾個讓人迷惑的地方,RACCommand的executionSignals屬性是一個每當Commands開始執行時就發送next:的Signal。問題在于Signal由Command創建,所以Signal中還有一層Signal。每次Command開始執行的時候, 我們會在ViewModel中通過mapSubscribeCommandStateToStatusMessage方法里面獲取到一個信號。同時在這個信號里面返回了一個字符串:
RACSignal *startedMessageSource = [self.subscribeCommand.executionSignals map:^id(RACSignal *subscribeSignal) {return NSLocalizedString(@"Sending request...", nil); }]; 復制代碼假如我們想用更函數的方式,來在Command執行完成后都能獲取string,我們需要做更多的工作:
RACSignal *completedMessageSource = [self.subscribeCommand.executionSignals flattenMap:^RACStream *(RACSignal *subscribeSignal) {return [[[subscribeSignal materialize] filter:^BOOL(RACEvent *event) {return event.eventType == RACEventTypeCompleted;}] map:^id(id value) {return NSLocalizedString(@"Thanks", nil);}]; }]; 復制代碼當Command執行時,flattenMap:方法調用一個帶subscribeSignal參數的block。這個block返回一個新的Signal并且它的值會被傳遞到下一個返回信號。materialize操作符讓我們捕獲到一個RACEvent(例如 next: complete 和 error:都是RACEvent的實例)。我們可以在信號完成之后過濾這些event并且映射成一個string。這些解釋讓你暈了嗎,不過你可以去看一下flattenMap:和materialize的文檔以助于你的理解。
我們可以用另一種不同但更容易理解的方式來實現:
@weakify(self); [self.subscribeCommand.executionSignals subscribeNext:^(RACSignal *subscribeSignal) {[subscribeSignal subscribeCompleted:^{@strongify(self);self.statusMessage = @"Thanks";}]; }]; 復制代碼但是我并不喜歡上面的寫法,因為這樣會block中的操作會更多并且會更多的在block中使用到self。所以在這里還使用了@weakify和@strongify(在libextobjc中定義)避免循環retain。
關于executionSignals屬性,有一個重要的細節。在這里的Signal所發送的event不包含error,所以對于那些有特殊errors屬性
RACSignal *failedMessageSource = [[self.subscribeCommand.errors subscribeOn:[RACScheduler mainThreadScheduler]] map:^id(NSError *error) {return NSLocalizedString(@"Error :(", nil); }]; 復制代碼如果我們有三個帶有狀態消息的Signal,我們可以將他們合并成一個信號并綁定到ViewModel的一個statusMessage屬性 (statusMessage綁定ViewController的statusLabel.text)。
RAC(self, statusMessage) = [RACSignal merge:@[startedMessageSource, completedMessageSource, failedMessageSource]]; 復制代碼那么以上是一個RACCommand在iOS app 開發中的一個example。我相信這種實現邏輯比使用UITextFieldDelegate有更多的優點,能在屬性和變量中體現更多的狀態。
其他有趣的RACCommand使用細節
RACCommand有一個executing屬性,實際上它是一個當execute:時會發送YES,終止時發送NO的信號。在訂閱信號時這個信號將會發送它的當前值,如果你只需要獲取當前值而不需要獲得信號,你可以通過以下方式:
BOOL commandIsExecuting = [[command.executing first] boolValue]; 復制代碼enabled屬性也是一個發送YES和NO的信號。當Command通過發送NO的enabledSignal信號創建,或者如果信號在執行并且allowsConcurrentExecutions 為 NO,enabled就會發送NO。
-execute:方法會自動訂閱原始Signal并且廣播它。這意味著你不需要去訂閱-execute:返回的信號,但是如果你訂閱了也不需要擔心它會被執行兩次。
有什么問題都可以在博文后面留言,或者微博上私信我,或者郵件我 coderfish@163.com。
博主是 iOS 妹子一枚。
希望大家一起進步。
我的微博:小魚周凌宇
打開一個 .h 文件可以看到相應內容:
[站外圖片上傳中...(image-95f25a-1511406635581)]
2 Dumpdecrypted
class dump 雖然能幫你解析出一個 app 的頭文件,但是對于 App Store 下載的 App 都是通過蘋果的一層簽名加密,通常我們成為『加殼』。對于已經加殼的 APP,解析后的效果就和加了代碼混淆類似。
比如直接去 dump 微信,出來的結果大概是這樣:
[站外圖片上傳中...(image-545ab-1511406635581)]
2.1 Download
dumpdecrypted GitHub 地址
2.2 Install
Dumpdecrypted 比另一個砸殼工具 Clutch 要難用的多。但由于 Clutch 無法砸開含有兼容 WatchOs 2 的 App,所以只能使用 Dumpdecrypted。
下載后打開 Makefile 文件,注意第三行:
$ SDK=`xcrun --sdk iphoneos --show-sdk-path` 復制代碼這里填寫的 SDK 必須與你越獄的 iPhone 系統等級一致,你可以這樣查看你 MAC 的 SDK :
$ xcrun --sdk iphoneos --show-sdk-path 復制代碼輸出:
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk 復制代碼而我的手機是 7.0 的,所以只能去這里下載對應的 SDK。
Makefile 所有需要填寫的填好后:
$ cd dumpdecrypted $ make 復制代碼在當前目錄下生成 dumpdecrypted.dylib 文件。
如果你覺得很麻煩,可以直接來這里或者這里下載。
2.3 Use
將 dumpdecrypted.dylib 放到需要砸殼 app 的 Documents 下。
查找微信的 Documents:
$ find / -name WeChat.app 復制代碼得到路徑:
/private/var/mobile/Applications/3DE3657E-0F69-45FF-928B-3DD5CD7A59FD/WeChat.app 復制代碼scp 傳輸:
$ scp ~/Desktop/dump/dumpdecrypted/dumpdecrypted_7.dylib root@172.16.212.217:/private/var/mobile/Applications/3DE3657E-0F69-45FF-928B-3DD5CD7A59FD/Documents 復制代碼砸殼:
$ cd /private/var/mobile/Applications/3DE3657E-0F69-45FF-928B-3DD5CD7A59FD/Documents $ DYLD_INSERT_LIBRARIES=dumpdecrypted_7.dylib /private/var/mobile/Applications/3DE3657E-0F69-45FF-928B-3DD5CD7A59FD/WeChat.app/WeChat 復制代碼注意 DYLD_INSERT_LIBRARIES= 后填寫的是你剛剛傳輸的 .dylib 文件名,我的是 dumpdecrypted_7.dylib
砸殼成功:
[+] detected 32bit ARM binary in memory. [+] offset to cryptid found: @0xfea4c(from 0xfe000) = a4c [+] Found encrypted data at address 00004000 of length 42237952 bytes - type 1.[+] Opening /private/var/mobile/Applications/3DE3657E-0F69-45FF-928B-3DD5CD7A59FD/WeChat.app/WeChat for reading. [+] Reading header [+] Detecting header type[+] Executable is a FAT image - searching for right architecture[+] Correct arch is at offset 16384 in the file[+] Opening WeChat.decrypted for writing. [+] Copying the not encrypted start of the file[+] Dumping the decrypted data into the file[+] Copying the not encrypted remainder of the file[+] Setting the LC_ENCRYPTION_INFO->cryptid to 0 at offset 4a4c [+] Closing original file[+] Closing dump file 復制代碼ls 查看 Documents 中文件多了一個 WeChat.decrypted,這就是砸殼過后的文件,scp出來:
$ scp WeChat.decrypted zhoulingyu@172.16.211.181:/Users/zhoulingyu/Desktop/dump/Wechat 復制代碼3 class dump 砸殼后的文件
上一步我們得到了 WeChat.decrypted,現在可以對其進行 dump:
class-dump --arch armv7 WeChat.decrypted -H -o /Users/zhoulingyu/Desktop/dump/Wechat/Wechat-decrypted-dump 復制代碼--arch armv7 是指定架構,dumpdecrypted 只會砸你手機處理器對應的那個殼,fat binary 的其它部分仍然是有殼的,而 class-dump 的默認目標又不是被砸殼的那個部分,如果不指定架構只能導出 CDStructures.h 一個文件
現在就可以看到 dump 后是明文的了:
[站外圖片上傳中...(image-135734-1511406635581)]
4 Learn More
當你能看到 .h 文件時候,意味著你知道了這個應用程序的各種接口,除了學習別人的優雅代碼之外,顯然也也可以做一些更有意思的事情,通過一些猜想和試驗,我們可以去嘗試做一個微信的插件。
下一次iOS攻防將會介紹動態庫的注入和微信插件的制作~
如果您感興趣~請點擊下方打賞支持萌妹子的原創喲~
有什么問題都可以在博文后面留言,或者微博上私信我,或者郵件我 coderfish@163.com。
博主是 iOS 妹子一枚。
希望大家一起進步。
我的微博:小魚周凌宇
date: 2016-08-05 10:33:00
原文地址
點擊這里
這幾天部門的前輩再用RAC的時候問到一個問題,RACCommand在RAC中具體的作用和起到的功能,到底應該如何應用它。
關于RAC的使用文章非常多,但是大多僅限于介紹和基本的使用方法,很少介紹RAC究竟應該如何優雅的嵌入到項目中。
在查閱資料的時候發現了此篇博文,寫的非常細致,所以做了一次搬運工。
另,妹子我的英文屬于渣渣系列,所以有什么翻譯不當,請一定要指教。
Code
文章中所有代碼在這里
RACCommand是你的新伙伴嗎?
RACCommand是ReactiveCocoa最精華的部分之一,它可以讓你在開發中節約大量的時間并讓你的iOS或者OS X app有更強的魯棒性。
我見過不少剛接觸ReactiveCocoa(后文將簡寫為RAC),還不能完全理解RACCommand是如何工作又不知何時應該使用RACCommand的同學。所以我認為這個小介紹將會很實用,可以給他們帶來一些啟發。官方文檔并沒有給出多少如何使用RACCommand的Examples,但是RACCommand頭文件的介紹還是很不錯的,不過這對剛開始用RAC的同學來說還是太難理解了。
RACCommand類是用于表示一些操作的執行。通常,是由于UI上的一些事件觸發了RACCommand的執行。比如當用戶按了一個按鈕,如果對應RACCommand實例可以被執行,就會執行相應的操作。這使得它很容易和UI進行綁定,同時可以保證當RACCommand處于not enabled時RACCommand實例的操作不會被執行。當Command可以執行時,常做的方式是把allowsconcuuent的屬性設置為NO,這可以保證Command已經執行完成后不會被重復執行。Command執行的結果是一個RACSignal,因此你可以調用next:、completed:、或者error:。后面將會展示具體使用方式。
Example App
我們假設我們正在設計一個簡單的app,其功能是讓用戶訂閱一個郵件。最簡單的方式是,用一個UITextField和一個UIButton。當用戶輸入email并且點擊按鈕的時候,email地址將會傳給某個web服務。看起來很簡單,但是我們應該確保用戶有最好的體驗。如果用戶按了兩次按鈕?``如何處理請求出錯?``如果email不合法?``RACCommand可以幫助我們處理這些情況。在這篇文章中將一步步完善這個小app以此來討論一些概念和工作原理。
可以從這里獲得源碼。
從一個非常簡單的ViewController可以很好的實踐MVVM模式。
- (void)bindWithViewModel {RAC(self.viewModel, email) = self.emailTextField.rac_textSignal;self.subscribeButton.rac_command = self.viewModel.subscribeCommand;RAC(self.statusLabel, text) = RACObserve(self.viewModel, statusMessage); } 復制代碼在上面的方法(在viewDidLoad中調用),在View和ViewModel中建立了綁定關系。下面是ViewModel的定義:
@interface SubscribeViewModel : NSObject@property(nonatomic, strong) RACCommand *subscribeCommand;// write to this property @property(nonatomic, strong) NSString *email;// read from this property @property(nonatomic, strong) NSString *statusMessage;@end 復制代碼如上所示,一個暴露出的RACCommand屬性。另外兩個是字符串屬性,它們和View的兩個屬性綁定在一起。ViewModel的完整實現如下:
static NSString *const kSubscribeURL = @"http://reactivetest.apiary.io/subscribers";@interface SubscribeViewModel () @property(nonatomic, strong) RACSignal *emailValidSignal; @end@implementation SubscribeViewModel- (id)init {self = [super init];if (self) {[self mapSubscribeCommandStateToStatusMessage];}return self; }- (void)mapSubscribeCommandStateToStatusMessage {RACSignal *startedMessageSource = [self.subscribeCommand.executionSignals map:^id(RACSignal *subscribeSignal) {return NSLocalizedString(@"Sending request...", nil);}];RACSignal *completedMessageSource = [self.subscribeCommand.executionSignals flattenMap:^RACStream *(RACSignal *subscribeSignal) {return [[[subscribeSignal materialize] filter:^BOOL(RACEvent *event) {return event.eventType == RACEventTypeCompleted;}] map:^id(id value) {return NSLocalizedString(@"Thanks", nil);}];}];RACSignal *failedMessageSource = [[self.subscribeCommand.errors subscribeOn:[RACScheduler mainThreadScheduler]] map:^id(NSError *error) {return NSLocalizedString(@"Error :(", nil);}];RAC(self, statusMessage) = [RACSignal merge:@[startedMessageSource, completedMessageSource, failedMessageSource]]; }- (RACCommand *)subscribeCommand {if (!_subscribeCommand) {NSString *email = self.email;_subscribeCommand = [[RACCommand alloc] initWithEnabled:self.emailValidSignal signalBlock:^RACSignal *(id input) {return [SubscribeViewModel postEmail:email];}];}return _subscribeCommand; }+ (RACSignal *)postEmail:(NSString *)email {AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];manager.requestSerializer = [AFJSONRequestSerializer new];NSDictionary *body = @{@"email": email ?: @""};return [[[manager rac_POST:kSubscribeURL parameters:body] logError] replayLazily]; }- (RACSignal *)emailValidSignal {if (!_emailValidSignal) {_emailValidSignal = [RACObserve(self, email) map:^id(NSString *email) {return @([email isValidEmail]);}];}return _emailValidSignal; }@end 復制代碼這看起來真是一大坨~看還是從小的地方來看吧。我們真正感興趣RACCommandRACCommand創建部分是以下代碼:
- (RACCommand *)subscribeCommand {if (!_subscribeCommand) {NSString *email = self.email;_subscribeCommand = [[RACCommand alloc] initWithEnabled:self.emailValidSignal signalBlock:^RACSignal *(id input) {return [SubscribeViewModel postEmail:email];}];}return _subscribeCommand; } 復制代碼Command通過一個enabledSignal參數來初始化。這個Signal可以指示Command是否可以被執行。在我們本次的用例中Command應該在用戶合法輸入email時允許被執行。self.emailValidSignal就是用來在email發生變化發送NO或者YES指示的。
signalBlock參數在Command需要執行時被調用。block應該返回一個signal。當我們設置allowsConcurrentExecution為NO,Command將會看守這個signal并且在本次執行未完成前不允許任何新的執行。
由于本次用例中的Command來自于按鈕的rac_command(在UIButtton+RACCommandSupport分類中定義),根據Command是否可以被執行,按鈕會自動切換enabled和disabled狀態。
當然,Command會在按鈕被用戶點擊的時候自動執行。我們可以通過RACCommand自由的實現這一切。如果你需要手動執行你可以調用-[RACCommand execute:],參數是可選的,你可以傳遞nil。我們的用例里不需要參數,不過這里的參數通常會十分有用(按鈕可以將自己當做-execute:的參數傳入)。-execute:方法也是一個你可以監控執行狀態的地方,你可以這樣寫:
[[self.viewModel.subscribeCommand execute:nil] subscribeCompleted:^{NSLog(@"The command executed"); }]; 復制代碼在我們的用例中按鈕為我們調用Command的執行(所以我們不需要手動調用-execute:),所以在Command執行時,為了及時更新UI,我們需要監聽Command的另一個屬性。有幾個讓人迷惑的地方,RACCommand的executionSignals屬性是一個每當Commands開始執行時就發送next:的Signal。問題在于Signal由Command創建,所以Signal中還有一層Signal。每次Command開始執行的時候, 我們會在ViewModel中通過mapSubscribeCommandStateToStatusMessage方法里面獲取到一個信號。同時在這個信號里面返回了一個字符串:
RACSignal *startedMessageSource = [self.subscribeCommand.executionSignals map:^id(RACSignal *subscribeSignal) {return NSLocalizedString(@"Sending request...", nil); }]; 復制代碼假如我們想用更函數的方式,來在Command執行完成后都能獲取string,我們需要做更多的工作:
RACSignal *completedMessageSource = [self.subscribeCommand.executionSignals flattenMap:^RACStream *(RACSignal *subscribeSignal) {return [[[subscribeSignal materialize] filter:^BOOL(RACEvent *event) {return event.eventType == RACEventTypeCompleted;}] map:^id(id value) {return NSLocalizedString(@"Thanks", nil);}]; }]; 復制代碼當Command執行時,flattenMap:方法調用一個帶subscribeSignal參數的block。這個block返回一個新的Signal并且它的值會被傳遞到下一個返回信號。materialize操作符讓我們捕獲到一個RACEvent(例如 next: complete 和 error:都是RACEvent的實例)。我們可以在信號完成之后過濾這些event并且映射成一個string。這些解釋讓你暈了嗎,不過你可以去看一下flattenMap:和materialize的文檔以助于你的理解。
我們可以用另一種不同但更容易理解的方式來實現:
@weakify(self); [self.subscribeCommand.executionSignals subscribeNext:^(RACSignal *subscribeSignal) {[subscribeSignal subscribeCompleted:^{@strongify(self);self.statusMessage = @"Thanks";}]; }]; 復制代碼但是我并不喜歡上面的寫法,因為這樣會block中的操作會更多并且會更多的在block中使用到self。所以在這里還使用了@weakify和@strongify(在libextobjc中定義)避免循環retain。
關于executionSignals屬性,有一個重要的細節。在這里的Signal所發送的event不包含error,所以對于那些有特殊errors屬性
RACSignal *failedMessageSource = [[self.subscribeCommand.errors subscribeOn:[RACScheduler mainThreadScheduler]] map:^id(NSError *error) {return NSLocalizedString(@"Error :(", nil); }]; 復制代碼如果我們有三個帶有狀態消息的Signal,我們可以將他們合并成一個信號并綁定到ViewModel的一個statusMessage屬性 (statusMessage綁定ViewController的statusLabel.text)。
RAC(self, statusMessage) = [RACSignal merge:@[startedMessageSource, completedMessageSource, failedMessageSource]]; 復制代碼那么以上是一個RACCommand在iOS app 開發中的一個example。我相信這種實現邏輯比使用UITextFieldDelegate有更多的優點,能在屬性和變量中體現更多的狀態。
其他有趣的RACCommand使用細節
RACCommand有一個executing屬性,實際上它是一個當execute:時會發送YES,終止時發送NO的信號。在訂閱信號時這個信號將會發送它的當前值,如果你只需要獲取當前值而不需要獲得信號,你可以通過以下方式:
BOOL commandIsExecuting = [[command.executing first] boolValue]; 復制代碼enabled屬性也是一個發送YES和NO的信號。當Command通過發送NO的enabledSignal信號創建,或者如果信號在執行并且allowsConcurrentExecutions 為 NO,enabled就會發送NO。
-execute:方法會自動訂閱原始Signal并且廣播它。這意味著你不需要去訂閱-execute:返回的信號,但是如果你訂閱了也不需要擔心它會被執行兩次。
有什么問題都可以在博文后面留言,或者微博上私信我,或者郵件我 coderfish@163.com。
博主是 iOS 妹子一枚。
希望大家一起進步。
我的微博:小魚周凌宇
總結
以上是生活随笔為你收集整理的iOS攻防——(四)class-dump-与-Dumpdecrypted-使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java solr 分词
- 下一篇: 活动推荐丨阿里云TechInsight论