~~~~~1、UI布局 约束相关
1.1 layoutSubviews在以下情况下会被调用:
1、init初始化不会触发layoutSubviews
但是是用initWithFrame 进行初始化时,当rect的值不为CGRectZero时,也会触发
2、addSubview会触发layoutSubviews
3、设置view的Frame会触发layoutSubviews,当然前提是frame的值设置前后发生了变化
4、滚动一个UIScrollView会触发layoutSubviews
5、旋转Screen会触发父UIView上的layoutSubviews事件
6、改变一个UIView大小的时候也会触发父UIView上的layoutSubviews事件
layoutSubviews
继承于UIView
的子类重写,进行布局更新,刷新视图。如果某个视图自身的bounds
或者子视图的bounds
发生改变,那么这个方法会在当前runloop
结束的时候被调用。为什么不是立即调用呢?因为渲染毕竟比较消耗性能,特别是视图层级复杂的时候。这种机制下任何UI控件布局上的变动不会立即生效,而是每次间隔一个周期,所有UI控件在布局上的变动统一生效并且在视图上更新,苹果通过这种高性能的机制保障了视图渲染的流畅性。
setNeedsLayout
标记为需要重新布局,异步调用layoutIfNeeded
刷新布局,不立即刷新,在下一轮runloop结束前刷新,对于这一轮runloop
之内的所有布局和UI上的更新只会刷新一次,layoutSubviews
一定会被调用。layoutIfNeeded
如果有需要刷新的标记,立即调用layoutSubviews
进行布局(如果没有标记,不会调用layoutSubviews
)。
关键点
layoutIfNeeded
不一定会调用layoutSubviews方法。setNeedsLayout
一定会调用layoutSubviews
方法(有延迟,在下一轮runloop结束前)。- 如果想在当前
runloop
中立即刷新,调用顺序应该是
[self setNeedsLayout];
[self layoutIfNeeded];
1、setNeedsUpdateConstraints
当一个自定义view的某个属性发生改变,并且可能影响到constraint时,需要调用此方法去标记constraints需要在未来的某个点更新,系统然后调用updateConstraints.
2、needsUpdateConstraints
constraint-based layout system使用此返回值去决定是否需要调用updateConstraints作为正常布局过程的一部分。
3、updateConstraintsIfNeeded
立即触发约束更新,自动更新布局。
4、updateConstraints
自定义view应该重写此方法在其中建立constraints. 注意:要在实现在最后调用[super updateConstraints]
1.2 masonry 基本使用
https://www.jianshu.com/p/a24dd8638d28
1.3 cell 自适应大小
http://www.cocoachina.com/ios/20171212/21504.html
核心: 在cell 中 给lable添加约束,在 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 中 返回cell高度,高度cell 中计算:获取子控件的fram ,根据子控件fram 计算cell高度
CGRect frame = cell.titleLabel.frame;
return
frame.origin.y + frame.size.height +
10
;
弃用:
self.tableView.estimatedRowHeight = 666;
self.tableView.rowHeight = UITableViewAutomaticDimension;
但是这个方法实际上在有多个子视图的cell
上滑动是很卡顿的,特别是在iOS8尤其是iOS10上卡顿尤为明显,这跟系统的算高机制有一定关系,具体可以看上面的文章(http://blog.sunnyxx.com/2015/05/17/cell-height-calculation/),这里不再解释了。
1.4 sizeToFit 和 sizeThatFits的使用区别
https://www.jianshu.com/p/c9ce5e195a07
~~~~~~~~~~2 内存管理 (待解决)
1.1自己生成的对象,自己所持有
非自己生成的对象,自己也能持有
不再需要自己持有的对象时释放
非自己持有的对象无法释放
~~~~~~~~~~~~~3 runtime
1 动态创建类、并添加属性和方法
2 已经存在的类,能否添加属性、方法;如何修改属性和方法
1.1 iOS_Runtime修改变量值,交换方法实现,动态添加类,成员变量和方法
https://blog.csdn.net/qq_27325349/article/details/52043120
class_addMethod的详解
https://blog.csdn.net/lvmaker/article/details/32396167
1.2 能否想编译后的类中添加实例变量,能否像运行时创建的类添加实例变量?
https://blog.csdn.net/hou_manager/article/details/79376656
1.3 resolveInstanceMethod和resolveClassMethod
https://www.jianshu.com/p/b2c6313fda98
使用methodSignatureForSelector与forwardInvocation实现消息转发(一起使用)
forwardingTargetForSelector
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
NSLog(@"崩溃 0");
// if (sel == @selector(eat)) {
//
// // 动态添加eat方法
//
//
//
// // 第一个参数:给哪个类添加方法
//
// // 第二个参数:添加方法的方法编号
//
// // 第三个参数:添加方法的函数实现(函数地址)
//
// // 第四个参数:函数的类型,(返回值+参数类型) v:void @:对象->self :表示SEL->_cmd
//
// class_addMethod(self, @selector(eat), (IMP)eat, "v@:");
// return yes;
// }
// return [super resolveInstanceMethod:sel];
}
void eat(id self,SEL sel)
{
NSLog(@"%@ %@",self,NSStringFromSelector(sel));
}
- (id)forwardingTargetForSelector:(SEL)aSelector
{
NSLog(@"崩溃 1");
// if(aSelector == @selector(eat)){
// return vc;
// }
return [super forwardingTargetForSelector:aSelector];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
NSLog(@"崩溃 2");
return nil; //不能返回nil
NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];
if ( !signature ) {
signature = [vc methodSignatureForSelector:aSelector];
}
return signature;
}
-
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
-
if(aSelector == @selector(testMethod))
-
{
-
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
-
}
-
return nil;
-
}
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
NSLog(@"崩溃 3");
// if ( [vc respondsToSelector:[anInvocation selector]] ) {
// [anInvocation invokeWithTarget:vc];
// } else {
// [super forwardInvocation:anInvocation];
// }
}
NSSetUncaughtExceptionHandler (&UncaughtExceptionHandler);
#pragma mark - 捕捉崩溃日志
void UncaughtExceptionHandler(NSException *exception) {
NSString *reason = [exception reason];
NSString *name = [exception name];
NSString *report = [NSString stringWithFormat:@"crash name: %@ \ncrash reason: %@", name, reason];
NSLog(@"%@~~%@",report,exception);
}
1.4 runtime 获取类的实例方法和类方法
https://www.jianshu.com/p/b30c2580d977
1.5 isa
isa:是一个Class 类型的指针. 每个实例对象有个isa的指针,他指向对象的类,而Class里也有个isa的指针, 指向meteClass(元类)。元类保存了类方法的列表。当类方法被调用时,先会从本身查找类方法的实现,如果没有,元类会向他父类查找该方法。同时注意的是:元类(meteClass)也是类,它也是对象。元类也有isa指针,它的isa指针最终指向的是一个根元类(root meteClass).根元类的isa指针指向本身,这样形成了一个封闭的内循环。
super_class:父类,如果该类已经是最顶层的根类,那么它为NULL。
isa 指针指向
实例对象的isa指针指向该实例对象的类,类指向该类元类,元类有点特殊,统一指向根元类,根元类指向自己。
1.6 runtime之类方法和实例方法
https://www.jianshu.com/p/187ff251f344
~~~~~~~~~~~~~4 内存分配
http://www.cocoachina.com/ios/20161009/17700.html
~~~~~~~~~~~~~5 算法
指针指向该实例对象的类
5.1 冒泡排序
oc:
NSMutableArray *arr_M = [NSMutableArray arrayWithObjects:@1,@4,@2,@3,@5,nil];
for (int i=0; i<arr_M.count; i++) {
for (int j=i+1; j<arr_M.count; j++) {
if (arr_M[i]<arr_M[j]) {
[arr_M exchangeObjectAtIndex:i withObjectAtIndex:j];
} } }
swift:
func bubbleSort(inout array: [Int]) {
if array.count > 1 {
for var i = 0; i < array.count; i++ {
for var j = array.count - 1; j > i; j-- {
if array[j] < array[j-1] {
swap(&array, index1: j, index2: j - 1)
}
}
}
}
}
5.2 NSPredicate
~~~~~~~~ 6 响应者链条
https://www.jianshu.com/p/77a1b6e5194d
~~~~~~~~~7 字符串 copy 与strong 区别
比如以下代码:
NSMutableString *string = [NSMutableString stringWithString:@"origin"];//copy
NSString *stringCopy = [string copy];
[string appendString:@"origion!"]
查看内存,会发现 string、stringCopy 内存地址都不一样,说明此时都是做内容拷贝、深拷贝。即使你进行如下操作:
[string appendString:@"origion!"]
stringCopy的值也不会因此改变,但是如果不使用copy,stringCopy的值就会被改变。 集合类对象以此类推。
~~~~~~~~8 线程与队列
https://www.jianshu.com/p/a28c5bbd5b4a(详细)
串行队列:任务按顺序执行
并行队列:任务不按顺序
同步:在当前线程执行,不会开辟新的线程(按照代码顺序,当前线程运行到此,当前线程便要执行~~~先执行block)
异步:可能会开辟新的线程(看实际需要,后执行block)
当前为主线程:(比如viewdiload:)
dispatch_queue_t queue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
NSLog(@"2~~~%@",[NSThread currentThread]);
}//可行(自己猜想,相当于在主线程中直接打印)
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"3~~~%@",[NSThread currentThread]);
}//不可行
dispatch_queue_t serialQueue = dispatch_queue_create("com.lai.www", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(serialQueue, ^{
NSLog(@"1~~~%@~~~%@",[NSThread currentThread],[NSDate date]);
dispatch_sync(dispatch_get_main_queue(), ^{//阻塞的是子线程,当前线程是子线程,block内为主线程执行
NSLog(@"2~~~%@~~~%@",[NSThread currentThread],[NSDate date]);
});//等 主队列中的其他任务处理完,再用主线程处理这个任务
NSLog(@"3~~~%@~~~%@",[NSThread currentThread],[NSDate date]);
});//可行 同步阻塞的是当前线程
打印结果:
1~~~<NSThread: 0x604000271a40>{number = 3, name = (null)}~~~Wed Nov 14 19:00:21 2018
2018-11-14 19:00:21.129314+0800 kkkkk[46715:1938117] viewWillAppear
2018-11-14 19:00:21.137304+0800 kkkkk[46715:1938117] viewDidAppear
2018-11-14 19:00:21.138455+0800 kkkkk[46715:1938117] 2~~~<NSThread: 0x604000075bc0>{number = 1, name = main}~~~Wed Nov 14 19:00:21 2018
2018-11-14 19:00:21.139365+0800 kkkkk[46715:1938188] 3~~~<NSThread: 0x604000271a40>{number = 3, name = (null)}~~~Wed Nov 14 19:00:21 2018
在子线程,调用同步主队列,可行
在主线程,调用同步主队列,不可行()
~~~~~当前队列只负责自己队列中的事物,不管其他队列中的事物~~~~
[NSThread sleepForTimeInterval:2 ];//当前线程休眠
+ (void)sleepUntilDate:(NSDate *)date;//休眠到指定时间
dispatch_set_target_queue 改变当前队列为目标队列
//优先级变更的串行队列,初始是默认优先级
dispatch_queue_t serialQueue = dispatch_queue_create("com.gcd.setTargetQueue.serialQueue", NULL);
//优先级不变的串行队列(参照),初始是默认优先级
dispatch_queue_t serialDefaultQueue = dispatch_queue_create("com.gcd.setTargetQueue.serialDefaultQueue", NULL);
//变更前
dispatch_async(serialQueue, ^{ NSLog(@"1"); });
dispatch_async(serialDefaultQueue, ^{ NSLog(@"2"); });
//获取优先级为后台优先级的全局队列 dispatch_queue_t globalDefaultQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
//变更优先级 dispatch_set_target_queue(serialQueue, globalDefaultQueue);
//变更后 dispatch_async(serialQueue, ^{ NSLog(@"1"); });
dispatch_async(serialDefaultQueue, ^{ NSLog(@"2"); });
dispatch_after
https://www.jianshu.com/p/cfcc0c302621
我们看到他就是在主线程,就是刚好延迟了2秒,当然,我说这个2秒并不是绝对的,为什么这么说?还记得我之前在介绍dispatch_async
这个特性的时候提到的吗?他的block中方法的执行会放在主线程runloop之后,所以,如果此时runloop周期较长的时候,可能会有一些时差产生
dispatch_group
dispatch_barrier_async
dispatch_apply
dispatch_suspend & dispatch_resume
dispatch_semaphore(信号量)的理解及使用
https://www.cnblogs.com/yajunLi/p/6274282.html
https://www.cnblogs.com/chims-liu-touch/p/5798708.html
dispatch_once和dispatch_apply
https://www.cnblogs.com/XYQ-208910/p/4859761.html
NSOperation
https://www.jianshu.com/p/4b1d77054b35
~~~~~~~~9网络相关
iOS之HTTP和HTTPS的基本知识和应用
https://www.cnblogs.com/xiaopin/p/6428941.html
get 与post
建议:提交用户的隐私数据一定要使用POST请求
相对POST请求而言,GET请求的所有参数都直接暴露在URL中,请求的URL一般会记录在服务器的访问日志中,而服务器的访问日志是黑客攻击的重点对象之一
用户的隐私数据如登录密码,银行账号等。
~~~~~~~10 kvc 与kvo
- (void)setNilValueForKey:(NSString *)key {
if ([key isEqualToString:@"name"]) {
[self setValue:@"" forKey:@”age”];
} else {
[super setNilValueForKey:key];
}
}
- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
if ([key isEqualToString:@"id"]) {
self.userId = [value integerValue];
}
}
kvo
http://www.cocoachina.com/ios/20180319/22651.html
https://www.jianshu.com/p/bf053a28accb(背)
手动调用kvo
- (
void
)setBalance:(double)theBalance {
if
(theBalance != _balance) {
[self willChangeValueForKey:@
"balance"
];
_balance = theBalance;
[self didChangeValueForKey:@
"balance"
];
}
}
|
~~~~~~~11 数据库
-(instancetype)init{
if (self = [super init]) {
NSString * docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString * dbPathLocal = [docPath stringByAppendingPathComponent:@"xingyunSanBox.db"];
NSError *error;
NSString *filenameAgo = [[NSBundle mainBundle] pathForResource:@"xingyun" ofType:@"db" ];
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager copyItemAtPath:filenameAgo toPath:dbPathLocal error:&error];
FMDatabaseQueue * localqueue = [FMDatabaseQueue databaseQueueWithPath:dbPathLocal];
self.localqueue = localqueue;
// if (!localqueue){
// NSLog(@"ERR: 创建数据库失败");
//
// }else{
// NSLog(@"ERR: 创建数据库成功");
//
// }
// QBLog(@"数据库地址:%@",dbPathLocal);
// __unsafe_unretained typeof(self) weakSelf = self;
// [localqueue inDatabase:^(FMDatabase *db) {
//
// [weakSelf createNewTableWithDB:db];
//
// }];
}
return self;
}
-(void)createNewTableWithDB:(FMDatabase *)db {
BOOL success1 = [db executeUpdate:@"CREATE TABLE IF NOT EXISTS t_City(xycode TEXT,nation_code TEXT,name TEXT,short_name TEXT,short_spell TEXT,english_name TEXT,first_letter TEXT,air_code TEXT,train_code TEXT,post_code TEXT,taxi BOOL,long_bus BOOL,tags TEXT,enabled BOOL)"];
if (!success1) QBLog(@"ERR: 创建表 t_City 失败");
BOOL success2 = [db executeUpdate:@"CREATE TABLE IF NOT EXISTS t_Location(id TEXT,catalog TEXT,code TEXT,name TEXT,xycode TEXT,lng TEXT,lat TEXT,enabled BOOL)"];
if (!success2) QBLog(@"ERR: 创建表 t_Location 失败");
}
查询
[self.localqueue inDatabase:^(FMDatabase *db) {
NSString * sqlStr = [NSString stringWithFormat:@"SELECT * FROM hotel_geo WHERE city_name LIKE '%%%@%%'",cityName];
FMResultSet * resultSet = [db executeQuery:sqlStr];
NSMutableArray * resultMutArr = [[NSMutableArray alloc]init];
while ([resultSet next]) {
NSMutableDictionary * dataDicM = [[NSMutableDictionary alloc]init];
NSString *channel_id = [resultSet stringForColumn:@"channel_id"];
NSString *Country = [resultSet stringForColumn:@"Country"];
NSString *province_id = [resultSet stringForColumn:@"province_id"];
NSString *province_name = [resultSet stringForColumn:@"province_name"];
NSString *city_name = [resultSet stringForColumn:@"city_name"];
NSString *city_code = [resultSet stringForColumn:@"city_code"];
[dataDicM setObject:[QBTools noNullString:channel_id] forKey:@"channel_id"];
[dataDicM setObject:[QBTools noNullString:Country] forKey:@"Country"];
[dataDicM setObject:[QBTools noNullString:province_id] forKey:@"province_id"];
[dataDicM setObject:[QBTools noNullString:province_name] forKey:@"province_name"];
[dataDicM setObject:[QBTools noNullString:city_name] forKey:@"city_name"];
[dataDicM setObject:[QBTools noNullString:city_code] forKey:@"city_code"];
[resultMutArr addObject:dataDicM];
}
if (resultMutArr.count !=0) {
result(YES,resultMutArr);
}else{
result(NO,nil);
}
}];
}
增
-(void)saveCarTicketCityList:(NSDictionary*)dataDic{
[self.localqueue inDatabase:^(FMDatabase *db) {
BOOL success = [db executeUpdate:@"INSERT INTO t_CarTicketCityList(cityName,cityCode) VALUES (?,?)",dataDic[@"cityName"],dataDic[@"cityCode"]];
if (success) {
}
}];
}
删
-(void)deleteChatUserInfo:(NSString*)chatUserName{
[self.queue inDatabase:^(FMDatabase *db) {
BOOL flag = [db executeUpdate:@"DELETE FROM t_ChatUserInfoTwo WHERE userName =?",chatUserName];
if (flag) {
NSLog(@"删除旧的聊天用户信息成功");
}else{
NSLog(@"删除旧的聊天用户信息失败");
}
}];
}
改
-(void)updateEmployeSignData:(SignDataList*)model{
[self.queue inDatabase:^(FMDatabase *db) {
BOOL flag = [db executeUpdate:@"UPDATE t_SignDataList SET clockBeginTime =?,clockEndTime =? WHERE userPK =? and classNum = ?;",model.clockBeginTime,model.clockEndTime,model.userPK,model.classNum];
if (flag) {
NSLog(@"更新打卡记录成功");
}else{
NSLog(@"更新打卡记录失败");
}
}];
}