- 🌱 Flutter、SwiftUI、Ruby学习中...
- Processing、p5js爱好者,维护小菜与老鸟-创意编程
- 💬 崇尚开源分享精神
- 📫 Email: [email protected]
微信公众号
Bilibili:https://space.bilibili.com/182054750
BLOG MARKDOWN BAK AND SOME EXERCISES
Home Page: http://dabing1022.github.io
微信公众号
Bilibili:https://space.bilibili.com/182054750
博客半年没有更新了,自己在嘀嗒拼车的这半年里,更多的是对自己写的代码的一些反思,特此书写记录下来。
有味道的代码永远都存在的,每个人都或多或少不定期的产生一些垃圾代码,而产生此类代码的原因一般都有哪些原因?我曾经问过自己这样的问题。常见的几个原因有以下几个:
前两个跟开发项目时的心理有很大关系。而第三条,知识方面的问题,则需要我们不能停止学习,多反思。
而你,而我,中枪了吗?三个我都中了。我在输出着垃圾代码。
以上原因都是引起软件腐烂的原因,它们会增大软件的熵。我从我们的移动总监(后文简称周)身上也学到了不少东西。周来公司的第一件事,就是干掉项目架构中不合理的地方,重新编写,并且每个版本持续重构。周做的就是变化的催化剂,虽然一开始重构丢掉了一些东西,但某种意义上,他重新定义了一部分产品,包括交互和设计中不合理的东西。
程序员对算法和数据结构的掌握是必不可少的,但软件工程的相关理论也十分有必要了解和熟练掌握。优雅的代码不是一朝一夕炼成的,它需要非常多的代码输出量以及对既有代码的反思。我们输出垃圾代码并不可怕,可怕的是从来不对垃圾代码进行改进或者重构。作为一个注重实效的程序猿,我容忍不了项目的的杂乱。但凡有时间,我都会对代码进行轻微的整理和改进,而整理的力度和改进同时也会受限于自己对项目的认知以及经验。而我们也要批判地思考所有代码,包括自己的。
有很多关于此类话题的书。推荐三本:
在《重构-改善既有代码的设计》一书中提到了重构的一些方法,很实用,也强烈推荐读者理解每一条重构方法,结合自己的项目,进行实践。
我摘取了一部分记录了下来:
重新组织函数
- 提炼函数
- 内联函数
- 内联临时变量
- 以查询取代临时变量
- 引入解释性变量
- 分解临时变量
- 移除对参数的赋值
- 以函数对象取代函数
- 替换算法
简化函数调用
- 函数重命名
- 添加、移除参数
- 将查询函数、修改函数分离
- 引入参数对象
- 以工厂函数取代构造函数
处理概括关系
- 字段上移、下移
- 函数上移、下移
- 构造函数本体上移、下移
- 提炼子类、提炼超类、提炼接口
- 塑造模板函数
- 折叠继承体系
- 委托、继承的取代
撸得了坏代码,翻得了好书籍,反的一脑袋好思,方可撸得了好代码。无他,仅此而已。
引用《重构-改善既有代码的设计》
- 重构改进软件设计
- 重构使软件更容易理解
- 重构帮助找到bug
- 重构提高编程速度
原则:三次法则--事不过三,三则重构
添加功能时重构
修补错误时重构
复审代码时重构
避免不清楚项目上线日期,而进行大规模重构,因为时间以及风险不可控。
阅读同事的代码,好处是不言而喻的。我强烈建议你在有时间的情况下,多阅读下同事的代码。无论好与坏。
在阅读他人代码的时候,觉得他们的代码写的不是很好,OK,提出你认为更好的解决办法或者提出代码中存在的bug漏洞。这何尝不是一种进步。记住,跟进代码,理解代码的函数设计、调用跳转从而分析出代码的设计思路也是一种锻炼。时间久了,会锻炼我们阅读源码的能力,同时也教会我们分辨出好与坏的代码。
阅读他人代码,如果发现竟然还可以这么设计、这么写,你就赚了!
所以,多阅读同事的代码。
公司有一个良好的分享氛围很重要,嘀嗒在朝着这方面努力。每周都会有分享会,我们移动内部也不定期会有分享。周的第一期是分享是关于Realm的,第二期我分享了LLDB调试以及UI调试,第三期是呆萌的敏捷开发相关分享,整体下来,有不少收获。而其他朋友在后面都会有分享,蛮期待的。
分享的好处:为了能够讲明白,需要自己在下面做好功课,这本身就是加强学习的过程。能讲出来,让别人学习到,本身也是快乐的。
在这半年里,或多或少自己写的代码,由于QA部门没有测试出来,而出现在线上的情况。我常常愧疚于自己产生的代码bug出现在线上而引起用户抱怨,降低了使用体验。
然而,愧疚没用!
拿起你的担当!
第一件事,Fix the bug。
第二件事,为什么会引起这个bug。
第三件事,总结,以后如何避免?同样的事情不要在犯。
这也是我在嘀嗒学到的很重要的一课。
这篇文章偏向感悟多点,也作为我重视代码质量的一个转折点。
而这条路,没有尽头···
Apologies if this is already possible, I couldn't figure out a way.
To add a new dependency I would love to be able to do this:
- Specify new dependency X/Y in Cartfile.
- carthage update --no-build.
- carthage build x/y.
echo -e "\033[31m 红色字 \033[0m"
echo -e "\033[34m 黄色字 \033[0m"
echo -e "\033[41;33m 红底黄字 \033[0m"
echo -e "\033[41;37m 红底白字 \033[0m"
echo -e "\033[30m 黑色字 \033[0m"
echo -e "\033[31m 红色字 \033[0m"
echo -e "\033[32m 绿色字 \033[0m"
echo -e "\033[33m 黄色字 \033[0m"
echo -e "\033[34m 蓝色字 \033[0m"
echo -e "\033[35m 紫色字 \033[0m"
echo -e "\033[36m 天蓝字 \033[0m"
echo -e "\033[37m 白色字 \033[0m"
echo -e "\033[40;37m 黑底白字 \033[0m"
echo -e "\033[41;37m 红底白字 \033[0m"
echo -e "\033[42;37m 绿底白字 \033[0m"
echo -e "\033[43;37m 黄底白字 \033[0m"
echo -e "\033[44;37m 蓝底白字 \033[0m"
echo -e "\033[45;37m 紫底白字 \033[0m"
echo -e "\033[46;37m 天蓝底白字 \033[0m"
echo -e "\033[47;30m 白底黑字 \033[0m"
using UnityEngine;
using System.Collections;
public class LookTowardMouse : MonoBehaviour {
void Update ()
{
//Mouse Position in the world. It's important to give it some distance from the camera.
//If the screen point is calculated right from the exact position of the camera, then it will
//just return the exact same position as the camera, which is no good.
Vector3 mouseWorldPosition = Camera.main.ScreenToWorldPoint(Input.mousePosition + Vector3.forward * 10f);
//Angle between mouse and this object
float angle = AngleBetweenPoints(transform.position, mouseWorldPosition);
//Ta daa
transform.rotation = Quaternion.Euler (new Vector3(0f,0f,angle));
}
float AngleBetweenPoints(Vector2 a, Vector2 b) {
return Mathf.Atan2(a.y - b.y, a.x - b.x) * Mathf.Rad2Deg;
}
}
feat:新功能(feature)
fix:修补bug
docs:文档(documentation)
style: 格式(不影响代码运行的变动)
refactor:重构(即不是新增功能,也不是修改bug的代码变动)
test:增加测试
chore:构建过程或辅助工具的变动
说真的,文章写在这里也挺不错。测试下:)
UIApplication *application = [UIApplication sharedApplication];
__block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
//这里做存储操作
} completion:^(BOOL success, NSError *error) {
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
在心城的开发过程中,在处理音频部分时,有这么段代码
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)
cd DemoTestProject
pod spec create DemoTestProject
vim DemoTestProject.podspec
git add .
git commit -m `commit description`
git tag 0.0.1
git push --tags
git push origin master
6.pod spec lint DemoTestProject.podspec
if you have warnings and errors, please delete your tag and then retry.
git tag -d 0.0.1
git push origin :refs/tags/0.0.1
7.pod trunk push DemoTestProject.podspec
if this is your first time to commit to CocoaPods,please register first:
pod trunk register [email protected] 'write a name here' --description='write descrption here'
比如我要复制从第1行到第5行的数据,复制到第9行
或者
或者
Nyy命令复制从光标所在行开始的N行,一般在5行以内非常直观的数字时使用的比较多
Xcode版本:Xcode7.1 beta
iOS版本:iOS9 beta
运行应用,报错:Xcode process launch failed: Security
,应用由于安全性问题无法启动,以前没有此情况。
解决办法:设置-》通用-》描述文件-》开发商应用里的开发者帐号-》信任
项目中使用到了从字符串创建选择器,编译时发现警告:"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通过头文件的函数定义来得到这些信息。所以平时我们用到的静态选择器就不会出现这个警告。因为在编译期间,这些信息都已经确定。
如:
...
[someController performSelector:@selector(someMethod)];
...
- (void)someMethod
{
//bla bla...
}
而使用[someController performSelector: NSSelectorFromString(@"someMethod")];
时ARC并不知道该方法的返回值是什么,以及该如何处理?该忽略?还是标记为ns_returns_retained
还是ns_returns_autoreleased
?
SEL selector = NSSelectorFromString(@"someMethod");
IMP imp = [_controller methodForSelector:selector];
void (*func)(id, SEL) = (void *)imp;
func(_controller, selector);
当有额外参数时,如
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);
#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编译器仅仅为某一特定部分的代码来忽视特定警告。
如果需要忽视的警告有多处,可以定义一个宏
#define SuppressPerformSelectorLeakWarning(Stuff) \
do { \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"") \
Stuff; \
_Pragma("clang diagnostic pop") \
} while (0)
在产生警告也就是performSelector
的地方用使用该宏,如
SuppressPerformSelectorLeakWarning(
[_target performSelector:_action withObject:self]
);
如果需要performSelector
返回值的话,
id result;
SuppressPerformSelectorLeakWarning(
result = [_target performSelector:_action withObject:self]
);
[self performSelector:aSelector withObject:nil afterDelay:0.0];
如果在接受范围内,允许在下一个runloop执行,可以这么做。Xcode5没问题,但据反映,Xcode6的话这个不能消除警告。
我们在用 Cocoapods 做第三方开源库管理的时候,有时候发现
$ pod search XXX
版本低于github上仓库的最新release版本 (注:XXX为仓库名称)
查看当前系统Cocoapods版本命令:pod --version
$ sudo gem update --system
$ gem sources --remove https://rubygems.org/
$ gem sources -a https://ruby.taobao.org/
$ sudo gem install cocoapods
$ pod setup
其中
$ gem sources --remove https://rubygems.org/
$ gem sources -a https://ruby.taobao.org/
这两句话可以省略,但我们在天朝,还是加上的好。国内网络原因(你懂的),如果使用原来的https://rubygems.org/
,那么在sudo gem install cocoapods
的时候,存放在 Amazon S3 上面的资源文件间歇性连接失败。
升级结束后再次pod --version
,会发现 Cocoapods 版本号高于之前的版本,升级成功了。
再次
$ pod search XXX
OK,github仓库的最新版已经有了。
问题经过:[https://github.com/HeshamMegid/HMSegmentedControl/issues/77)(https://github.com/HeshamMegid/HMSegmentedControl/issues/77)
远程分支已经将分支AAABBB
删除,本地有个分支叫ABBB
,当在终端中输入A
,然后用Tab
键自动补全的时候,老是把AAABBB
给候选出来。很纳闷。远程分支AAABBB
和本地分支AAABBB
都已经被我删除,为什么还会补全提示呢?
当前本地和所有的远程分支 以及 那些已经【死亡的无效的】分支
GitLab/Carpooling_iOS git:(F_Route) ▶ git br -a
* F_Route
I_chat_module
developer
master
remotes/origin/B_DIDA-8432
remotes/origin/B_DIDA-8553
remotes/origin/B_DIDA-8591
remotes/origin/B_DIDA-8598
remotes/origin/B_DIDA-8617/8504
remotes/origin/B_DIDA-8691
remotes/origin/B_DIDA-9156
remotes/origin/B_DIDA_8504
remotes/origin/B_DIDA_9156
remotes/origin/B_Friend_Reddot
remotes/origin/B_fix_booksetting
remotes/origin/B_fix_chat_polling_timer
remotes/origin/B_fix_hole_guide_position
remotes/origin/B_fix_messageLeaving
remotes/origin/B_fix_today_widget
remotes/origin/B_fix_trip_cell
remotes/origin/B_fix_trip_cell_related_error
remotes/origin/B_smallfix
remotes/origin/B_someFix
remotes/origin/B_some_small_fix
remotes/origin/F_Browser_Web_View
remotes/origin/F_DIDA-8250
remotes/origin/F_DIDA-8702
remotes/origin/F_DIDA_8076
remotes/origin/F_DIDA_8365
remotes/origin/F_EZhiFu
remotes/origin/F_LoginWaterMarkView
remotes/origin/F_NewWelcomePages
remotes/origin/F_New_Book_Passenger
remotes/origin/F_Route
remotes/origin/F_TmpChatPrivacyControl
remotes/origin/F_cancel_hide_recent_chat_logic
remotes/origin/F_delete_nearby_drivers
remotes/origin/F_replace_welcome_page
remotes/origin/F_text_modify
remotes/origin/HEAD -> origin/master
remotes/origin/I_chat_module
remotes/origin/I_trip_waiting
remotes/origin/developer
remotes/origin/master
remotes/origin/tmpChatPrivacyControl
git fetch --prune --all
prune
英文单词为“剪枝”的意思,这个命令会将远程已经不存在的分支删除掉
GitLab/Carpooling_iOS git:(F_Route) ▶ git fetch --prune --all
Fetching origin
From ssh://gitlab.didapinche.com:9122/mobile/Carpooling_iOS
x [deleted] (none) -> origin/B_DIDA-8432
x [deleted] (none) -> origin/B_DIDA-8553
x [deleted] (none) -> origin/B_DIDA-8591
x [deleted] (none) -> origin/B_DIDA-8598
x [deleted] (none) -> origin/B_DIDA-8617/8504
x [deleted] (none) -> origin/B_DIDA-8691
x [deleted] (none) -> origin/B_DIDA-9156
x [deleted] (none) -> origin/B_DIDA_8504
x [deleted] (none) -> origin/B_Friend_Reddot
x [deleted] (none) -> origin/B_fix_booksetting
x [deleted] (none) -> origin/B_fix_chat_polling_timer
x [deleted] (none) -> origin/B_fix_hole_guide_position
x [deleted] (none) -> origin/B_fix_messageLeaving
x [deleted] (none) -> origin/B_fix_today_widget
x [deleted] (none) -> origin/B_fix_trip_cell
x [deleted] (none) -> origin/B_fix_trip_cell_related_error
x [deleted] (none) -> origin/B_smallfix
x [deleted] (none) -> origin/B_someFix
x [deleted] (none) -> origin/B_some_small_fix
x [deleted] (none) -> origin/F_DIDA-8250
x [deleted] (none) -> origin/F_DIDA-8702
x [deleted] (none) -> origin/F_DIDA_8076
x [deleted] (none) -> origin/F_DIDA_8365
x [deleted] (none) -> origin/F_EZhiFu
x [deleted] (none) -> origin/F_LoginWaterMarkView
x [deleted] (none) -> origin/F_NewWelcomePages
x [deleted] (none) -> origin/F_TmpChatPrivacyControl
x [deleted] (none) -> origin/F_cancel_hide_recent_chat_logic
x [deleted] (none) -> origin/F_delete_nearby_drivers
x [deleted] (none) -> origin/F_replace_welcome_page
x [deleted] (none) -> origin/F_text_modify
x [deleted] (none) -> origin/I_trip_waiting
x [deleted] (none) -> origin/tmpChatPrivacyControl
再来看看吧:)
GitLab/Carpooling_iOS git:(F_Route) ▶ git br -a
* F_Route
I_chat_module
developer
master
remotes/origin/B_DIDA_9156
remotes/origin/F_Browser_Web_View
remotes/origin/F_MyOrderList
remotes/origin/F_New_Book_Passenger
remotes/origin/F_Route
remotes/origin/HEAD -> origin/master
remotes/origin/I_chat_module
remotes/origin/R_Order_Info
remotes/origin/developer
remotes/origin/master
一些僵尸分支已经被删除了,这下自动补全就不会再补全那些分支名称了 。
curl -fsSL https://raw.github.com/supermarin/Alcatraz/master/Scripts/install.sh | sh
rm -rf ~/Library/Application\ Support/Developer/Shared/Xcode/Plug-ins/Alcatraz.xcplugin
~/Library/Application\ Support/Developer/Shared/Xcode/Plug-ins
defaults read /Applications/Xcode.app/Contents/Info DVTPlugInCompatibilityUUID
defaults read /Applications/Xcode-beta.app/Contents/Info DVTPlugInCompatibilityUUID
在iOS9以前,检测微信是否安装,用微信SDK中的[WXApi isWXAppInstalled]
来检测就可以了,但在iOS9之后,需要在Info.plist
中将要使用的URL Schemes
列为白名单,才可正常检查应用是否安装。
<key>LSApplicationQueriesSchemes</key>
<array>
<string>wechat</string>
<string>weixin</string>
<string>baidumap</string>
<string>iosamap</string>
<string>mqq</string>
<string>weibo</string>
<string>tencentweibo</string>
<string>sinaweibo</string>
</array>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
在接触到开源项目 Masonry 后,里面的布局约束的链式写法让我颇感兴趣,就像下面这样
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 中,如何实现链式语法呢?
注:这里讨论的链式语法特指的是点链式语法,不同于中括号链式语法,如
[[[[someObj method1] method2] method3] method4:someParam]
。中括号链式语法相对而言更简单些,每个方法的返回值是下一个方法的发送者即可。
查看 Masonry 源码,起初没看明白,于是搜索了下 Stackoverflow,没有发现类似的问题,便将这个问题发布在了 Stackoverflow 上。这里是地址。
总结了下,贴下代码,做个说明。
//------------------------------------------
@interface ClassB : NSObject
@property(nonatomic, readonly) ClassB *(^ddd)(BOOL enable);
- (id)initWithString:(NSString *)str;
@end
@implementation ClassB
- (ClassB *(^)(BOOL))ddd
{
return ^(BOOL enable) {
//code
if (enable) {
NSLog(@"ClassB yes");
} else {
NSLog(@"ClassB no");
}
return self;
};
}
@end
@class ClassB;
@interface ClassA : NSObject
// 1. 定义一些 block 属性
@property(nonatomic, readonly) ClassA *(^aaa)(BOOL enable);
@property(nonatomic, readonly) ClassA *(^bbb)(NSString* str);
@property(nonatomic, readonly) ClassB *(^ccc)(NSString* str);
@end
@implementation ClassA
// 2. 实现这些 block 方法,block 返回值类型很关键,影响着下一个链式
- (ClassA *(^)(BOOL))aaa
{
return ^(BOOL enable) {
//code
if (enable) {
NSLog(@"ClassA yes");
} else {
NSLog(@"ClassA no");
}
return self;
};
}
- (ClassA *(^)(NSString *))bbb
{
return ^(NSString *str) {
//code
NSLog(@"%@", str);
return self;
};
}
// 这里返回了ClassB的一个实例,于是后面就可以继续链式 ClassB 的 block 方法
// 见下面例子 .ccc(@"Objective-C").ddd(NO)
- (ClassB * (^)(NSString *))ccc
{
return ^(NSString *str) {
//code
NSLog(@"%@", str);
ClassB* b = [[ClassB alloc] initWithString:str];
return b;
};
}
@end
// 最后我们可以这样做
id a = [ClassA new];
a.aaa(YES).bbb(@"HelloWorld!").ccc(@"Objective-C").ddd(NO);
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.