面试题总结(附答案1)

~~~~~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"];

    }

}

+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {

    BOOL automatic = NO;

    if ([theKey isEqualToString:@"balance"]) {

        automatic = NO;

    }

    else {

        automatic = [super automaticallyNotifiesObserversForKey:theKey];

    }

    return automatic;

}

~~~~~~~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(@"更新打卡记录失败");
        }
        
    }];


}

猜你喜欢

转载自blog.csdn.net/heyachaodeios/article/details/83958298