V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
iOS 开发实用技术导航
NSHipster 中文版
http://nshipster.cn/
cocos2d 开源 2D 游戏引擎
http://www.cocos2d-iphone.org/
CocoaPods
http://cocoapods.org/
Google Analytics for Mobile 统计解决方案
http://code.google.com/mobile/analytics/
WWDC
https://developer.apple.com/wwdc/
Design Guides and Resources
https://developer.apple.com/design/
Transcripts of WWDC sessions
http://asciiwwdc.com
Cocoa with Love
http://cocoawithlove.com/
Cocoa Dev Central
http://cocoadevcentral.com/
NSHipster
http://nshipster.com/
Style Guides
Google Objective-C Style Guide
NYTimes Objective-C Style Guide
Useful Tools and Services
Charles Web Debugging Proxy
Smore
summer1991
V2EX  ›  iDev

iOS: 请问大家下面这段 ViewController 的代码( app logic 的部分)怎么优化?我不想在 VC 里面放这些琐碎的代码怎么怎么搞?

  •  1
     
  •   summer1991 · 2017-06-15 12:09:35 +08:00 · 4291 次点击
    这是一个创建于 2721 天前的主题,其中的信息可能已经有所发展或是发生改变。
    @implementation ASHTempViewController
    
    #pragma mark - app logic
    
    - (void)sendBroadcastWithSuccessBlock:(void (^)(void))successBlock
    {
        BOOL haveRead = [[NSUserDefaults standardUserDefaults] boolForKey:@"haveReadBroadcastTip"];
        if (haveRead == NO) {
            NSString *title = @"1、发布广播,全服玩家都可以看到;\n2、禁止发布反动、政治、色情、辱骂、广告等不良言论,否则将会遭到删除、封号处理。";
            __weak typeof(self) wself = self;
            [self showAlertWithMsg:title callback:^{
                [[NSUserDefaults standardUserDefaults] setObject:@(YES) forKey:@"haveReadBroadcastTip"];
                [wself showSendViewWithSuccessBlock:successBlock];
            }];
        } else {
            [self showSendViewWithSuccessBlock:successBlock];
        }
    }
    
    - (void)showSendViewWithSuccessBlock:(void(^)(void))successBlock
    {
        __weak typeof(self) wself = self;
        UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"发送广播" message:nil preferredStyle:UIAlertControllerStyleAlert];
        [alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
            textField.placeholder = @"说点什么...";
        }];
        [alertController addAction:[UIAlertAction actionWithTitle:@"发送" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            NSString *msg = [[alertController textFields][0] text];
            [wself sendBroadcast:msg successBlock:successBlock];
        }]];
        [alertController addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
            NSLog(@"Canelled");
        }]];
        [self presentViewController:alertController animated:YES completion:nil];
    }
    
    - (void)sendBroadcast:(NSString *)msg successBlock:(void(^)(void))successBlock
    {
        __weak typeof(self) wself = self;
        [self sendBroadcastRequestWithMsg:msg successBlock:^{
            //data operation
            !successBlock ?: successBlock();
        } failureBlock:^(NSError *error) {
            [wself showAlertWithMsg:error.localizedDescription callback:nil];
        }];
    }
    
    #pragma mark - networks
    
    - (void)sendBroadcastRequestWithMsg:(NSString *)msg successBlock:(void(^)(void))successBlock failureBlock:(void(^)(NSError *error))failureBlock
    {
        //send request
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            !successBlock ?: successBlock();
        });
    }
    
    #pragma mark - utils
    
    - (void)showAlertWithMsg:(NSString *)msg callback:(void(^)(void))callback
    {
        UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:msg preferredStyle:UIAlertControllerStyleAlert];
        [alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            !callback ?: callback();
        }]];
        [self presentViewController:alert animated:YES completion:nil];
    }
    
    @end
    
    第 1 条附言  ·  2017-06-15 14:12:29 +08:00
    Network & Utils 这些都在其他文件中,大家不用考虑,这里列出来只是示意
    第 2 条附言  ·  2017-06-15 14:19:50 +08:00
    这个只是一个按钮的点击逻辑,界面中存在 N 个按钮,与这个逻辑类似。于是 vc 中充斥大量的 逻辑判断,网络调用,弹窗调用,界面跳转,界面刷新等。请问怎么优化呢?
    第 3 条附言  ·  2017-06-15 15:25:53 +08:00

    我又出了一个版本,根据大家的意见整理了下

    
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    //macro.h
    
    #ifndef	weakify
    #if __has_feature(objc_arc)
    #define weakify( x )	autoreleasepool{} __weak __typeof__(x) __weak_##x##__ = x;
    #else	// #if __has_feature(objc_arc)
    #define weakify( x )	autoreleasepool{} __block __typeof__(x) __block_##x##__ = x;
    #endif	// #if __has_feature(objc_arc)
    #endif	// #ifndef	weakify
    
    #ifndef	normalize
    #if __has_feature(objc_arc)
    #define normalize( x )	try{} @finally{} __typeof__(x) x = __weak_##x##__;
    #else	// #if __has_feature(objc_arc)
    #define normalize( x )	try{} @finally{} __typeof__(x) x = __block_##x##__;
    #endif	// #if __has_feature(objc_arc)
    #endif	// #ifndef	@normalize
    
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    //store/pref/ASHPrefStore.h
    
    @interface ASHPrefStore : NSObject
    
    + (BOOL)haveReadBroadcastTip;
    + (void)setHaveReadBroadcastTip;
    
    @end
    
    //store/pref/ASHPrefStore.m
    
    @implementation ASHPrefStore
    
    static NSString *const kHaveReadBroadcastTipKey = @"haveReadBroadcastTip";
    
    + (BOOL)haveReadBroadcastTip
    {
        return [[NSUserDefaults standardUserDefaults] boolForKey:kHaveReadBroadcastTipKey];
    }
    
    + (void)setHaveReadBroadcastTip
    {
        [[NSUserDefaults standardUserDefaults] setObject:@(YES) forKey:kHaveReadBroadcastTipKey];
    }
    
    @end
    
    第 4 条附言  ·  2017-06-15 15:26:39 +08:00
    ```Objective-c
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    //network/ASHNetworkAPI.h

    @interface ASHNetworkAPI : NSObject

    + (void)sendBroadcastRequestWithMsg:(NSString *)msg successBlock:(void(^)(void))successBlock failureBlock:(void(^)(NSError *error))failureBlock;

    @end

    //network/ASHNetworkAPI.m

    @implementation ASHNetworkAPI

    + (void)sendBroadcastRequestWithMsg:(NSString *)msg successBlock:(void(^)(void))successBlock failureBlock:(void(^)(NSError *error))failureBlock
    {
    //send request
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    !successBlock ?: successBlock();
    });
    }

    @end
    ```
    第 5 条附言  ·  2017-06-15 15:27:21 +08:00
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    //network/ASHNetworkAPI.h
    
    @interface ASHNetworkAPI : NSObject
    
    + (void)sendBroadcastRequestWithMsg:(NSString *)msg successBlock:(void(^)(void))successBlock failureBlock:(void(^)(NSError *error))failureBlock;
    
    @end
    
    //network/ASHNetworkAPI.m
    
    @implementation ASHNetworkAPI
    
    + (void)sendBroadcastRequestWithMsg:(NSString *)msg successBlock:(void(^)(void))successBlock failureBlock:(void(^)(NSError *error))failureBlock
    {
        //send request
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            !successBlock ?: successBlock();
        });
    }
    
    @end
    
    第 6 条附言  ·  2017-06-15 15:28:06 +08:00
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    //utils/ui/ASHAlertUtils.h
    
    @interface ASHAlertUtils : NSObject
    
    + (void)showAlertWithController:(UIViewController *)controller msg:(NSString *)msg callback:(void(^)(void))callback;
    
    + (void)showEditAlertWithController:(UIViewController *)controller title:(NSString *)title placeholder:(NSString *)placeholder callback:(void(^)(NSString *content))callback;
    
    @end
    
    //utils/ui/ASHAlertUtils.m
    
    @implementation ASHAlertUtils
    
    #pragma mark - utils
    
    + (void)showAlertWithController:(UIViewController *)controller msg:(NSString *)msg callback:(void(^)(void))callback
    {
        UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:msg preferredStyle:UIAlertControllerStyleAlert];
        [alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            !callback ?: callback();
        }]];
        [controller presentViewController:alert animated:YES completion:nil];
    }
    
    
    
    第 7 条附言  ·  2017-06-15 15:28:32 +08:00
    + (void)showEditAlertWithController:(UIViewController *)controller title:(NSString *)title placeholder:(NSString *)placeholder callback:(void(^)(NSString *content))callback
    {
        UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleAlert];
        [alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
            textField.placeholder = placeholder;
        }];
        [alertController addAction:[UIAlertAction actionWithTitle:@"发送" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            NSString *content = [[alertController textFields][0] text];
            !callback ?: callback(content);
        }]];
        [alertController addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
            NSLog(@"Canelled");
        }]];
        [controller presentViewController:alertController animated:YES completion:nil];
    }
    
    @end
    
    第 8 条附言  ·  2017-06-15 15:28:55 +08:00
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    //controller/ASHTempViewController.m
    
    @implementation ASHTempViewController
    
    #pragma mark - app logic
    
    - (void)sendBroadcastWithSuccessBlock:(void (^)(void))successBlock
    {
        @weakify(self)
        void (^showSendBroadcast) (void) = ^{
            [ASHAlertUtils showEditAlertWithController:self title:@"发送广播'" placeholder:@"说点什么..." callback:^(NSString *content) {
                [ASHNetworkAPI sendBroadcastRequestWithMsg:content successBlock:successBlock failureBlock:^(NSError *error) {
                    @normalize(self)
                    !self ?: [ASHAlertUtils showAlertWithController:self msg:error.localizedDescription callback:nil];
                }];
            }];
        };
        
        if ([ASHPrefStore haveReadBroadcastTip]) {
            showSendBroadcast();
        } else {
            NSString *title = @"1、发布广播,全服玩家都可以看到;\n2、禁止发布反动、政治、色情、辱骂、广告等不良言论,否则将会遭到删除、封号处理。";
            [ASHAlertUtils showAlertWithController:self msg:title callback:^{
                [ASHPrefStore setHaveReadBroadcastTip];
                showSendBroadcast();
            }];
        }
    }
    
    @end
    
    22 条回复    2017-07-04 12:37:27 +08:00
    summer1991
        1
    summer1991  
    OP
       2017-06-15 12:11:24 +08:00
    求答疑!
    blacklist
        2
    blacklist  
       2017-06-15 13:29:39 +08:00
    提取出来做个 Helper 类或者函数的合集?
    blacklist
        3
    blacklist  
       2017-06-15 13:31:58 +08:00
    像 NetworkHelper/NetworkUtils,UIHelper/UIUtils,StringHelper/StringUtils 之类独立的代码文件。。。。。。。。。
    winglight2016
        4
    winglight2016  
       2017-06-15 13:37:09 +08:00
    加起来也才 100 行代码,等超过 400 行再来优化不迟
    renshaojuncool
        5
    renshaojuncool  
       2017-06-15 13:41:00 +08:00
    可以抽取出来做个工具类或者辅助类,另外,不要为了优化了优化,任何优化都有可能带来新的问题
    summer1991
        6
    summer1991  
    OP
       2017-06-15 14:10:47 +08:00
    @winglight2016 比如界面上有十个按钮,每个大概都是这个逻辑怎么办?
    guomiaoyou7784
        8
    guomiaoyou7784  
       2017-06-15 14:21:02 +08:00
    VC 做轻,可以考虑将一些常见的 delegate 实现 做"代理实现",网络层逻辑下沉或抽取到 ViewModel 中,再组合到 VC 上。 方法实现可以考虑多用 Category 来减少 同一个.m 文件的代码量(可以查看 Apple 一些 UIKIt 的编码风格,也是有不同的 Category 来划分代码

    这么做了之后,直观表现,VC 的职责还是在的,代码并不会集中在一个文件内了
    summer1991
        9
    summer1991  
    OP
       2017-06-15 14:24:13 +08:00
    @guomiaoyou7784 这个文章我以前看过的,但针对于代码中的上述情况,文章中说的做法没有一个是有效的。能不能将你的想法结合代码具体说明下?
    summer1991
        10
    summer1991  
    OP
       2017-06-15 14:32:34 +08:00
    @guomiaoyou7784 上边代码中网络请求的代码在网络请求模块,网络调用代码在 vc 中也只有 一行调用 + block 回调,放到 view model 中只是多增加了一层调用,除了更复杂好像没有什么优化。不同按钮的逻辑放在 viewController 的不同分类中,如果在这个逻辑处理中有一些状态需要放到 vc 中,岂不是很麻烦;而且我觉得分类这种形式使用来增强类本身的功能,如果这些逻辑放在这个类的分类中,且用于被这个类自身调用,是不是比较牵强。有具体的例子可以发出来吗?
    summer1991
        11
    summer1991  
    OP
       2017-06-15 14:40:26 +08:00
    @blacklist 你还木有看清楚我的问题呀亲
    andyL
        12
    andyL  
       2017-06-15 14:54:44 +08:00
    1.alert 的方法,抽象一些参数出来封装一下
    2.数据持久化可以有专门的类来处理,所以也可以封装一下
    3.这些确定的字符串用常量来定义比较好
    4.这些带 block 的方法已经不用再封装了,但是如果 其他类也要复用的话,可以抽象一下。
    andyL
        13
    andyL  
       2017-06-15 14:56:22 +08:00
    5.针对 block 上下文 对 self 的弱引用和强引用的变换 可以抽象成宏定义或者静态方法,基本上整个工程都要处处使用到的
    amon
        14
    amon  
       2017-06-15 15:11:27 +08:00
    你这个本来就属于逻辑了啊,Controller 里面放逻辑没毛病。
    你再优化,也只是把代码挪到别处而已。
    比如,你把按钮的事件放到 block 里面,然后把逻辑代码放到 view 里面去,本质上并没有优化,只是让 Controller 代码少了,其它地方代码多了。

    感觉把函数分好类就行了。
    summer1991
        15
    summer1991  
    OP
       2017-06-15 15:23:49 +08:00
    ```Objective-c
    //版本 v2

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    //store/pref/ASHPrefStore.h

    @interface ASHPrefStore : NSObject

    + (BOOL)haveReadBroadcastTip;
    + (void)setHaveReadBroadcastTip;

    @end

    //store/pref/ASHPrefStore.m

    @implementation ASHPrefStore

    static NSString *const kHaveReadBroadcastTipKey = @"haveReadBroadcastTip";

    + (BOOL)haveReadBroadcastTip
    {
    return [[NSUserDefaults standardUserDefaults] boolForKey:kHaveReadBroadcastTipKey];
    }

    + (void)setHaveReadBroadcastTip
    {
    [[NSUserDefaults standardUserDefaults] setObject:@(YES) forKey:kHaveReadBroadcastTipKey];
    }

    @end

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    //network/ASHNetworkAPI.h

    @interface ASHNetworkAPI : NSObject

    + (void)sendBroadcastRequestWithMsg:(NSString *)msg successBlock:(void(^)(void))successBlock failureBlock:(void(^)(NSError *error))failureBlock;

    @end

    //network/ASHNetworkAPI.m

    @implementation ASHNetworkAPI

    + (void)sendBroadcastRequestWithMsg:(NSString *)msg successBlock:(void(^)(void))successBlock failureBlock:(void(^)(NSError *error))failureBlock
    {
    //send request
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    !successBlock ?: successBlock();
    });
    }

    @end

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    //utils/ui/ASHAlertUtils.h

    @interface ASHAlertUtils : NSObject

    + (void)showAlertWithController:(UIViewController *)controller msg:(NSString *)msg callback:(void(^)(void))callback;

    + (void)showEditAlertWithController:(UIViewController *)controller title:(NSString *)title placeholder:(NSString *)placeholder callback:(void(^)(NSString *content))callback;

    @end

    //utils/ui/ASHAlertUtils.m

    @implementation ASHAlertUtils

    #pragma mark - utils

    + (void)showAlertWithController:(UIViewController *)controller msg:(NSString *)msg callback:(void(^)(void))callback
    {
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:msg preferredStyle:UIAlertControllerStyleAlert];
    [alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
    !callback ?: callback();
    }]];
    [controller presentViewController:alert animated:YES completion:nil];
    }

    + (void)showEditAlertWithController:(UIViewController *)controller title:(NSString *)title placeholder:(NSString *)placeholder callback:(void(^)(NSString *content))callback
    {
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleAlert];
    [alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
    textField.placeholder = placeholder;
    }];
    [alertController addAction:[UIAlertAction actionWithTitle:@"发送" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
    NSString *content = [[alertController textFields][0] text];
    !callback ?: callback(content);
    }]];
    [alertController addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
    NSLog(@"Canelled");
    }]];
    [controller presentViewController:alertController animated:YES completion:nil];
    }

    @end

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    //macro.h

    #ifndef weakify
    #if __has_feature(objc_arc)
    #define weakify( x ) autoreleasepool{} __weak __typeof__(x) __weak_##x##__ = x;
    #else // #if __has_feature(objc_arc)
    #define weakify( x ) autoreleasepool{} __block __typeof__(x) __block_##x##__ = x;
    #endif // #if __has_feature(objc_arc)
    #endif // #ifndef weakify

    #ifndef normalize
    #if __has_feature(objc_arc)
    #define normalize( x ) try{} @finally{} __typeof__(x) x = __weak_##x##__;
    #else // #if __has_feature(objc_arc)
    #define normalize( x ) try{} @finally{} __typeof__(x) x = __block_##x##__;
    #endif // #if __has_feature(objc_arc)
    #endif // #ifndef @normalize

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    //controller/ASHTempViewController.m

    @implementation ASHTempViewController

    #pragma mark - app logic

    - (void)sendBroadcastWithSuccessBlock:(void (^)(void))successBlock
    {
    @weakify(self)
    void (^showSendBroadcast) (void) = ^{
    [ASHAlertUtils showEditAlertWithController:self title:@"发送广播'" placeholder:@"说点什么..." callback:^(NSString *content) {
    [ASHNetworkAPI sendBroadcastRequestWithMsg:content successBlock:successBlock failureBlock:^(NSError *error) {
    @normalize(self)
    !self ?: [ASHAlertUtils showAlertWithController:self msg:error.localizedDescription callback:nil];
    }];
    }];
    };

    if ([ASHPrefStore haveReadBroadcastTip]) {
    showSendBroadcast();
    } else {
    NSString *title = @"1、发布广播,全服玩家都可以看到;\n2、禁止发布反动、政治、色情、辱骂、广告等不良言论,否则将会遭到删除、封号处理。";
    [ASHAlertUtils showAlertWithController:self msg:title callback:^{
    [ASHPrefStore setHaveReadBroadcastTip];
    showSendBroadcast();
    }];
    }
    }
    ```
    summer1991
        16
    summer1991  
    OP
       2017-06-15 15:40:43 +08:00
    @andyL 可能之前没有描述好,大家都抓着代码细节看 我整理了下代码放在 https://www.v2ex.com/t/368630, 你可以再看看
    zhangchioulin
        17
    zhangchioulin  
       2017-06-15 23:42:08 +08:00 via iPhone
    想简化 vc 代码的话,使用 MVVM 会是一个比较好的途径,另外 MVC 的话可以考虑创建一个 Manager.
    holy_sin
        18
    holy_sin  
       2017-06-16 11:43:48 +08:00
    我觉得 rx 可以拯救你
    winglight2016
        19
    winglight2016  
       2017-06-28 21:18:08 +08:00
    @summer1991 你提的问题像是不满意这个类,但是说起同个界面 10 个按钮的例子,这又是个方法级的重用,可以理解吧?
    summer1991
        20
    summer1991  
    OP
       2017-07-03 11:32:04 +08:00
    @winglight2016 可以这么说,需要重用方法,并且这个方法里面能和 VC 一样,能弹窗,能发起网络请求
    summer1991
        21
    summer1991  
    OP
       2017-07-03 11:32:18 +08:00
    @winglight2016 有好的想法吗亲?
    winglight2016
        22
    winglight2016  
       2017-07-04 12:37:27 +08:00
    @summer1991 要求能够弹窗,这个需要完整框架的支持了,不是普通代码能做的,你可以参考一下 MVVM 的框架
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5266 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 03:51 · PVG 11:51 · LAX 19:51 · JFK 22:51
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.