本文会持续记录自己在学习、工作中,接触的和iOS开发相关的各种技术。包括写代码时容易忽视的细节问题,项目中接触到实用技术以及优秀的三方框架。欢迎收藏,点赞,若有不足欢迎各位指出来一起探讨,共同进步。
Section One — Coding Tips
1.为私有方法名加前缀
- 这条建议是我最近看《Effective Objective-C 2.0》才知道的。
- 因为一个类要做的事情通常比外面看到的要多。在·m文件中,经常要写一些内部使用的代码。而我们应该给这些方法加上某些前缀,这有助于调试,因为据此很容易就能把公共方法和私有方法区分开。
- 具体使用何种前缀完全可以根据个人喜好来,没有固定标准。
- 但有一点要注意:苹果官方保留用单一下划线作为前缀命名私有方法
- 如果你想模仿苹果的写法也使用单一下划线,很可能在无意中重写了父类的同名方法,造成很多莫名其妙的问题。
- 我个人对于私有方法喜欢以 p_methordName 的形式命名,p即代表pravite.
2.建议书写枚举模仿苹果——在列出枚举内容的同时绑定了枚举数据类型NSUInteger,这样带来的好处是增强的类型检查和更好的代码可读性,示例:
typedef NS_ENUM(NSUInteger, GPSectionType) {
GPSectionTypeNone = 0,
GPSectionTypeNews = 1 ,
GPSectionTypeInformation = 2,
};
3.头文件 #import的顺序
- 写法模板
#import <系统库>
#import <第三方库>
#import “其他类”
- 尽量按照先系统类 第三方类 自己写的类顺序导入 中间不能有空格
- 建议的写法
#import<UIKit/UIkit.h>
#import<Google/Analytics.h>
#import"GPCustomView.h
4.@Class的写法
- 写法模板:@class class1, class2;
// 建议的写法
@class UIView, UIImage;
// 不建议的写法
@class UIView;
@class UIImage;
5.声明const的字符串
- 开头用k标识
- 推荐k+模板名字首字母大写+作用名称 防止和其他的重复
- 比如:CartViewModel类需要声明更新购物车列表的通知
- kCVMNoticationUpdateCartList
6.方法尽量控制最多五十行
- 一个方法内部最多五十行
- 如果超过就精简代码 就分开方法写
- 方便之后进行热修复 代码重构
7.注释一定要写
自己管理的类一定注释属性用途 方法的用途 参数的说明
属性如果设置默认值 一定注明默认值是什么
如果方法内部存在逻辑判断 方法跳转 一定注释判断用法 方法跳转用法
除了初始化操作
其他声明变量 赋值 判断 应该注明注释用途
方法注释
/**
* @brief 直播间发送礼物
*
* @param gift 礼物
* @param groupId 直播间id
* @param count 数量
* @param success 成功回调
* @param failure 失败回调
*/
+ (void)readySendGiftWithGift:(MoerLiveRoomGift*)gift
groupId:(NSString*)groupId
count:(NSInteger)count
success:(void(^)(Goods *goods))success
failure:(Failure)failure;
/**
* @brief 根据字典信息实例化订单
*
* @param dictionary 字典信息
*
* @return 订单实例
*/
-(instancetype)initWithDictionary:(NSDictionary*)dictionary;
- 属性注释
/**
* 打赏对象
*/
@property (nonatomic, copy) NSString *targetId;
- 枚举注释
typedef NS_ENUM(NSInteger, MoerGoodsType) {
/**
* 无
*/
MoerGoodsTypeNone,
/**
* 文章
*/
MoerGoodsTypeArticle = 1,
/**
* 活动
*/
MoerGoodsTypeActivity = 2
}
8.头文件引入其他类的时候使用@class
- 可以防止互相引入导致编译失败,造成不容易查找的bug
- 同时在.m文件中使用#import导入对应的类
9.多使用pragma mark
- 尤其是一个控制器包含非常多的业务逻辑,可以让结构更加清晰
10.控件的命名规范
- 不要使用简写,password不要写成pwd,button不要写成btn等;
- UILabel结尾加上Label;
- UIImageView结尾记上ImageView
- 等等让其他的编程人员看名字就知道变量的用法 和属于什么控件
11.对于#define宏命名
- 单词全部的大写 单词之间用_分割
- 建议的写法
#define NS_AVAILABLE_IOS(_ios) CF_AVAILABLE_IOS(_ios)
12.对于局部的变量尽量的初始化
- 局部的变量要初始化 属性有默认的值 所以我们不必须对于属性进行初始化
- 我之前遇到的一个BUG就是int类型没有初始化给我默认Nan造成崩溃
//正确的写法
int index = 0;
//错误的写法
int index;
- 对于一些对象判断是否赋值可以不进行初始化 但是对于一定不会为nil要进行初始化
13.block的命名规范
- 之前研究过很多的第三方的命名 对于苹果官方的没找到
- 有CallBack结尾 Complete结尾 Block结尾 还有CompletionHandle结尾的
- 我看到苹果很多的结尾都是用CompletionHandle结尾
// 建议的写法
typedef void(DidUpdateViewCompletionHandle)(void)
// 不建议的写法
typedef void(DidUpdateViewCallBack)
14.在dealloc方法中移除通知
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
15.属性要尽量使用懒加载
- 我们一个界面有很多控件
- 利用懒加载可以美化代码
- 所有的懒加载放在Getter的mark的下面
16.多使用字面量
- NSString @“”
- NSNumber @()
- NSDictionary @{}
- NSArray @[]
17.为第三方类添加分类添加前缀
- 比如为系统UIView添加分类Add的添加前缀
@interface UIView (GP_Add)
- (void)gp_addCustomView:(CustomView *)customView;
@end;
18.数组和字典最好指定元素的类型
NSArray<NSString *> *names = [NSArray array];
Section Two — Work Experience
1.建议加载xib,xib名称用NSStringFromClass(),避免书写错误
// 推荐写法
[self.tableView registerNib:[UINib nibWithNibName:NSStringFromClass([GPTableViewCell class]) bundle:nil] forCellReuseIdentifier:ID];
// 不推荐写法
[self.tableView registerNib:[UINib nibWithNibName:@"GPTableViewCell" bundle:nil] forCellReuseIdentifier:ID];
2.自定义cell的时候,将重用标识符同cell文件绑定在一起,而不是在控制器里定义一堆重用标识符
- 在.h中
#import "GPBaseCell.h"
UIKIT_EXTERN NSString * const GPContentCelltCellIdentifier;
@interface GPContentCell : GPBaseCell
@end
- 在.m中
#import "GPContentCell.h"
NSString * const "GPContentCell = @"GPContentCell";
@interface GPContentCell ()
@property (weak, nonatomic) IBOutlet UILabel *contentLabel;
@end
@implementation MoerQAAskerContentCell
- (void)awakeFromNib {
[super awakeFromNib];
self.selectionStyle = UITableViewCellSelectionStyleNone;
self.contentLabel.textColor = [UIColor redColor];
}
- UIKIT_EXTERN简单来说,就是将函数修饰为兼容以往C编译方式的、具有extern属性(文件外可见性)、public修饰的方法或变量库外仍可见的属性
- 参考资料
3.使用XIB创建自定义View
- 我们创建一个继承自UIView的类时,底下Also Creat XIB file处于灰色不可选择的状态。或许是苹果官方不建议这样做,但我们仍然可以单独创建一个View文件将其关联起来。
- 在.h文件中声明一个初始化方法
- (instancetype)initWithXib;
- 在.m中实现它
- (instancetype)initWithXib {
self = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:nil options:nil] lastObject];
// 初始化设置调整UI样式
...
return self;
}
4.善于利用autoresizingMask技术
- 用于处理比较简单的、单一的父子视图关系
- 一般情况下,确定一个子控件在其父控件中的位置,我们只需要确定4个方向中的两个有效方向就够了,如果多于两个有效方向,那么多出来的方向约束会被替代。具体说来就是,4个方向的约束线是有优先级的,顶部的约束线优先级高于底部的约束线,左边的约束线优先级高于右边的约束线。如果你同时约束了左边和右边,那么起作用的就是左边;如果你同时约束了上边和下边,那么起作用的就是上边;如果你同时约束了4个方向,那么起作用的是左上角。为了保证约束有效,不能同时约束上、下或者是左、右。
- 参考资料
5.拉伸图片
- 有些时候UI给的图片不能满足我们的需求,这个时候我们就要自己简单处理一下图片。
- 比如以某edgeInsets进行拉伸图片
- 涉及到的方法
- (UIImage *)resizableImageWithCapInsets:(UIEdgeInsets)capInsets NS_AVAILABLE_IOS(5_0); // create a resizable version of this image. the interior is tiled when drawn.
- (UIImage *)resizableImageWithCapInsets:(UIEdgeInsets)capInsets resizingMode:(UIImageResizingMode)resizingMode NS_AVAILABLE_IOS(6_0); // the interior is resized according to the resizingMode
6.使用SDWebImage给button设置图片
// 第一步:导入头文件
#import <SDWebImage/UIButton+WebCache.h>
// 第二步:调用方法并设置占位图片
[self.avatarButton sd_setImageWithURL:[NSURL URLWithString:model.avatarUrlString] forState:UIControlStateNormal placeholderImage:[UIImage imageNamed:@"PlaceholderImage"]];
7.使用SDWebImage加载gif图片
// 第一步:导入头文件
#import "UIImage+GIF.h"
// 第二步:调用方法
UIImage *hoopImage = [UIImage sd_animatedGIFNamed:@"hula_hoop"];
self.hoopImageView = [[UIImageView alloc] initWithImage:hoopImage];
self.hoopImageView.frame = CGRectMake(10, 10, 100, 100);
[self.view addSubview:self.hoopImageView];
8.使用dSYM文件查找线上程序的crash日志
- 什么是 dSYM 文件
- Xcode编译项目后,我们会看到一个同名的 dSYM 文件,dSYM 是保存16进制函数地址映射信息的中转文件,我们调试的 symbols 都会包含在这个文件中,并且每次编译项目的时候都会生成一个新的 dSYM 文件
- 位于 /Users/<用户名>/Library/Developer/Xcode/Archives 目录下,对于每一个发布版本我们都很有必要保存对应的 Archives 文件 。
- dSYM 文件有什么作用
- 当我们软件 release 模式打包或上线后,不会像我们在 Xcode 中那样直观的看到用崩溃的错误,这个时候我们就需要分析 crash report 文件了,iOS 设备中会有日志文件保存我们每个应用出错的函数内存地址
- 通过 Xcode 的 Organizer 可以将 iOS 设备中的 DeviceLog 导出成 crash 文件,这个时候我们就可以通过出错的函数地址去查询 dSYM 文件中程序对应的函数名和文件名。
- 大前提是我们需要有软件版本对应的 dSYM 文件,这也是为什么我们很有必要保存每个发布版本的 Archives 文件了。
- 如何将文件一一对应
- 每一个 xx.app 和 xx.app.dSYM 文件都有对应的 UUID,crash 文件也有自己的 UUID,只要这三个文件的 UUID 一致,我们就可以通过他们解析出正确的错误函数信息了。
1.查看 xx.app 文件的 UUID,terminal 中输入命令 :
dwarfdump --uuid xx.app/xx (xx代表你的项目名)2.查看 xx.app.dSYM 文件的 UUID ,在 terminal 中输入命令:
dwarfdump --uuid xx.app.dSYM3.crash 文件内第一行 Incident Identifier 就是该 crash 文件的 UUID。
9.block的注意事项
- 在类中声明block为属性时,如果使用assgin修饰,那么它被放到了栈中,方法一过就会被销毁,
- 所以,尽量使用copy作为修饰词,这样一来block就被存放到了堆中,生命周期就会延长,保证block不会被立即销毁;
- 要防止循环引用,所以在block外部通常要先弱引用一次,然后在block内部强引用一次。
- __weak typeof(self) weakSelf = self;
- __strong typeof(self) strongSelf = weakSelf;
- block使用的时候要先进行判断
if (self.backBlock) {
self.backBlock(self.textView.text)
}
10.不要把本地调试的代码提交到git上
- 如果在提交前忘记改回来,要善于利用宏
- 下面的代码只会在debug模式下运行
- 如果切换到release模式不会运行
#ifdef DEBUG
isDebug = YES;
#else
isDebug = NO;
#endif
11.使用自定义标签警告
- // TODO:标识将来要完成的内容;
- // FIXME:标识以后要修正或完善的内容。
- // ???: 疑问的地方
- /// !!!: 需要注意的地方
12.使用runtime关联对象
13.使用contentMode让图片长得好看
14.控件字体颜色设置相同时使用IBOutletCollection
15.修改状态栏字体颜色
// 首先写一个属性
@property (nonatomic, assign) UIStatusBarStyle statusBarStyle;
// 根据具体需求来修改状态栏的样式
self.statusBarStyle = UIStatusBarStyleLightContent;// 白色
self.statusBarStyle = UIStatusBarStyleDefault; // 黑色
// 告诉系统更新状态栏样式
[self setNeedsStatusBarAppearanceUpdate];
// 重写该方法,将新的样式返回给系统
- (UIStatusBarStyle)preferredStatusBarStyle {
return self.statusBarStyle;
}
16.关闭刷新section的动画效果
// 虽然动画效果选择的是UITableViewRowAnimationNone
// 但实际上还是会动画效果
[self.tableView reloadSections:[[NSIndexSet alloc] initWithIndex:GPSectionTypeNews] withRowAnimation:UITableViewRowAnimationNone];
- 正确做法
[UIView performWithoutAnimation:^{
[self.tableView reloadSections:[[NSIndexSet alloc] initWithIndex:GPSectionTypeNews] withRowAnimation:UITableViewRowAnimationNone];
}];
17.善于利用round/ceil/floorf函数
- round:如果参数是小数,则求本身的四舍五入。
- ceil:如果参数是小数,则求最小的整数但不小于本身.
- floor:如果参数是小数,则求最大的整数但不大于本身.
Example:如何值是3.4的话,则
3.4 -- round 3.000000
-- ceil 4.000000
-- floor 3.00000
18.APP内拨打电话
- 方法一
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"tel://010-88888888"]];
- 方法二
#import <UIKit/UIWebView.h>
UIWebView * callWebview = [[UIWebView alloc]init];
[callWebview loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"tel:010-88888888"]]];
[[UIApplication sharedApplication].keyWindow addSubview:callWebview];
- 区别
区别一:第一种会先跳出程序到系统的打电话程序,第二种是一直都在自己的app中运行,没有出去过。
区别二:第一种触发直接到打电话界面,第二种会先弹出一个对话框,可以选择打不打电话,对话框如下。
19.善于利用UIStackView
20.iOS中常用动画详解
21.使用NSTextAttachment实现图文混排
//
NSMutableAttributedString *titleString = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:@"%@ ", article.title]];
NSTextAttachment *textAttachment = [[NSTextAttachment alloc] initWithData:nil ofType:nil];
textAttachment.image = [UIImage imageNamed:@"icon"];
CGFloat offsetY = -1.0;
textAttachment.bounds = CGRectMake(0, offsetY, textAttachment.image.size.width, textAttachment.image.size.height);
NSAttributedString *textAttachmentString = [NSAttributedString attributedStringWithAttachment:textAttachment];
[titleString insertAttributedString:textAttachmentString];
22.TextKit的学习
23.支付相关
24.关于dispatch_semaphore的使用
25.NSStringFromSelector(_cmd)说明
- _cmd 代表本方法的名称
- _cmd是隐藏的参数,代表当前方法的selector,他和self一样都是每个方法调用时都会传入的参数,动态运行时会提及如何传的这两个参数,
你在方法里加入CCLOG(@"%@, %@",NSStringFromSelector(_cmd),self);语句之后,执行这个方法就会输出方法的名称,
这样做是为了跟踪查看方法调用的前后顺序,或者想看看程序到底在那个方法内部崩溃的!
- 在Apple的官方介绍里看到轻描淡写的说了一句:“The _cmd variable is a hidden argument passed to every method that is the current selector”,其实说的就是_cmd在Objective-C的方法中表示当前方法的selector,正如同self表示当前方法调用的对……
26.iOS方法警告
当苹果SDK升级时废弃某些方法,就需要方法警告。
外部调用方法警告
__deprecated_msg("方法废弃,请使用...替换")
- 当前方法警告
__attribute__((visibility("警告")));
- 如
- (void)test __deprecated_msg("方法废弃,请使用...替换") __attribute__((visibility("方法废弃")));
27.友盟推送踩的坑
- 首先友盟的推送是分为开发环境和生产环境,开发环境就是指我们在开发阶段,应用还没有上架到app store的时候,这时候我们按照友盟的官方文档集成友盟消息推送就可以了。
- 然后要测试的时候,这时候选择测试模式进行测试,要在下面的方法中获取当前设备的device token,然后测试模式添加测试设备,只要上传的证书没有错误,发送消息手机就可以收到推送消息了
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
NSLog(@"%@",[[[[deviceToken description] stringByReplacingOccurrencesOfString: @"<" withString: @""]
stringByReplacingOccurrencesOfString: @">" withString: @""]
stringByReplacingOccurrencesOfString: @" " withString: @""]);
}
- 开发环境只能在测试模式下测试,消息列表那边只能是app上线后,变成生产环境了才可以在消息列表发送消息测试。
- app上线后,第一天一般发送推送消息是失败的,因为device token在友盟那边的入库是有一天的延时的,具体可以看友盟上的这篇文章
- 关于app点击消息跳转到指定页面,分为当前app是在前台还是后台后者未运行,所以需要在
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
- 两个方法里面写页面的跳转,这个需要和后台约定好传过来的消息需要跳转到什么页面,再自己做跳转,页面跳转只能自己在代码中进行操作.
- 推送消息一直显示发送失败,不知道该如何查找原因,找了客服很多次,说device token无效,重新获取,可是重新获取很多次也还是无效
- 后来碰到好心人建议查看证书的包名和bundle ID是否一致,于是把导出的上传到友盟的推送证书在钥匙串中打开,发现包名确实不一样了,但是证书制作过程没有出错的,所以想不通,把证书删除了,重新导出,上传,OK。可以推送了。如果用户第一次打开app 没有允许推送,后面在设置中打开允许推送也是可以的,因为在第一次打开和每天的第一次运行app的时候都会发送token给友盟的服务器,所以推送开关的开启和关闭不影响token。
- 最后如果需要在生产环境下将应用程序安装到设备上测试,或者APP并没有上线也需要推送组件,要以Ad Hoc方式打包应用
28.配置Gitlab
- GitLab是企业版的GitHub,并且GitLab是开源的,也就是说可以部署到自己的内网上。
- 如果配置好了SSH公钥还是不能通过Sourcetree把公司的代码拉到本地的电脑上,可能是你的老大忘了把你的Gitlab账号开通权限
- GitLab的简单使用
29.过滤掉字符串前后的空格
- 有时候服务端返回的数据前后会带\n
- 我们可以调用下面的方法将它去掉
NSString* trimedURL = [stringURL stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
Section Three — Third Party
1.一款非常强大的文字编辑器
2.实现顶部弹窗的HUD
3.非常好用的一款加载弹窗
4.每一个网络请求封装成对象的网络库
5.让响应手势变得更简单
6.让每一个试图控制器拥有独立的导航栏
7.绘制图表、折线图的框架
8.一个比UISegmentedControl更好用的东西
9.轮播图必备
10.tableView算高神器
11.超级强大的开源框架
12.小抽屉展示
13.侧滑抽屉
14.夜间模式
15.图片浏览器
16.HTML标签解析
Section Four — Development Tool
1.捕捉网络请求 — Charles
2.切换host的工具 — Gas Mask
3.高效的编辑器 — Subline Text
4.模拟发送网络请求 — Postman
5.图形化Git工具 — SourceTree
6.分析他人APP界面 — Reveal
7.翻墙必备 — Shadowsocks
8.快捷键太多记不住? — Cheetsheet
Section Five — About Git
很多人做iOS开发,可能先接触方便好用的Sourcetree,记住了合并代码的那几个步骤,然后才接触git。为了能更好的理解Sourcetree做的每一步,很有必要学习博大精深的git,以下内容为本人恶补git知识时的笔记,感谢廖雪峰老师写的git教程。
- 创建一个文件夹(默认路径:/Users/fsl/learngit)
- mkdir learngit
- 进入该文件夹
- cd learngit
- 显示当前目录
- pwd
- 初始化git仓库
- git init
- 把文件添加到git仓库
- git add <file>
- git commit
-
git commit -m "提交信息"
-
- 查看当前仓库的状态
- git status
- 查看当前仓库某个文件的改变
- git diff readme.txt
- 显示从最近到最远的提交日志
-
git log
-
- 版本回退
- git reset --hard HEAD^
- HEAD指向的版本就是当前版本,因此,Git允许我们在版本的历史之间穿梭,使用命令git reset --hard commit_id。
- 穿梭前,用git log可以查看提交历史,以便确定要回退到哪个版本。
-
要重返未来,用git reflog查看命令历史,以便确定要回到未来的哪个版本。
版本库(Repository)
-
查看工作区和版本库里最新文件的区别
- git diff HEAD -- readme.txt
-
撤销修改
- git checkout -- readme.txt
-
git checkout -- file命令中的--很重要,没有--,就变成了“切换到另一个分支”的命令
-
撤销已经提交到暂存区的修改(重新放回工作区)
- git reset HEAD readme.txt
- git reset命令既可以回退版本,也可以把暂存区的修改回退到工作区。当我们用HEAD时,表示最新的版本。
-
删除文件
- rm test.txt
- git checkout其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。
- 命令git rm用于删除一个文件。如果一个文件已经被提交到版本库,那么你永远不用担心误删,但是要小心,你只能恢复文件到最新版本,你会丢失最近一次提交后你修改的内容。
添加到远程仓库
- 关联一个远程库
- git remote add origin https://github.com/Bestmer/learngit.git
- git push -u origin master (第一次推送master分支的所有内容)
- 把本地库的内容推送到远程,用git push命令,实际上是把当前分支master推送到远程。
- 由于远程库是空的,我们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。
- 此后,每次本地提交后,只要有必要,就可以使用命令git push origin master推送最新修改;
- 从现在起,只要本地做了提交,就可以通过
- git push origin master
- 把本地master分支的最新修改推送至GitHub,现在,就拥有了真正的分布式版本库!
查看分支
- git branch
切换分支(创建新分支)
- git checkout -b dev
- git checkout命令加上-b参数表示创建并切换,相当于以下两条命令:
- $ git branch dev
- $ git checkout dev
- Switched to branch 'dev'
- 切换分支
- git checkout <name>
合并指定分支(dev)到当前分支(master)
- git merge dev
删除分支
- git branch -d dev
- 查看分支
- git branch
查看分支图
- git log --graph
- git log --graph --pretty=oneline --abbrev-commit
分支管理策略
-
通常情况下,合并分支时,Git会使用Fast Forward(快进)模式,即改变指针指向
- 使用Fast Forward模式有一个缺点,就是删除分支后,会丢掉分支信息。
- 如果要强制禁用Fast Forward模式,Git就会在merge时生成一个新的commit,这样的话在分支历史上就能看到分支信息。
- git merge --no-ff -m "merge with no-ff" dev
- 因为本次合并会创建一个新的commit,所以要加上-m 参数,把commit的描述写进去。
Bug分支
- 修复bug时,我们会通过创建新的bug分支进行修复,然后合并,最后删除;
- 当手头工作没有完成时,先把工作现场git stash一下,然后去修复bug,修复后,再git stash pop,回到工作现场。
- 查看暂存列表
- git stash list
- 恢复工作现场
- 方式一:用git stash apply恢复,但是恢复后,藏匿内容并不删除,需要你用git stash drop来删除;
- 方式二:用git stash pop,恢复的同时把stash内容也删了
Feature分支
- 添加一个新功能时,你肯定不希望因为一些实验性质的代码,把主分支搞乱了,所以,每添加一个新功能,最好新建一个feature分支,在上面开发,完成后,合并,最后,删除该feature分支。
- 有一种可能是该分支开发到一半,被告知新功能取消。此时执行 git branch -d 分支名,是直接删不掉分支的
- git会提醒你,该分支还没有被合并,如果删除将丢失掉修改
- 如果要强行删除,需要使用命令git branch -D 分支名。
多人协作
- 多人协作的工作模式通常是这样:
- 首先,可以试图用git push origin branch-name推送自己的修改;
- 如果推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并;
- 如果合并有冲突,则解决冲突,并在本地提交;
- 没有冲突或者解决掉冲突后,再用git push origin branch-name推送就能成功!
- 如果git pull提示“no tracking information”,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream branch-name origin/branch-name。
- 查看远程仓库信息
- git remote -v
- 如果新建的分支不推送到远程,对其他人就是不可见的
- 从本地推送分支,使用git push origin branch-name,如果推送失败,先用git pull抓取远程的新提交;
- 在本地创建和远程分支对应的分支,使用git checkout -b branch-name origin/branch-name,本地和远程分支的名称最好一致
标签管理
- 发布一个版本时,我们通常先在版本库中打一个标签(tag),这样,就唯一确定了打标签时刻的版本。将来无论什么时候,取某个标签的版本,就是把那个打标签的时刻的历史版本取出来。所以,标签也是版本库的一个快照。
- tag就是一个让人容易记住的有意义的名字,它跟某个commit绑在一起。
- 创建标签
- git tag <name>用于新建一个标签,默认为HEAD,也可以指定一个commit id
- git tag -a <tagname> -m "blablabla..."可以指定标签信息;
- git tag -s <tagname> -m "blablabla..."可以用PGP签名标签;
- 命令git tag可以查看所有标签
- 操作标签
- 删除标签
- git tag -d <tagname>
- 推送一个本地标签到远程
- git push origin <tagname>
- 一次性推送全部尚未推送到远程的本地标签
- git push origin --tags
- 删除远端的标签
- 先删除本地的标签 git tag -d <tagname>
- 在使用git push origin :refs/tags/<tagname> 删除远端的标签
- 删除标签
忽略文件
- 忽略某些文件时,需要编写.gitignore
- .gitignore文件本身要放到版本库里,并且可以对.gitignore做版本管理
使用rebase而不是merge
- 为了让提交代码的时间线是笔直的一条线,而不是各种分叉,我们要使用rebase(变基)
- 关于rebase的知识可以看下面的链接
- git rebase使用笔记
最后
学而时习之,不亦说乎。