2019-IOS面试题与答案

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

Objective-C不可以多重继承;可以实现多个接口;Category是分类;
重写一个类的方法最好是用分类,因为分类不会影响其他类的方法实现;方法的修改只在本类范围内有效

2、#import 跟#include 又什么区别,@class呢, #import<> 跟 #import“”又什么区别?

#import和#include都可以作为引入头文件的关键字;区别在与#import用于Objective-C引入头文件,只会被自动导入一次,不会导致文件的重复引用;#include用于c/c++引入头文件,会导致重复引用,可以使用#pragma once或者#ifndef解决重复引用;
@class用于告诉编译器类的声明,只有在执行的时候才会去检查类的实现;可以解决头文件的相互引用;#import导入的是工程中自定义类的头文件;#inport<>用于引入系统框架或者第三方框架的头文件;

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

readwrite读写属性,多用于需要生成getter和setter方法的时候;
readonly只读属性,只会生成getter方法,多用于不希望在外部改变属性;
assign赋值属性,setter方法将传入参数赋值给实例变量;仅设置变量时;
retain持有属性,setter方法将传入参数先保存一份,在赋值;传入参数的retainCount会加1;
copy赋值属性,setter方法将传入参数复制一份;多用于完全需要一份新的变量时;
nonatomic非原子属性,告诉编译器生成getter和setter采用非原子操作,是线程不安全;
atomic原子属性,是线程安全(仅对getter和setter)

4、对于语句NSString * obj = [[NSData alloc] init]; obj在编译时和运行时分别时什么类型的对象?

obj编译时是NSString对象,运行时是NSData对象

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

@property(nonatomic,retain) NSString* name
(void) setName:(NSString*) name {
    [name retain];
    [_name release];
    _name = name;
}

@property(nonatomic,copy) NSString* name
(void) setName:(NSString*) name {
    id obj =  [name copy];
    [_name release];
    _name = obj;
}

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

Objective-C的数据类型有:NSString、NSArray、NSDictionary、NSData、NSNumber、NSMutableString、NSMutableArray、NSMutableDictionary等;
C的基本数据类型有:int、float、double、long等;
区别在于Objective-C的数据类型都是class类型,创建后便是对象;而C的数据类型int只是一定大小的内存空间,用于存放数值;
NSInteger不属于NSNumber,当然也不属于NSObject;NSInteger是数据类型int和long的别名;区别在于NSInteger会根据系统是32位或者64位来确定本身是int或者long;

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

内存管理方法有:手动管理内存(MRC)、自动管理内存(ARC)、内存池;
MRC遵循的原则是:谁创建,谁释放;谁添加,谁释放;对象retain之后,必须在相应的地方release;只有当对象的retainCount为0的时候,对象内存才会被回收;对象调用alloc、init、retain、copy等方法,引用计数会+1;数组的添加方法也会导致引用计数+1;
ARC:编译器会在编译期间,给我们的代码中自动添加retain、release等操作,减少了程序员手动维护的烦恼和可能会遗漏的地方;
内存池:由autorelease添加到系统内存池中,这样的对象不会立即释放,只有当内存池被释放的时候,内存池中的对象才会被释放;

8、原子(atomic)跟非原子(non-atomic)属性有什么区别?

atomic是多线程安全的,在getter和setter过程中,会加锁来确保线程安全;
nonatomic是非线程安全的,相比atomic速度较快;

9、看看下面的代码,分别说出其引用计数是多少?

NSObject* obj1 = [[NSObject alloc] init];
int count = [obj1 retainCount];
NSLog(@"------%d",count);//对象创建,此时的引用计数为1
NSObject* obj2 = obj1;
[obj2 retain];
count = [obj2 retainCount];
NSLog(@"------%d",count);//此时的引用计数为2,因为对象的引用计数都是针对的内存来说,而obj2对象的内存实际指向为obj1的内存

NSArray* array = [[NSArray alloc] init];
NSString* str = [NSString stringWithFormat:@"test"];
[str retain];
[array addObject:str];
count = [str retainCount];
NSLog(@"------%d",count);//此时的引用计数为3,因为str对象创建+1,retain+1,加入数组+1
[str retain];
[str release];
[str release];
count = [str retainCount];
NSLog(@"------%d",count);//此时的引用计数为2,因为str对象retain+1,release-1
[array removeAllObjects];
count = [str retainCount];
NSLog(@"------%d",count);//此时的引用计数为1,因为数组移除,数组里面的所有对象引用计数-1

10、MVC是什么?Objective-C有那些设计模式?

MVC是一种经典的设计模式,整体分为model(模型层,用来存储和处理数据)、view(视图层,用来展示数据)、controller(控制层,用来进行业务逻辑处理);
Objective-C的设计模式有:代理模式、通知模式、工厂模式、单例模式、观察者模式;

11、浅复制和深复制的区别?

浅复制只是对象指针的复制,指向都还是同一块内存;
深复制是对象的完全拷贝,开辟一块新的内存存放对象;

12、类别的作用?继承和类别在实现中有何区别?类扩展又是什么?

类别是一种非正式协议,它只能添加新的方法;不能修改和删除原有方法;也不能添加属性(通过runtime可以);如果类别中的方法名称冲突,则会覆盖本类的方法,因为类别的优先级较高;
类别的作用:
a、将类的实现分散到多个不同的文件或者框架中
b、创建对私有方法的前向引用
c、为对象添加非正式协议
继承可以添加、修改和删除方法,也可以添加属性;
类扩展是一种私有的类别,可以添加属性,也可以添加方法,类扩展添加的方法必须要实现;它寄托于某个类本身,作用域仅限寄托类本身,外部类不可访问;

13、什么是KVO和KVC?KVO有什么优缺点?

KVC是一种键值编码机制,提供了一种间接访问对象属性而不是通过存取方法的机制,使用字符串来标识属性;
KVO是基于KVC的扩展,是一种观察者机制;通过对象属性的观察,间接通知观察者其变化,极大的简化代码复杂度;
>KVO的优点
提供了一种简单的方法实现两个对象的同步
能够对非我们创建的对象,即内部对象的状态改变做出响应,而不需要改变内部对象的实现
利用keyPath的机制,可以观察嵌套对象
>KVO的缺点
观察的属性必须使用NSString来定义,因此在编译器不会出现错误;不易于后期错误排查;
对属性的重构将导致观察者功能失效;
如果观察了多个对象,需要使用复杂的if判断语句来进行区分
当释放观察者时需要移除观察者

14、谈一谈KVC中setter和getter方法的实现原理?

KVC中常用的方法有:
valueForKey://通过key来获取值
setValue:forKey:// 通过key来赋值
valueForKeyPath://通过keyPath来获取值
setValue:forKeyPath://通过keyPath来赋值
accessInstanceVariablesDirectly//是否允许直接访问成员变量;如果返回YES,则set的时候会根据_key,_isKey,key,isKey来搜索成员变量;如果返回NO,则禁止使用KVC;
validateValue:forKey:error://提供属性值正确性的验证API,它可以对值做一个验证,如果不正确则可以进行替换、拒绝并且返回出错原因
valueForUndefinedKey://如果key不存在,且KVC无法搜索到与key相关的属性,则会调用此方法,系统默认是抛出异常
setValue:forUndefinedKey://与上述方法一致,该方法用在赋值的时候
setNilValueForKey://当调用setter方法,传入参数为nil的时候调用
>setter原理
a、首先寻找setKey,_setKye方法,没有则会搜索setIsKey,_setIskey方法;
b、如果上述方法都没有找到,则检查accessInstanceVariablesDirectly方法:
如果返回YES,则搜索_key,_isKey,key,isKey的属性;如果这些属性也没有找到的话,则会调用setValue:forUndefinedKey方法;
如果返回NO,则直接调用setValue:forUndefinedKey方法;
结论:setKey、_setKey、setIsKey、_setIsKey、_key、_isKey、key、isKey(搜索优先级从左到右);
>getter原理
a、首先寻找getKey、key、isKey方法;
b、如果上述方法没找到,则检查accessInstanceVariablesDirectly方法:
如果返回YES,则搜索_key,_isKey,key,isKey的属性;如果这些属性也没有找到的话,则会调用valueForUndefinedKey方法;
如果返回NO,则直接调用valueForUndefinedKey方法;
结论:getKey、key、isKey、_key、_isKey、key、isKey(搜索优先级从左到右);

15、谈谈APNS的推送原理

a、首先设备向APNS服务器申请注册推送服务,APNS服务器返回唯一标识token;
b、设备获取返回的token进行本地缓存,同时也将token传递到业务服务器进行保存;
c、业务服务器根据业务需要调用APNS服务器接口,传递token和消息内容;
d、APNS服务器根据token找到对应设备,同时下发消息内容和appId,设备根据appId找到具体的应用并进行消息展示;

16、为什么说Objective-C是动态运行时语言?

这个问题其实设计到多态和运行时两个问题:
运行时:其实就是将数据类型的确定,由编译器阶段推迟到了运行时阶段;只有在程序运行时,才会真正确定对象的类型,以及对象的方法调用;
多态:不同的对象采用不用的方法相应相同消息的机制;

17、如何创建一个单例?

在Objective-C中实现一个单例类,有以下几步:
a、创建一个静态变量,并初始化,设置为nil;
b、实现一个构造函数,在该函数中检测静态变量是否为空,如果为空则创建一个本类的实例并赋值给实例变量;改过程需要确保在整个APP运行过程中只会被执行一次,比如:dispath_once
c、重写allocWithZone方法,确保直接使用alloc、init方法创建实例的时候不会新建一个实例;
d、适当实现allocWithZone、copyWithZone、release和autoRelease;

18、frame和bounds的区别

frame:指view在父view中的坐标(参照的是父视图坐标体系)
bounds:指view在本身系统的坐标(参照的是本身坐标体系)

19、方法与选择器的区别

方法:包含了方法名称、传入参数和输出参数;
选择器:只是一个字符串表示的方法名称

20、说说NSArray和NSMutableArray的区别

NSArray:是一个不可变对象,是线程安全的;在运行时不能添加、删除元素,但不表示其元素的内容不能被改变;
NSMutableArray:是一个可变对象,是非线程安全的;在运行时可以动态添加、删除元素;

21、什么是简便构造方法

Foundation框架提供了很多简便构造方法,方便我们快速获取对象实例,而且不需要我们手动管理内存;比如:NSNumber的numberWithBool、numberWithInt等

22、UIView的动画有哪些

系统UIKitk框架中的UIView.h文件提供了UIView动画的2中调用方式,普通方式和block方式,我们一般采用block方式;
>普通方式:
a、开始动画
      [UIView beginAnimations:@"test" context:nil];
      参数一:动画标识
      参数二:附加参数,在设置代理的情况下;此参数将发送到setAnimationWillStartSelector和setAnimationDidStopSelector所指定的方法,大部分情况下,设置为nil;
b、设置动画参数
      [UIView setAnimationDuration:2.f];//设置动画时间
      [UIView setAnimationDelegate:self];//设置动画代理
      [UIView setAnimationWillStartSelector:@selector(xxx)];//设置动画开始时代理所执行的方法
      [UIView setAnimationDidStopSelector:@selector(xxx)];//设置动画结束时代理所执行的方法
      [UIView setAnimationDelay:1.f];//设置动画延迟时间
      [UIView setAnimationRepeatCount:4];//设置动画重复次数
      [UIView setAnimationStartDate:[NSDate date]];//设置动画开始时间,默认now
      /**
      UIViewAnimationCurve枚举值
      UIViewAnimationEaseInOut 慢进慢出(默认值)
      UIViewAnimationEaseIn 慢进
      UIViewAnimationEaseOut 慢出
      UIViewAnimationLinear 匀速
      **/
      [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];//设置动画曲线
      /**
      假设上一个动画正在播放,并未完成;此时我们需要播放一个新的动画:
      YES:动画将从上一个动画的所在状态开始播放;
      NO:动画将从上一个动画所指定的最终状态开始播放(上一个动画会立马结束)
      **/
      [UIView setAnimationBeginsFromCurrentState:YES];//设置动画是否从当前状态开始播放
      [UIView setAnimationRepeatAutoreverses:YES];//设置动画是否继续执行相反的动画
      [UIView setAnimationEnabled:YES];//是在是否使用动画,YES使用,NO禁止
      /**
      UIViewAnimationTransition 过渡效果,枚举类型
      UIViewAnimationTransitionNone 无动画(默认值)
      UIViewAnimationTransitionFlipFromLeft 从左往右旋转翻页
      UIViewAnimationTransitionFlipFromRight 从右往左旋转翻页
      UIViewAnimationTransitionCurlUp 从下往上卷曲翻页
      UIViewAnimationTransitionCurlDown 从上往下卷曲翻页
      **/
      [UIView setAnimationTransition:UIViewAnimationTransitionNone forView:nil cache:YES];//设置动画的过渡效果
c、结束动画方法
      [UIView commitAnimations];
>block方式
a、最简单的动画方式:包含时间和动画
      [UIView animateWithDuration:1.0f animations:^{
          // 动画执行代码
      }];
b、带有动画提交回调的block
      [UIView animateWithDuration:1.0f animations:^{
          // 动画执行代码
      } completion:^(BOOL isFinished){
          //动画执行完的代码
      }];
c、可以设置动画延时时间、过渡效果的动画
      /**
      UIViewAnimationOptions 过渡动画,枚举类型
      UIViewAnimationOptionLayoutSubviews 提交动画的时候布局子视图,表示子视图和父视图一起动画
      UIViewAnimationOptionAllowUserInteraction 允许动画期间用户交流,比如:触摸
      UIViewAnimationOptionBeginFromCurrentState 从当前状态开始动画
      UIViewAnimationOptionRepeat 动画无限重复
      UIViewAnimationOptionAutoreverse 执行动画回路,前提是设置了动画无限重复
      UIViewAnimationOptionOverrideInheritedDuration 忽略外层动画嵌套时间
      UIViewAnimationOptionOverrideInheritedCurve 忽略外层动画嵌套的变化曲线
      UIViewAnimationOptionAllowAnimatedContent 转场:进行动画时重绘视图
      UIViewAnimationOptionShowHideTransitionViews 转场:移除动画效果(添加和删除图层)
      UIViewAnimationOptionOverrideInheritedOptions 不继承父动画设置
      // 时间函数曲线相关
      UIViewAnimationOptionCurveEaseInOut 时间函数,慢进慢出(中间快)
      UIViewAnimationOptionCurveEaseIn  时间函数,慢进(快出)
      UIViewAnimationOptionCurveEaseOut 时间函数,慢出(快进)
      UIViewAnimationOptionCurveLinear 时间函数,匀速
      // 转场动画相关
      UIViewAnimationOptionTransitionNone            //转场,不使用动画
      UIViewAnimationOptionTransitionFlipFromLeft    //转场,从左向右旋转翻页
      UIViewAnimationOptionTransitionFlipFromRight  //转场,从右向左旋转翻页
      UIViewAnimationOptionTransitionCurlUp          //转场,下往上卷曲翻页
      UIViewAnimationOptionTransitionCurlDown        //转场,从上往下卷曲翻页
      UIViewAnimationOptionTransitionCrossDissolve  //转场,交叉消失和出现
      UIViewAnimationOptionTransitionFlipFromTop    //转场,从上向下旋转翻页
      UIViewAnimationOptionTransitionFlipFromBottom  //转场,从下向上旋转翻页
      **/
      [UIView animateWithDuration:1.0f 
      delay:1.0f
      options:UIViewAnimationOptionCurveLinear
      animations:^{
          // 动画执行代码
      } completion:^(BOOL isFinished){
          //动画执行完的代码
      }];

23、Objective-C中的数据存储方式

NSUserDefault
NSCoder归档
SQLLite、CoreData

24、写一个block的定义

typedef void (^TestBlock)(NSString* str);

25、谈谈你对block的理解,在使用的时候有哪些需要注意的地方?在MRC和ARC中block有什么区别?

block:俗称闭包,是一个代码块;可以理解为是一种匿名函数,提供了一直回传值的机制,可以在一定程度上简化代码结构;
block的类型,取决于block中捕获的内容:
全局block,没有引用外部变量或者引用了静态变量的block,不持有对象;
栈block,引用了外部变量(非静态)的block,不持有对象;
堆block,对栈block进行了copy操作(比如:手动copy,把block当做返回值自动copy,block被强引用自动copy),持有对象;
ARC和MRC中block的区别:
ARC中block使用=会进行block的copy操作;MRC中不会,所以同样的代码在ARC中内存存放在堆中,而在MRC中存放在栈中;
需要注意的地方:
如果要在block中改变外部变量的值,需要使用__block来修饰变量;
如果要在block中避免循环引用,需要使用__weak来修饰;
如果要在block中延时执行引用对象,需要在block中使用__strong来修改引用对象
block在创建的时候,他的内存是存放在栈上的;它的作用域仅限创建时候的作用域;如果要在其他地方(作用域之外)使用,则需要手动对其进行copy或者retain,否则会导致程序崩溃。比如:
typedef void(^testBlock)(int number);

-(void) viewDidLoad {
    testBlock = ^(int number){
      NSlog(@"---%d",number);
    };
    [testBlock copy];  // 如果不加,会导致在viewDidLoad方法之外的其他地方调用时程序崩溃;
}

-(void)testMethod{
  testBlock(5);
}

26、+sendSynchronousRequest:returningResponse:error:与– initWithRequest:delegate:两个方法的区别?

sendSynchronousRequest:returningResponse:error:方法是同步网络请求的方法,该方法会阻塞当前线程,直到获取request返回的response;
initWithRequest:delegate:方法是异步网络请求方法,该网络请求完成后,会通过delegate回到主线程;

27、Objective-C有私有方法吗?有私有变量吗?

Objective-C没有私有方法,只有静态方法(+)和实例方法(-);
Objective-C有私有变量,@private可以修饰私有变量;
Objective-C所有的实例变量都是私有的,所有的实例方法都是共有的;

28、C和Objective-C如何混用?

.m后缀的文件,可以使用Objective-C和C语言;
.mm后缀的文件,可以使用Objective-C、C和C++语言;
.cpp后缀的文件,只能使用C和C++语言,而且include的头文件中也不用是有Objective-C的语言;

29、Objective-C中堆与栈的区别?

栈:是向低地址扩展的数据结构,是一块连续的内存区域;栈的空间大小是有限的,如果申请的内存大于栈的剩余空间,则会导致overflow
堆:是向高地址扩展的数据结构,是不连续的内存区域;这是因为系统使用链表来存储空闲的内存区域,所以是不连续的;堆的大小是受限于系统中有效的虚拟内存,所以相对来讲,堆可用的内存较大,灵活性也大一些;
管理方式:对于存放在栈中的内存,由编译器自动管理,无需手动管理;对于存放在堆中的内存,需要开发者手动管理,管理不当容易产生内存泄露;
碎片问题:对于堆来讲,连续的new/delete会导致大量的内存空间不连续,从而导致大量的碎片,降低系统的使用效率;栈则不会有此问题,因为栈是先进后出的队列,他们是一一对应的;
分配方式:堆只有动态分配;栈有动态分配与静态分配,静态分配是由编译器完成,比如:局部变量;动态分配是由alloc函数进行分配,栈的动态分配是由编译器进行释放;
分配效率:栈是由系统提供的数据结构,它的进栈和出栈都有专门的指令,所以效率较高;堆是有c/c++函数库提供的数据结构,相对较为复杂;

30、用预处理指令#define声明一个常数,用以表明1年中有多少秒?

#define SECOND_FOR_YEAR (60*60*24*365)UL
结论:#define不能以分号结束,使用括号等

31、写一个标准的宏定义,输入两个参数并返回较小的哪一个

#define MIN(A,B) ((A)>(B)?(B):(A))
结论:宏定义中需要对参数和整个宏都使用括号;

32、关键字const有什么含意?修饰类呢?static的作用,用于类呢?还有extern c的作用?volatile又有什么作用?

const:只读的意思;合理的使用const来修饰,可以有效的保护不被修改的参数,防止被无意的修改代码;
对于指针来说,const可以修饰指针所指向的数据,也可以同事修饰指针和被指向的数据;
对于一个函数的声明,const可以修饰形参,表明它是一个输入参数,不希望在函数内部被改变;
对于类的成员函数,如果被const修饰,表明其是一个常函数;在函数内部不能修改类的成员变量;
对于类的成员函数,有时候必须指定其返回值为const类型,避免其返回值为“左值”;
static修饰局部变量:
a、局部变量只会初始化一次;
b、局部变量只会有一份内存;
c、局部变量的作用域不变,但生命周期改变了(程序销毁时才会示释放内存)
static修饰全局变量:
a、全局变量的作用域仅限当前文件,外部类无法使用改变量;
extern外部常量的最佳方法
extern const 关键字,只是表面变量已经声明,只能引用,不可改变;
volatile表示不稳定的,说明变量有可能被改变;编译器就不会随便假设变量的值,编译器在读取变量的时候会小心的读取,而不是从缓存中读取;

32、进程与线程的区别和联系

进程与线程都是系统程序运行的基本单元,系统利用该基本单元实现应用的并发性;
进程有独立的地址空间,一个进程崩溃了,不会影响其他进程;线程只是进程中的一个执行路径;
线程有自己的堆栈和局部变量,没有独立的地址空间,所以一个线程崩溃了会导致整个进程奔溃;所以多进程的应用比多线程的应用健壮,但在切换进程的时候,消耗的资源比较多,效率较差;
对于一些要求同时进行并共享变量的并发操作,只能使用多线程,不能使用多进程;

33、简单说一说进程的同步机制有哪些?进程的死锁的原因?进程的通信方式?

同步机制:原子操作、信号量机制、自旋锁、管程、会合、分布式系统;
死锁原因:资源竞争、进程推进顺序非法;
通信方式:以文件系统为基础

34、谈谈死锁的必要条件以及死锁的解决方式

死锁的四个必要条件:互斥条件、请求与保持条件、不可剥夺条件、循环等待条件;
死锁的解决方式:预防死锁、避免死锁、检测与解除死锁

35、自动释放池是什么?如何工作?

自动释放池是由系统创建和维护的内存池;当我们使用autorelease的时候,系统自动将对象的引用添加到内存池中,并不会立即释放;所以任然和向对象发送消息;当程序运行到结束位置时,内存池会被释放,存放在内存池中的所有对象也会被释放;

36、Objective-C的优缺点

>优点
多态
运行时
分类
动态识别
指针计算
可与C++混编
弹性消息传递
>缺点
不支持命名空间
不支持运算符重载
不支持多重继承

37、sprintf,strcpy,memcpy使用上有什么要注意的地方

sprintf:字符串格式化函数,将一段数据通过特定的格式,格式化到字符串缓冲区;由于字符串长度不可控,可能会导致格式化后的字符串超出缓冲区的大小,造成溢出;
strcpy:字符串拷贝函数,由于字符串的长度不可控,容易导致拷贝函数出错;
memcpy:内存拷贝函数,这个函数长度可控,但会造成内存叠加的问题;

38、http和socket的区别

http:客户端采用http协议发送请求,需要封装请求头,并绑定请求体;客户端发送一次请求,服务端返回数据后,请求立即中断,一次请求完成。服务器不能主动向客户端发送消息(除非采用http长连接,通过在请求中设置connection:keep-alive)
socket:客户端与服务器主要是通过socket(套接字)进行连接,是一种长连接方式,客户端和服务器都可以主动发送消息;

39、TCP与UDP的区别

TCP:一种传输控制协议,它提供面向连接,可靠的数据传输协议;相对较安全
UDP:它不是面向连接的,不可控的数据传输协议,特点是速度快,但安全性一般
>数据传输协议主要功能:
a、确保IP数据包的成功传递;
b、对程序发送的大块数据进行分段和重组;
c、确保程序正确排序和按顺序传递分段的数据;
d、通过计算校验和,验证传输数据的完整性;

40、简单说说沙盒机制

沙盒机制是苹果提供的一种对应用程序数据的保护机制,每个应用都有一个独立的沙盒,不允许跨沙盒读写数据;
沙盒主要包含:
a、Documents目录:应用永久性数据一般存放在此目录
b、app目录:这是应用包目录,由于应用程序需要签名,所以在运行过程中不可修改此目录,可能会导致应用无法启动
c、tmp目录:临时文件存放目录,应用程序重新启动会清空
d、Library目录:包含Caches和Preferences两个目录,preferences目录主要存放应用程序的偏好设置,一般NSUserDefault保存的数据存放在此目录;Caches主要用来存放需要缓冲的数据,应用程序重新启动不会清空;
获取沙盒路径的方法:
// 获取沙盒主目录路径
NSHomeDirectory() 
// 获取Documents路径
NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
[NSString stringWithFormat:@"%@/Documents",NSHomeDirectory];
// 获取Libraray路径
NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,NSUserDomainMask,YES);
[NSString stringWithFormat:@"%@/Library",NSHomeDirectory];
// 获取Caches路径
NSSearchPathForDirectoriesInDomains(NSCachesDirectory,NSUserDomainMask,YES);
[NSString stringWithFormat:@"%@/Library/Caches",NSHomeDirectory];
// 获取tmp路径
NSTemporaryDirectory();
[NSString stringWithFormat:@"%@/tmp",NSHomeDirectory];
常见的私有API:
>直接发送短信
>访问沙盒以外的磁盘文件
>根据包名判断APP是否存在

私有API详见
41、如果在项目中使用了私有API,如何绕过审核?

苹果审核禁止使用私有API,一般是通过一些关键字来进行搜索;可以先将类名、方法名、框架路径等内存进行加密处理,当使用私有API的时候,对其进行解密后在使用(仅供参考);

42、在一个对象中,self.name=@"test"和_name=@"test"有啥区别?

self.name会调用对象的setName()方法;
_name是直接将结果赋值给_name属性;

43、谈谈你对内存的理解

>内存分类(RAM和ROM)
RAM:运行内存,不能掉电存储;
ROM:存储内存,可以掉电存储;如:内存卡
RAM:运行速度远高于ROM,价格较高,CPU只能从RAM读取指令
ROM:app一般存储在ROM中,APP启动时,系统会将APP拷贝到RAM中
>内存分区
从高到低依次为:栈区、堆区、数据区(全局/静态变量和常量)和代码区;后面2个区域由程序运行加载

44、简述队列和栈的区别

队列:是一种先进先出的数据结构,它可以在两端进行操作,一端进入,一端出去;
栈:是一种先进后出的数据结构,它只可以在栈顶操作,进栈与出栈都在栈顶;

45、IOS的系统架构

IOS系统架构主要分为:核心系统层,核心服务层,媒体层和界面服务层;

46、控件主要响应的3种事件

基于触摸的事件、基于值的事件、基于编辑的事件;

47、动画有几种方式

>显式动画
显式动画是不存在的,如需显示需要创建;比如由beginsAnimation:context和commitAnimations创建
>隐式动画
隐式动画是一直存在的,如需关闭需要设置;比如由[UIView animateWithDuration:animations:]创建

48、实现简单表格UITableView的显示需要设置什么属性,实现什么协议?

需要设置其delegate和dataSource的属性;需要实现UITableViewDelegate和UITableViewDataSource协议

49、Cocoa Touch提供了哪几种Core Animation过渡类型?

4种:交叉淡化、挤推、覆盖和显示

50、简述UIView与CALayer的联系和区别

UIView是IOS系统中所有界面的基础单元,所有的界面元素都继承UIView;UIView是由Core Animation完成绘制的,其中有一个属性layer主要负责内容的绘制;UIView可以看做是CALayer的管理器,属性layer还可以设置子layer,即CALayer是可以嵌套的;
UIView主要负责内容的显示,CALayer主要负责内容的绘制;

51、Quatrz 2D的绘图功能的三个核心概念是什么并简述其作用

上下文:主要描述图层显示在哪里
路径:主要是图层显示的内容
状态:用于保存配置变化的值,填充、轮廓和alpha等:

52、Objective-C有几种手势通知的方法?

touchesBegan:withEvent: // 开始
touchesMoved:withEvent: // 移动
touchesEnded:withEvent: // 结束
touchesCancled:withEvent: // 取消

53、Core Foundation中提供了几种操作socket的方法

3种:CFNetwork、CFSocket和BSD socket

54、解析XML格式的方式有几种

3种:以DOM方式解析、以SAX方式解析和以XMLReader方式解析

55、谈谈MVC、MVVM和MVP的区别

>MVC
  可分三层:
    Model:模型层,用于存储和处理数据
    View:展示层,用于显示数据视图
    Controller:控制层,用于处理业务逻辑
  数据流转:
    View传递指令到Controller
    Controller完成业务逻辑处理,通知Model状态改变
    Model完成数据处理,将新的数据发送给View,用户得到反馈
    结论:所有的通信都是单向的
>MVP
    可分三层:
      Model:模型层,用于存储和处理数据
      View:展示层,用于显示数据视图
      Presenter:用于处理业务逻辑
    数据流转:
      View传递指令到Presenter
      Presenter完成业务逻辑处理,通知Model状态改变
      Model完成数据处理,将新的数据发送给Presenter
      Presenter接收新的数据,进行业务处理,最后发送给View,用户得到反馈
    结论:所有通讯都是双向的,View与Model不直接发送联系,都是通过Presenter传递;View比较轻量化,但Presenter则较重;
>MVVM
    可分三层:
       Model:模型层,用于存储和处理数据
       View:展示层,用于显示数据视图
       ViewModel:用于处理业务逻辑
    数据流转:
       与MVP相比就是把Presenter换成了ViewModel,不同的是View与ViewModel之间实现的数据绑定,当ViewModel层数据发生变化,系统自动将数据传递到View层实现数据更新;

56、@property的本质是什么?ivar、getter和setter是如何生存并添加到类中的

@property的本质是ivar(实例变量)和getter、setter(存取方法)的集合
Objective-C中的对象往往需要采用ivar来保存数据,同时也需要setter写入数据值、getter获取数据值;

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

@property按照属性特质可分五类:
a、原子性:nonatomic、atomic
b、读写性:readwrite、readonly
c、内存管理性:weak、retain、assgin、strong、copy、unsafe_unretained
d、方法名性:getter=<name>、setter=<name>
e、不常用:nullable、null_resettable、nonnull

58、什么情况下使用weak关键字?与assgin有什么不同?

在ARC中有可能出现循环引用的地方,可以使用weak来解决;比如:delegate可以使用weak修饰
如果已经对对象添加了强引用,此时不需要强引用可以使用weak修饰;比如:IBOutlet控件的属性一般使用weak修饰,因为父控件的subviews数据已经对控件做了强引用
assgin可以修饰非OC对象,而weak只能修饰OC对象;
weak对象在释放的时候,会自动将对象置为nil;而assgin不会,会变成野指针;

59、怎么用copy关键字?

Objective-C中的基本数据类型NSString、NSArray、NSDictionary可以采用copy修饰,因为他们都有对应的可变对象;
block也可以使用copy修饰

60、在Objective-C中经常使用copy修饰NSString、NSArray、NSDictionary,这是为什么?如果改成strong修饰,会有什么后果?

因为NSString、NSArray、NSDictionary是不可变对象,他们都有对应的可变对象;采用copy修饰,主要是为了完全复制一份新的,当被赋值的变量发生改变的时候,原来的那份一直可以保持不变;
如果使用strong,那么这个对象就有可能被改变

61、系统对象的copy与mutableCopy的区别

不管是系统的集合对象(NSArray,NSDictionary,NSSet等),还是非集合对象(NSString、NSNumber等)都遵循以下原则:copy:返回的是不可变对象;mutableCopy:返回的是可变对象;
非集合类对象:copy是指针复制,mutableCopy是内容复制;
集合类对象:对不可变对象进行copy是指针复制,mutableCopy是内容复制;对可变对象进行copy和mutableCopy都是内容复制;但内容复制进行对象本身,集合类的元素任然是指针复制;
总结:只有对不可变对象进行copy才是指针复制(浅复制),其他情况都是内容复制(深复制);

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

因为采用了copy关键字,所以返回的是不可变对象;当进行对象的添加、删除原生等方法时,会导致程序崩溃;

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

自定义的类要使用copy关键字,步骤如下:
a、必须实现NSCopying协议;如果对象分可变和不可变两个版本,就需要同时实现NSCopying和NSMutableCopying协议
b、实现协议方法:copyWithZone
重写带copy的setter:
-(void)setName:(NSString*) name{
  _name = [name copy];
}

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

@synthesize:合成实例变量,默认值;告诉编译器,如果没有手动实现getter、setter方法,系统自动实现;
@dynamic:告诉编译器,getter、setter方法用户自行实现,不需要系统自动实现

65、简述UIViewController的生命周期

initWithFrame:  // 当从代码实例化view的时候调用
initWithCoder:  // 当从文件实例化view的时候调用,比如nib(xib、storyboard)
awakeFromNib // 当从文件实例化view的时候,系统在完成initWithCoder之后,会调用此方法
loadView // 开始主动加载视图控制器自带的view
viewDidLoad // 视图view加载完成
viewWillAppear // 视图view即将显示在UIWindow上
updateViewConstraints // 视图view将更新autolayout的约束
viewWillLayoutSubviews // 视图view将更新子视图位置
viewDidLayoutSubviews // 视图view已经更新子视图位置
viewDidAppear // 视图view已经显示在UIWindow上
viewWillDisappear // 视图view即将从UIWindow上移除
viewDidDisappear // 视图view已经从UIWindow上移除

66、谈谈Objective-C中的反射机制?怎么用

>classs反射
通过类名的字符串实例化对象
Class class = NSClassFromString(@"NSObject");
NSObject* obj = [[class alloc] init];
将类名变为字符串
Class class2 = [obj class];
NSString* classStr = NSStringFromClass(class2);
>SEL反射
通过方法名的字符串实例化方法
SEL select = NSSelectorFromString(@"selector");
[obj preformSelector:select withObject:@""];
将方法名改为字符串
NSString* str = NSStringFromSelector(select);

67、Objective-C中调用方法的方式有几种

2种:通过方法名称直接调用;通过SEL的方式调用(preformSelector:withObject:)

68、类变量中的@public、@protected、@private和@package的区别

@public:所有类中都可以访问
@protected:本类和子类中可以访问
@private:仅本类可以访问
@package:本包内可以访问,跨包不能访问

69、说说你对isa指针的理解

isa指针是一个Class类型的指针,isa指针指向对象本身;而Class里面也有一个isa指针,它指向的是meteClass(对象的元类),元类中保存了对象的类方法;对象的元类也是类,它也有isa指针,元类的isa指针最终指向的是根元类,根元类的isa指针指向的是它本身;,这样就形成了一个闭环;如下图:
1853063-c1521fc9f842523d.png

70、如何访问并修改一个类的私有属性

通过KVC的方式;通过runtime方式

71、一个OC对象在内存中占用多少空间

占用4个字节(32位)、8个字节(64位);因为OC对象实际上是一个Class类型的指针,那么它所占大小和指针的大小一致;

72、下面的代码输出什么?为什么?

@implementation Son : Father
- (id)init {
   if (self = [super init]) {
       NSLog(@"%@", NSStringFromClass([self class])); // Son
       NSLog(@"%@", NSStringFromClass([super class])); // Son
   }
   return self;
}
@end
解析:
两者输出的都是Son,因为self本身是类的隐藏参数,所以它指向的累的实例对象;而super本身是一个标志符,和self是指向的同一个消息接收者,不同的是,super会告诉编译器采用父类的构造方法来回去实例对象,而不是本类里面的方法;

73、写一个完整的代理,包括声明与实现

// 创建
@protocol TestDelegate
@required // 必须实现
-(void) testMethod;
@optional // 可选
-(void) testMethod2;
@end
// 声明
@interfase TestClass : NSObject<TestDelegate>
@end
// 实现
@implementation TestClass
-(void) testMethod {
  // 实现代码
}
@end

74、isKindOfClass、isMemberOfClass、selector作用分别是什么

isKindOfClass:判断对象类型属于某个类型或者继承自某个类型;
isMemberOfClass:判断对象类型是不是等于某个类型
selector:通过方法名获取方法的入口地址

75、lldb(gdb)常用的控制台调试命令?

p 基本数据类型的打印命令,需要指定类型;p (int)[self.view.subviews count]
po 对象的打印命令,会调用对象的description方法;po self.view
expr 可以在调试时动态执行指定表达式,并打印结果;常用于调试过程中修改变量的值
bt 打印堆栈的调用,加all可以打印所有Thread的堆栈信息

76、在Instruments中,你常用的工具有哪些?

Time Profiler:性能分析工具
Leaks:内存泄露检测工具
Allocations:内存分配检测工具
Zombies:僵尸对象检测工具

77、Objective-C中多线程的实现方式有哪些?

pthread、NSThread、GCD、NSOperation

78、谈谈GCD与NSOperation的区别

GCD:是基于C语言实现的API,一般结合block来实现,多用于简单的项目;
NSOperation:是Objective-C类型的对象,是基于GCD的高级封装,多用于比较复杂的项目,采用面向对象的方式管理线程;

79、写几个GCD中常用的方法

dispatch_once // 只执行一次,多用于创建单例类
dispatch_async // 创建异步线程
dispatch_sync // 创建同步线程
dispatch_semaphore // 信号量,多用于给线程加锁,避免死锁
dispatch_after // 延迟执行线程
dispatch_async(dispatch_get_main_queue,^{})  // 获取主线程
dispatch_async(dispatch_get_global_queue(0,0),^{}) // 创建子线程

80、用伪代码实现GCD,要求在n个图片下载完成后,合成为一张图片

// 创建线程分组
dispatch_group_t group = dispatch_group_create();
// 获取全局队列
dispatch_queue_t queue = dispatch_get_global_queue(0,0);
// 将下载任务加到队列中
dispatch_group_async(group,queue,^{
  // 图片下载任务1
});
dispatch_group_async(group,queue,^{
  // 图片下载任务2
});
......
dispatch_group_async(group,queue,^{
  // 图片下载任务n
});
// 所有图片都下载完成后,系统调用此方法通知图片合成
dispatch_group_notify(group,dispatch_get_main_queue,^{
  // 图片合成代码
});

81、栅栏函数的作用是什么?

栅栏函数:dispatch_barrier_async(queue,^{});
特点:
在栅栏函数之前的任务执行完成,它才会执行;
在栅栏寒湿之后的任务,需要等待它执行完成才会执行;
栅栏函数主要用于避免资源竞争;

82、什么是Runloop?什么又是Runtime?

Runloop:是一中循环运行机制;一个线程对应一个Runloop,它的作用就是确保程序的持续运行,处理APP中的各种事件;其特点是:有任务执行就执行,没有任务执行就休眠,等有新的任务时在唤醒,不会造成CPU的浪费,提高性能;
Runtime:是一种运行时机制,它可以方便开发者通过代码的方式,在程序运行时动态的对类进行修改,包含添加、删除和修改类的方法和成员变量等;

83、Runtime的实现机制是什么?一般怎么使用?它可以做些什么事情?

使用的时候需要引入头文件:<objc/message.h>、<objc/runtime.h>
Runtime实际上是一套c语言库
实际上我们所编写的Objective-C代码,在运行时都会比转换为Runtime的内容;比如:
  a、OC类在运行时,被转换成了Runtime的结构体
  b、OC类的方法运行时,被转换成了Runtime的C语言函数
  c、OC类的方法调用运行时,被转换成了objc_msgSend函数
Runtime可以做的事情:
  a、获取类的所有成员变量和实例方法
  b、动态为类添加成员变量
  c、动态改变类的方法实现
  d、动态为类添加新的方法等

84、什么是Method Swizzle,什么情况下使用它?

在没有类的源码情况下,想要对其中一个类的方法进行修改,除了了继承和分类强行修改,可以动态使用Method Swizzle来实现;
Method Swizzle的实现原理:
在OC中调用方法,实际是向该对象发送消息,而查找消息的唯一标识就是selector的名称,所以只要在运行时,我们动态修改selector的实际指向地址即可实现Method Swizzle;
Method Swizzle的实现方法:
可以采用method_exchangeImplementations来交换方法的IMP
可以利用class_replaceMethod来修改类
可以利用method_setImplementation来直接设置方法的IMP

85、_objc_msgForward函数的作用是什么?直接调用会有什么影响?

_objc_msgForward是IMP类型,向对象发送消息的时候,而它又没有实现的时候会调用_objc_msgForward进行消息转发;
直接调用可能会导致程序崩溃;

86、通信底层原来(OSI的分层)

物理层、数据链路层、网络层、传输层、会话层、表示层和应用层

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

创建线程的方法:
[self preformSelectorInBackground:nil withObject:nil];
[NSThread detachNewThreadSelector:nil toTarget:nil withObject:nil];
dispatch_async(dispatch_get_global_queue(0,0),^{});
[[NSThread alloc] initWithTarget:nil select:nil object:nil]
[[NSOperationQueue new] addOpreation:nil];
在主线程执行的方法:
dispatch_async(dispatch_get_main_queue(),^{});
[self preformSelectorOnMainThread:withObject:nil];
[[NSOpreationQueue mainQueue] addOpreation:nil];

88、说说UITableView的重用机制

UITableView是利用单元格的重用来节省内存;
通过给单元格设置标识符,当单元格滑出屏幕的时候,会将该单元格缓存在重用队列中;
当有新的单元格进入屏幕的时候,根据标识符,先从缓存的重用队列中获取,如果有直接使用,如果没有则创建;

89、用伪代码写一个线程安全的单例类

static id obj;
+(instance) getInstance {
  static dispatch_once_t once;
  dispatch_once(&once,^{
    obj = [[self alloc] init];
  });
  return obj;
}
// 写此方法的目的,只要是确保通过alloc、init获取对象实例的时候,也只执行一次
+(id)allocWithZone:(struct _NSZone*) zone {
  static dispatch_once_t once;
  dispatch_once(&once,^{
    obj = [super allocWithZone:zone];
  });
  return obj;
}
// 适当实现此方法,如果调用了copy会调用此方法
-(id)copyWithZone:(NSZone*) zone {
  return obj;
}

90、如何实现视图的变形

通过修改视图的transform属性

91、常用的手势类型有哪些?哪些手势只会响应一次?

UITapGestureRecognizer——点击 //只会响应一次
UIPinchGestureRecognizer——捏合
UIRotationGestureRecognizer——旋转
UISwipeGestureRecognizer——轻扫 // 只会响应一次
UIPanGetureRecognizer——平移(拖动)
UIScreenEdgePanGestureRecognizer——屏幕边缘附近开始的平移(拖动)
UILongPressGestureRecognizer——长按

92、如何高效的给UIImageView添加圆角?

使用绘图技术:
- (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 *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];

93、谈谈UITableView的优化

1). 正确的复用cell。
2). 设计统一规格的Cell
3). 提前计算并缓存好高度(布局),因为heightForRowAtIndexPath:是调用最频繁的方法;
4). 异步绘制,遇到复杂界面,遇到性能瓶颈时,可能就是突破口;
4). 滑动时按需加载,这个在大量图片展示,网络加载的时候很管用!
5). 减少子视图的层级关系
6). 尽量使所有的视图不透明化以及做切圆操作。
7). 不要动态的add 或者 remove 子控件。最好在初始化时就添加完,然后通过hidden来控制是否显示。
8). 使用调试工具分析问题。

94、如何实现UITableView中cell的动态行高

实现动态行高必须设置2个属性:预估行高和设置自定义行高
预估行高:tableview.estimatedRowHeight = 200;
设置自定义行高:tableview.rowHeight = UITableViewAutomaticDimension;
如果要自定义行高有效,必须让视图控制器有一个自上而下的约束

95、谈谈IOS中延迟执行的几种方式

preformSelector:withObject:afterDelay
preformSelector:onThread:withObject:waitUntilDone
[NSThread sleepForTimeInterval:3]
GCD中的dispatch_after(dispatch_time,queue,^{});

96、说一说OC中反向传值的方式

block传值
通知传值
代理传值
单例传值
extern传值

97、id与NSObject的区别

id可以修饰所有OC对象;NSObject修饰的必须是NSObject的子类或本类;OC中不是所有的对象都属于NSObject,还有一些继承自NSProxy

98、如何重写类方法

创建子类,并定义一个和基类一样名称的静态方法;在调用的时候,需要采用[[self class] methodName]来调用,因为用类名调用是早绑定,是在编译期间;用self class是晚绑定,是在运行时才确定

99、如何在定时器中调用静态方法

定时器中只能调用实例方法,但在实例方法中可以调用静态方法,在实例方法中采用[self class]的方式调用

100、NSTimer创建后在哪个线程执行?

用scheduledTimerWithTimeInterval创建的,在哪个线程创建就会加入到哪个线程的Runloop中就在哪个线程运行
自己创建的Timer,加入到那个线程的Runloop就在哪个线程执行

101、 runtime如何通过selector找到对应的IMP地址?(分别考虑类方法和实例方法)

IMP class_getMethodImplementation(Class cls, SEL name);
IMP method_getImplementation(Method m)
实例方法与类方法的区别在于Class的获取:
objc_getClass // 实例方法
objc_getMateClass // 静态方法

102、runtime如何实现weak修饰的变量置为nil

weak的底层是hash表,以对象地址作为key,以对象作为value;

103、layoutSubviews和drawRect 的使用和区别

两个方法相同点

  1 都是异步执行
  2 都是UIView 的方法


两个方法不同点

  1   layoutSubviews方便数据计算,
  2   drawRect方便视图重绘。

layoutSubviews在以下情况下会被调用:
1、init初始化不会触发layoutSubviews
2、addSubview会触发layoutSubviews (向对象添加子视图,或者对象添加到父视图,frame为0时不会)
3、改变view的width和hight的时候会触发layoutSubviews
4、滚动一个UIScrollView会触发layoutSubviews(受contentSize 的影响)
5、旋转Screen会触发父UIView上的layoutSubviews事件
6、改变一个UIView大小的时候也会触发父UIView上的layoutSubviews事件
7、直接调用setLayoutSubviews。

layoutSubview的调用时机问题
- (void)viewWillAppear:(BOOL)animated; // 会调用一次layoutSubviews
- (void)viewDidAppear:(BOOL)animated;  //触发了layoutSubviews 上面的几种情况,会调用layoutSubviews

什么时候用layoutSubviews
1仅仅在以下情况下:自动布局达不到想要效果时你才有必要重写这个方法.可以直接设置subviews的尺寸.
2不要直接调用这个方法,因为不会有任何的作用.如果你需要强制layout刷新,调用setNeedsLayout来代替, 
如果你想要立即刷新你的view,调用layoutIfNeeded ,一个view是不能够自己调用layoutSubviews,如果要调用,需要调用setNeedsLayout或者 layoutIfNeeded
drawRect在以下情况下会被调用:

1、如果在UIView初始化时没有设置rect大小,将直接导致drawRect不被自动调用。
drawRect 调用是在Controller->loadView, Controller->viewDidLoad 两方法之后掉用的.所以不用担心在 控制器中,这些View的drawRect就开始画了.这样可以在控制器中设置一些值给View(如果这些View draw的时候需要用到某些变量 值).
2、该方法在调用sizeToFit后被调用,所以可以先调用sizeToFit计算出size。然后系统自动调用drawRect:方法。
3、通过设置contentMode属性值为UIViewContentModeRedraw。那么将在每次设置或更改frame的时候自动调用drawRect:。
4、直接调用setNeedsDisplay,或者setNeedsDisplayInRect:触发drawRect:,但是有个前提条件是rect不能为0。以上1,2推荐;而3,4不提倡

drawRect方法使用注意点:

1、 若使用UIView绘图,只能在drawRect:方法中获取相应的contextRef并绘图。如果在其他方法中获取将获取到一个invalidate 的ref并且不能用于画图。drawRect:方法不能手动显示调用,必须通过调用setNeedsDisplay 或 者 setNeedsDisplayInRect,让系统自动调该方法。
2、若使用calayer绘图,只能在drawInContext: 中(类似鱼drawRect)绘制,或者在delegate中的相应方法绘制。同样也是调用setNeedDisplay等间接调用以上方法
3、若要实时画图,不能使用gestureRecognizer,只能使用touchbegan等方法来掉用setNeedsDisplay实时刷新屏幕

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352