runLoop 定时器(NSTimer, CADisplayLink)需要加入runLoop,注意不用时需要销毁,因为是addTarget传入self,self又对定时器强引用。开发中只有这一个地方会用到runLoop
c语言中没有对象,c语言中对象其实就是结构体
RAC接管了苹果的所有事件机制(addTarget,代理,通知,KVO,时钟,网络的异步回调),在信号内部通过3个方法sendNext,error,completed,订阅者只需要监听不同的代码块就可以接收到对应的信息,冷信号热信号,信号一点有订阅者就会执行信号里面的代码
解耦了,只需要订阅就能获取信息
信号组,是用来同时监听多个事件,reduce参数可以返回想要的数值,比如两个文本框都有内容时返回真,这时next打印是1,来控制按钮等的状态,在这里再做相应的登录操作
自学方法,使用每个控件时,先到头文件看看有什么方法
UI和模型之间的双向绑定,UI到模型只需要监听UI的属性变化,即使用信号,模型到UI用RAC(nameTextField, text) = RACObserve(self.person, name); RAC(ageTextField, text) = [RACObserve(self.person, age) map:];类型转换用map
使用颜色不能带alpha透明度,控件的父控件有背景颜色,如果使用alpha需要判断父子控件叠加的面积大小和叠加后的颜色,影响性能
项目中尽量实现右滑pop到上一层的功能,人性化
终端停止pod用Ctrl+C
SDWebImage没有网络指示器功能,需要自己手动添加;AFNetworking(在完成启动的方法中,但是再次运行会闪一下。如果图片太大的话就不会缓存,而且使用的是系统缓存)和YYWebImage(在setImageWithUrl方法中直接设置optional)有网络指示器功能
、SDWebImage使用静态图片完全没问题,但如果使用动态gif图片,占用内存过多
[self.navigationController setNavigationBarHidden:NO animated:YES];导航条的显示和隐藏
中途改变了状态栏的颜色,需要使用[self.navigationController setNeedsStatusBarAppearanceUpdate];进行重绘
UI尽量不适用第三方框架
POP第三方,主要看POPAnimatableProperty.h。iOS开发必备
字典转模型就是工厂方法
单元测试主要是边界测试(逻辑测试),是测试暴露在.h的方法,不包含UI测试,MVVM适合单元测试,因为所有的逻辑代码都没有在控制器,不用进入某个页面来测试
性能耗时测试:CACurrentMediaTime和单元测试
单元测试包括:常规测试,性能测试,异步测试
UI测试:以前是instrument,现在可以用Xcode自带的UIText功能。猴子测试:使用instrument
KVC可以给只读属性赋值
表格性能问题:两帧刷新之间运算没有做完(减少计算量)。测试时一定要用真机测试(内存、cpu和电脑差别很大)。栅格化,将cell中所有内容生成一张独立图片,在滚动过程中只显示图片,如微信。cell.layer.shouldRasterize = YES; cell.layer.rasterizationScale = [UIScreen mianScreen].scale; 如果cell比较复杂,可以使用异步绘制(慎用) cell .layer.drawsAsynchronously = YES; 不要动态创建子视图,所有的子视图都需要设置背景颜色,并且不能带alpha
关于购物车:将数据源的每一条数据存成一个独立的字典,key为行号,value为选中状态,并将这些字典存入一个数组中。不论是去做一个购物软件,或是其他软件,有购物车或者类似于购物车的页面都是很常见的。
常见的购物车一般可分存本地和存服务器。存在哪里对于我们客户端的开发其实差别不大。
以存储在本地为例,我们可以使用数据库来做,如果数据结构并不复杂,也可以使用NSUserDefaults来存储一个数组,但切记不要传入空。
我们以这样子简单的界面效果为例:
每一行左边有一个按钮可以来选择,也可以进行全选操作。
我们可以吧每一行的按钮用一个自定义的UIButton来布局,可以给这个button添加一个属性用来记录他所在的行号。
为了简化操作,我们给全选按钮添加监听,来区分选中状态和默认状态,就像这样。
在viewdidload中加入这样的代码:
[objc] view plain copy
[_allChooseButton addObserver:self forKeyPath:@"buttonState" options:NSKeyValueObservingOptionNew context:@"_allChooseButtonStateChange"];
并实现它的回调方法:
[objc] view plain copy
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(voidvoid *)context {
if (context ==@"_allChooseButtonStateChange") {
if (_allChooseButton.buttonState == ButtonStateNormal) {
[_allChooseButton setBackgroundImage:[UIImage imageNamed:@"goods_n"] forState:UIControlStateNormal];
}else {
[_allChooseButton setBackgroundImage:[UIImage imageNamed:@"goods_s"] forState:UIControlStateNormal];
}
}
}
接下来,我们需要完成选中,未选中,全选,等状态的判断和存储。
我们可以准备一个全局的可变数组并初始化,用来存储所有数据每一个的选中状态。
[objc] view plain copy
_allCellStateArray = [[NSMutableArray alloc]init];
并将我们的数据每一个对象的选中状态存入数组,或者说每一行的状态,我们让他默认不被选中:
[objc] view plain copy
for (int i =0; i < _resultValue.count; i ++) {
NSMutableDictionary *dic = [[NSMutableDictionary alloc]init];
[dic setValue:@"0" forKey:[NSString stringWithFormat:@"%ld",(long)i]];
[_allCellStateArray addObject:dic];
}
存入的字典的key为当前的行号,value为选中状态,我们用1表示被选中,0表示未被选中。
当我们点击全选按钮时,按钮的图片会发生变化,并且选中状态也会发生变化,先在cellforrow方法中加入代码:
[objc] view plain copy
cell.chooseButton.indexNumber = indexPath.row;
if ([[[_allCellStateArray objectAtIndex:indexPath.row] objectForKey:[NSString stringWithFormat:@"%ld",(long)indexPath.row]] intValue]==0) {
// 当value为0时,按钮的状态为不选中
cell.chooseButton.buttonState = ButtonStateNormal;
}else {
cell.chooseButton.buttonState = ButtonStateClicked;
}
if (cell.chooseButton.buttonState == ButtonStateNormal) {
// 当状态为不选中时
[cell.chooseButton setBackgroundImage:[UIImage imageNamed:@"goods_n"] forState:UIControlStateNormal];
}else {
// 当状态为选中时
[cell.chooseButton setBackgroundImage:[UIImage imageNamed:@"goods_s"] forState:UIControlStateNormal];
}
[cell.chooseButton addTarget:self action:@selector(cellChooseClick:) forControlEvents:UIControlEventTouchUpInside];
上面两段代码可以写到一个if语句中。
当我们点击按钮时,我们要改变按钮的状态,并且要将存放我们状态的数组做修改。
[objc] view plain copy
#pragma mark - 选择
- (void)cellChooseClick:(EMButton *)sender {
if (sender.buttonState == ButtonStateNormal) {
NSMutableDictionary *dic = [[NSMutableDictionary alloc]init];
[dic setValue:@"1" forKey:[NSString stringWithFormat:@"%ld",(long)sender.indexNumber]];
[_allCellStateArray replaceObjectAtIndex:sender.indexNumber withObject:dic];
BOOL isAll =YES;
for (int i =0; i < _allCellStateArray.count; i ++) {
int a = [[[_allCellStateArray objectAtIndex:i] objectForKey:[NSString stringWithFormat:@"%d",i]] intValue];
if (a ==0) {
isAll = 0;
}
}
if (isAll) {
_allChooseButton.buttonState = ButtonStateClicked;
}
}else {
_allChooseButton.buttonState = ButtonStateNormal;
NSMutableDictionary *dic = [[NSMutableDictionary alloc]init];
[dic setValue:@"0" forKey:[NSString stringWithFormat:@"%ld",(long)sender.indexNumber]];
[_allCellStateArray replaceObjectAtIndex:sender.indexNumber withObject:dic];
}
NSIndexPath *indexPath=[NSIndexPath indexPathForRow:sender.indexNumber inSection:0];
[self.mainTable reloadRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath,nil] withRowAnimation:UITableViewRowAnimationMiddle];
}
中间的bool判断是用来实现:当用户在未全选状态下,选择了所有行,全选按钮变为全选状态;当用户点击了全选按钮进入全选状态,后又将其中一行取消选择,则全选状态取消。如图:
至此,所有的选择取消逻辑就完成了,不管是点击了提交按钮来获取选择的对象,还是在下方显示商品数量和价格。我们都可以通过保存每行数据状态的数组来在我们的数据源中取对象。
[objc] view plain copy
#pragma mark - 提交按钮
- (void)submitMethod:(EMButton *)sender {
NSMutableArray *array = [[NSMutableArray alloc]init];
for (int i =0; i < _allCellStateArray.count; i ++) {
if ([[[_allCellStateArray objectAtIndex:i] objectForKey:[NSString stringWithFormat:@"%d",i]] intValue]==1) {
[array addObject:[_resultValue objectAtIndex:i]];
}
}
NSLog(@"submit:%@ %ld",array,(long)array.count);
}
用来改变下方的商品数量的方法:
[objc] view plain copy
- (void)changeInfoLabel {
NSMutableArray *array = [[NSMutableArray alloc]init];
for (int i =0; i < _allCellStateArray.count; i ++) {
if ([[[_allCellStateArray objectAtIndex:i] objectForKey:[NSString stringWithFormat:@"%d",i]] intValue]==1) {
[array addObject:[_resultValue objectAtIndex:i]];
}
}
_countNumber = (int)array.count;
_money = 0;
for (int i =0;i < array.count; i ++) {
_money += [[[array objectAtIndex:i] objectForKey:@"buyNum"] intValue];
}
_tabInfoLabel.text = [NSString stringWithFormat:@"共%d件商品,共%d元",_countNumber,_money];
NSMutableAttributedString *str = [[NSMutableAttributedString alloc] initWithString:_tabInfoLabel.text];
UIColor * color1 = [UIColor colorWithRed:214.0/255.0 green:74.0/255.0 blue:115.0/255.0 alpha:1.0] ;
[str addAttribute:NSForegroundColorAttributeName value:color1 range:NSMakeRange(1,[NSString stringWithFormat:@"%d",_countNumber].length)];
[str addAttribute:NSForegroundColorAttributeName value:color1 range:NSMakeRange(1+[NSString stringWithFormat:@"%d",_countNumber].length+5,[NSString stringWithFormat:@"%d",_money].length)];
[str addAttribute:NSFontAttributeName value:[UIFont fontWithName:@"Helvetica" size:16.0] range:NSMakeRange(1,2)];
_tabInfoLabel.attributedText = str;
}
我们可以在全选按钮的方法,单选按钮的方法中调用此方法,这样在我们每次点击的时候,都能时刻改变下方的数量价格信息。
我们的购物车基本上就写好啦。
如果想加入加减某个商品的数量,或者删除某个商品的功能,如:
那我们就需要多加一些判断了,比如加减按钮,并不会影响到我们存放每行选中状态的数组,这个改变的是数据源,我们需要实时的改变我们原来在本地存储的数据源(也可以在退出购物车页面的时候统一存储,但如果用户在此页面时退出程序,便无法存储新修改的数据)。
删除功能需要我们修改数据文件的同时,也不要忘记同时需要修改状态数组。这样子,我们的购物车又多了两个新功能。
如果你的购物车数据是从服务器下载的,道理和存本地一样,只是我们不能时时修改数据文件,这里我们最好还是在离开此页面时同意修改,否则会很费流量。
最后,总结一下最核心的部分:将数据源的每一条数据存成一个独立的字典,key为行号,value为选中状态,并将这些字典存入一个数组中。
关于socket:
①服务端创建一个ServerSocket对象,指定端口号,ServerSocket对象等待客户端的连接请求。
②客户端创建一个Socket对象,指定主机地址和端口号,向服务端发出连接请求。
③服务端接收到客户端的连接请求,建立一条TCP连接,再创建一个Socket对象与客户端的Socket对象进行通信。
④服务端和客户端分别创建字节输入流和字节输出流,通过字节输入流获得对方发来的数据,通过字节输出流向对方发送数据。
⑤当一方决定结束通信时,向对方发送结束信息;另一方接收到结束信息后,双方分别关闭各自的TCP连接。
⑥ServerSocket对象停止等待客户端的连接请求。
1. 使用长链接减少请求,这个有框架来这着忘记叫什么了。
2.使用Hybird混合开发这个有个框架WebJavascriptBridge可以用还有Reactnative。
3.Hotfix JSPatch 和Android的 Andfix(或dexposed)
4.后台业务主系统与移动App之间建立一个网关系统。这个比较困难,需要重新建立一个后端研发组开发网关系统接收与相应App请求,同时调用业务系统处理请求,网关的意义在于1.快速响应由于后端变动导致的数据异常造成App Crash。2.优化与App之间的网络请求。3.建立App使用情况信息收集
尽量使图片大小和UIImageView大小相同,缩放图片很耗性能
由于 CADisplayLink 绑定的方法会在每次屏幕刷新时被调用,精确度相当之高。正是基于这个特点,CADisplayLink非常适合 UI 的重绘
逆向开发:主要目的是在不能轻易获得必要的生产信息的情况下,直接从成品分析,推导出产品的设计原理。作用:开发周期短,成本低。可以研究和学习其他app的技术
关于父类子类继承关系
一.继承:
子类可以直接复用父类中的成员.子类继承父类所有方法的声明和实现非私有的实例变量以及协议 继承时要在.h中声明一下继承具有单根性和传递性
继承的根类:大多都继承自 NSObject 类,所以在定义一个类时,要继承NSObject 类。
继承就是代码优化公共部分交给父类
例如:
(Person是父类Worker和Worker都继承了Person成为子类)(注:oc中的所有类都继承与NSObject)
#import "Person.h"
@interface Worker : Person
@end
#import "Person.h"
@interface Worker : Person
@end
二.继承的特点:
使用继承可以实现代码的复用,减少代码冗余;
Objective-C中一个类可以继承另一个类;
被继承的类称为父类或超类(基类);
继承的类称为子类或派生类;(孩子类);
子类可以直接拥有父类中所有允许子类继承的属性和方法;
(继承关系是可以传递的)子类除了可以调用父类的方法,也可以调用父类的父类的方法,也就是说继承可以确保某个父类型之下的所有类都会有父类型所持有的全部方法;
子类可以有自己的成员变、量属性和方法;
(单一继承性)Objective-C不支持多重继承。
三.继承的作用:
继承的标识为英文的“:”冒号(),使用继承可以定义一个具有父类所有功能(成员和方法)的新类,它继承了父类的功能。
四.继承中的方法重写:
编写子类时,某些方法(从父类继承过来)需要自己的代码实现;
子类允许重写父类的方法,在调用子类对象的重写方法时,就会调用重写后的方法;
父类中被子类所重写的方法则在调用子类方法时被忽略;
五.super关键字:
如果在子类中 需要调用父类的功能,可以借助super关键字;
子类中使用super发送消息时,实际上是告诉子类调用父类的方法;
如果父类没有定义该方法,则继续在继承链上查找,直到找到为止;
如果查到NSobject为止仍然未找到,则报错;
重写方法时经常会使用super关键字调用父类的方法。
六.为何要使用继承?
将所有重复的内容合并在一起,可以使代码有效率,简洁,才意味着是一个成功的架构。否则,修改代码时需要修改多处,就很容易出错。
七.继承使用注意:
父类要写在子类前面
OC中不允许子类中拥有和父类中相同名称的成员变量
OC中子类可以重写父类方法,当调用子类时优先调用子类方法
继承造成类与类之间耦合性太强,若父类不见,则子类不能使用
父类类型的变量不能用来调用子类的方法
八.继承中方法调用的流程:
首先到子类去找,如果有该方法,就调用子类方法,如果没有,就再到父类去找,如果父类还没有,再到父类的父类去找,如果最后还没有找到,程序会崩溃。
联网检测,除了AFNetworking包含的AFNetworkReachAbility类,还有第三方的Reachability,可以用pod集成,具体可以上网搜索一下