博客半年没有更新了,自己在嘀嗒拼车的这半年里,更多的是对自己写的代码的一些反思,特此书写记录下来。
- 代码如何写的优雅?
- 这段代码是否写的很罗嗦?
- 是否可以换一种写法或者是否能够应用某个设计模式很好的解决这个问题?
- ···
谈代码的坏味道
有味道的代码永远都存在的,每个人都或多或少不定期的产生一些垃圾代码,而产生此类代码的原因一般都有哪些原因?我曾经问过自己这样的问题。常见的几个原因有以下几个:
思维世界的发展,在某种意义上说,就是对惊奇的不断摆脱。
博客半年没有更新了,自己在嘀嗒拼车的这半年里,更多的是对自己写的代码的一些反思,特此书写记录下来。
有味道的代码永远都存在的,每个人都或多或少不定期的产生一些垃圾代码,而产生此类代码的原因一般都有哪些原因?我曾经问过自己这样的问题。常见的几个原因有以下几个:
从上海创业做游戏、IOS产品《心城》到离开来到北京已经有半个多月了。
两年多后再次回到北京,刚来的那几天,眼睛酸疼的想流眼泪,污浊的空气让我嗓子干燥的难受。北京的空气更差了,要不是考虑女朋友,也许我还是更愿意待在上海发展或者回到西安。
既来之,则安之吧。
跑了一周多快两周的工作,也收到了一些offer,考虑了下我选择了嘀嗒拼车。这家公司的现状可能会更加的对我胃口。
入职后,老大给了我几天的时间阅读了源代码,同事都很nice,抽了时间,对项目的目录,架构做了一些简介。之后,结合真机流程测试,大概对整体和核心的部分有了一些认识。
从干程序以来,游戏,应用,因为公司也不大,一直都是一个人在单打独斗负责前端。这次头次和同事一起开发,心里不禁有一丝喜悦和兴奋。
多人协作开发会涉及到版本的管理,公司采用了svn来进行版本控制。用惯了git,对svn不太熟悉,现阶段遵循公司的方式,我想如果将来可能的话,建议公司切换到git。
项目由不同的同事开发,其中的代码风格也不尽相同。阅读项目时,也发现了一些味道比较坏的代码。我想将来有时间的话,应该和同事将部分部分代码进行重构,最重要的是将不合理的架构和类进行修改,提高阅读性,否则后续开发我想会越来越乱。关于对代码的重构,后面有时间的话很想和大家做个探讨。
随笔到这先。
票不好买,我买了今天回家的票。下午5点多的汽车,明天到郑州。
坐在电脑前,回想从我们两人(另外一人,一哥是负责产品),从开始做心城,磕磕碰碰,一路走到现在,对自己坚持的毅力也感到吃惊。
整个程序我一个人写,从一个从游戏开发转应用开发的“新手”而言,刚开始对应用开发,完全摸不到节奏。节奏太重要了。
怎么去寻找节奏?感谢R网站,跟着教程,我试着去理清ios应用开发的一些知识脉络。时间不等人。一边学习,一边开发,遇到问题,不断调试,查阅苹果文档,google和stackoverflow已经成了我解决问题离不开的渠道。知识面的不足,可以通过学习来弥补。但经验的不足,些许可以从旁人中吸取,但自己摔过的跟头,才会永远透彻的记得。审核时犯的错,容不得犯第二次。
迭代了4个版本,也是近半年的开发成果,上周六通过了1.4.0版本,但存在一个bug,用户的数据没能正确显示出来,这是致命的。我不得不说,这次的跟头跌的太大了,完全是我的疏忽,提交前没有对升级进行严格测试。
在心城的开发过程中,在处理音频部分时,有这么段代码
1 | NSError* error; self.audioRecorder = [[AVAudioRecorder alloc] initWithURL:[self getTempAudioPath] settings:settingDic error:&error];; //... if ([self.audioRecorder prepareToRecord]) { [self.audioRecorder record]; } |
在模拟器上崩溃,始终停在了prepareToRecord
这一行,但真机测试没问题。怀疑是模拟器的bug。
Stackoverflow问题讨论
在测试音频录制功能时,先暂时禁用掉Xcode的全局断点功能。之后,恢复正常开启全局断点。(默认快捷键:Command+Y)
在接触到开源项目 Masonry 后,里面的布局约束的链式写法让我颇感兴趣,就像下面这样
1 | UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10); [view1 mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(superview.mas_top).with.offset(padding.top); //with is an optional semantic filler make.left.equalTo(superview.mas_left).with.offset(padding.left); make.bottom.equalTo(superview.mas_bottom).with.offset(-padding.bottom); make.right.equalTo(superview.mas_right).with.offset(-padding.right); }]; |
其他语言比如 Lua, 实现链式语法很容易。但在 Objective-C 中,如何实现链式语法呢?
《心城》1.3.0版本目前可以在AppStore下载。
迭代的这几个版本,还没有正式迈入到我们设想的功能点。
在下面的1.4.0版本中,我们俩决定按照之前设想的,加入标签系统,这套系统会成为组织数据的一个核心,今天上午我自己出了个概念图,不过当务之急要将功能、流程走通。
我们在用 Cocoapods 做第三方开源库管理的时候,有时候发现
1 | $ pod search XXX |
版本低于github上仓库的最新release版本 (注:XXX为仓库名称)
查看当前系统Cocoapods版本命令:pod --version
1 | $ sudo gem update --system |
其中1
2$ gem sources --remove https://rubygems.org/
$ gem sources -a http://ruby.taobao.org/
这两句话可以省略,但我们在天朝,还是加上的好。国内网络原因(你懂的),如果使用原来的https://rubygems.org/
,那么在sudo gem install cocoapods
的时候,存放在 Amazon S3 上面的资源文件间歇性连接失败。
升级结束后再次pod --version
,会发现 Cocoapods 版本号高于之前的版本,升级成功了。
再次1
$ pod search XXX
OK,github仓库的最新版已经有了。
项目中使用到了从字符串创建选择器,编译时发现警告:”performSelector may cause a leak because its selector is unknown”(因为performSelector的选择器未知可能会引起泄漏),为什么在ARC模式下会出现这个警告?
经过搜索后,在Stackoverflow上发现了一个令人满意的答案。见http://stackoverflow.com/questions/7017281/performselector-may-cause-a-leak-because-its-selector-is-unknown。
在ARC模式下,运行时需要知道如何处理你正在调用的方法的返回值。这个返回值可以是任意值,如void
,int
,char
,NSString
,id
等等。ARC通过头文件的函数定义来得到这些信息。所以平时我们用到的静态选择器就不会出现这个警告。因为在编译期间,这些信息都已经确定。
如:
1 | ... [someController performSelector:@selector(someMethod)]; ... - (void)someMethod { //bla bla... } |
而使用[someController performSelector: NSSelectorFromString(@"someMethod")];
时ARC并不知道该方法的返回值是什么,以及该如何处理?该忽略?还是标记为ns_returns_retained
还是ns_returns_autoreleased
?
1 | SEL selector = NSSelectorFromString(@"someMethod"); IMP imp = [_controller methodForSelector:selector]; void (*func)(id, SEL) = (void *)imp; func(_controller, selector); |
当有额外参数时,如
1 | SEL selector = NSSelectorFromString(@"processRegion:ofView:"); IMP imp = [_controller methodForSelector:selector]; CGRect (*func)(id, SEL, CGRect, UIView *) = (void *)imp; CGRect result = func(_controller, selector, someRect, someView); |
1 | #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" [someController performSelector: NSSelectorFromString(@"someMethod")] #pragma clang diagnostic pop |
通过使用#pragma clang diagnostic push/pop
,你可以告诉Clang编译器仅仅为某一特定部分的代码来忽视特定警告。
如果需要忽视的警告有多处,可以定义一个宏
1 | #define SuppressPerformSelectorLeakWarning(Stuff) \ do { \ _Pragma("clang diagnostic push") \ _Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"") \ Stuff; \ _Pragma("clang diagnostic pop") \ } while (0) |
在产生警告也就是performSelector
的地方用使用该宏,如
1 | SuppressPerformSelectorLeakWarning( [_target performSelector:_action withObject:self] ); |
如果需要performSelector
返回值的话,
1 | id result; SuppressPerformSelectorLeakWarning( result = [_target performSelector:_action withObject:self] ); |
1 | [self performSelector:aSelector withObject:nil afterDelay:0.0]; |
如果在接受范围内,允许在下一个runloop执行,可以这么做。xCode5没问题,但据反映,xCode6的话这个不能消除警告。