块语法Block在MVC思维的妙用之多重M层代理传值
? ? 注:以下代碼均來自真實項目案例。
? ?
? ?在項目開發中,經常避免一些與系統工具交互的功能需求。比如說開啟藍牙,開啟相機,通訊錄功能,還有數據加密等等。
? ?由于這些功能的實現沒有實例化的必要,并且又是許多項目都共用的功能,所以一般我們會作為類的靜態方法去作為自己的工具類。
??
? 以下是一段將字典的鍵值對導入通訊錄的靜態方法代碼。
? 假如說現在有一個這樣的邏輯流程,C層按鈕交互,將頁面某個數據加密導入通訊錄。
? 讓我們以MVC的思維梳理一下整過流程。
? 在這整個事件中,有三個參與者。頁面(C層),加密(M層),通訊錄導入(M層)。
? 為了簡寫,我們把頁面(C)定義為A,加密(M層)定義為B,通訊錄導入(M層)定義為C。
? 結果也有三種,第一種是導入失敗,第二種是能夠導入成功但是通訊錄中已經存在該聯系人(需要提示A【用戶】該次導入是否要覆蓋掉已經存在的聯系人),第三種是能夠導入成功并且通訊錄中沒有該聯系人。
? 方案也有三種,第一種是A將加密導入通訊錄指令告知B,B執行加密命令后再將數據傳遞給C,C進行通訊錄導入將結果告知B,B再告知C。 實現上為了簡潔可以將該邏輯代碼封裝為B的一個方法,B調用C的靜態方法將返回參數通過自己的方法告知A,B和C的兩個方法返回參數為int類型123分別表示三種情況。
? 第二種方案跟第一種方案類似,主要是由于int類型的表述結果不夠直觀,可以通過回調代理去執行,如-(void)setAddressSuccess ? ?-(void)setAddresFaild ??-(void)setAddresExist ?代理方式更為直觀,便于開發人員自身檢測以及維護人員維護。
? 第三種方案是A將處理三種結果的代碼塊作為Block參數送給B,B執行完加密之后將A交于自己的Block送給C,C在執行完導入操作之后直接執行Block,不需要將自己的結果告知任何人。
? ?以下代碼是第二種方案和第三種方案融合處理,主要為了便于看到各種方案的利與弊。
? ?首先是C的靜態方法
+ (BOOL)SetAddressBookWithInfo:(NSMutableDictionary *)info RePetBlock:(void (^)(void))block
{
? ? //獲取通訊錄信息
? ? ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(nil, nil);
? ? // ABRecordRef是一個屬性的集合,相當于通訊錄中聯系人的對象
? ? ABRecordRef person = ABPersonCreate();
? ? //寫入名字
? ? NSString *firstName = [info objectForKey:@"firstName"];
? ? // 保存到聯系人對象中,每個屬性都對應一個宏,例如:kABPersonFirstNameProperty
? ? ? ? // 設置firstName屬性
? ? ? ? ABRecordSetValue(person, kABPersonFirstNameProperty, (__bridge CFTypeRef) firstName, NULL);
? ? //寫入圖片
? ? if(![[info objectForKey:@"image"] isKindOfClass:[NSNull class]])
? ? {
? ? ? ? UIImage *image = [UIImage imageNamed:@"icon80-80.png"];
? ? ? ? NSData *data = UIImageJPEGRepresentation(image, 1.0);
? ? ? ? ABPersonSetImageData(person, (__bridge CFDataRef) data, NULL);
? ? }
? ? //寫入公司
? ? if([info objectForKey:@"company"])
? ? {
? ? ? ? ABRecordSetValue(person, kABPersonOrganizationProperty, (__bridge CFTypeRef) [info objectForKey:@"company"], NULL);
? ? }
? ? //寫入公司地址
? ? if([info objectForKey:@"companyAddress"])
? ? {
? ? ? ? ABMutableMultiValueRef multiAddress = ABMultiValueCreateMutable(kABMultiDictionaryPropertyType);
? ? ? ? NSMutableDictionary *addressDictionary = [[NSMutableDictionary alloc] init];
? ? ? ? [addressDictionary setObject:[info objectForKey:@"companyAddress"] forKey:(NSString *) kABPersonAddressStreetKey];
? ? ? ? ABMultiValueAddValueAndLabel(multiAddress, (__bridge CFTypeRef)(addressDictionary), kABWorkLabel, NULL);
?? ? ? ?
? ? ? ? //設置address
? ? ? ? ABRecordSetValue(person, kABPersonAddressProperty, multiAddress, nil);
? ? }
? ? //寫入電話
? ? NSMutableArray *phones = [NSMutableArray arrayWithCapacity:0];
? ? NSMutableArray *phonesName = [NSMutableArray arrayWithCapacity:0];
? ? ? ? if(![[info objectForKey:[NSString stringWithFormat:@"phone%d",i]] isKindOfClass:[NSNull class]])
? ? ? ? {
? ? ? ? ? ? [phones addObject:[info objectForKey:[NSString stringWithFormat:@"phone%d",0]]];
? ? ? ? ? ? [phonesName addObject:[NSString stringWithFormat:@"phone%d",0]];
? ? ? ? }
? ??
?? ?
? ? if(phones.count != 0 || phonesName.count != 0)
? ? {
? ? ? ? // ABMultiValueRef類似是Objective-C中的NSMutableDictionary
? ? ? ? ABMultiValueRef mv = ABMultiValueCreateMutable(kABMultiStringPropertyType);
? ? ? ? // 添加電話號碼與其對應的名稱內容
? ? ? ? for (int i = 0; i < [phones count]; i ++) {
? ? ? ? ? ? ABMultiValueIdentifier mi = ABMultiValueAddValueAndLabel(mv, (__bridge CFStringRef)[phones objectAtIndex:i], (__bridge CFStringRef)[phonesName objectAtIndex:i], &mi);
? ? ? ? }
? ? ? ? // 設置phone屬性
? ? ? ? ABRecordSetValue(person, kABPersonPhoneProperty, mv, NULL);
? ? }
?? ?
? ? //寫入郵箱
? ? if([info objectForKey:@"email"])
? ? {
? ? ? ??
? ? ? ? ABMultiValueRef em = ABMultiValueCreateMutable(kABPersonEmailProperty);
? ? ? ? ABMultiValueIdentifier ma = ABMultiValueAddValueAndLabel(em, (__bridge CFTypeRef)([info objectForKey:@"email"]), (__bridge CFStringRef)@"email", &ma);
? ? ? ? ABRecordSetValue(person, kABPersonEmailProperty, em, NULL);
? ? }
//
//? ? //寫入網址
//? ? if([info objectForKey:@"url"])
//? ? {
//? ? ? ? NSMutableArray *y = [NSMutableArray arrayWithObjects:[info objectForKey:@"url"],@"http://www.ucardpro.com", nil];
//? ? ? ? NSMutableArray *z = [NSMutableArray arrayWithObjects:@"個人主頁",@"Ucard官網", nil];
//? ? ? ? ABMultiValueRef url = ABMultiValueCreateMutable(kABPersonURLProperty);
//? ? ? ? for (int i = 0; i < [phones count]; i ++) {
//? ? ? ? ? ? ABMultiValueIdentifier ur = ABMultiValueAddValueAndLabel(url, (__bridge CFStringRef)[y objectAtIndex:i], (__bridge CFStringRef)[z objectAtIndex:i], &ur);
//? ? ? ? }
//? ? ? ? ABRecordSetValue(person, kABPersonURLProperty, url, NULL);
//? ? }
//
//? ? //寫入日期
//? ? NSDate *date = [NSDate date];
//? ? NSDateFormatter *f = [[NSDateFormatter alloc] init];
//? ? f.dateFormat = @"yyyy年MM月dd日 HH:mm:SS";
//? ? NSString *s = [NSString stringWithFormat:@"名片導入時間:%@ ? 導入來自Ucard",[f stringFromDate:date]];
//? ? NSLog(@"%@",s);
//? ? ABRecordSetValue(person,kABPersonNoteProperty, (__bridge CFTypeRef)(s), NULL);
?? ?
? ? //寫入前檢查是否聯系人已存在
? ? if([ZxkSetAddressBook examineAddressWithName:firstName] == NO)
? ? {
? ? ? ? block();
? ? ? ? return NO;
? ? }
? ? // 獲取通訊錄中所有的聯系人
? ? NSArray *array = (__bridge NSArray *)ABAddressBookCopyArrayOfAllPeople(addressBook);
? ? // 遍歷所有的聯系人并刪除
? ? for (id obj in array) {
? ? ? ? ABRecordRef people = (__bridge ABRecordRef)obj;
? ? ? ? NSString *first = (__bridge NSString *)ABRecordCopyValue(people, kABPersonFirstNameProperty);
? ? ? ? if ([first isEqualToString:firstName])
? ? ? ? {
? ? ? ? ? ? ABAddressBookRemoveRecord(addressBook, people, nil);
? ? ? ? }
? ? }
?? ?
? ? // 將新建的聯系人添加到通訊錄中
? ? ABAddressBookAddRecord(addressBook, person, NULL);
?? ?
? ? if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined) {
? ? ? ? dispatch_semaphore_t sema = dispatch_semaphore_create(0);
? ? ? ? ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
? ? ? ? ? ? ? ? dispatch_semaphore_signal(sema);
?? ? ? ? ? ?
? ? ? ? });
? ? ? ? dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
? ? }
? ? else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized) {
? ? ? ? // The user has previously given access, add the contact
? ? ? ? if(ABAddressBookSave(addressBook, NULL))
? ? ? ? {
? ? ? ? ? ? return YES;
?? ? ? ? ? ?
? ? ? ? }
? ? }
? ? else {
? ? ? ? // The user has previously denied access
? ? ? ? // Send an alert telling user to change privacy setting in settings app
? ? ? ? UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示" message:@"導入失敗,請在設置-隱私中檢查是否有權限訪問您的通訊錄" delegate:self cancelButtonTitle:@"確認" otherButtonTitles:nil];
? ? ? ? [alert show];
? ? }
? ? return NO;
}
以下是B的加密操作,部分核心代碼有刪減
//通訊錄加密
-(void)encryptWithAccountList:(AccountList *)list RePetBlock:(void(^)(void))block
{
? ? if([ZxkSetAddressBook SetAddressBookWithInfo:[dic mutableCopy] RePetBlock:block] == YES)
? ? {
? ? ? ? if(self.delegate && [self.delegate respondsToSelector:@selector(setAddressSuccess:)])
? ? ? ? {
? ? ? ? ? ? [self.delegate setAddressSuccess:self.accountList.remark];
? ? ? ? }
? ? }
}
最后是A的按鈕交互代碼
-(void)buttonClick
{
__block NSArray *arr = self.p_tableArr;
? ? ? ? __block int select = self.p_didSelect;
? ? ? ? __block AccountViewController *v = self;
? ? ? ? __block UIAlertView *alt;
? ? ? ? [self.encrypt encryptWithAccountList:[self.p_tableArr objectAtIndex:self.p_didSelect] RePetBlock:^
?? ? ? ? {
?? ? ? ? ? ? self.p_alertBlock = ^(NSInteger buttonIndex)
?? ? ? ? ? ? {
? ? ? ? ? ? ? ? ?
?? ? ? ? ? ? ? ? if(buttonIndex==1)
?? ? ? ? ? ? ? ? {
?? ? ? ? ? ? ? ? ? ? AccountList *list = [arr objectAtIndex:select];
?? ? ? ? ? ? ? ? ? ? [v setAddressSuccess:list.remark];
?? ? ? ? ? ? ? ? }
?? ? ? ? ? ? };
?? ? ? ? ? ? alt = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"tip",nil) message:NSLocalizedString(@"setaddressTips", nil) delegate:v cancelButtonTitle:NSLocalizedString(@"cancel", nil) otherButtonTitles:NSLocalizedString(@"ok",nil), nil];
?? ? ? ? ? ? [alt show];
? ? ? ? ? ? ?
?? ? ? ? }];
}
以下是B的結果回調代理 部分核心代碼有刪減
#pragma mark - AddressbookEncryptDelegate
- (void)setAddressSuccess:(NSString *)firstName
{
?? ?
? ? [self.navigationController pushViewController:self.p_peoplePicker animated:YES];
? ?
}
? ?以上實現既有代理也有Block,可以看到代理實現的過程較為繁雜,而Block實現更加的便捷直觀,根據上一章節在Block中講到的系統UIAlertView的集中代碼用法,這一整個過程不需要任何的回調代理,也不需要任何的邏輯判斷,實際上只需三個Block代碼快就可以完成,第一個是處理UIAlertView交互的bLOCK,一個是處理三種結果情況的Block,一個是組合前兩個Block之后的最終Block。?
? 也許從實現來看,三種方法都可以達到目的,但是在MVC的思維中前兩種實現方法都存在M層越界的情況。
? 在這個過程中,作為數據層,B的職責只是加密,C的職責只是導入,A作為Controller層主要是想拿到C的處理結果來做下一步的邏輯。第一種方案和第二種方案的實現中,由于C都是把自己的結果先交給B,B再交給A,所以這里存在A把自己與B無關的機密信息泄漏給了B,因為是B先知道這三種結果,A后知道。所以從理論來講,這里可能會存在B操作不當沒有正確的把C的結果告知A。當然,由于以上過程較為簡單,一般不會出現這種情況。而由于C作為一個工具類的靜態方法,無法做到代理告知,這也是一個問題。
??
? Block語法在這個過程中的好處就是:當Controller的某一個命令需要多個Model層協調進行的時候,Controller只需要將自己對最終結果處理的邏輯代碼寫在這個命令傳達之前,并且準確的交由真正那個能夠給自己明確答復的Model層去直接執行命令,既能夠集中自己的邏輯代碼,也避免了其他M層作為傳聲筒傳遞結果的重復代碼。
??
??
與50位技術專家面對面20年技術見證,附贈技術全景圖
總結
以上是生活随笔為你收集整理的块语法Block在MVC思维的妙用之多重M层代理传值的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: IOS开发之__bridge,__bri
- 下一篇: s3c2440移植MQTT