Al03's blog

如何减少iOS视图控制器间耦合

iOS应用多是通过多个UIViewController的跳转来展示多个界面,通常负责这一系列的是UINavigationController。

这样实现?

SomeViewController *someVC = [[SomeViewController alloc] init];
   someVC.someID = 100;
[self.navigationController pushViewController:someVC animated:YES];

这样两个ViewController之间个耦合关系就加大了,当前VC需要实例化下一个VC,并设置这个VC的一些属性,然后调用自己的navigationControllerpush。

![](/images/20150728/Screen Shot 2015-07-28 at 1.29.21 PM.png)

这样吧,把这些事情都交给navigationController

UINavigationController+PushVCByName.h

@interface  UINavigationController (PushVCByName)
- (void)pushViewControllerName:(NSString *)viewControllerName para:(NSDictionary *)para animated:(BOOL)animated;
- (void)pushViewControllerName:(NSString *)viewControllerName animated:(BOOL)animated;
@end

UINavigationController+PushVCByName.m

#import <objc/runtime.h>

@implementation UINavigationController (PushVCByName)

- (void)pushViewControllerName:(NSString *)viewControllerName para:(NSDictionary *)para animated:(BOOL)animated
{

    const char *name = [viewControllerName UTF8String];
    Class vcClass = objc_getClass(name);
    if (vcClass != NULL) {
        UIViewController *vc = [[vcClass alloc]init];

        if (para != nil) {
            [para enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
                NSString *property = key;
                [vc setValue:obj forKey:property];
            }];
        }

        [self pushViewController:vc animated: animated];
    }
}

- (void)pushViewControllerName:(NSString *)viewControllerName animated:(BOOL)animated
{
    [self pushViewControllerName:viewControllerName para:nil animated:animated];
}

首先根据viewControllerName利用runtime得到Class,然后利用KVO设置property,这样视图间的耦合关系就少了。 ![](/images/20150728/Screen Shot 2015-07-28 at 1.30.35 PM.png)

当然情况没有这么简单,需要考虑的情况还有很多,这里只是一个思路.

首先就是KVO set value检查。

- (void)pushViewControllerName:(NSString *)viewControllerName para:(NSDictionary *)para animated:(BOOL)animated
{

    const char *name = [viewControllerName UTF8String];
    Class vcClass = objc_getClass(name);
    if (vcClass != NULL) {
        UIViewController *vc = [[vcClass alloc]init];

        if (para != nil) {
            [para enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
                 NSString *propertyName = key;
                objc_property_t property = class_getProperty(vcClass, [propertyName UTF8String]);
                if (property != NULL) {
                  [vc setValue:obj forKey:propertyName];
                }else{
                    @throw [NSException exceptionWithName:@"UIViewController set Wrong property" reason:[NSString stringWithFormat:@"%@ don't have property %@", viewControllerName, propertyName] userInfo:nil];
                }

            }];
        }

        [self pushViewController:vc animated:YES];
    }
}

setValue: 前先检查Class是否有对应的属性名,没有则抛出异常。

还有

UIViewControllerinit方式有很多,是否需要加载视图文件(xib、storyboard),这也是要考虑在内的。

Github Demo