平时自己开发记录,会持续更新补充到这个文章中,相关的知识点不会展开讲,主要就是说下大概这个东西,权当平时自己的记录,方便工作查询
MRC相关
-
mrc下dealloc方法,释放属性内存的时候
self.xxx = nil就可以了不用release,因为set方法会先release旧值,再赋新值 -
mrc写set方法的时候要注意先relese旧值,retain新值
-
MRC中创建ARC的类,要注意用autorelease,不然ARC类会释放不掉
-
alloc, init, copy, mutablecopy, new开头的方法是返回一个引用计数为1的对象,所以外部需要对其进行内存管理,其他则返回autorelease对象,当然这是apple的编程规范,平时我们自定义方法也要按照这个规范
-
局部变量创建加autorelease,全局变量delloc置nil
-
在ARC下打印引用计数
NSObject *test = [NSObject new];
NSLog(@"%p",test);
NSLog(@"1 == %@", [test valueForKey:@"retainCount"]);
_ref = test;
NSLog(@"2 == %@", [test valueForKey:@"retainCount"]);
self.delegate = test;
NSLog(@"3 == %@", [test valueForKey:@"retainCount"]);
NSLog(@"%p",self.delegate);
UITableView 相关
- cellForRowAtIndexPath不执行的原因
1.dataSource和delegate没有设置
2.numberOfRowsInSection和numberOfRowsInSection返回的数据不是大于0的整数
3.tableView没有add到父视图
- UITableView ReloadData抖动,闪动
1.取消高度预算
self.tableView.estimatedRowHeight = 0;
self.tableView.estimatedSectionFooterHeight = 0;
self.tableView.estimatedSectionHeaderHeight = 0;
2.只reload一行或者一个cell
[self reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationNone];
[self reloadRowsAtIndexPaths:@[
[NSIndexPath indexPathForRow:0 inSection:0]
] withRowAnimation:UITableViewRowAnimationNone]
3.没有动画reloadData
[UIView performWithoutAnimation:^{
[self reloadData];
}];
4.如果reloadData只是为了新加入数据,可以用这个
- (void)insertRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;
5.自定义cell,用了SDWebImageView,在下载完图片的回调,这里回调出去再异步去布局
6.UItableView设置为Grounp时会自动偏移64,设置下tableHeaderView,高度不能为0
self.tableView.tableHeaderView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, CGFLOAT_MIN)];
7.设置滚动条出现的时间或者一直出现
8.设置为group后,尾部出现留白处理,footerView的高度设置为CGFLOAT_MIN,对应的view return nil
- 取消cell的选中效果
cell.selectionStyle = UITableViewCellSelectionStyleNone;
10.去掉分割线
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone
杂乱的Tip
- ARC下
dealloc内不需要写[super dealloc]因为在运行时编辑器会自动加上 - <#出现代码块#>
- 刚更新完iphone系统,打开设置,是看不到开发者选项的,这个得连下xcode才会出现的
- block里面如果代码比较长,应该封装起来,避免太长的代码,忘记用weakself,导致了循环引用
- UICollectionView不走datasource代理
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
原因是继承之`UICollectionViewFlowLayout`的类`init`方法中设置的`itemSize`将其弄为负数了
width 为负数
self.itemSize = CGSizeMake(width, width);
- 导出xcode自定义代码块
Xcode中的代码块默认存储在~/Library/Developer/Xcode/UserData/CodeSnippets下,可以直接拷贝出来,放在新电脑的该目录下即可
-
分类不能添加实例变量,之所以不可以是因为分类不是真正意义上的类,类的本质是一个结构体,里面有对应的指针来指向对应的属性列表,方法列表等,但是分类的本质也是一个结构体,但是这个结构没有这个指针指向一个属性列表,所以分类并没有这个结构来存实例变量
-
解决异步回调
如果有个方法有异步的回调,那么我们可以用gcd组和信号量,还有递归来解决这个问题
- pod官方提供了公开的 hook (地址:guides.cocoapods.org/syntax/podf… ) podfile是ruby,你也可以随便改。www.rubydoc.info/gems/xcodep… 这个库是cocopods用于修改Xcode配置文件的,有api文档
2、方法中局部变量的alloc后,由于要将指针传递出去,所以无法在方法内和方法外release,所以用autorelease来解决这个问题。
3、NSMutableArray每当add或remove一个对象时,会自动retain和release一次。
4、stringWithFormat:默认返回autorelease类型,或者不用手动release的初始化都会默认返回autorelease。
5、NSString和NSArray的property声明一般用copy代替retain或strong,防止外界对其进行修改,而delegate基本都声明为weak,防止cycle retain。
6、关于自建tableView: 如果继承UITableViewController,则可以直接使用其自带的tableView,并设置大小和其他属性; 如果继承的是UIViewController,则需要再添加一个tableView,并设置其delegate和dataSource。 PS:记住不要使用xib,如果删掉xib文件,之前xib中的内容还在!
7、关于自定义tableViewCell: 将新建的cell的class设置为对应的cell class,而不是file,owner,然后将cell中的自定义view和controller连线,即可方便获取自定义的label或button。 使用自定义cell时要先register自定义的nib,然后直接dequeuereuse即可。如果没有xib, 则需要类文件里边定义label,然后和系统默认方案一样去创建cell。
8、要设置cell被选中的背景状态颜色,在setSelected:animated中设置cell.selectedBackgroundView。 要想改变cell的样式,重写layoutSubviews。
9、需要执行drawRect方法是应调用setNeedsDisplay
10、委托的功能之一是使得程序对任何情况都能做出正确的响应。
11、振动:加入框架AudioToolbox.framework 导入AudioToolbox.h 在任何需要振动的地方加代码: AudioServicesPlaySystemSound(kSystemSoundID_vibrate);
12、重写drawRect方法: 首先需要获得context UIGraphicsGetCurrentContext()。 绘画时,将代码填在UIGraphicPushContext(context)和 UIGraphicPopContext()之间,如果需要保存当前绘图状态的话。
13、如果某个property和画面关联,则可以重写其setters来redraw,即调用setNeedDisplay来触发drawRect;另外为了提高效率,要在setter中判断只有在值有变化时才调用, if(_foo!=foo){ }
14、segue create a new view controller,always
15、修改navigationBar的backBarItem必须在父controller的push代码前
16、viewDidLoad时controller.view还是最原始的尺寸,若要调整其尺寸,最好放在接下来的viewWillAppear中。viewWillAppear中还适合放耗时的任务,比如下载,但要另开一个线程。
17、除非在loadView里面,否则永远不要让self.view指向其他任何东西。只要用了storyboard或者xib,就不要用loadView方法;但重写loadView方法一定要设置self.view
18、尽量不要使用awakeFromNib,初始化最好都放viewDidLoad中。
19、alloc initWithFrame比较少用。 UIImage用alloc的话会改变UIImageView的frame为图片大小,设置imageView.image则不会。
20、webView中scalePagesToFit需要设置为YES。
21、一般用NSURL来代替NSString设置文件名
22、使用scrollView时一定不要忘了设置其contentSize。要想缩放,则去设置scrollView.minimumZoomScale和maximumZoomScale,并且实现其delegate中的(UIView *)viewForZoomingInScrollview并连接其delegate。
23、block中的任何对象都需要一个strong pointer。但这可能引起memory cycle,解决办法就是声明__weak。
24、任何UIKit相关的东西都必须放在主线程里做。
NSUserDefault存的数组如果包含自定义类型,那么需要让自定义类实现<NSCoding>
6.关于方法中的if return和if else,if else是用来区分鲜明的互斥的,if return是用来处理特殊的错误状态,而且if return好处是简洁提高可读性,如果怕if return坑了后面的代码,一般这种情况是方法太冗长才会,那么当方法太冗长的时候,应该尽量的去抽取方法的代码
7.关于递归,尽量不要用递归,迫不得已用递归也得加标志位,让他走一次,不走第二次,防止一直递归卡死的情况,当然while也是同理的
8.尽量不要在set方法里面写逻辑
9.自定义cell的时候,最好用在一个地方,不要看着两个cell差不多,就通过type去控制,这样后面这个类会很冗杂
10.复制代码过来一定要看清楚代码里面做什么操作,比如我有一次是要获取所有系统字体,所以了一个代码,但是里面做了数据库的插入,更新,那么是不适合我这个方法的getAllSystemFont,所以要把数据库的操作去掉
11.代理方法的写法,方法的前缀就是类名,第一个参数就是self,类似了tableview的代理方法的写法
***
iOS开发 - Xcode不走断点
第一种,debug模式下,Xcode不走断点
解决方法:edit scheme -> info,build configuration 修改为Debug,Debug executable前的对勾打上。
第二种,debug模式下,xcode不在断点代码处停止,会进入线程
解决方法:Debug->Debug Workflow->always show disassembly前的对勾关掉。
第三种,debug模式下,xcode不走pod第三方库的断点
解决方法:试一下 clean一下再重新build。或者重启xcode,或者将断点全部删掉,重新添加。
***
- 父类声明了类方法A,子类是不能调用类方法A的
- oc的容器类都是不可以继承的,因为容器类都是用了类簇的设计模式
遇到bug的解决Tip
-
场景是点击UICollectionCell,present一个页面,点击关闭页面,再点这个cell,无响应,再点一次才有响应;造成这个问题的原因是设置了UICollectionView的allowsMultipleSelection属性为YES,可以多选,导致再次点击是触发了取消选中的代理方法
__block KSOFontInfo *currertFont = nil;
if (mCurrentFontIndex >= 0 && mCurrentFontIndex < oldFontList.count) {
currertFont = [oldFontList objectAtIndex:mCurrentFontIndex];
}
editFontListViewController.willDismissBlock = ^{
if (!currertFont) return;
[weakSelf resetWordFontList];
[mfontList enumerateObjectsUsingBlock:^(KSOFontInfo *obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([obj isKindOfClass:[KSOFontInfo class]]) {
// 这里的currertFont会野指针
if ([obj.familyName isEqualToString:currertFont.familyName]) {
mCurrentFontIndex = idx;
*stop = YES;
isExist=YES;
}
}
}];
}
// 造成野指针的原因,看下resetWordFontList方法的部分实现
currertFont是指向mfontList数组中的某个元素,但是在resetWordFontList之后,将这个数组的元素清空了,这就导致了这个元素野指针了
// 所以这个的Tip就是,当一个指针指针一个地方的时候,尤其是在之后的回调中使用,要考虑是否会被更改,这种情况还可以考虑,使用copy
// 还有一个点,记得如果是MRC,在copy使用autorelease,这样是错误的,这样还是会被释放,应该在回调完成后调release
- (void)resetWordFontList
{
if (mfontList == nil)
{
mfontList = [[NSMutableArray alloc] init];
}
else
{
[mfontList removeAllObjects];
}
...
}
调试技巧
1
我们也可以在LLDB中直接用watchpoint命令,可以通过选项实现更多效果。
watchpoint set self->testVar //为该变量地址设置watchpoint
watchpoint set expression 0x00007fb27b4969e0 //为该内存地址设置watchpoint,内存地址可从前文提及的`p`命令获取
watchpoint command add -o 'frame info' 1 //为watchpoint 1号加上子命令 `frame info`
watchpoint list //列出所有watchpoint
watchpoint delete // 删除所有watchpoint
2
第一种,debug模式下,Xcode不走断点
解决方法:edit scheme -> info,build configuration 修改为Debug,Debug executable前的对勾打上。
第二种,debug模式下,xcode不在断点代码处停止,会进入线程
解决方法:Debug->Debug Workflow->always show disassembly前的对勾关掉。
第三种,debug模式下,xcode不走pod第三方库的断点
解决方法:试一下 clean一下再重新build。或者重启xcode,或者将断点全部删掉,重新添加。
3
打印图层结构
po [[UIWindow keyWindow] recursiveDescription]
打印某个视图的图层结构
po [[[UIWindow keyWindow] rootViewController] _printHierarchy]
打印堆栈
bt
打印所有线程的堆栈
bt all
Xcode错误提示
Init methods must return a type related to the receiver type
init开头的方法中只能返回该对象,不能返回除该对象之外的对象类型。
Xcode编译相关
1.获取当前内核数:
$ sysctl -n hw.ncpu
2.设置编译线程数:
$ defaults write com.apple.dt.Xcode IDEBuildOperationMaxNumberOfConcurrentCompileTasks 8
3.获取编译线程数:
$ defaults read com.apple.dt.Xcode IDEBuildOperationMaxNumberOfConcurrentCompileTasks
4.显示编译时长:
$ defaults write com.apple.dt.Xcode ShowBuildOperationDuration YES
工作心得
1.在设计UI组件的时候,要考虑给外部提供接口用于修改这个组件的UI状态,而不应单方面的想着,点击我的UI触发事件流,需要考虑一种其他情况,从外部事件流入UI组件,改变UI状态,要考虑双向情况,不要把UI组件封的太死
2.多个按钮状态:
点击高亮一下;
点击选中,再点击取消选中;
点击选中,再点击不取消选中,点了其他才取消选中;
多选,单选
部分多选+部分单选
3.一个UI类对外暴露UI对象,其实有一点坑的,外部可能会对这个对象进行修改,在很久后维护很麻烦,不知道外部哪里做了修改,所以对外的属性最好的readonly,然后再提供block方法,出去给外部去设置,这样后面直接搜索block方法,就知道外部哪里设置了
4.关于ipad分屏的,在窄屏的时候,可能是用iphone的样式,导航栏是隐藏的,当切到宽屏的时候,重新布局,导航栏是显示的,但是如果在这个动画的过程中去做布局,如果用到导航栏是否隐藏做判断,那么这时候会造成错误,因为动画过程中的导航栏还是iphone的状态,得在分屏完成再做布局稳妥点
5.当将一个类的属性,赋值给另一个类的属性的时候,比如AVc的属性Name,赋值给AVc上的view的属性Name的时候,要考虑,这个Name是指针进行,view对其进行修改,如果影响了AVc对Name的使用,比如需要特殊注重的是赋值一个Mode过去,这种比较需求特别注意
6.头文件尽量不要暴露太多允许读写属性,比如A类中有个属性view,然后很多地方对这个A类的view.hidden或者其他属性进行设置,这时候有时候调试起来是比较麻烦的,要找debug代码,断点不好找,打断点地方也多,所以比较好的做法是暴露接口出去,然后属性尽量只读,这样以后调试起来,以及寻找对这个A类有什么操作的时候,只能在A类的.m中看,大部分可以看出外部对其有什么操作
7.不要太依赖一些警告宏,比如API_AVAILABLE(ios12),正常会出警告说应该12使用,但是掉过坑,没有出警告,导致了ios11线上崩溃,所以需要用到API_AVAILABLE的方法,最好还是加if return稳妥点
8.init做UI处理的代码,最好加安全主线程宏,预防外部使用,比如懒加载,这种情况就有可能子线程调到,然后就崩溃了
9.修改一个类的一个UI属性的时候,尤其是取消addSubView,要注意下外部是否拿这个view做约束,因为取消addSubView,外部还拿这个view去做约束,就会崩溃了
10.关于写UI的思考,之前有分享发送的模块,它的结构大概是这样的,VC chileVc,VC主要是业务逻辑,跳转,chileVc是内容视图,本来以为这样算视图和逻辑拆分得挺好了。有一次需求,需要将这个分享模式作为view add在某个视图上,那时候我是Vc.view add上去了,效果是看出来了;但是分享里面的点击视图,尤其是跳转的代码,如self dismiss...或者self present...都是基于这个Vc是present出来的。所以说到这里就有个思考点了,这个模块的缺点就是一切要基于它是present出来的,比如现在的add view,就会导致跳转的代码没法用了,self dismiss...或者self present...都无效,因为self没有被presenting。所以就有个思考点,平时在vc写类似这样的代码,可以将self dismiss...或者self present...写成self.xxx,这个xxx由外部决定,这样就扩展性强了一点
容易忘记代码
1.数组元素执行方法
[view.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
2.PresentedViewController 与 PresentingViewController区别
假设从A控制器通过present的方式跳转到了B控制器,那么 A.presentedViewController 就是B控制器;B.presentingViewController 就是A控制器。
官方API使用注意点
1.[NSURL URLWithString:urlStr]如果urlStr包括中文字符或者特殊字符,那么调用这个方法会返回nil
开发行为
1.拔数据线的时候要断了xcode再拔
review代码主要review点
1.block是否有直接用self
2.while是否会造成无限循环
3.声明为强引用的属性是否外部会赋值,是否会循环引用
4.改动了方法名,.h有没有对应改动,调用地方有没有对应改动,尤其是删除方法的时候,一定要搜下有没有对应调用的,尤其是perforSelect的,KVO,通知监听的
5.多人开发入库的时候,要想着这个项目是可以编起来的
没怎么见的API
1.intrinsicContentSize,也就是控件的内置大小。比如UILabel,UIButton等控件,他们都有自己的内置大小。控件的内置大小往往是由控件本身的内容所决定的,比如一个UILabel的文字很长,那么该UILabel的内置大小自然会很长。控件的内置大小可以通过UIView的intrinsicContentSize属性来获取内置大小,也可以通过invalidateIntrinsicContentSize方法来在下次UI规划事件中重新计算intrinsicContentSize。如果直接创建一个原始的UIView对象,显然它的内置大小为0。
线程相关
1.如果一个方法的回调是在子线程的,那么想要同步他可以用信号量
如果回调是主线程,那么可以用CFRunLoopRun();然后主线程空跑等待,然后回调到主线程后,再调用CFRunLoopStop(CFRunLoopGetCurrent());停止空跑