iOS 核心框架
CoreAnimation
CoreGraphics
CoreLocation
AVFoundation
Foundation
iOS核心机制
UITableView 重用
ObjC内存管理;自动释放池,ARC如何实现
runloop
runtime
Block的定义、特性、内存区域、如何实现
Responder Chain
NSOperation
GCD
数据结构
8大排序算法
二叉树实现
二分查找实现
面向对象编程
封装、继承、多态
设计模式6个原则
设计一个类的功能,如何划分粒度(单一职责)
接口隔离。
如果有一个鸟类,有飞的动作,一个鸵鸟继承它是合适的吗(里氏替换)
类之间的依赖如何依赖偶合度最小(依赖倒转)
高层依赖低层,低层不能依赖高层。依赖接口,不能依赖具体的类。
如果A要调用C函数,但C是B的成员类,应该如何设计?(迪米特)
如何设计类,能做到只增加代码,而不修改代码,有哪些经验(开放封闭)
通过设计模式解决。
计算机技术
计算机网络: TCP/IP、HTTPCDN、SPDY
计算机安全: RSA、AES、DES
操作系统:线程、进程、堆栈、死锁、调度算法
iOS新特性、新技术
iOS7 UIDynamic、SpritKit、新布局、扁平化
iOS8 应用程序扩展、HealthKit、SceneKit、CoreLocation、TouchID、PhotoKit
iOS9
Apple Watch
第三方库:SDWebImage、AFNetwork、JSONKit、wax
swift
Objective-c
理解与特性
OC作为一六面向对象的语言,自然具有面向对象的语言特性:封装、继承、多态。它既有表态语言的特性(如c++)又有动态语言的效率(动态类型、动态绑定、动态加载)。
优点及缺点
优点
- Cateogies
- Posing
- 动态识别
- 指标计算
- 弹性讯息传递
- 不是一个过度复杂的C衍生语言
- Objective-C与C++可混合编程
缺点
- 不支持命名空间
- 不支持运算符重载
- 不支持多重继承
- 使用动态运行时类型,所有的方法都是函数调用,所以很多编译时优化方法都用不到(如内联函数等),性能低劣。
内存管理机制
- 管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memoryleak。
- 碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的—对应,以至于永远都不可能有一个内存块从栈中弹出。
- 分配方式:堆都是动态分配的,没有静态分配的堆。栈有两种分配方式:静态和动态分配。静态是编译器完成的,比如局部变量的分配。动态有alloc?函数进行分配,但是栈的动态分配和堆是不同的,他的动态是由编译器进行释放,无需我们手动实现。
- 分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持;分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的。它的机制是很复杂的。
进程与线程的区别和联系?
定时器与线程的区别?
进程死锁
原因:资源竞争及进程推进顺序非法
条件:互斥、请求保持、不可剥夺、环路
处理:鸵鸟策略、预防策略、避免策略、检测与解除死锁
_block/weak修饰符区别
_block在arc和mrc环境下都能用,可以修饰对象,也能修饰基本数据类型
_weak只能在arc环境下使用,只能修饰对象(NSString),不能修饰基本数据类型(int)
_block对象可以在block中重新赋值,_weak不行。
sprintf,strcpy,memcpy使用上有什么要注意的地方?
strcpy是一个字符串拷贝的函数,它的函数原型为strcpy(char *dst, const char *src);将src开始的一段字符串拷贝到dst开始的内存中去,结束的标志符号为’\0’,由于拷贝的长度不是由我们自己控制的,所以这个字符串拷贝很容易出错。
具备字符串拷贝功能的函数有memcpy,这是一个内存拷贝函数,它的函数原型为memcpy(char dst,const char src, unsigned int len);将长度为len的一段内存,从src拷贝到dst中去,这个函数的长度可控。但是会有内存叠加的问题。
sprintf是格式化函数。将一段数据通过特定的格式,格式化到一个字符串缓冲区中去。sprintf格式化的函数的长度不可控,有可能格式化后的字符串会超出缓冲区的大小,造成溢出。
简述 NULL、nil、Nil、NSNull 的区别?
1).nil和C语言的NULL相同,在objc/objc.h中定义。nil表示Objective-C对象的值为空。在C语言中,指针的空值用NULL表示。在Objective-C中,nil对象调用任何方法表示什么也不执行,也不会崩溃。
2).Nil:那么对于我们Objective-C开发来说,Nil也就代表((void *)0)。但是它是用于代表空类的. 比如:Class myClass = Nil;
3).NULL: 在C语言中,NULL是无类型的,只是一个宏,它代表空. 这就是在C/C++中的空指针。对于我们Objective-C开发来说,NULL就表示((void*)0).
4).NSNull:NSNull是继承于NSObject的类型。它是很特殊的类,它表示是空,什么也不存储,但是它却是对象,只是一个占位对象。使用场景就不一样了,比如说服务端接口中让我们在值为空时,传空。NSDictionry *parameters = @{@”arg1” : @”value1”,@”arg2” : arg2.isEmpty ? [NSNull null] : arg2};
NULL、nil、Nil这三者对于Objective-C中值是一样的,都是(void *)0,那么为什么要区分呢?又与NSNull之间有什么区别:
NULL是宏,是对于C语言指针而使用的,表示空指针
nil是宏,是对于Objective-C中的对象而使用的,表示对象为空
Nil是宏,是对于Objective-C中的类而使用的,表示类指向空
NSNull是类类型,是用于表示空的占位对象,与JS或者服务端的null类似的含意
类变量的@protected,@private,@public,@package声明各有什么含义?
@protected 该类和子类中访问,是默认的;
@private 只能在本类中访问;
@public 任何地方都能访问;
@package 本包内使用,跨包不可以
static有什么作用?
1)函数体内 static 变量的作用范围为该函数体,不同于 auto 变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值;
2)在模块内的 static 全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;
3)在模块内的 static 函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内;
4)在类中的 static 成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;
5)在类中的 static 成员函数属于整个类所拥有,这个函数不接收 this 指针,因而只能访问类的static 成员变量。
@property的本质是什么?ivar、getter、setter是如何生成并添加到这个类中的?
@property = ivar(实例变量) + getter(取方法) + setter(存方法)
“属性” (property)有两大概念:ivar(实例变量)、存取方法(access method = getter + setter)
ivar、getter、setter如何生成并添加到类中:
这是编译器自动合成的,通过@synthesize关键字指定,若不指定,默认为@synthesize propertyName = _propertyName;若手动实现了getter/setter方法,则不会自动合成。
现在编译器已经默认为我们添加@synthesize propertyName = _propertyName;因此不再需要手动添加了,除非你真的要改成员变量名。
生成getter方法时,会判断当前属性名是否有,比如声明属性为@property (nonatomic, copy) NSString *_name;那么所生成的成员变量名就会变成__name,如果我们要手动生成getter方法,就要判断是否以开头了。
不过,命名都要有规范,是不允许声明属性是使用_开头的,不规范的命名,在使用runtime时,会带来很多的不方便的。
@synthesize和@dynamic分别有什么作用?
1).@property有两个对应的词,一个是 @synthesize,一个是 @dynamic。如果 @synthesize和 @dynamic都没写,那么默认的就是@syntheszie var = _var;
@synthesize 的语义是如果你没有手动实现 setter 方法和 getter 方法,那么编译器会自动为你加上这两个方法。
2).@dynamic 告诉编译器:属性的 setter 与 getter 方法由用户自己实现,不自动生成。(当然对于 readonly 的属性只需提供 getter 即可)。假如一个属性被声明为 @dynamic var,然后你没有提供 @setter方法和 @getter 方法,编译的时候没问题,但是当程序运行到 instance.var = someVar,由于缺 setter 方法会导致程序崩溃;或者当运行到 someVar = var 时,由于缺 getter 方法同样会导致崩溃。编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定。
属性readwrite,readonly,assign,retain,copy,nonatomic各是什么作用,在哪种情况下用?
- readwrite是可读可写特性,需要生成getter方法和setter方法。
- assign是赋值特性,setter方法将传入参数赋值给实例变量。
- retain表示持有特性,setter方法将传入参数先保留,再赋值,传入参数的retaincount会+1。
- copy表示赋值特性,setter方法将传入对象复制一份,需要完全一份新的变量。
- nonatomic非原子操作,决定编译器生成的setter,getter是否是原子操作。
- atomic表示多线程安全,一般使用nonatomic。
1)import指令是Object-C针对#include的改进版本,#include是C/C++导入头文件的关键字,使用#import头文件会自动导入一次,不会重复导入,相当于#include和#pragma once;这样你就不会陷入递归包含的问题中。
2)import与@class二者的区别在于:
import会链入该头文件的全部信息,包括实例变量和方法等;而@class只是告诉编译器,其后面声明的名称是类的名称,至于这些类是如何定义的,暂时不用考虑。
在头文件中一般使用@class来声明这个名称是类的名称,不需要知道其内部的实体变量和方法.
而在实现类里面,因为会用到这个引用类的内部的实体变量和方法,所以需要使用#import来包含这个被引用类的头文件。
在编译效率方面,如果你有100个头文件都#import了同一个头文件,或者这些文件是依次引用的,如A–>B, B–>C, C–>D这样的引用关系。当最开始的那个头文件有变化的话,后面所有引用它的类都需要重新编译,如果你的类有很多的话,这将耗费大量的时间。而是用@class则不会。
如果有循环依赖关系,如:A–>B, B–>A这样的相互依赖关系,如果使用#import来相互包含,那么就会出现编译错误,如果使用@class在两个类的头文件中相互声明,则不会有编译错误出现。
说说NSArray、NSSet、NSDictionary与NSMutableArray、NSMutableSet、NSMutableDictionary元素的特性和作用
特性:
1)NSArray表示不可变数组,是有序元素集,只能存储对象类型,可通过索引直接访问元素,而且元素类型可以不一样,但是不能进行增、删、改操作;NSMutableArray是可变数组,能进行增、删、改操作。通过索引查询值很快,但是插入、删除等效率很低。
2)NSSet表示不可变集合,具有确定性、互异性、无序性的特点,只能访问而不能修改集合;NSMutableSet表示可变集合,可以对集合进行增、删、改操作。集合通过值查询很快,插入、删除操作极快。
3)NSDictionary表示不可变字典,具有无序性的特点,每个key对应的值是唯一的,可通过key直接获取值;NSMutableDictionary表示可变字典,能对字典进行增、删、改操作。通过key查询值、插入、删除值都很快。
作用:
1)数组用于处理一组有序的数据集,比如常用的列表的dataSource要求有序,可通过索引直接访问,效率高。
2)集合要求具有确定性、互异性、无序性,在iOS开发中是比较少使用到的,笔者也不清楚如何说明其作用
3)字典是键值对数据集,操作字典效率极高,时间复杂度为常量,但是值是无序的。在ios中,常见的JSON转字典,字典转模型就是其中一种应用。
利用Socket建立网络连接的步骤
建立Socket连接至少需要一对套接字,其中一个运行于客户端,称为ClientSocket ,另一个运行于服务器端,称为ServerSocket 。
套接字之间的连接过程分为三个步骤:服务器监听,客户端请求,连接确认。
1。服务器监听:服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态,等待客户端的连接请求。
2。客户端请求:指客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。
3。连接确认:当服务器端套接字监听到或者说接收到客户端套接字的连接请求时,就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,双方就正式建立连接。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求
链表和数组的区别在哪里?
二者都属于一种数据结构
从逻辑结构来看
数组必须事先定义固定的长度(元素个数),不能适应数据动态地增减的情况。当数据增加时,可能超出原先定义的元素个数;当数据减少时,造成内存浪费;数组可以根据下标直接存取。
链表动态地进行存储分配,可以适应数据动态地增减的情况,且可以方便地插入、删除数据项。(数组中插入、删除数据项时,需要移动其它数据项,非常繁琐)链表必须根据next指针找到下一个元素
从内存存储来看
1.(静态)数组从栈中分配空间, 对于程序员方便快速,但是自由度小
2.链表从堆中分配空间, 自由度大但是申请管理比较麻烦
从上面的比较可以看出,如果需要快速访问数据,很少或不插入和删除元素,就应该用数组;相反, 如果需要经常插入和删除元素就需要用链表数据结构了。
简叙RunLoop 与RunTime的区别
答案:
一.RunLoop:
Runloop是事件接收和分发机制的一个实现。
Runloop提供了一种异步执行代码的机制,不能并行执行任务。
在主队列中,Main RunLoop直接配合任务的执行,负责处理UI事件、定时器以及其他内核相关事件。
1).RunLoop的主要目的:
保证程序执行的线程不会被系统终止。
2).什么时候使用Runloop ?
当需要和该线程进行交互的时候才会使用Runloop.
每一个线程都有其对应的RunLoop,但是默认非主线程的RunLoop是没有运行的,需要为RunLoop
添加至少一个事件源,然后去run它。 一般情况下我们是没有必要去启用线程的RunLoop的,除非你在
一个单独的线程中需要长久的检测某个事件。主线程默认有Runloop。当自己启动一个线程,如果只是用于
处理单一的事件,则该线程在执行完之后就退出了。 所以当我们需要让该线程监听某项事务时,就得让线程一
直不退出,runloop就是这么一个循环,没有事件的时候,一直卡着,有事件来临了,执行其对应的函数。
RunLoop,正如其名所示,是线程进入和被线程用来相应事件以及调用事件处理函数的地方.需要在代码中使用控制
语句实现RunLoop的循环,也就是说,需要代码提供while或者for循环来驱动RunLoop.在这个循环中,使用一个runLoop
对象[NSRunloop currentRunloop]执行接收消息,调用对应的处理函数. Runloop接收两种源事件:input sources和timer sources。
input sources 传递异步事件,通常是来自其他线程和不同的程序中的消息; timer sources(定时器) 传递同步事件(重复执行或者在
特定时间上触发)。除了处理input sources,Runloop 也会产生一些关于本身行为的notificaiton。注册成为Runloop的observer,
可以接收到 这些notification,做一些额外的处理。(使用CoreFoundation来成为runloop的observer)。
Runloop工作的特点:
1>当有时间发生时,Runloop会根据具体的事件类型通知应用程序作出相应的反应;
2>当没有事件发生时,Runloop会进入休眠状态,从而达到省电的目的;
3>当事件再次发生时,Runloop会被重新唤醒,处理事件.
提示:一般在开发中很少会主动创建Runloop,而通常会把事件添加到Runloop中.
二.Runtime:
> RunTime简称运行时。就是系统在运行的时候的一些机制,其中最主要的是消息机制。对于C语言,函数的调用在编译的时候会决定调用哪个函数(C语言的函数调用请看这里 )。编译完成之后直接顺序执行,无任何二义性。OC的函数调用成为消息发送。 属于动态调用过程。在编译的时候并不能决定真正调用哪个函数(事实证明,在编译阶段,OC可以调用任何函数,即使这个函数并未实现,只要申明过就不会报错。而C语言在编译阶段就会报错)。只有在真正运行的时候才会根据函数的名称找到对应的函数来调用。举例说明:比如你[obj makeText];则运行时就这样的:首先,编译器将代码[obj makeText];转化为objc_msgSend(obj, @selector (makeText));,在objc_msgSend函数中。首先通过obj的isa指针找到obj对应的class。在Class中先去cache中 通过SEL查找对应函数method(猜测cache中method列表是以SEL为key通过hash表来存储的,这样能提高函数查找速度),若 cache中未找到。再去methodList中查找,若methodlist中未找到,则取superClass中查找。若能找到,则将method加 入到cache中,以方便下次查找,并通过method中的函数指针跳转到对应的函数中去执行。
KVO,NSNotification,delegate及block区别
一. KVO就是cocoa框架实现的观察者模式,一般同KVC搭配使用,通过KVO可以监测一个值的变化,比如View的高度变化。是一对多的关系,一个值的变化会通知所有的观察者。
二.NSNotification是通知,也是一对多的使用场景。在某些情况下,KVO和NSNotification是一样的,都是状态变化之后告知对方。NSNotification的特点,就是需要被观察者先主动发出通知,然后观察者注册监听后再来进行响应,比KVO多了发送通知的一步,但是其优点是监听不局限于属性的变化,还可以对多种多样的状态变化进行监听,监听范围广,使用也更灵活。
三.delegate 是代理,就是我不想做的事情交给别人做。比如狗需要吃饭,就通过delegate通知主人,主人就会给他做饭、盛饭、倒水,这些操作,这些狗都不需要关心,只需要调用delegate(代理人)就可以了,由其他类完成所需要的操作。所以delegate是一对一关系。
四.block是delegate的另一种形式,是函数式编程的一种形式。使用场景跟delegate一样,相比delegate更灵活,而且代理的实现更直观。
五.KVO一般的使用场景是数据,需求是数据变化,比如股票价格变化,我们一般使用KVO(观察者模式)。delegate一般的使用场景是行为,需求是需要别人帮我做一件事情,比如买卖股票,我们一般使用delegate。
六.Notification一般是进行全局通知,比如利好消息一出,通知大家去买入。delegate是强关联,就是委托和代理双方互相知道,你委托别人买股票你就需要知道经纪人,经纪人也不要知道自己的顾客。Notification是弱关联,利好消息发出,你不需要知道是谁发的也可以做出相应的反应,同理发消息的人也不需要知道接收的人也可以正常发出消息。
如何缩减安装包体积
- 缩减安装包的体积要从资源文件上下手,尽可能用Core Graphics自绘代替资源图片
2.如果是用户可能用不到的功能,那么应该只把代码合成进去而资源文件应该在用户第一次使用的时候从服务器下载并缓存在本地
3.裁剪第三方开源库的代码等等
- 入口 setImageWithURL:placeholderImage:options: 会先把 placeholderImage 显示,然后 SDWebImageManager 根据 URL 开始处理图片。
- 进入 SDWebImageManager-downloadWithURL:delegate:options:userInfo:,交给 SDImageCache 从缓存查找图片是否已经下载 queryDiskCacheForKey:delegate:userInfo:.
- 先从内存图片缓存查找是否有图片,如果内存中已经有图片缓存,SDImageCacheDelegate 回调 imageCache:didFindImage:forKey:userInfo: 到 SDWebImageManager。
- SDWebImageManagerDelegate 回调 webImageManager:didFinishWithImage: 到 UIImageView+WebCache 等前端展示图片。
- 如果内存缓存中没有,生成 NSInvocationOperation 添加到队列开始从硬盘查找图片是否已经缓存。
- 根据 URLKey 在硬盘缓存目录下尝试读取图片文件。这一步是在 NSOperation 进行的操作,所以回主线程进行结果回调 notifyDelegate:。
- 如果上一操作从硬盘读取到了图片,将图片添加到内存缓存中(如果空闲内存过小,会先清空内存缓存)。SDImageCacheDelegate 回调 imageCache:didFindImage:forKey:userInfo:。进而回调展示图片。
- 如果从硬盘缓存目录读取不到图片,说明所有缓存都不存在该图片,需要下载图片,回调 imageCache:didNotFindImageForKey:userInfo:。
- 共享或重新生成一个下载器 SDWebImageDownloader 开始下载图片。
- 图片下载由 NSURLConnection 来做,实现相关 delegate 来判断图片下载中、下载完成和下载失败。
- connection:didReceiveData: 中利用 ImageIO 做了按图片下载进度加载效果。
- connectionDidFinishLoading: 数据下载完成后交给 SDWebImageDecoder 做图片解码处理。
- 图片解码处理在一个 NSOperationQueue 完成,不会拖慢主线程 UI。如果有需要对下载的图片进行二次处理,最好也在这里完成,效率会好很多。
- 在主线程 notifyDelegateOnMainThreadWithInfo: 宣告解码完成,imageDecoder:didFinishDecodingImage:userInfo: 回调给 SDWebImageDownloader。
- imageDownloader:didFinishWithImage: 回调给 SDWebImageManager 告知图片下载完成。
- 通知所有的 downloadDelegates 下载完成,回调给需要的地方展示图片。
- 将图片保存到 SDImageCache 中,内存缓存和硬盘缓存同时保存。写文件到硬盘也在以单独 NSInvocationOperation 完成,避免拖慢主线程。
- SDImageCache 在初始化的时候会注册一些消息通知,在内存警告或退到后台的时候清理内存图片缓存,应用结束的时候清理过期图片。
- SDWI 也提供了 UIButton+WebCache 和 MKAnnotationView+WebCache,方便使用。
- SDWebImagePrefetcher 可以预先下载图片,方便后续使用。
1、NULL是C语言中的空指针。
2、nil是OC中指向空对象的指针。
3、Nil是OC指向类的空指针。
4、NSNull是在数组或字典集合对象中表示空值对象。
内存的使用和优化
重用问题:如UITableViewCells、UICollectionViewCells、UITableViewHeaderFooterViews设置正确的reuseIdentifier,充分重用。
尽量把views设置为不透明:当opque为NO的时候,图层的半透明取决于图片和其本身合成的图层为结果,可提高性能。
不要使用太复杂的XIB/Storyboard:载入时就会将XIB/storyboard需要的所有资源,包括图片全部载入内存,即使未来很久才会使用。那些相比纯代码写的延迟加载,性能及内存就差了很多。
选择正确的数据结构:学会选择对业务场景最合适的数组结构是写出高效代码的基础。比如,数组: 有序的一组值。使用索引来查询很快,使用值查询很慢,插入/删除很慢。字典: 存储键值对,用键来查找比较快。集合: 无序的一组值,用值来查找很快,插入/删除很快。
gzip/zip压缩:当从服务端下载相关附件时,可以通过gzip/zip压缩后再下载,使得内存更小,下载速度也更快。
延迟加载:对于不应该使用的数据,使用延迟加载方式。对于不需要马上显示的视图,使用延迟加载方式。比如,网络请求失败时显示的提示界面,可能一直都不会使用到,因此应该使用延迟加载。
数据缓存:对于cell的行高要缓存起来,使得reload数据时,效率也极高。而对于那些网络数据,不需要每次都请求的,应该缓存起来,可以写入数据库,也可以通过plist文件存储。
处理内存警告:一般在基类统一处理内存警告,将相关不用资源立即释放掉
重用大开销对象:一些objects的初始化很慢,比如NSDateFormatter和NSCalendar,但又不可避免地需要使用它们。通常是作为属性存储起来,防止反复创建。
避免反复处理数据:许多应用需要从服务器加载功能所需的常为JSON或者XML格式的数据。在服务器端和客户端使用相同的数据结构很重要。
使用Autorelease Pool:在某些循环创建临时变量处理数据时,自动释放池以保证能及时释放内存。
正确选择图片加载方式:详情阅读细读UIImage加载方式
写一个NSString类的实现
+(id)initWithCString:(const char *)nullTerminatedCStringencoding:(NSStringEncoding)encoding;
+ (id)stringWithCString: (const char*)nullTerminatedCString encoding: (NSStringEncoding)encoding
{ NSString *obj;
obj = [selfallocWithZone: NSDefaultMallocZone()];
obj = [objinitWithCString: nullTerminatedCString encoding: encoding];
returnAUTORELEASE(obj);
}
求下面打印的结果
main()
{ int a[5]={1,2,3,4,5};
int ptr=(int )(&a+1);
printf(“%d,%d”,(a+1),(ptr-1));
}
2,5 (a+1)就是a[1],(ptr-1)就是a[4],执行结果是2,5
&a+1不是首地址+1,系统会认为加一个a数组的偏移,是偏移了一个数组的大小(本例是5个int)
int ptr=(int )(&a+1);则ptr实际是&(a[5]),也就是a+5
原因如下:&a是数组指针,其类型为 int (*)[5];
而指针加1要根据指针类型加上一定的值,不同类型的指针+1之后增加的大小不同。
a是长度为5的int数组指针,所以要加 5*sizeof(int),所以ptr实际是a[5],
但是prt与(&a+1)类型是不一样的(这点很重要),所以prt-1只会减去sizeof(int*)
a,&a的地址是一样的,但意思不一样
a是数组首地址,也就是a[0]的地址,&a是对象(数组)首地址, a+1是数组下一元素的地址,即a[1],&a+1是下一个对象的地址,即a[5].
描述iOS程序的运行流程 ?
1.系统调用app的main函数
2.main函数调用UIApplicationMain.
3.UIApplicationMain创建sharedapplication instance, UIApplication默认的instance.
4.UIApplicationMain读取Info.plist找到主nib文件, 加载nib,把shared applicationinstance 设为nib的owner.
5.通过nib文件,创建app的独立UIWindows object.
6.通过nib,实例化了程序的AppDelegate object.
7.app内部启动结束,application:didFinishLaunchingWith-Options: 被设定成 wAppDelegate instance.
8.AppDelegate向UIWindowinstance发makeKeyAndVisible消息, app界面展示给用户. app准备好接收用户的操作指令.
怎样防止指针的越界使用问题?
1 .防止数组越界,必须让指针指向一个有效的内存地址,
2.防止向一块内存中拷贝过多的内容
3.防止使用空指针
4.防止改变const修改的指针
5.防止改变指向静态存储区的内容
6.防止两次释放一个指针
7.防止使用野指针.
将一个函数在主线程执行的4种方法
dispatch_async(dispatch_get_main_queue(), ^{
//需要执行的方法
});
2.NSOperation 方法
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue]; //主队列
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
//需要执行的方法
}];
[mainQueue addOperation:operation];
3.NSThread 方法
[self performSelector:@selector(method) onThread:[NSThread mainThread] withObject:nil waitUntilDone:YES modes:nil];
[self performSelectorOnMainThread:@selector(method) withObject:nil waitUntilDone:YES];
[[NSThread mainThread] performSelector:@selector(method) withObject:nil];
4.RunLoop方法
[[NSRunLoop mainRunLoop] performSelector:@selector(method) withObject:nil];