OC的理解与特性
OC作为一门面向对象的语言,自然具有面向对象的语言特性:封装、继承、多态。它既具有静态语言的特性(如C++),又有动态语言的效率(动态绑定、动态加载等)。总体来讲,OC确实是一门不错的编程语言,多态:不同对象以自己的方式响应相同的消息的能力叫做多态。意思就是假设生物类(life)都用有一个相同的方法-eat;
Objective-C具有相当多的动态特性,表现为三方面:动态类型(Dynamic typing)、动态绑定(Dynamic binding)和动态加载(Dynamic loading)。动态——必须到运行时(run time)才会做的一些事情。
动态类型:即运行时再决定对象的类型,这种动态特性在日常的应用中非常常见,简单来说就是id类型。事实上,由于静态类型的固定性和可预知性,从而使用的更加广泛。静态类型是强类型,而动态类型属于弱类型,运行时决定接受者。
动态绑定:基于动态类型,在某个实例对象被确定后,其类型便被确定了,该对象对应的属性和响应消息也被完全确定。
动态加载:根据需求加载所需要的资源,最基本就是不同机型的适配,例如,在Retina设备上加载@2x的图片,而在老一些的普通苹设备上加载原图,让程序在运行时添加代码模块以及其他资源,用户可根据需要加载一些可执行代码和资源,而不是在启动时就加载所有组件,可执行代码可以含有和程序运行时整合的新类。
autorelease:即延迟释放。
自动释放池的作用:在程序执行完毕即池子即将销毁的时候会对池子中所有调用autorelease的对象进行一次release操作。
面试题一:自动释放池什么时候创建,什么时候销毁?
每一次运行循环执行后,也就是每当事件被触发时都会创建自动释放池。在程序执行的过程中,所有autorelease的对象在出了作用域之后会被添加到最近创建的自动释放池中。运行循环结束前会释放自动释放池,还有池子满了也会销毁。
其工作原理是:自动释放池被销毁或耗尽时会向池中的所有对象发送release消息,释放所有autorelease对象。
自动释放池常见面试题代码
for (int i = 0; i < 100000; ++i) {
NSString *str = @"Hello World";
str = [str stringByAppendingFormat:@"- %d",i]; //字符串拼接
str = [str uppercaseString]; //将字符串替换成大写
}
如果循环的次数过大,会出现什么问题?该怎么解决?
会出现内存溢出,循环内部创建大量的临时对象,没有被释放
每次循环都将上一次创建的对象release
for(inti =0; i <100000; ++i) {
@autoreleasepool{
NSString*str =@"Hello World";
str = [strstringByAppendingFormat:@"- %d",i];
str = [struppercaseString];
}
}
不会自动管理内存的情况?
编译器不会自动管理CoreFoundation框架的数据类型对象的生命周期,因此使用CoreFoundation框架直接桥接数据类型,就必须手动管理这些对象的内存
AFNetwork
AF底层基于 NSURlsession (session:会议)
AF的五个功能模块
1.网络通信 (AFURLsessionManager)(Manager:管理)
2.网络通讯安全
3.网络状态监听
4.网络通讯序列化反序列化
5.对UIkit的封装
AF的实现流程
通过AFHTTPRequestOperationManager---->(确定请求方式 get ,post ,请求参数)----->给到AFURLRequestSerialization(Serialization:序列化;进行参数拼接)----->AFHTTPRequestOperation(Operation:经营,计算;下放到请求队列 NSOperationQueue中)---->最后由最底层的AFURLconnectionOperation (去完成请求,做https认证)--->数据返回到AFhttpRequestOperation----->通过AFURLResponseSerialization (Response [rɪ'spɒns] 响应)--->进行数据解析成json xml格式 返回到AFHTTPRequestOperationManager中
1、带block形式, 内部是任务队列进行下载 ,就是对operation的一个封装下载。
2、还对uikit 内部的类进行类别形式添加方法(异步下载图片)。
3、还可以检测网络状态。
多线程:
GCD 与 NSOperation 的区别:NSThread
GCD 和 NSOperation 都是用于实现多线程:
GCD 基于C语言的底层API,GCD主要与block结合使用,代码简洁高效。
NSOperation 属于Objective-C类,是基于GCD更高一层的封装。复杂任务一般用NSOperation实现。
performSelectorOnMainThread:
使用NSOperation的情况:各个操作之间有依赖关系、操作需要取消暂停、并发管理、控制操作之间优先级,限制同时能执行的线程数量.让线程在某时刻停止/继续等。
使用GCD的情况:一般的需求很简单的多线程操作,用GCD都可以了,简单高效。
CGD :1.充分利用设备的多核最高效 2.自动管理线程的生命周期3.CGD 是先进先出的 4.基于c实现
NSoperation: 1.基于GCD 封装的 使用更加面向对象 2.可以改变队列的执行顺序3.比GCD多了一些简单实用的功能
GCD 的方法1.获取全局并行队列 dipatch _ get_grolop_queue
2.获取主队列 dispatch_get_main_queue
3.创建串行行队列 dispatch _queue_serial
4.创建并行行队列 dispatch _queue_concurrent [kən'kʌr(ə)nt]
5.延迟提交 dispatch_after【啊 f t】
dispatch_time_t
6.行完成后 dispatch_group_notify
barrier【百瑞额】:表示栅栏,当在并发队列里面使用栅栏时候,栅栏之前的并发任务开始并发执行,执行完毕后,执行栅栏内的任务,等栅栏任务执行完毕后,再并发执行栅栏后的任务。
Objective-C 中创建线程的方法是什么?如果在主线程中执行代码,方法是什么?如果想延时执行代码、方法又是什么?
答:线程创建有三种方法:使用NSThread创建、使用GCD的dispatch、使用子类化的NSOperation,然后将其加入NSOperationQueue;
NSOperation在主线程执行代码,方法是performSelectorOnMainThread,perform[执行]
如果想延时执行代码可以用performSelector:onThread:withObject:waitUntilDone:
GCD:Main Dispatch Queue/Global Dispatch Queue 主队列特殊的串行、全局的并行队列
dispatch _after 延迟执行
FMDB
插入数据 @"insert into user (name, password) values(?, ?) ";表名 (字段1, 字段2...) values (值1, 值2...);
删除数据 @"delete from user where";
更新数据 @"UPDATE USER SET id = ? WHERE name = ?";
FMDatabaseQueue多线程安全: 原理创建一个队列(串行线程队列),然后将放入的队列的block顺序执行,这样避免了多线程同时访问数据库
设计模式
代理 单利 观察者 MVC 工厂
单例模式: 一个类只有一个实例对象。
创建方法:GCD的一次函数(dispatch_once_t)通知中心就是单利
应用场景 封装FMDB 整个程序使用
工厂方法模式:定义创建对象的接口,让子类决定实例化哪一个类。工厂方法使得一个类的实例化延迟到其子类。
应用场景 :多种样式的cell 根据model 产出不同的cell布局
KVO: 的实现依赖于 Runtime实现 . 当某个类的属性对象第一次被观察时,系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的setter 方法。派生类在被重写的setter方法内实现真正的通知机制
KVO 的键值观察通知依赖于 NSObject 的两个方法:willChangeValueForKey:和 didChangevlueForKey:
KVC:即键值编码
1,对私有变量进行赋值
2,字典转模型
当观察某对象 A 时,KVO 机制动态创建一个对象A当前类的子类,并为这个新的子类重写了被观察属性 keyPath 的 setter 方法。setter 方法随后负责通知观察对象属性的改变状况。
Notification 通知
默认是以同步的方式发送通知的;
异步发送通知:将通知加到通知队列中,就可以将一个通知异步的发送到当前的线程,这些方法调用后会立即返回,不用再等待通知的所有监听者都接收并处理完。
做多线程开发时,需要在线程调度方法中手动添加自动释放池,尤其是当执行循环的时候,如果循环内部有使用类的快速创建方法创建的对象, 一定要将循环体放到自动释放池中。
Block:获取其它函数局部变量的匿名函数
匿名函数:没有函数名的函数,一对{}包裹的内容是匿名函数的作用域,OC中的Block则可以用指针来直接调用一个函数;
虽然使用Block不用声明类,但是Block提供了类似Objective-C的类一样可以通过成员变量来保存作用域外变量值的方法,那些在Block的一对{}里使用到但却是在{}作用域以外声明的变量,就是Block截获的自动变量。
block的注意点
1). 在block内部使用外部指针且会造成循环引用情况下,需要用__week修饰外部指针:
__weak typeof(self) weakSelf = self;
2). 在block内部如果调用了延时函数还使用弱指针会取不到该指针,因为已经被销毁了,需要在block内部再将弱指针重新强引用一下。
__strong typeof(self) strongSelf = weakSelf;
3). 如果需要在block内部改变外部栈区变量的话,需要在用__block修饰外部变量。
__block在__main_block_func_0里面可以看到传递的是指针。所以成功改变了变量的值。
什么时候在 block 中不需要使用 weakSelf
问题:我们知道,在使用 block 的时候,为了避免产生循环引用,通常需要使用 weakSelf 与 strongSelf,写下面这样的代码:
__weaktypeof(self) weakSelf =self
;[selfdoSomeBlockJob:^{
__strongtypeof(weakSelf) strongSelf = weakSelf;if(strongSelf)
{ ... }}];
那么请问:什么时候在 block里面用self,不需要使用weakself?
当block本身不被self 持有,而被别的对象持有,同时不产生循环引用的时候,就不需要使用weakself了。最常见的代码就是UIView的动画代码,我们在使用UIView animateWithDuration:animations方法 做动画的时候,并不需要使用weakself,因为引用持有关系是:
UIView 的某个负责动画的对象持有block,block 持有了self因为 self 并不持有 block,所以就没有循环引用产生,因为就不需要使用 weak self 了。
[UIView animateWithDuration:0.2 animations:^{
self.alpha = 1;
}];
当动画结束时,UIView会结束持有这个 block,如果没有别的对象持有block的话,block 对象就会释放掉,从而 block会释放掉对于 self 的持有。整个内存引用关系被解除。
Block不允许修改外部变量的值,这里所说的外部变量的值,指的是栈中指针的内存地址。__block 所起到的作用就是只要观察到该变量被 block 所持有,就将“外部变量”在栈中的内存地址放到了堆中。进而在block内部也可以修改外部变量的值。
介绍一下分类,能用分类做什么?内部是如何实现的?它为什么会覆盖掉原来的方法?
category:我们可以给类或者系统类添加实例方法方法。我们添加的实例方法,会被动态的添加到类结构里面的methodList列表里面。categort
类别 (category)和扩展(Extension) 继承
使用分类(category)。
1.分类(category)的作用
1.1作用:可以在不修改原来类的基础上,为一个类扩展方法。
1.2最主要的用法:给系统自带的类扩展方法。
1.类扩展(extension)是category的一个特例,有时候也被称为匿名分类。他的作用是为一个类添加一些私有的成员变量和方法。
继承:子类不仅拥有父类所有的属性和方法,而且可以创建属于自己的属性和方法。
iOS手动局框架-Masonry
运用JavaScriptCore框架进行交互
在iOS 7之后,apple添加了一个新的库JavaScriptCore,用来做JS交互,因此JS与原生OC交互也变得简单了许多。这是我做的一个例子
- (void)webViewDidFinishLoad:(UIWebView *)webView {
self.webTitle = [self.webView stringByEvaluatingJavaScriptFromString:@"document.title"];
[self needNavigationleftButtonTitle:nil RightButtonTiele:nil TitleLable:self.webTitle targat:self];
self.jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
self.jsContext[@"submitFrom"] = self;
self.jsContext.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) {
context.exception = exceptionValue;
NSLog(@"异常信息:%@", exceptionValue);
};
}
析核心动画CoreAnimation
基础动画(CABaseAnimation)
关键帧动画(CAKeyframeAnimation)
组动画(CAAnimationGroup)
过渡动画(CATransition)
Instruments提供了很多功能,我会重点介绍一下我最常用的几类:
1.Time Profiler:CPU分析工具分析代码的执行时间。
2.Core Animation:离屏渲染,图层混合等GPU耗时。
3.Leaks:内存检测,内存泄漏检测工具。
4.Energy Log:耗电检测工具。
5.Network:流量检测工具。
栈区和堆区
栈区(stack)由编译器自动分配释放 ,存放方法(函数)的参数值, 局部变量的值等,栈是向低地址扩展的数据结构,是一块连续的内存的区域。即栈顶的地址和栈的最大容量是系统预先规定好的。
堆区(heap)一般由程序员分配释放, 若程序员不释放,程序结束时由OS回收,向高地址扩展的数据结构,是不连续的内存区域,从而堆获得的空间比较灵活。
碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出.
全局区(静态区)(static),全局变量和静态变量的存储是放在一块 的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后有系统释放。
文字常量区—常量字符串就是放在这里的。程序结束后由系统释放。
程序代码区—存放函数体的二进制代码
CornerStone使用分享[康那思透恩]
CornerStone是mac下非常流行的一个SVN管理工具,mac版本svn管理中最推荐使用的一个。
用@property声明的 NSString / NSArray / NSDictionary 经常使用 copy 关键字,为什么?如果改用strong关键字,可能造成什么问题?
答:用 @property 声明 NSString、NSArray、NSDictionary 经常使用 copy 关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary,他们之间可能进行赋值操作(就是把可变的赋值给不可变的),为确保对象中的字符串值不会无意间变动,应该在设置新属性值时拷贝一份。
1. 因为父类指针可以指向子类对象,使用 copy 的目的是为了让本对象的属性不受外界影响,使用 copy 无论给我传入是一个可变对象还是不可对象,我本身持有的就是一个不可变的副本。
2. 如果我们使用是 strong ,那么这个属性就有可能指向一个可变对象,如果这个可变对象在外部被修改了,那么会影响该属性。
//总结:使用copy的目的是,防止把可变类型的对象赋值给不可变类型的对象时,可变类型对象的值发送变化会无意间篡改不可变类型对象原来的值。
@synthesize 和 @dynamic 分别有什么作用?
@property有两个对应的词,一个是@synthesize(合成实例变量),一个是@dynamic。
如果@synthesize和@dynamic都没有写,那么默认的就是 @synthesize var = _var;
// 在类的实现代码里通过 @synthesize 语法可以来指定实例变量的名字。(@synthesize var = _newVar;)
1. @synthesize 的语义是如果你没有手动实现setter方法和getter方法,那么编译器会自动为你加上这两个方法。
2. @dynamic 告诉编译器,属性的setter与getter方法由用户自己实现,不自动生成(如,@dynamic var)。
如何对iOS设备进行性能测试?
答: Profile-> Instruments[in吹门特] ->Time Profiler
写一个完整的代理,包括声明、实现
// 创建
@protocol MyDelagate
@required
-(void)eat:(NSString *)foodName;
@optional
-(void)run;
@end
// 声明 .h
@interface person: NSObject
@end
// 实现 .m
@implementation person
- (void)eat:(NSString *)foodName {
NSLog(@"吃:%@!", foodName);
}
- (void)run {
NSLog(@"run!");
}
@end
iOS的沙盒目录结构是怎样的?
沙盒结构:
1). Application:存放程序源文件,上架前经过数字签名,上架后不可修改。
2). Documents:常用目录,iCloud备份目录,存放数据。(这里不能存缓存文件,否则上架不被通过)
3). Library:
Caches:存放体积大又不需要备份的数据。(常用的缓存路径)
Preference:设置目录,iCloud会备份设置信息。
4). tmp:存放临时文件,不会被备份,而且这个文件下的数据有可能随时被清除的可能。
介绍一下XMPP?
XMPP是一种以XML为基础的开放式实时通信协议。
简单的说,XMPP就是一种协议,一种规定。就是说,在网络上传东西,XMM就是规定你上传大小的格式。
冒泡排序
/**
* 【冒泡排序】:相邻元素两两比较,比较完一趟,最值出现在末尾
* 第1趟:依次比较相邻的两个数,不断交换(小数放前,大数放后)逐个推进,最值最后出现在第n个元素位置
* 第2趟:依次比较相邻的两个数,不断交换(小数放前,大数放后)逐个推进,最值最后出现在第n-1个元素位置
* …… ……
* 第n-1趟:依次比较相邻的两个数,不断交换(小数放前,大数放后)逐个推进,最值最后出现在第2个元素位置
*/
void bublleSort(int *arr, int length) {
for(int i = 0; i < length - 1; i++) { //趟数
for(int j = 0; j < length - i - 1; j++) { //比较次数
if(arr[j] > arr[j+1]) {
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
热更新
Weex
跨平台,一套代码,iOS、Android都可以运行。用前端语法实现原生效果。比React Native更好用。
weex基于vue.js,ReactNative使用React。
ReactNative安装配置麻烦。 weex安装cli之后就可以使用。
react模板JSX有一定的学习成本,vue和常用的web开发类似,模板是普通的html,数据绑定用mustache风格,样式直接使用css。
四、React Native
不像Weex能一套代码多端运行,需要自己分别做修改。
React Native 可以动态添加业务模块,但无法做到修改原生OC代码。
JSPatch、lua 配合React Native可以让一个原生APP时刻处于可扩展可修改的状态。
UIImage初始化一张图片有几种方法?简述各自的优缺点。
imageNamed:系统会先检查系统缓存中是否有该名字的Image,如果有的话,则直接返回,如果没有,则先加载图像到缓存,然后再返回。
initWithContentsOfFile【文件的内容】:系统不会检查系统缓存,而直接从文件系统中加载并返回。
函数式编程
函数式编程总结
如果想再去调用别的方法,那么就需要返回一个对象;
如果想用()去执行,那么需要返回一个block;
如果想让返回的block再调用对象的方法,那么这个block就需要返回一个对象(即返回值为一个对象的block)。