块语法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 = @"yyyyMMdd 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层作为传声筒传递结果的重复代码。

  

  





郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。