首页 > 代码库 > 块语法Block在MVC思维的妙用之多重M层代理传值

块语法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(![[infoobjectForKey:@"image"]isKindOfClass:[NSNullclass]])

    {

       UIImage *image = [UIImageimageNamed:@"icon80-80.png"];

       NSData *data = http://www.mamicode.com/UIImageJPEGRepresentation(image,1.0);

       ABPersonSetImageData(person, (__bridgeCFDataRef) data, NULL);

    }

    //写入公司

   if([info objectForKey:@"company"])

    {

        ABRecordSetValue(person,kABPersonOrganizationProperty, (__bridgeCFTypeRef) [info objectForKey:@"company"],NULL);

    }

    //写入公司地址

    if([infoobjectForKey:@"companyAddress"])

    {

        ABMutableMultiValueRef multiAddress =ABMultiValueCreateMutable(kABMultiDictionaryPropertyType);

        NSMutableDictionary *addressDictionary = [[NSMutableDictionaryalloc] init];

        [addressDictionarysetObject:[info objectForKey:@"companyAddress"]forKey:(NSString *)kABPersonAddressStreetKey];

       ABMultiValueAddValueAndLabel(multiAddress, (__bridgeCFTypeRef)(addressDictionary), kABWorkLabel, NULL);

        

        //设置address

       ABRecordSetValue(person, kABPersonAddressProperty, multiAddress, nil);

    }

    //写入电话

    NSMutableArray *phones = [NSMutableArrayarrayWithCapacity:0];

    NSMutableArray *phonesName = [NSMutableArrayarrayWithCapacity:0];

        if(![[infoobjectForKey:[NSStringstringWithFormat:@"phone%d",i]]isKindOfClass:[NSNullclass]])

        {

            [phonesaddObject:[info objectForKey:[NSStringstringWithFormat:@"phone%d",0]]];

            [phonesNameaddObject:[NSStringstringWithFormat:@"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, (__bridgeCFStringRef)[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 = @"yyyyMMdd HH:mm:SS";

//    NSString *s = [NSString stringWithFormat:@"名片导入时间:%@  导入来自Ucard",[f stringFromDate:date]];

//    NSLog(@"%@",s);

//    ABRecordSetValue(person,kABPersonNoteProperty, (__bridge CFTypeRef)(s), NULL);

    

    //写入前检查是否联系人已存在

   if([ZxkSetAddressBookexamineAddressWithName:firstName] == NO)

    {

        block();

       return NO;

    }

    //获取通讯录中所有的联系人

   NSArray *array = (__bridgeNSArray *)ABAddressBookCopyArrayOfAllPeople(addressBook);

    //遍历所有的联系人并删除

   for (id objin array) {

       ABRecordRef people = (__bridgeABRecordRef)obj;

        NSString *first = (__bridgeNSString *)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);

    }

    elseif (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 = [[UIAlertViewalloc] initWithTitle:@"提示"message:@"导入失败,请在设置-隐私中检查是否有权限访问您的通讯录"delegate:selfcancelButtonTitle:@"确认"otherButtonTitles:nil];

        [alertshow];

    }

    return NO;

}



以下是B的加密操作,部分核心代码有删减

//通讯录加密

-(void)encryptWithAccountList:(AccountList *)list RePetBlock:(void(^)(void))block

{

    if([ZxkSetAddressBookSetAddressBookWithInfo:[dic mutableCopy]RePetBlock:block] == YES)

    {

       if(self.delegate && [self.delegaterespondsToSelector:@selector(setAddressSuccess:)])

        {

            [self.delegatesetAddressSuccess:self.accountList.remark];

        }

    }

}



最后是A的按钮交互代码

-(void)buttonClick

__blockNSArray *arr = self.p_tableArr;

       __block int select =self.p_didSelect;

        __blockAccountViewController *v = self;

       __block UIAlertView *alt;

        [self.encryptencryptWithAccountList:[self.p_tableArrobjectAtIndex:self.p_didSelect]RePetBlock:^

         {

            self.p_alertBlock = ^(NSInteger buttonIndex)

             {

                 

                if(buttonIndex==1)

                 {

                    AccountList *list = [arr objectAtIndex:select];

                     [vsetAddressSuccess:list.remark];

                 }

             };

             alt = [[UIAlertViewalloc] initWithTitle:NSLocalizedString(@"tip",nil)message:NSLocalizedString(@"setaddressTips",nil) delegate:v cancelButtonTitle:NSLocalizedString(@"cancel",nil) otherButtonTitles:NSLocalizedString(@"ok",nil),nil];

             [altshow];

             

         }];



以下是B的结果回调代理 部分核心代码有删减

#pragma mark - AddressbookEncryptDelegate

- (void)setAddressSuccess:(NSString *)firstName

{


    

    [self.navigationControllerpushViewController:self.p_peoplePickeranimated: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层作为传声筒传递结果的重复代码。

  

  





块语法Block在MVC思维的妙用之多重M层代理传值