iOS开发之路-菜鸟探索

我的博客文档 2015.10.13 

毕业从事iOS工作一年多了,从毕业前自学iOS到工作,再到独立开发了几个项目,有一种感觉就是永远都是嫌弃自己的代码,即使有时候做出来的功能并不差,可是代码能力真的不敢苟同。我也认真看了几个优秀的开源项目,比如OSChina、AmericanEnglish等,项目架构、代码布局、封装集成等都做得很出色,用最少的代码、最方便易改的代码做出优秀的项目,这是极难的,没有一定的经验积累只是照搬硬抄也很难有所突破。但是,程序员,不就是要保持一颗永远不畏惧勇往直前不停学习的屌丝心态吗。下面,发点自己的平时的一些循循渐进的感悟,记录码工的代码点滴。

一、关于代码布局:

代码布局向来是一个巨大的难题,每个不同的优秀程序员都可能拥有自己的布局方式,但是总体而言,肯定都是为了更加的整洁清晰和便于更新修改。记得刚工作自己做的项目,不知道如何分类代码,不知道如何集成封装,所有的代码全部写在viewDidLoad里面,先按顺序布局页面,然后请求数据重新赋值刷新页面,不同的代码用多个空格隔开,看起来还算整齐,可是,毫无章法。

有经验的同事看了我的代码,然后偷偷的告诉我,代码千万不要全部写到一块,要不然,太菜!然后,我学会了拆分各个功能不同的代码到各个void方法中,然后在viewDidLoad里面按顺序self调用。是不是感觉很低级?没错,我想这是每个程序员一开始都会遇到的问题。

本来以为方法按顺序写就行了,后来界面功能增多,方法也增多,寻找方法也变得困难,后来用#pragma mark进行了分类,初始化界面initSubviews,代理,点击事件,服务器请求,其他方法,这样,整个代码界面相对来说比较规范,便于查找修改。但是主控器的代码多的时候甚至超过1000行,代码冗余度过高,也不够简洁化,所以,我开始考虑减少主控器的代码。

    1. 从基础控件下手。开始尝试独立的storyboard和灵活的XIB,UI布局代码一下子减少了很多。但是考虑到纯代码的复用性比较高,所以,开始利用公共方法创建常见的UI控件:

然后创建输入框和按钮时直接调用该方法:(需要调用头文件#import "CXRKitTool.h"

 

2. 从复杂的view下手。又时候,我们发现有些view里面控件十分多,写在住控制器里面难免混乱,或者说不够简单。


比如这样一个简单地显示界面,要写一个view,然后在view添加图片,两个标签,又是不少代码,因此,考虑创建一个自定义TimerView,然后简单的调用即可。

创建一个继承于UIView的TimerView,然后初始化各个控件:


然后通过简单的几句代码调用就可以实现这个view了;


关于简化代码还有很多特别优秀的方法,比如把每个功能模块都单独集成封装出来,使用第三方工具等,我还经验欠缺,继续学习。

 

二、关于常见的方法封装:

比如,我们总是需要多处展示加载条,我们需要多处把时间戳转化为时间格式的字符串,需要多次把字典转化为data,需要多处验证手机号、邮箱和身份证号,需要多处显示某个自定义弹出框等等,如果每次都重新写一遍代码的话,只能说明自己太low了,一开始的我就是,但是谁叫我不愿意做一名菜鸟呢,我就搜集资料,然后自己总结,终于创建了自己第一个常见的类NSObject方法。

举个例子,我想把时间戳转化为时间格式的字符串方法封装起来,建一个类,叫MyEngine,在MyEngine.h文件里面声明方法:

+ (NSString*)timestmpToDatetimeString:(NSString *)string;

然后在MyEngine.m文件里面写明具体实现方法:

+ (NSString*)timestmpToDatetimeString:(NSString *)string

{

    NSString *dateStr = nil;

    if (!string) {

        dateStr = @"0000-00-0000:00:00";

    }else {

        NSDateFormatter *formatter =[[NSDateFormatter alloc] init];

        [formattersetDateFormat:@"yyyy-MM-dd HH:mm:ss"];

        NSTimeInterval time = [stringdoubleValue];

        NSDate *date = [NSDatedateWithTimeIntervalSince1970:time];

        NSLog(@"date:%@",[datedescription]);

        dateStr = [formatterstringFromDate:date];

    }

   

    return dateStr;

}

这样,在项目里面每次需要时间戳转时间字符串时就可以直接引用MyEngine.h文件,然后测试:

NSString*timestmp = @"1447405653";

    NSString*dateTime= [MyEnginetimestmpToDatetimeString:timestmp];

   NSLog(@"time:%@", dateTime);

打印出来的结果就是time:2015-11-13 09:07:33,这样,这个方法就变成了可重复使用的代码,并且减少了代码量。如果需要添加添加其他方法,同上,分别在.h文件和.m文件里面分别定义方法和实现即可。当然,很多大牛不建议把各种大杂烩的处理方法放在一个类里面,比如,如果是关于日期时间处理的方法全部放在一个类(比如DateEngine)里面,处理加载框的方法全部放在类(比如Hud)里面,处理服务器请求的方法全部放在类(比如RequestClass)里面,这样分类就比较清楚,后者修改起来也变得更加清晰,更加方便。有一些大牛代码封装的极其厉害,其他程序员查看其代码的时候通常要看很多遍才能找到他的逻辑,或者根本无法理解,那是因为代码集成封装过多过乱,然后有没有明确地标记和分类,才导致这个问题,一般不建议这么做,最好做好明确地分类。

三、关于传值:

大家有没有发现,在项目里面,因为功能代码的集成和控制器之间的各种跳转,某些信息的传递总是不可避免,传值的方法网上分类统计的也很清楚了,直接传值、代理、通知、block和本地存取等方式,我就不复制粘贴了。我想谈谈我自己的一步一步的进阶学习和感悟。

记得第一次页面之间的传值,我第一次接触了代理方法delegate,一开始不知道怎么回事,只是强制的记住它的实现方法,界面A->B,如果A要把某个字符串productId传递给B,那么在A里面B *b = [[B alloc] init]; b. productId = a. productId;就可以直接传递,如果B需要把值传递到A,那么不能直接传值,这时候可以使用代理方法,在B中声明代理方法并在合适的时候触发,A如果引入了B的代理方法,在A中便会自动调用B的代理方法。

这个方法贯穿了我整个第一个项目,可是又来了新的需求,没有页面直接跳转,我想在界面C获取A的productId,该怎么办呢?当时还很急躁,又想不到其它方法,老板又在催,正好当时正在使用一个本地存储用户登录数据的方法NSUserDefaults,然后很高兴的把productId存到了NSUserDefaults(在A里面),

   NSUserDefaults *userDefaults =[NSUserDefaults standardUserDefaults];

    [userDefaults setObject: productId forKey:@"productId"];

    [userDefaults synchronize];

然后在C里面获取:

NSUserDefaults*userDefaults = [NSUserDefaults standardUserDefaults];

    NSString *productId = [userDefaultsobjectForKey:@"productId"];

方法虽然笨拙,但是确实解决了当时的问题。

那么问题又来了,项目用户登录用到了NSUserDefaults,但是退出登录的时候需要清空登录信息,如果如果删除不慎,也会不小心把productId也给删了,这如果本地存储的数据更多,就会更加的繁杂。宝宝一下子又不开心了,怎么办?怎么办??还是看看别人是怎么做的吧,开源项目,用到了通知NSNotification和block,都能够解决不同需求的传值问题。例如,界面A要实时获取弹出的登录界面C中的登录成功返回的信息,可以在C中发送通知:[[NSNotificationCenter defaultCenter] postNotificationName:@”NotificationSuccess”object:@(YES)];

然后在A中

-(void)viewWillAppear:(BOOL)animated {

    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(doSomething) name:@"NotificationSuccess " object:nil];

}

 

- (void)viewWillDisappear:(BOOL)animated{

    [[NSNotificationCenter defaultCenter]removeObserver:self name:@" NotificationSuccess " object:nil];

}

然后就可以在doSomething里面做登录成功后想要做的任何事,必须注意的是通知不会自动消失,会一直保留通知的功能,会造成消耗内存等弊端,所以获取通知过后一定要及时移除通知,移除方法最好放在viewWillDisappear里面。

最后,重点介绍一下block,因为用过的都知道,block用起来最灵活方便,依然用A和登录界面C,在C中,.h文件里面,定义block方法,

typedefvoid(^LoginBlock)(BOOL logined);

 

@property (copy,nonatomic) LoginBlock block;

-      (void)loginSuccess:(LoginBlock)block;//用于登录成功的回调

然后在.m文件,设置登录成功方法:

-(void)loginSuccess:(LoginBlock)block {

    self.block = block;

}

然后在登录成功的方法里面调用self.block(YES);这样就把登录成功的值放在了block里面了,记住block里面几乎可以放任何参数,多个参数,这里只是简单的传递一个YES。然后重点来了,在A界面需要跳转到C的某个地方:

LoginViewController*viewc = [[LoginViewController alloc] init];

            [viewc loginSuccess:^(BOOL logined){

                if (logined) {

                    [self doSomething];

                }

            }];

            UINavigationController *nav =[[UINavigationController alloc] initWithRootViewController:viewc];

[self.tabBarController presentViewController:nav animated:YEScompletion:nil];

看到了,在A里面只需要调用block方法就可以直接获取C实时返回数据处理当前A的界面操作,是不是比代理方法还要清晰简单?当然了,block也有一些弊端,我也没有深入研究,但是你发现了吗,苹果官方代码基本都是用的block!可见它的优点不是一点点!当然,实际项目中,很多优秀程序员都是推荐代理和block混合使用,这样能够提高代码效率。自己慢慢体会吧。

好了,今天就写到这吧,这是我的第一篇博客,写的不太清楚,毛病也很多,可能还有一些地方不够准确,所以欢迎指正,希望可以分享学习更多更好的东西,谢谢大家。

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐