美文网首页
iOS——总结

iOS——总结

作者: coder1003 | 来源:发表于2018-10-23 22:51 被阅读0次

    1、为什么说Objective-C是一门动态的语言?
    答:什么叫动态静态->静态、动态是相对的,这里动态语言指的是不需要在编译时确定所有的东西,在运行时还可以动态的添加变量、方法和类;Objective-C 可以通过Runtime 这个运行时机制,在运行时动态的添加变量、方法、类等,所以说Objective-C 是一门动态的语言。

    其他说法
    Objective-C 是C 的超集,在C 语言的基础上添加了面向对象特性,并且利用Runtime 这个运行时机制,为Objective-C 增添了动态的特性。
    Objective-C 使用的是 “消息结构” 并非 “函数调用”:使用消息结构的的语言,其运行时所应执行的代码由运行期决定;而使用函数调用的语言,则由编译器决定;
    (1)动态类型语言:动态类型语言是指在运行期间才去做数据类型检查的语言,也就是说,在用动态类型的语言编程时,永远也不用给任何变量指定数据类型,该语言会在你第一次赋值给变量时,在内部将数据类型记录下来。Python和Ruby就是一种典型的动态类型语言,其他的各种脚本语言如VBScript也多少属于动态类型语言。
    (2)静态类型语言:静态类型语言与动态类型语言刚好相反,它的数据类型是在编译其间检查的,也就是说在写程序时要声明所有变量的数据类型,C/C++是静态类型语言的典型代表,其他的静态类型语言还有C#、JAVA等。
    因为在运行期可以继续向类中添加方法,所以编译器在编译时还无法确定类中是否有某个方法的实现。对于类无法处理一个消息就会触发消息转发机制

    消息转发分为两大阶段:
    “动态方法解析”:先征询接收者,所属的类,能否动态添加方法,来处理这个消息,若可以则结束,如不能则继续往下走
    “完整的消息转发机制”:
    请接收者看看有没其他对象能处理这条消息,若有,就把这个消息转发给那个对象然后结束
    运行时系统会把与消息有关细节全部封装到NSInvocation 对象中,再给对象最后一次机会,令其设法解决当前还未处理的这条消息

    Objective-c的动态性
    Objective-C的动态性,让程序在运行时判断其该有的行为,而不是像c等静态语言在编译构建时就确定下来。它的动态性主要体现在3个方面:
    1.动态类型:如id类型。实际上静态类型因为其固定性和可预知性而使用的特别广泛。静态类型是强类型,动态类型是弱类型,运行时决定接收者。
    2.动态绑定:让代码在运行时判断需要调用什么方法,而不是在编译时。与其他面向对象语言一样,方法调用和代码并没有在编译时连接在一起,而是在消息发送时才进行连接。运行时决定调用哪个方法。
    3.动态载入。让程序在运行时添加代码模块以及其他资源。用户可以根据需要执行一些可执行代码和资源,而不是在启动时就加载所有组件。可执行代码中可以含有和程序运行时整合的新类。

    //------------------------
    为什么说 Objective-C 是一种动态语言?这看起来似乎是一个简单而不简约而又老生常谈的问题,然而真正弄懂 Objective-C 的动态特性,三言两语还真是说不清楚。
    Objective-C 是 C 的超集,在 C 语言的基础上添加了面向对象的特性,并且用 Runtime 这个运行时机制,为 Objective-C 增添了动态的特性。这篇文章不以博大精深的 Runtime 作为重点,而以开发过程中的一些具体实例来诠释 Objective-C 的动态特性。下面分别以 Objective-C 多态的三个特性动态类型、动态绑定和动态加载来加以说明。
    *动态类型
    即运行时再决定对象的类型。举个程序中的实例,即 id 类型,任何对象都可以被 id 指针所指,只有在运行时才能决定是什么类型。而静态类型在编译时就能确定是什么类型,如 int , NSString 等,若程序发生了类型不对应的问题,编译器就会发出警告。而动态类型在编译器编译的时候是不能被识别的,要等到运行时(run time)根据语境来识别确定。

    总结为一点:动态类型和静态类型在时序的确定上是不同的,要分清运行时和编译时。
    *动态绑定
    在 Objective-C 中,一个对象能否调用指定的方法不是由编译器决定而是由运行时决定,这被称作是方法的动态绑定。基于动态类型,在某个实例对象被确定后,其类型便被确定了。该对象对应的属性和响应的消息也被完全确定,比如我们一般向一个 NSObject 对象发送 -respondsToSelector: 消息来确定对象是否可以对某个 SEL 作出响应,而在 OC 消息转发机制被触发之前,对应的类的 +resolveClassMethod: 将会被调用,在此时有机会动态地向类或实例中添加新的方法,也就是说,类的实现是可以动态绑定的。

    *动态加载
    让程序在运行时添加代码模块和资源,程序员可以根据需要执行一些可执行代码和资源,而不是在启东市就加载所有组件。举个非常通俗易懂的例子,开发的时候,需要为某种 icon 提供多个不同大小的图片,@2x,@3x,以保证设备更换的时候,图片也会自动地更换,这也体现了其动态加载的特性

    2、讲一下MVC和MVVM,MVP?

    MVVM分别指什么

    Model-数据层
    ViewController/View-展示层
    ViewModel- 数据模型

    MVVM与MVC的不同

    首先我们简化一下MVC的架构模式图:

    image

    在这里,Controller需要做太多得事情,表示逻辑、业务逻辑,所以代码量非常的大。而MVVM:

    [图片上传失败...(image-379863-1540304692145)]

    比如我们有一个需求:一个页面,需要判断用户是否手动设置了用户名。如果设置了,正常显示用户名;如果没有设置,则显示“简书0122”这种格式。(虽然这些本应是服务器端判断的)
    我们看看MVC和MVVM两种架构都是怎么实现这个需求的

    MVC:

    Model类:

    #import <Foundation/Foundation.h>
    @interface User : NSObject
    @property (nonatomic, copy) NSString *userName;
    @property (nonatomic, assign) NSInteger userId;
    @end
    

    ViewController类:

    #import "HomeViewController.h"
    #import "User.h"
    @interface HomeViewController ()
    @property (nonatomic, strong) UILabel *lb_userName;
    @property (nonatomic, strong) User *user;
    @end
    
    @implementation HomeViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        if (_user.userName.length > 0) {
            _lb_userName.text = _user.userName;
        } else {
            _lb_userName.text = [NSString stringWithFormat:@"简书%ld", _user.userId];
        }
    }
    

    这里我们需要将表示逻辑也放在ViewController中。

    MVVM:

    Model类:

    #import <Foundation/Foundation.h>
    @interface User : NSObject
    @property (nonatomic, copy) NSString *userName;
    @property (nonatomic, assign) NSInteger userId;
    @end
    

    ViewModel类:
    声明:

    #import <Foundation/Foundation.h>
    #import "User.h"
    @interface UserViewModel : NSObject
    @property (nonatomic, strong) User *user;
    @property (nonatomic, copy) NSString *userName;
    
    - (instancetype)initWithUser:(User *)user;
    @end
    

    实现:

    #import "UserViewModel.h"
    
    @implementation UserViewModel
    
    - (instancetype)initWithUser:(User *)user {
        self = [super init];
        if (!self) return nil;
            _user = user;
        if (user.userName.length > 0) {
            _userName = user.userName;
        } else {
            _userName = [NSString stringWithFormat:@"简书%ld", _user.userId];
        }
            return self;
    }
    @end
    

    Controller类:

    #import "HomeViewController.h"
    #import "UserViewModel.h"
    
    @interface HomeViewController ()
    
    @property (nonatomic, strong) UILabel *lb_userName;
    @property (nonatomic, strong) UserViewModel *userViewModel;
    
    @end
    
    @implementation HomeViewController
    - (void)viewDidLoad {
        [super viewDidLoad];
            _lb_userName.text = _userViewModel.userName;
    }
    

    可见,Controller中我们不需要再做多余的判断,那些表示逻辑我们已经移植到了ViewModel中,ViewController明显轻量了很多。

    总结:

    • MVVM同MVC一样,目的都是分离Model与View,但是它更好的将表示逻辑分离出来,减轻了Controller的负担;
    • ViewController中不要引入Model,引入了就难免会在Controller中对Model做处理;

    疑问:把处理逻辑放到model里面写可以吗?
    其实感觉你这样做,在该场景下达到了更好的效果。在model层里面进行了业务逻辑的处理。这样,同样减轻了massive viewcontroller。
    但是这样有几个坏处,第一,破坏了model的原始性,model是原始数据,本不应该直接操作。第二,诸如其它需要在ViewModel里处理的逻辑,比如数据请求,数据缓存等不适合在Model里面处理的业务,就又得放到ViewController里面进行处理了。
    ViewModel是新增加的一个层,有了这个概念,就可以想法来减轻ViewController了。


    MVP

    全称:Model-View-Presenter ;MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方 Controller/Presenter负责逻辑的处理,Model提供数据,View负责显示。

    3、为什么代理要用weak?代理的delegate和dataSource有什么区别?block和代理的区别?
    1>.为什么代理要用weak?
       防止循环引用。例如View有一个协议,需要一个代理实现回调。一个Controller添加这个View,并且遵守协议,成为View的代理。如果不用week,用strong,Controller ->View -> delegate -> Controller,就循环引用了。
    2>.代理的delegate和dataSource有什么区别?
       delegate偏重于与用户交互的回调,有那些方法可以供我使用,例如UITableviewDelegate;dataSource偏重于数据的回调,view里面有什么东西,属性都是什么,例如UITableviewDatasource;
    3>.block和代理的区别?
    block和代理是iOS开发中实现回调的两种方式,本文主要是对两者的应用场景做一下对比。

    1.block简介

    在 iOS中, block一共分三种。
      (1)全局静态 block,不会访问任何外部变量,执行完就销毁。

     ^{
            NSLog(@"Hello World!");
        }();
    

    (2)保存在栈中的 block,当函数返回时会被销毁,和第一种的区别就是调用了外部变量。

        [UIView animateWithDuration:3 animations:^{
            self.view.backgroundColor = [UIColor redColor];
        }];
    

    (3)保存在堆中的 block,当引用计数为 0 时会被销毁。例如按钮的点击事件,一直存在,即使执行过,也不销毁,因为按钮还可能被点击。直到持有按钮的View被销毁,它才会被销毁。

    #import <UIKit/UIKit.h>
    
    typedef void(^ButtonClickBlcok)();
    
    @interface TestView : UIView
    
    @property (nonatomic, copy) ButtonClickBlcok buttonClickBlcok;
    
    @end
    
    #import "TestView.h"
    
    @implementation TestView
    
    - (IBAction)buttonClick:(id)sender {
        if (self.buttonClickBlcok) {
            self.buttonClickBlcok();
        }
    }
    @end
    

    2.block优点

    block的代码可读性更好。因为block只要实现就可以了,而代理需要遵守协议并且实现协议里的方法,而两者还不在一个地方。代理使用起来也更麻烦,因为要声明协议、声明代理属性、遵守协议、实现协议里的方法。block不需要声明,也不需要遵守,只需要声明属性和实现就可以了。
      block是一种轻量级的回调,可以直接访问上下文,由于block的代码是内联的,运行效率更高。block就是一个对象,实现了匿名函数的功能。所以我们可以把block当做一个成员变量、属性、参数使用,使用起来非常灵活。像用AFNetworking请求数据和GCD实现多线程,都使用了block回调。

    3.block缺点

    blcok的运行成本高。block出栈需要将使用的数据从栈内存拷贝到堆内存,当然对象的话就是引用计数加1,使用完或者block置nil后才销毁。delegate只是保存了一个对象指针(一定要用week修饰delegate,不然也会循环引用),直接回调,没有额外消耗。就像C的函数指针,只多做了一个查表动作。
      block容易造成循环引用,而且不易察觉。因为为了blcok不被系统回收,所以我们都用copy关键字修饰,实行强引用。block对捕获的变量也都是强引用,所以就会造成循环引用。

    #import "ViewController.h"
    
    typedef void(^TestBlock)(void);
    
    @interface ViewController ()
    {
        void (^_testCycleBlock)(void);
    }
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
      
        [super viewDidLoad];
        
        __weak ViewController *weakSelf = self;
        _testCycleBlock = ^{
    
            /**
              //引发循环引用
              NSLog(@"%@", self);
             */
            //防止循环引用
            NSLog(@"%@", weakSelf);
        };
    }
    @end
    
    

    4.如何使用
      优先使用block。
      如果回调的状态很多,多于三个使用代理。
      如果回调的很频繁,次数很多,像UITableview,每次初始化、滑动、点击都会回调,使用代理。
      block和代理都各有优缺点,所以我们一定要理解区分使用场景,应用适合的回调方式。优化APP的性能,提高流畅性,从点滴做起.

    4、属性的实质是什么?包括哪几个部分?属性默认的关键字都有哪些?@dynamic关键字和@synthesize关键字是用来做什么的?

    1>.属性的实质是什么?包括哪几个部分

    @property = ivar + getter + setter;
      利用class_copyPropertyList 查看类的所有属性
      利用class_copyIvarList查看类的所有成员变量
      利用class_copyMethodList查看类的所有方法

    2>.属性默认的关键字都有哪些?

    iOS关于属性关键字,你又知道多少?

    3>.@dynamic关键字

    @dynamic告诉编译器,属性的setter与getter方法由用户自己实现。

    4>.@synthesize关键字

    @synthesize的语义是如果你没有手动实现setter方法和getter方法,那么编译器会自动为你加上这两个方法。
    链接:https://www.jianshu.com/p/0ce46e09dd1e

    9、为什么IBOutlet修饰的UIView也适用weak关键字?
    1.为什么代理要用weak?代理的delegate和dataSource有什么区别?block和代理的区别?

    A:为了避免循环引用。weak指明该对象并不负责保持delegate这个对象,delegate这个对象的销毁由外部控制。strong该对象强引用delegate,外界不能销毁delegate对象,会导致循环引用。DataSource是关于View的内容的东西包括属性,数据等等,而Delegate则是一些我们可以调用的方法,全是操作。block和代理都能解决对象间交互的问题,block更轻型,更简单,能够直接访问上下文,代码通常在同一个地方,这样读代码也连贯。缺点是容易引起循环引用。delegate更重一些,需要实现接口,它的方法分开来,很多时候需要存储一些临时数据,另外相关的代码需要分离到各处没有block好读,其优点就是它是用weak关键字修饰的,不会引起循环引用。

    2.属性的实质是什么?包括哪几个部分?属性默认的关键字都有哪些?@dynamic关键字和@synthesize关键字是用来做什么的?

    A:属性的本质是@property = ivar+getter+setter,也就是说@property系统会自动生成getter和setter方法。属性默认的关键字包括atomic,nonatomic,@synthesize,@dynamic,getter=getterName,setter=setterName,readwrite,readonly,assign,retain,copy。
    @dynamic:表示变量对应的属性访问器方法,是动态实现的,你需要在 NSObject 中继承而来的 +(BOOL) resolveInstanceMethod:(SEL) sel 方法中指定 动态实现的方法或者函数。
    @synthesize:如果没有实现setter和getter,编译器能够自动实现getter和setter方法。

    3.NSString为什么要用copy关键字,如果用strong会有什么问题?(注意:这里没有说用strong就一定不行。使用copy和strong是看情况而定的)

    A:针对于当把NSMutableString赋值给NSString的时候,才会有不同,用copy的话NSString的值不会发生变化,用strong则会发生变化,随着NSMutableString的值变化。如果是赋值是NSString对象,那么使用copy还是strong,结果都是一样的,因为NSString对象根本就不能改变自身的值,他是不可变的。

    4.如何令自己所写的对象具有拷贝功能?

    A:若想让自己写的对象具有拷贝功能,则需要实现NSCopying协议。如果自定义的对象分为可变版本和非可变版本,那么就要同时实现NSCopying和NSMutableCopying协议,不过一般没什么必要,实现NSCopying协议就够了。

    5.可变集合类 和 不可变集合类的 copy 和 mutablecopy有什么区别?如果是集合是内容复制的话,集合里面的元素也是内容复制么?

    A:对于不可变对象,copy操作是浅复制,mutableCopy是深复制。对于不可变对象,mutableCopy不仅仅是深复制,返回的对象类型还是不可变对象类型相应的可变对象的类型。内容复制也就是深拷贝,集合的深复制有两个方法,可以用initWithArray:copyItems:将第二个参数设置为YES即可进行深复制,如:NSDictionary shallowCopyDict = [NSDictionary alloc]initWithDictionary:someDictionary copyItems:YES];如果用这个方法深复制,集合里的每个元素都会收到copyWithZone:消息。如果集合里的对象遵循NSCopying协议,那么对象就会深复制到新的集合。如果对象没有遵循NSCopying协议,而尝试用这种方法进行深复制则会出错。copyWithZone:这种拷贝方式只能提供一层内存拷贝,而非真正的深拷贝。第二种方法是将集合进行归档解档,如:NSArray trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:oldArray]];

    6.为什么IBOutlet修饰的UIView也适用weak关键字?

    A:因为既然有外链那么视图在xib或者storyboard中肯定存在,视图已经对它有一个强引用了。

    1. nonatomic和atomic的区别?atomic是绝对的线程安全么?为什么?如果不是,那应该如何实现?

    A:nonatomic和atomic的区别在于两者自动生成getter和setter的方法不一样,如果你自己写getter和setter方法,那么(getter,setter,retain,copy,assign)只起提示作用,写不写都一样。
    对于atomic的属性,系统生成的getter和setter会保证get,set的操作完整性,不受其他线程影响。比如线程A的getter方法运行到一半,线程B调用了setter,那么线程A的getter还是能得到一个完整的对象。
    而nonatomic就没有这个保证了,所以速度要比atomic快。
    不过atomic可不能保证线程安全,如果线程A调用了getter,与此同时线程B和线程C都调了setter,那最后线程Aget到的值,三种都有可能:可能是B,C set之前原始的值,也可能是B set的值,也可能是C set的值。同时这个最终的值,也可能是B set的值,也可能是C set的值。要保证安全,可以使用线程锁。

    8.UICollectionView自定义layout如何实现?

    A:UICollectionViewLayoutAttributes,UICollectionViewFlowLayout。

    9.用StoryBoard开发界面有什么弊端?如何避免?

    A:难以维护,如果需要改动全局的一个字体,如果是代码的话就很好办,pch或头文件中改动就好了。如果是storyboard中就需要一个一个改动很麻烦。
    如果storyboard中scene太多,打开storyboard会比较慢。
    错误定位比较困难,好多错误提示模棱两可。

    10.进程和线程的区别?同步异步的区别?并行和并发的区别?

    A:进程是一个内存中运行的应用程序,比如在Windows系统中,一个运行的exe就是一个进程。
    线程是指进程中的一个执行流程。
    同步是顺序执行,执行完一个再执行下一个。需要等待,协调运行。
    异步就是彼此独立,在等待某事件的过程中继续做自己的事,不需要等待这些事件完成后再工作。
    并行和并发 是前者相当于三个人同时吃一个馒头,后者相当于一个人同时吃三个馒头。
    并发性(Concurrence):指两个或两个以上的事件或活动在同一时间间隔内发生。并发的实质是一个物理CPU(也可以多个物理CPU) 在若干道程序之间多路复用,并发性是对有限物理资源强制行使多用户共享以提高效率。
    并行性(parallelism)指两个或两个以上事件或活动在同一时刻发生。在多道程序环境下,并行性使多个程序同一时刻可在不同CPU上同时执行。
    区别:(并发)一个处理器同时处理多个任务和(并行)多个处理器或者是多核的处理器同时处理多个不同的任务。

    11.线程间通信?

    A:NSThread、GCD、NSOperation。

    12.GCD的一些常用的函数?(group,barrier,信号量,线程同步)

    A:1.延迟执行任务函数:dispatch_after(.....)。
    2.一次性执行dispatch_once(...)。
    3.栅栏函数dispatch_barrier_async/dispatch_barrier_sync。
    4.队列组的使用dispatch_group_t。
    5.GCD定时器。

    13.如何使用队列来避免资源抢夺?

    A:dispatch_barrior_async 作用是在并行队列中,等待前面两个操作并行操作完成。

    14.数据持久化的几个方案(fmdb用没用过)

    A:Coredata,realm,fmdb。

    15.说一下AppDelegate的几个方法?从后台到前台调用了哪些方法?第一次启动调用了哪些方法?从前台到后台调用了哪些方法?

    A:
    1.当程序第一次运行并且将要显示窗口的时候执行,在该方法中我们完成的操作

    • (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
      2.程序进入后台的时候首先执行程序将要取消活跃该方法

    • (void)applicationWillResignActive:(UIApplication *)application
      3.该方法当应用程序进入后台的时候调用

    • (void)applicationDidEnterBackground:(UIApplication *)application
      4.当程序进入将要前台的时候调用

    • (void)applicationWillEnterForeground:(UIApplication *)application
      5.应用程序已经变得活跃(应用程序的运行状态)

    • (void)applicationDidBecomeActive:(UIApplication *)application
      6.当程序将要退出的时候调用,如果应用程序支持后台运行,该方法被applicationDidEnterBackground:替换

    • (void)applicationWillTerminate:(UIApplication *)application
      16.NSCache优于NSDictionary的几点?

    A:NSCache 是一个容器类,类似于NSDIctionary,通过key-value 形式存储和查询值,用于临时存储对象。
    注意一点它和NSDictionary区别就是,NSCache 中的key不必实现copy,NSDictionary中的key必须实现copy。
    NSCache中存储的对象也不必实现NSCoding协议,因为毕竟是临时存储,类似于内存缓存,程序退出后就被释放了。

    17.知不知道Designated Initializer(指定初始化函数)?使用它的时候有什么需要注意的问题?

    A:比如:

    • (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil NS_DESIGNATED_INITIALIZER;
    • (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;
      18.实现description方法能取到什么效果?

    A:1.NSLog(@"%@", objectA);这会自动调用objectA的description方法来输出ObjectA的描述信息.
    2.description方法默认返回对象的描述信息(默认实现是返回类名和对象的内存地址)
    3.description方法是基类NSObject 所带的方法,因为其默认实现是返回类名和对象的内存地址, 这样的话,使用NSLog输出OC对象,意义就不是很大,因为我们并不关心对象的内存地址,比较关心的是对象内部的一些成变量的值。因此,会经常重写description方法,覆盖description方法的默认实现。

    19.objc使用什么机制管理对象内存?

    A:通过 retainCount 的机制来决定对象是否需要释放。 每次 runloop 的时候,都会检查对象的 retainCount,如果retainCount 为 0,说明该对象没有地方需要继续使用了,可以释放掉了。

    20.block的实质是什么?一共有几种block?都是什么情况下生成的?

    A:block对象就是一个结构体,里面有isa指针指向自己的类(global malloc stack),有desc结构体描述block的信息,forwarding指向自己或堆上自己的地址,如果block对象截获变量,这些变量也会出现在block结构体中。最重要的block结构体有一个函数指针,指向block代码块。block结构体的构造函数的参数,包括函数指针,描述block的结构体,自动截获的变量(全局变量不用截获),引用到的block变量。(block对象也会转变成结构体)
    block代码块在编译的时候会生成一个函数,函数第一个参数是前面说到的block对象结构体指针。执行block,相当于执行block里面forwarding里面的函数指针。

    相关文章

      网友评论

          本文标题:iOS——总结

          本文链接:https://www.haomeiwen.com/subject/badzzftx.html