美文网首页
2018年iOS面试题

2018年iOS面试题

作者: 龍飝 | 来源:发表于2018-09-11 11:30 被阅读65次

    1、设计模式是什么? 你知道哪些设计模式,并简要叙述?

    设计模式是一种编码经验,就是用比较成熟的逻辑去处理某一种类型的事情。
    1). MVC模式:Model View Control,把模型 视图 控制器 层进行解耦合编写。
    2). MVVM模式:Model View ViewModel 把模型 视图 业务逻辑 层进行解耦和编写。
    3). 单例模式:通过static关键词,声明全局变量。在整个进程运行期间只会被赋值一次。
    4). 观察者模式:KVO是典型的通知模式,观察某个属性的状态,状态发生变化时通知观察者。
    5). 委托模式:代理+协议的组合。实现1对1的反向传值操作。
    6). 工厂模式:通过一个类方法,批量的根据已有模板生产对象。

    2、MVC 和 MVVM 的区别

    1). MVVM是对胖模型进行的拆分,其本质是给控制器减负,将一些弱业务逻辑放到VM中去处理。
    2). MVC是一切设计的基础,所有新的设计模式都是基于MVC进行的改进。

    3、#import跟 #include 有什么区别,@class呢,#import<> 跟 #import””有什么区别?

    答:
    1). #import是Objective-C导入头文件的关键字,#include是C/C++导入头文件的关键字,
    使用#import头文件会自动只导入一次,不会重复导入。
    2). @class告诉编译器某个类的声明,当执行时,才去查看类的实现文件,可以解决头文件的相互包含。
    3). #import<>用来包含系统的头文件,#import””用来包含用户头文件。

    4、frame 和 bounds 有什么不同?

    frame指的是:该view在父view坐标系统中的位置和大小。(参照点是父view的坐标系统)
    bounds指的是:该view在本身坐标系统中的位置和大小。(参照点是本身坐标系统)

    5、Objective-C的类可以多重继承么?可以实现多个接口么?Category是什么?重写一个类的方式用继承好还是分类好?为什么?

    答:Objective-C的类不可以多重继承;可以实现多个接口(协议);
    Category是类别;一般情况用分类好,用Category去重写类的方法,仅对本Category有效,不会影响到其他类与原有类的关系。

    6、@property 的本质是什么?ivar、getter、setter 是如何生成并添加到这个类中的

    @property 的本质是什么?
    @property = ivar + getter + setter;
    “属性” (property)有两大概念:ivar(实例变量)、getter+setter(存取方法)
    “属性” (property)作为 Objective-C 的一项特性,主要的作用就在于封装对象中的数据。
    Objective-C 对象通常会把其所需要的数据保存为各种实例变量。
    实例变量一般通过“存取方法”(access method)来访问。
    其中,“获取方法” (getter)用于读取变量值,而“设置方法” (setter)用于写入变量值。

    7、@property中有哪些属性关键字?/ @property 后面可以有哪些修饰符?

    属性可以拥有的特质分为四类:
    1.原子性--- nonatomic 特质
    2.读/写权限---readwrite(读写)、readonly (只读)
    3.内存管理语义---assign、strong、 weak、unsafe_unretained、copy
    4.方法名---getter=<name> 、setter=<name>
    5.不常用的:nonnull,null_resettable,nullable

    8、属性关键字 readwrite,readonly,assign,retain,copy,nonatomic 各是什么作用,在那种情况下用?

    答:
    1). readwrite 是可读可写特性。需要生成getter方法和setter方法。
    2). readonly 是只读特性。只会生成getter方法,不会生成setter方法,不希望属性在类外改变。
    3). assign 是赋值特性。setter方法将传入参数赋值给实例变量;仅设置变量时,assign用于基本数据类型。
    4). retain(MRC)/strong(ARC) 表示持有特性。setter方法将传入参数先保留,再赋值,传入参数的retaincount会+1。
    5). copy 表示拷贝特性。setter方法将传入对象复制一份,需要完全一份新的变量时。
    6). nonatomic 非原子操作。决定编译器生成的setter和getter方法是否是原子操作,
    atomic表示多线程安全,一般使用nonatomic,效率高。

    9、什么情况使用 weak 关键字,相比 assign 有什么不同?

    1.在 ARC 中,在有可能出现循环引用的时候,往往要通过让其中一端使用 weak 来解决,比如: delegate 代理属性。
    2.自身已经对它进行一次强引用,没有必要再强引用一次,此时也会使用 weak,
    自定义 IBOutlet 控件属性一般也使用 weak;当然,也可以使用strong。

    IBOutlet连出来的视图属性为什么可以被设置成weak?
    因为父控件的subViews数组已经对它有一个强引用。

    不同点:
    assign 可以用非 OC 对象,而 weak 必须用于 OC 对象。
    weak 表明该属性定义了一种“非拥有关系”。在属性所指的对象销毁时,属性值会自动清空(nil)。

    10、怎么用 copy 关键字?

    用途:

    1. NSString、NSArray、NSDictionary 等等经常使用copy关键字,
      是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary;
    2. block 也经常使用 copy 关键字。

    说明:
    block 使用 copy 是从 MRC 遗留下来的“传统”,在 MRC 中,方法内部的 block 是在栈区的,
    使用 copy 可以把它放到堆区.在 ARC 中写不写都行:
    对于 block 使用 copy 还是 strong 效果是一样的,但写上 copy 也无伤大雅,还能时刻提醒我们:
    编译器自动对 block 进行了 copy 操作。如果不写 copy ,该类的调用者有可能会忘记或者根本不知道
    “编译器会自动对 block 进行了 copy 操作”,
    他们有可能会在调用之前自行拷贝属性值。这种操作多余而低效。

    11、用@property声明的 NSString / NSArray / NSDictionary 经常使用 copy 关键字,为什么?如果改用strong关键字,可能造成什么问题?

    答:用 @property 声明 NSString、NSArray、NSDictionary 经常使用 copy 关键字,
    是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary,
    他们之间可能进行赋值操作(就是把可变的赋值给不可变的),
    为确保对象中的字符串值不会无意间变动,应该在设置新属性值时拷贝一份。

    1. 因为父类指针可以指向子类对象,使用 copy 的目的是为了让本对象的属性不受外界影响,
      使用 copy 无论给我传入是一个可变对象还是不可对象,我本身持有的就是一个不可变的副本。
    2. 如果我们使用是 strong ,那么这个属性就有可能指向一个可变对象,如果这个可变对象在外部被修改了,那么会影响该属性。

    //总结:使用copy的目的是,防止把可变类型的对象赋值给不可变类型的对象时,
    // 可变类型对象的值发送变化会无意间篡改不可变类型对象原来的值。

    12、浅拷贝和深拷贝的区别?

    答:
    浅拷贝:只复制指向对象的指针,而不复制引用对象本身。
    深拷贝:复制引用对象本身。内存中存在了两份独立对象本身,当修改A时,A_copy不变。

    13、系统对象的 copy 与 mutableCopy 方法

    不管是集合类对象(NSArray、NSDictionary、NSSet ... 之类的对象),
    还是非集合类对象(NSString, NSNumber ... 之类的对象),接收到copy和mutableCopy消息时,都遵循以下准则:

    1. copy 返回的是不可变对象(immutableObject);如果用copy返回值调用mutable对象的方法就会crash。
    2. mutableCopy 返回的是可变对象(mutableObject)。

    一、非集合类对象的copy与mutableCopy
    在非集合类对象中,对不可变对象进行copy操作,是指针复制,mutableCopy操作是内容复制;
    对可变对象进行copy和mutableCopy都是内容复制。用代码简单表示如下:
    NSString *str = @"hello word!";
    NSString *strCopy = [str copy] // 指针复制,strCopy与str的地址一样
    NSMutableString *strMCopy = [str mutableCopy] // 内容复制,strMCopy与str的地址不一样

    NSMutableString *mutableStr = [NSMutableString stringWithString: @"hello word!"];
    NSString *strCopy = [mutableStr copy] // 内容复制
    NSMutableString *strMCopy = [mutableStr mutableCopy] // 内容复制
    

    二、集合类对象的copy与mutableCopy (同上)
    在集合类对象中,对不可变对象进行copy操作,是指针复制,mutableCopy操作是内容复制;
    对可变对象进行copy和mutableCopy都是内容复制。
    但是 集合对象的内容复制仅限于对象本身,对集合内的对象元素仍然是指针复制。(即单层内容复制)
    NSArray *arr = @[@[@"a", @"b"], @[@"c", @"d"];
    NSArray *copyArr = [arr copy]; // 指针复制
    NSMutableArray *mCopyArr = [arr mutableCopy]; //单层内容复制

    NSMutableArray *array = [NSMutableArray arrayWithObjects:[NSMutableString 
    stringWithString:@"a"],@"b",@"c",nil];
    NSArray *copyArr = [mutableArr copy]; // 单层内容复制
    NSMutableArray *mCopyArr = [mutableArr mutableCopy]; // 单层内容复制
    

    【总结一句话】:
    只有对不可变对象进行copy操作是指针复制(浅复制),其它情况都是内容复制(深复制)!

    14、这个写法会出什么问题:@property (nonatomic, copy) NSMutableArray *arr;

    问题:添加,删除,修改数组内的元素的时候,程序会因为找不到对应的方法而崩溃。
    //如:-[__NSArrayI removeObjectAtIndex:]: unrecognized selector sent to instance 0x7fcd1bc30460
    // copy后返回的是不可变对象(即 arr 是 NSArray 类型,NSArray 类型对象不能调用 NSMutableArray 类型对象的方法)
    原因:是因为 copy 就是复制一个不可变 NSArray 的对象,不能对 NSArray 对象进行添加/修改。

    15、如何让自己的类用 copy 修饰符?如何重写带 copy 关键字的 setter?

    若想令自己所写的对象具有拷贝功能,则需实现 NSCopying 协议。
    如果自定义的对象分为可变版本与不可变版本,那么就要同时实现 NSCopying 与 NSMutableCopying 协议。
    具体步骤:
    1. 需声明该类遵从 NSCopying 协议
    2. 实现 NSCopying 协议的方法。
    // 该协议只有一个方法:
    - (id)copyWithZone:(NSZone *)zone;
    // 注意:使用 copy 修饰符,调用的是copy方法,其实真正需要实现的是 “copyWithZone” 方法。

    16、写一个 setter 方法用于完成 @property (nonatomic, retain) NSString *name,写一个 setter 方法用于完成 @property (nonatomic, copy) NSString *name

    答:
    // retain

    • (void)setName:(NSString *)str {
      [str retain];
      [_name release];
      _name = str;
      }
      // copy
    • (void)setName:(NSString *)str {
      id t = [str copy];
      [_name release];
      _name = t;
      }

    17、@synthesize 和 @dynamic 分别有什么作用?

    @property有两个对应的词,一个是@synthesize(合成实例变量),一个是@dynamic。
    如果@synthesize和@dynamic都没有写,那么默认的就是 @synthesize var = _var;
    // 在类的实现代码里通过 @synthesize 语法可以来指定实例变量的名字。(@synthesize var = _newVar;)

    1. @synthesize 的语义是如果你没有手动实现setter方法和getter方法,那么编译器会自动为你加上这两个方法。
    2. @dynamic 告诉编译器,属性的setter与getter方法由用户自己实现,不自动生成(如,@dynamic var)。

    18、常见的 Objective-C 的数据类型有那些,和C的基本数据类型有什么区别?如:NSInteger和int

    答:
    Objective-C的数据类型有NSString,NSNumber,NSArray,NSMutableArray,NSData等等,
    这些都是class,创建后便是对象,而C语言的基本数据类型int,只是一定字节的内存空间,用于存放数值;NSInteger是基本数据类型
    并不是NSNumber的子类,当然也不是NSObject的子类。
    NSInteger是基本数据类型Int或者Long的别名(NSInteger的定义typedef long NSInteger),
    它的区别在于,NSInteger会根据系统是32位还是64位来决定是本身是int还是long。

    19、id 声明的对象有什么特性?

    答:id 声明的对象具有运行时的特性,即可以指向任意类型的Objcetive-C的对象。

    20、Objective-C 如何对内存管理的,说说你的看法和解决方法?

    答:Objective-C的内存管理主要有三种方式ARC(自动内存计数)、手动内存计数、内存池。
    1). 自动内存计数ARC:由Xcode自动在App编译阶段,在代码中添加内存管理代码。
    2). 手动内存计数MRC:遵循内存谁申请、谁释放;谁添加,谁释放的原则。
    3). 内存释放池Release Pool:把需要释放的内存统一放在一个池子中,当池子被抽干后(drain),
    池子中所有的内存空间也被自动释放掉。内存池的释放操作分为自动和手动。自动释放受runloop机制影响。

    21、Objective-C 中创建线程的方法是什么?如果在主线程中执行代码,方法是什么?如果想延时执行代码、方法又是什么?

    答:线程创建有三种方法:使用NSThread创建、使用GCD的dispatch、使用子类化的NSOperation,然后将其加入NSOperationQueue;
    在主线程执行代码,方法是performSelectorOnMainThread,
    如果想延时执行代码可以用performSelector:onThread:withObject:waitUntilDone:

    22、Category(类别)、 Extension(扩展)和继承的区别

    区别:

    1. 分类有名字,类扩展没有分类名字,是一种特殊的分类。
    2. 分类只能扩展方法(属性仅仅是声明,并没真正实现),类扩展可以扩展属性、成员变量和方法。
    3. 继承可以增加,修改或者删除方法,并且可以增加属性。

    23、我们说的OC是动态运行时语言是什么意思?

    答:主要是将数据类型的确定由编译时,推迟到了运行时。
    简单来说, 运行时机制使我们直到运行时才去决定一个对象的类别,以及调用该类别对象指定方法。

    24、为什么我们常见的delegate属性都用是week而不是retain/strong?

    答:是为了防止delegate两端产生不必要的循环引用。
    @property (nonatomic, weak) id<UITableViewDelegate> delegate;

    25、什么时候用delete,什么时候用Notification?

    Delegate(委托模式):1对1的反向消息通知功能。
    Notification(通知模式):只想要把消息发送出去,告知某些状态的变化。但是并不关心谁想要知道这个。

    1、写出使用GCD方式从子线程回到主线程的方法代码

    答:dispatch_sync(dispatch_get_main_queue(), ^{ });

    2、如何用GCD同步若干个异步调用?(如根据若干个url异步加载多张图片,然后在都下载完成后合成一张整图)

    // 使用Dispatch Group追加block到Global Group Queue,这些block如果全部执行完毕,
    // 就会执行Main Dispatch Queue中的结束处理的block。
    // 创建队列组
    dispatch_group_t group = dispatch_group_create();
    // 获取全局并发队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_async(group, queue, ^{ /*加载图片1 / });
    dispatch_group_async(group, queue, ^{ /
    加载图片2 / });
    dispatch_group_async(group, queue, ^{ /
    加载图片3 */ });
    // 当并发队列组中的任务执行完毕后才会执行这里的代码
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    // 合并图片
    });

    3、dispatch_barrier_async(栅栏函数)的作用是什么?

    函数定义:dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
    作用:
    1.在它前面的任务执行结束后它才执行,它后面的任务要等它执行完成后才会开始执行。
    2.避免数据竞争

    // 1.创建并发队列
    dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
    // 2.向队列中添加任务
    dispatch_async(queue, ^{ // 1.2是并行的
    NSLog(@"任务1, %@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
    NSLog(@"任务2, %@",[NSThread currentThread]);
    });

    dispatch_barrier_async(queue, ^{
    NSLog(@"任务 barrier, %@", [NSThread currentThread]);
    });

    dispatch_async(queue, ^{ // 这两个是同时执行的
    NSLog(@"任务3, %@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
    NSLog(@"任务4, %@",[NSThread currentThread]);
    });

    // 输出结果: 任务1 任务2 ——》 任务 barrier ——》任务3 任务4
    // 其中的任务1与任务2,任务3与任务4 由于是并行处理先后顺序不定。

    4、以下代码运行结果如何?

    • (void)viewDidLoad {
      [super viewDidLoad];
      NSLog(@"1");
      dispatch_sync(dispatch_get_main_queue(), ^{
      NSLog(@"2");
      });
      NSLog(@"3");
      }
      // 只输出:1。(主线程死锁)

    5、什么是 RunLoop

    从字面上讲就是运行循环,它内部就是do-while循环,在这个循环内部不断地处理各种任务。
    一个线程对应一个RunLoop,基本作用就是保持程序的持续运行,处理app中的各种事件。
    通过runloop,有事运行,没事就休息,可以节省cpu资源,提高程序性能。

    主线程的run loop默认是启动的。iOS的应用程序里面,程序启动后会有一个如下的main()函数
    int main(int argc, char * argv[]) {
    @autoreleasepool {
    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
    }

    6、什么是 Runtime

    Runtime又叫运行时,是一套底层的C语言API,其为iOS内部的核心之一,我们平时编写的OC代码,底层都是基于它来实现的。
    61、Runtime实现的机制是什么,怎么用,一般用于干嘛?

    1). 使用时需要导入的头文件 <objc/message.h> <objc/runtime.h>
    2). Runtime 运行时机制,它是一套C语言库。
    3). 实际上我们编写的所有OC代码,最终都是转成了runtime库的东西。
    比如:
    类转成了 Runtime 库里面的结构体等数据类型,
    方法转成了 Runtime 库里面的C语言函数,
    平时调方法都是转成了 objc_msgSend 函数(所以说OC有个消息发送机制)
    // OC是动态语言,每个方法在运行时会被动态转为消息发送,即:objc_msgSend(receiver, selector)。
    // [stu show]; 在objc动态编译时,会被转意为:objc_msgSend(stu, @selector(show));
    4). 因此,可以说 Runtime 是OC的底层实现,是OC的幕后执行者。
    有了Runtime库,能做什么事情呢?
    Runtime库里面包含了跟类、成员变量、方法相关的API。
    比如:
    (1)获取类里面的所有成员变量。
    (2)为类动态添加成员变量。
    (3)动态改变类的方法实现。
    (4)为类动态添加新的方法等。
    因此,有了Runtime,想怎么改就怎么改。

    7、什么是 Method Swizzle(黑魔法),什么情况下会使用?

    1). 在没有一个类的实现源码的情况下,想改变其中一个方法的实现,
    除了继承它重写、和借助类别重名方法暴力抢先之外,还有更加灵活的方法 Method Swizzle。
    2). Method Swizzle 指的是改变一个已存在的选择器对应的实现的过程。OC中方法的调用能够在运行时通过改变,
    通过改变类的调度表中选择器到最终函数间的映射关系。
    3). 在OC中调用一个方法,其实是向一个对象发送消息,查找消息的唯一依据是selector的名字。利用OC的动态特性,
    可以实现在运行时偷换selector对应的方法实现。
    4). 每个类都有一个方法列表,存放着selector的名字和方法实现的映射关系。IMP有点类似函数指针,指向具体的方法实现。
    5). 我们可以利用 method_exchangeImplementations 来交换2个方法中的IMP。
    6). 我们可以利用 class_replaceMethod 来修改类。
    7). 我们可以利用 method_setImplementation 来直接设置某个方法的IMP。
    8). 归根结底,都是偷换了selector的IMP。

    8、_objc_msgForward 函数是做什么的,直接调用它将会发生什么?

    答:_objc_msgForward是 IMP 类型,用于消息转发的:当向一个对象发送一条消息,
    但它并没有实现的时候,_objc_msgForward会尝试做消息转发。

    9、什么是 TCP / UDP ?

    TCP:传输控制协议。
    UDP:用户数据协议。

    TCP 是面向连接的,建立连接需要经历三次握手,是可靠的传输层协议。
    UDP 是面向无连接的,数据传输是不可靠的,它只管发,不管收不收得到。
    简单的说,TCP注重数据安全,而UDP数据传输快点,但安全性一般。

    10、通信底层原理(OSI七层模型)、

    OSI采用了分层的结构化技术,共分七层:
    物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。

    11、介绍一下XMPP?

    XMPP是一种以XML为基础的开放式实时通信协议。
    简单的说,XMPP就是一种协议,一种规定。就是说,在网络上传东西,XMM就是规定你上传大小的格式。

    12、OC中创建线程的方法是什么?如果在主线程中执行代码,方法是什么?

    // 创建线程的方法

    [NSThread detachNewThreadSelector:nil toTarget:nil withObject:nil]
    [self performSelectorInBackground:nil withObject:nil];
    [[NSThread alloc] initWithTarget:nil selector:nil object:nil];
    dispatch_async(dispatch_get_global_queue(0, 0), ^{});
    [[NSOperationQueue new] addOperation:nil];
    // 主线程中执行代码的方法

    [self performSelectorOnMainThread:nil withObject:nil waitUntilDone:YES];
    dispatch_async(dispatch_get_main_queue(), ^{});
    [[NSOperationQueue mainQueue] addOperation:nil];

    13、tableView的重用机制?

    答:UITableView 通过重用单元格来达到节省内存的目的: 通过为每个单元格指定一个重用标识符,即指定了单元格的种类,当屏幕上的单元格滑出屏幕时,系统会把这个单元格添加到重用队列中,等待被重用,当有新单元格从屏幕外滑入屏幕内时,从重用队列中找看有没有可以重用的单元格,如果有,就拿过来用,如果没有就创建一个来使用。

    14、用伪代码写一个线程安全的单例模式

    static id _instance;

    (id)allocWithZone:(struct _NSZone *)zone {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
    _instance = [super allocWithZone:zone];
    });
    return _instance;
    }

    (instancetype)sharedData {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
    _instance = [[self alloc] init];
    });
    return _instance;
    }

    (id)copyWithZone:(NSZone *)zone {
    return _instance;
    }

    15、如何实现视图的变形?

    答:通过修改view的 transform 属性即可。

    16、在手势对象基础类UIGestureRecognizer的常用子类手势类型中哪两个手势发生后,响应只会执行一次?

    答:UITapGestureRecognizer,UISwipeGestureRecognizer是一次性手势,手势发生后,响应只会执行一次。

    17、字符串常用方法:

    NSString str = @"abc123";
    NSArray arr = [str componentsSeparatedByString:@""];
    //以目标字符串把原字符串分割成两部分,存到数组中。@[@"abc", @"123"];

    18、如何高性能的给 UIImageView 加个圆角?

    不好的解决方案:使用下面的方式会强制Core Animation提前渲染屏幕的离屏绘制,
    而离屏绘制就会给性能带来负面影响,会有卡顿的现象出现。
    self.view.layer.cornerRadius = 5.0f;
    self.view.layer.masksToBounds = YES;

    正确的解决方案:使用绘图技术
    (UIImage *)circleImage {
    // NO代表透明
    UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0);
    // 获得上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    // 添加一个圆
    CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
    CGContextAddEllipseInRect(ctx, rect);
    // 裁剪
    CGContextClip(ctx);
    // 将图片画上去
    [self drawInRect:rect];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    // 关闭上下文
    UIGraphicsEndImageContext();
    return image;
    }
    还有一种方案:使用了贝塞尔曲线"切割"个这个图片, 给UIImageView 添加了的圆角,其实也是通过绘图技术来实现的。
    UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
    imageView.center = CGPointMake(200, 300);
    UIImage *anotherImage = [UIImage imageNamed:@"image"];
    UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, NO, 1.0);
    [[UIBezierPath bezierPathWithRoundedRect:imageView.bounds
    cornerRadius:50] addClip];
    [anotherImage drawInRect:imageView.bounds];
    imageView.image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    [self.view addSubview:imageView];

    19、你是怎么封装一个view的

    1). 可以通过纯代码或者xib的方式来封装子控件
    2). 建立一个跟view相关的模型,然后将模型数据传给view,通过模型上的数据给view的子控件赋值

    /**

    纯代码初始化控件时一定会走这个方法
    /
    (instancetype)initWithFrame:(CGRect)frame {
    if(self = [super initWithFrame:frame]) {
    [self setupUI];
    }
    return self;
    }
    /
    *

    通过xib初始化控件时一定会走这个方法
    */
    (id)initWithCoder:(NSCoder *)aDecoder {
    if(self = [super initWithCoder:aDecoder]) {
    [self setupUI];
    }
    return self;
    }

    (void)setupUI {
    // 初始化代码
    }

    20、HTTP协议中 POST 方法和 GET 方法有那些区别?

    GET用于向服务器请求数据,POST用于提交数据
    GET请求,请求参数拼接形式暴露在地址栏,而POST请求参数则放在请求体里面,因此GET请求不适合用于验证密码等操作
    GET请求的URL有长度限制,POST请求不会有长度限制

    21、请简单的介绍下APNS发送系统消息的机制

    APNS优势:杜绝了类似安卓那种为了接受通知不停在后台唤醒程序保持长连接的行为,由iOS系统和APNS进行长连接替代。
    APNS的原理:
    1). 应用在通知中心注册,由iOS系统向APNS请求返回设备令牌(device Token)
    2). 应用程序接收到设备令牌并发送给自己的后台服务器
    3). 服务器把要推送的内容和设备发送给APNS
    4). APNS根据设备令牌找到设备,再由iOS根据APPID把推送内容展示

    相关文章

      网友评论

          本文标题:2018年iOS面试题

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