块语法Block在MVC思维的妙用之多重M层代理传值
? ? 注:以下代碼均來自真實項目案例。
? ?
? ?在項目開發(fā)中,經(jīng)常避免一些與系統(tǒng)工具交互的功能需求。比如說開啟藍牙,開啟相機,通訊錄功能,還有數(shù)據(jù)加密等等。
? ?由于這些功能的實現(xiàn)沒有實例化的必要,并且又是許多項目都共用的功能,所以一般我們會作為類的靜態(tài)方法去作為自己的工具類。
??
? 以下是一段將字典的鍵值對導(dǎo)入通訊錄的靜態(tài)方法代碼。
? 假如說現(xiàn)在有一個這樣的邏輯流程,C層按鈕交互,將頁面某個數(shù)據(jù)加密導(dǎo)入通訊錄。
? 讓我們以MVC的思維梳理一下整過流程。
? 在這整個事件中,有三個參與者。頁面(C層),加密(M層),通訊錄導(dǎo)入(M層)。
? 為了簡寫,我們把頁面(C)定義為A,加密(M層)定義為B,通訊錄導(dǎo)入(M層)定義為C。
? 結(jié)果也有三種,第一種是導(dǎo)入失敗,第二種是能夠?qū)氤晒Φ峭ㄓ嶄浿幸呀?jīng)存在該聯(lián)系人(需要提示A【用戶】該次導(dǎo)入是否要覆蓋掉已經(jīng)存在的聯(lián)系人),第三種是能夠?qū)氤晒Σ⑶彝ㄓ嶄浿袥]有該聯(lián)系人。
? 方案也有三種,第一種是A將加密導(dǎo)入通訊錄指令告知B,B執(zhí)行加密命令后再將數(shù)據(jù)傳遞給C,C進行通訊錄導(dǎo)入將結(jié)果告知B,B再告知C。 實現(xiàn)上為了簡潔可以將該邏輯代碼封裝為B的一個方法,B調(diào)用C的靜態(tài)方法將返回參數(shù)通過自己的方法告知A,B和C的兩個方法返回參數(shù)為int類型123分別表示三種情況。
? 第二種方案跟第一種方案類似,主要是由于int類型的表述結(jié)果不夠直觀,可以通過回調(diào)代理去執(zhí)行,如-(void)setAddressSuccess ? ?-(void)setAddresFaild ??-(void)setAddresExist ?代理方式更為直觀,便于開發(fā)人員自身檢測以及維護人員維護。
? 第三種方案是A將處理三種結(jié)果的代碼塊作為Block參數(shù)送給B,B執(zhí)行完加密之后將A交于自己的Block送給C,C在執(zhí)行完導(dǎo)入操作之后直接執(zhí)行Block,不需要將自己的結(jié)果告知任何人。
? ?以下代碼是第二種方案和第三種方案融合處理,主要為了便于看到各種方案的利與弊。
? ?首先是C的靜態(tài)方法
+ (BOOL)SetAddressBookWithInfo:(NSMutableDictionary *)info RePetBlock:(void (^)(void))block
{
? ? //獲取通訊錄信息
? ? ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(nil, nil);
? ? // ABRecordRef是一個屬性的集合,相當(dāng)于通訊錄中聯(lián)系人的對象
? ? ABRecordRef person = ABPersonCreate();
? ? //寫入名字
? ? NSString *firstName = [info objectForKey:@"firstName"];
? ? // 保存到聯(lián)系人對象中,每個屬性都對應(yīng)一個宏,例如:kABPersonFirstNameProperty
? ? ? ? // 設(shè)置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);
?? ? ? ?
? ? ? ? //設(shè)置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);
? ? ? ? // 添加電話號碼與其對應(yīng)的名稱內(nèi)容
? ? ? ? for (int i = 0; i < [phones count]; i ++) {
? ? ? ? ? ? ABMultiValueIdentifier mi = ABMultiValueAddValueAndLabel(mv, (__bridge CFStringRef)[phones objectAtIndex:i], (__bridge CFStringRef)[phonesName objectAtIndex:i], &mi);
? ? ? ? }
? ? ? ? // 設(shè)置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);
? ? }
//
//? ? //寫入網(wǎng)址
//? ? if([info objectForKey:@"url"])
//? ? {
//? ? ? ? NSMutableArray *y = [NSMutableArray arrayWithObjects:[info objectForKey:@"url"],@"http://www.ucardpro.com", nil];
//? ? ? ? NSMutableArray *z = [NSMutableArray arrayWithObjects:@"個人主頁",@"Ucard官網(wǎng)", 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:@"名片導(dǎo)入時間:%@ ? 導(dǎo)入來自Ucard",[f stringFromDate:date]];
//? ? NSLog(@"%@",s);
//? ? ABRecordSetValue(person,kABPersonNoteProperty, (__bridge CFTypeRef)(s), NULL);
?? ?
? ? //寫入前檢查是否聯(lián)系人已存在
? ? if([ZxkSetAddressBook examineAddressWithName:firstName] == NO)
? ? {
? ? ? ? block();
? ? ? ? return NO;
? ? }
? ? // 獲取通訊錄中所有的聯(lián)系人
? ? NSArray *array = (__bridge NSArray *)ABAddressBookCopyArrayOfAllPeople(addressBook);
? ? // 遍歷所有的聯(lián)系人并刪除
? ? 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);
? ? ? ? }
? ? }
?? ?
? ? // 將新建的聯(lián)系人添加到通訊錄中
? ? 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:@"導(dǎo)入失敗,請在設(shè)置-隱私中檢查是否有權(quán)限訪問您的通訊錄" delegate:self cancelButtonTitle:@"確認(rèn)" 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的結(jié)果回調(diào)代理 部分核心代碼有刪減
#pragma mark - AddressbookEncryptDelegate
- (void)setAddressSuccess:(NSString *)firstName
{
?? ?
? ? [self.navigationController pushViewController:self.p_peoplePicker animated:YES];
? ?
}
? ?以上實現(xiàn)既有代理也有Block,可以看到代理實現(xiàn)的過程較為繁雜,而Block實現(xiàn)更加的便捷直觀,根據(jù)上一章節(jié)在Block中講到的系統(tǒng)UIAlertView的集中代碼用法,這一整個過程不需要任何的回調(diào)代理,也不需要任何的邏輯判斷,實際上只需三個Block代碼快就可以完成,第一個是處理UIAlertView交互的bLOCK,一個是處理三種結(jié)果情況的Block,一個是組合前兩個Block之后的最終Block。?
? 也許從實現(xiàn)來看,三種方法都可以達到目的,但是在MVC的思維中前兩種實現(xiàn)方法都存在M層越界的情況。
? 在這個過程中,作為數(shù)據(jù)層,B的職責(zé)只是加密,C的職責(zé)只是導(dǎo)入,A作為Controller層主要是想拿到C的處理結(jié)果來做下一步的邏輯。第一種方案和第二種方案的實現(xiàn)中,由于C都是把自己的結(jié)果先交給B,B再交給A,所以這里存在A把自己與B無關(guān)的機密信息泄漏給了B,因為是B先知道這三種結(jié)果,A后知道。所以從理論來講,這里可能會存在B操作不當(dāng)沒有正確的把C的結(jié)果告知A。當(dāng)然,由于以上過程較為簡單,一般不會出現(xiàn)這種情況。而由于C作為一個工具類的靜態(tài)方法,無法做到代理告知,這也是一個問題。
??
? Block語法在這個過程中的好處就是:當(dāng)Controller的某一個命令需要多個Model層協(xié)調(diào)進行的時候,Controller只需要將自己對最終結(jié)果處理的邏輯代碼寫在這個命令傳達之前,并且準(zhǔn)確的交由真正那個能夠給自己明確答復(fù)的Model層去直接執(zhí)行命令,既能夠集中自己的邏輯代碼,也避免了其他M層作為傳聲筒傳遞結(jié)果的重復(fù)代碼。
??
??
與50位技術(shù)專家面對面20年技術(shù)見證,附贈技術(shù)全景圖
總結(jié)
以上是生活随笔為你收集整理的块语法Block在MVC思维的妙用之多重M层代理传值的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: IOS开发之__bridge,__bri
- 下一篇: Capturing 'self' str