面试题参考5 : 面试 面试题参考6 : runtime理解
面试题参考7 : runloop理解 面试题参考8 : 多线程的理解
面试题参考9 : Cocoa和Cocoa Touch框架浅析
面试题参考10 :面试题参考
面试题参考11 : runtime详解
面试题参考8 :
面试题参考8 :
面试题参考8 :
面试题参考8 :
面试题参考8 :
面试题参考8 :
面试题参考8 :
面试题参考8 :
面试题参考8 :
面试题参考8 :
面试题参考8 :
面试题参考8 :
1)在-和(void)之间应该有一个空格
2)enum中驼峰命名法和下划线命名法混用错误:枚举类型的命名规则和函数的命名规则相同:命名时使用驼峰命名法,勿使用下划线命名法。
3)enum左括号前加一个空格,或者将左括号换到下一行
4)enum右括号后加一个空格
5)UserModel :NSObject 应为UserModel : NSObject,也就是:右侧少了一个空格。
6)@interface与@property属性声明中间应当间隔一行。
7)两个方法定义之间不需要换行,有时为了区分方法的功能也可间隔一行,但示例代码中间隔了两行。
8)-(id)initUserModelWithUserName: (NSString*)name withAge:(int)age;方法中方法名与参数之间多了空格。而且- 与(id)之间少了空格。
9)-(id)initUserModelWithUserName: (NSString*)name withAge:(int)age;方法中方法名与参数之间多了空格:(NSString*)name前多了空格。
10)-(id)initUserModelWithUserName: (NSString*)name withAge:(int)age;方法中(NSString*)name,应为(NSString *)name,少了空格。
11)doLogIn方法命名不清晰:笔者猜测是login的意思,应该是粗心手误造成的。
12)第二个@property中assign和nonatomic调换位置
weak属性需要在dealloc中置nil么?
不需要。
在ARC环境无论是强指针还是弱指针都无需在deallco设置为nil,ARC会自动帮我们处理。
即便是编译器不帮我们做这些,weak也不需要在dealloc中置nil:
用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?
1)因为父类指针可以指向子类对象,使用copy的目的是为了让本对象的属性不受外界影响,使用copy无论给我传入是一个可变对象还是不可对象,我本身持有的就是一个不可变的副本.
2)如果我们使用是strong,那么这个属性就有可能指向一个可变对象,如果这个可变对象在外部被修改了,那么会影响该属性.
copy此特质所表达的所属关系与strong类似。然而设置方法并不保留新值,而是将其“拷贝” (copy)。 当属性类型为NSString时,经常用此特质来保护其封装性,因为传递给设置方法的新值有可能指向一个NSMutableString类的实例。这个类是NSString的子类,表示一种可修改其值的字符串,此时若是不拷贝字符串,那么设置完属性之后,字符串的值就可能会在对象不知情的情况下遭人更改。所以,这时就要拷贝一份“不可变” (immutable)的字符串,确保对象中的字符串值不会无意间变动。只要实现属性所用的对象是“可变的” (mutable),就应该在设置新属性值时拷贝一份。
为了理解这种做法,首先要知道,对非集合类对象的copy操作:
在非集合类对象中:对immutable对象进行copy操作,是指针复制,mutableCopy操作时内容复制;对mutable对象进行copy和mutableCopy都是内容复制。用代码简单表示如下:
[immutableObject copy] // 浅复制
[immutableObject mutableCopy] //深复制
[mutableObject copy] //深复制
[mutableObject mutableCopy] //深复制
比如以下代码:
NSMutableString *string = [NSMutableString stringWithString:@"origin"];//copy
NSString *stringCopy = [string copy];
查看内存,会发现 string、stringCopy 内存地址都不一样,说明此时都是做内容拷贝、深拷贝。即使你进行如下操作:
1[string appendString:@"origion!"]
stringCopy的值也不会因此改变,但是如果不使用copy,stringCopy的值就会被改变。 集合类对象以此类推。 所以,
用@property声明 NSString、NSArray、NSDictionary 经常使用copy关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary,他们之间可能进行赋值操作,为确保对象中的字符串值不会无意间变动,应该在设置新属性值时拷贝一份。
下面的代码输出什么?
@implementation Son : Father
- (id)init
{
self = [superinit];
if(self) {
NSLog(@"%@", NSStringFromClass([self class]));
NSLog(@"%@", NSStringFromClass([superclass]));
}
returnself;
}
@end
答案:
都输出 Son
NSStringFromClass([self class]) = Son
NSStringFromClass([superclass]) = Son
上面的例子不管调用[self class]还是[super class],接受消息的对象都是当前 Son *xxx 这个对象。而不同的是,super是告诉编译器,调用 class 这个方法时,要去父类的方法,而不是本类里的。
当使用 self 调用方法时,会从当前类的方法列表中开始找,如果没有,就从父类中再找;而当使用 super 时,则从父类的方法列表中开始找。然后调用父类的这个方法。
2. 什么情况使用 weak 关键字,相比 assign 有什么不同?
什么情况使用 weak 关键字?
1)在ARC中,在有可能出现循环引用的时候,往往要通过让其中一端使用weak来解决,比如:delegate代理属性
2)自身已经对它进行一次强引用,没有必要再强引用一次,此时也会使用weak,自定义IBOutlet控件属性一般也使用weak;当然,也可以使用strong。在下文也有论述:《IBOutlet连出来的视图属性为什么可以被设置成weak?》
不同点:
1)weak 此特质表明该属性定义了一种“非拥有关系” (nonowning relationship)。为这种属性设置新值时,设置方法既不保留新值,也不释放旧值。此特质同assign类似, 然而在属性所指的对象遭到摧毁时,属性值也会清空(nil out)。 而 assign 的“设置方法”只会执行针对“纯量类型” (scalar type,例如 CGFloat 或 NSlnteger 等)的简单赋值操作。
2)assigin 可以用非OC对象,而weak必须用于OC对象
@synthesize 和 @dynamic 分别有什么作用?
@property有两个对应的词,一个是@synthesize(合成实例变量),一个是@dynamic。
如果@synthesize和@dynamic都没有写,那么默认的就是 @synthesize var = _var;
// 在类的实现代码里通过 @synthesize 语法可以来指定实例变量的名字。(@synthesize var = _newVar;)
1. @synthesize 的语义是如果你没有手动实现setter方法和getter方法,那么编译器会自动为你加上这两个方法。
2. @dynamic 告诉编译器,属性的setter与getter方法由用户自己实现,不自动生成(如,@dynamic var)。
如何用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(), ^{
// 合并图片
});
写出使用GCD方式从子线程回到主线程的方法代码
答:dispatch_sync(dispatch_get_main_queue(), ^{ });
你一般是怎么用Instruments的?
Instruments里面工具很多,常用:
1). Time Profiler: 性能分析
2). Zombies:检查是否访问了僵尸对象,但是这个工具只能从上往下检查,不智能。
3). Allocations:用来检查内存,写算法的那批人也用这个来检查。
4). Leaks:检查内存,看是否有内存泄露。
11.内存管理语义(assign、strong、weak等的区别)
assign “设置方法” 只会执行针对“纯量”的简单赋值操作。
strong 此特质表明该属性定义了一种“拥有关系”。为这种属性设置新值时,设置方法会先保留新值,并释放旧值,然后再将新值设置上去。
weak 此特质表明该属性定义了一种“非拥有关系”。为这种属性设置新值时,设置方法既不保留新值,也不释放旧值。此特质同assign类似,然而在属性所指的对象遭到推毁时,属性值也会清空。
unsafe_unretained 此特质的语义和assign相同,但是它适用于“对象类型”,该特质表达一种“非拥有关系”,当目标对象遭到推毁时,属性值不会自动清空,这一点与weak有区别。
copy 此特质所表达的所属关系与strong类似。然而设置方法并不保留新值,而是设置方法并不保留新值,而是将其“拷贝”。当属性类型为NSString*时,经常用此特质来保护其封装性,因为传递给设置方法的新值有可能指向一个NSMutableString类的实例。这个类是NSString的子类,表示一种可以修改其值的字符串,此时若是不拷贝字符串,那么设置完属性之后,字符串的值就可能会在对象不知情的情况下遭人更改。所以,这时就要拷贝一份“不可变”的字符串,确保对象中的字符串值不会无意间变动。只要实现属性所用的对象是“可变的”,就应该在设置新属性值时拷贝一份。
7.说说assign vs weak,_block vs _weak的区别
assign适用于基本数据类型,weak是适用于NSObject对象,并且是一个弱引用。
assign其实页可以用来修饰对象,那么为什么不用它呢?因为被assign修饰的对象在释放之后,指针的地址还是存在的,也就是说指针并没有被置为nil。如果在后续内存分配中,刚才分到了这块地址,程序就会崩溃掉。而weak修饰的对象在释放之后,指针地址会被置为nil。
_block是用来修饰一个变量,这个变量就可以在block中被修改。
_block:使用_block修饰的变量在block代码块中会被retain(ARC下,MRC下不会retain)
_weak:使用_weak修饰的变量不会在block代码块中被retain
nonatomic和atomic的区别?atomic是绝对的线程安全么?为什么?如果不是,那应该如何实现?
nonatomic:表示非原子,不安全,但是效率高。
atomic:表示原子行,安全,但是效率定。
atomic:不能绝对保证线程的安全,当多线程同时访问的时候,会造成线程不安全。可以使用线程锁来保证线程的安全。
dispatch_sync添加到主线程关联的串行队列卡死问题理解
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"1");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"2");
});
NSLog(@"3");
// Do any additional setup after loading the view, typically from a nib.
}
上面的代码只会打印1 然会就会线程卡死,这是为什么呢?因为dispatch_get_main_queue()得到的是一个串行队列,串行队列的特点: 一次只调度一个任务,队列中的任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)同步(sync) 操作,它会阻塞当前线程并等待 Block 中的任务执行完毕,然后当前线程才会继续往下运行dispatch_sync提交一个打印任务NSLog(@”2”)到主线程关联的串行队列中,主线程关联的串行队列现在有一个viewDidLoad任务,打印任务NSLog(@”2”)排在viewDidLoad后面,队列FIFO(先进先出)的原则,打印任务NSLog(@”2”);想要得到执行必须等到viewDidLoad执行完毕后才能得到执行,但是viewDidLoad想要执行完毕必须要等打印任务NSLog(@”2”)执行完毕,所以就卡死在这了。
第三方框架
AFNetworking 底层原理分析
AFNetworking主要是对NSURLSession和NSURLConnection(iOS9.0废弃)的封装,其中主要有以下类:
1). AFHTTPRequestOperationManager:内部封装的是 NSURLConnection, 负责发送网络请求, 使用最多的一个类。(3.0废弃)
2). AFHTTPSessionManager:内部封装是 NSURLSession, 负责发送网络请求,使用最多的一个类。
3). AFNetworkReachabilityManager:实时监测网络状态的工具类。当前的网络环境发生改变之后,这个工具类就可以检测到。
4). AFSecurityPolicy:网络安全的工具类, 主要是针对 HTTPS 服务。
5). AFURLRequestSerialization:序列化工具类,基类。上传的数据转换成JSON格式
(AFJSONRequestSerializer).使用不多。
6). AFURLResponseSerialization:反序列化工具类;基类.使用比较多:
7). AFJSONResponseSerializer; JSON解析器,默认的解析器.
8). AFHTTPResponseSerializer; 万能解析器; JSON和XML之外的数据类型,直接返回二进制数据.对服务器返回的数据不做任何处理.
9). AFXMLParserResponseSerializer; XML解析器;
商户系统和微信支付系统主要交互说明:
步骤1:用户在商户APP中选择商品,提交订单,选择微信支付。
步骤2:商户后台收到用户支付单,调用微信支付统一下单接口。参见【统一下单API】。
步骤3:统一下单接口返回正常的prepay_id,再按签名规范重新生成签名后,将数据传输给APP。参与签名的字段名为appid,partnerid,prepayid,noncestr,timestamp,package。注意:package的值格式为Sign=WXPay
步骤4:商户APP调起微信支付。api参见本章节【app端开发步骤说明】步骤5:商户后台接收支付通知。api参见【支付结果通知API】
步骤6:商户后台查询支付结果。,api参见【查询订单API】
Xcode 升级Xcode7后模拟器不能连接网络解决方法
1- 在Info.plist中添加NSAPPTransportSecurity类型Dictionary。
2- 在NSAppTransportSecurity下添加NSAllowsArbitraryLoads类型Boolean,值设为YES
UITableView优化?
UITableViewCell的重用机制能够提高cell的使用效率,减少了内存消耗;
UITableview是先计算cell的高度确定将要显示的cell(所有cell的height都会刷新),再显示cell(只刷新显示的cell),所以主要优化在 tableView:heightForRowAtIndexPath 与 tableView:cellForRowAtIndexPath 方法;
heightForRowAtIndexPath是调用最频繁的方法;
height计算与cell填充分离开,不要重叠;
缓冲数据直接读取,数据不需要动态更新的,不需要每次计算;
直接绘绘制控件(在cell上添加系统控件的时候,实质上系统都需要调用底层的接口进行绘制,当我们添加大量控件时,对资源的开销很大,所以可以直接绘制,提高效率);
//异步绘制dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{CGRectrect = [_data[@"frame"]CGRectValue];UIGraphicsBeginImageContextWithOptions(rect.size,YES,0);CGContextRefcontext =UIGraphicsGetCurrentContext();//整个内容的背景[[UIColorcolorWithRed:250/255.0green:250/255.0blue:250/255.0alpha:1] set];CGContextFillRect(context, rect);//内容如果是图文混排,就添加View,用CoreText绘制//图文混排,可以查阅Google,研究下CoreText,内容比较多[selfdrawText];}
滑动很快时,按需加载目标范围内的cell,可提高流畅度;在大量图片展示时,网络加载比较有效(SDWebImage已经实现异步加载);
尽量使所有的view opaque(不透明),包括cell自身;
尽量少用或不用透明图层(非常耗时);
避免使用圆角、阴影、遮罩等属性;
如果cell内显示的内容矮子web,使用异步加载,缓存请求结果;
减少subviews的数量;
在heightForRowAtIndexPath:中尽量不使用cellForRowAtIndexPath:,如果你需要用到它,只用一次然后缓存结果;
尽量少用addView给Cell动态添加View,开始初始化时添加,通过hide来控制显示;
想提高效率,还是手动写cell,不要用xib或storyboard(看情况而论);
将GPU的部分渲染转给CPU,GPU与CPU的合理分配使用(了解gitbook上图像渲染的知识点);
应用程序生命周期/UIViewController生命周期
ViewController程序生命周期:alloc创建分配内存、init初始化数据,可用、loadView视图加载,如果重写必须要创建一个 view。viewDidLoad视图加载完成,可以将一些subViews的添加在这里处理。viewWillAppear视图将要出现,处理一些过渡 动画。viewDidAppear,视图已经出现。viewWillDisApear视图将要消失,viewDidDisApear视图已经消失。 Dealloc释放一些对象,释放空间。
应用程序生命周期:DidLoad应用程序加载完成。
ViewControler 的生命周期
按照执行顺序排列:
1. initWithCoder:通过nib文件初始化时触发。
2. awakeFromNib:nib文件被加载的时候,会发生一个awakeFromNib的消息到nib文件中的每个对象。
3. loadView:开始加载视图控制器自带的view。
4. viewDidLoad:视图控制器的view被加载完成。
5. viewWillAppear:视图控制器的view将要显示在window上。
6. updateViewConstraints:视图控制器的view开始更新AutoLayout约束。
7. viewWillLayoutSubviews:视图控制器的view将要更新内容视图的位置。
8. viewDidLayoutSubviews:视图控制器的view已经更新视图的位置。
9. viewDidAppear:视图控制器的view已经展示到window上。
10. viewWillDisappear:视图控制器的view将要从window上消失。
11. viewDidDisappear:视图控制器的view已经从window上消失。
OC 有没有私有方法? 如果没有怎么实现方法的私有化
严格来说OC没有私有方法,最简单就是在.m中定义方法而不是在.h中。
动态隐藏NavigationBar
//1.当我们的手离开屏幕时候隐藏- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset{if(velocity.y >0) {[self.navigationController setNavigationBarHidden:YES animated:YES];}else {
[self.navigationController setNavigationBarHidden:NO animated:YES];
}
}
velocity.y这个量,在上滑和下滑时,变化极小(小数),但是因为方向不同,有正负之分,这就很好处理了。
//2.在滑动过程中隐藏//像safari(1) self.navigationController.hidesBarsOnSwipe = YES;(2)- (void)scrollViewDidScroll:(UIScrollView *)scrollView{ CGFloat offsetY = scrollView.contentOffset.y + __tableView.contentInset.top; CGFloat panTranslationY = [scrollView.panGestureRecognizer translationInView:self.tableView].y;if (offsetY >64) {if (panTranslationY >0) { //下滑趋势,显示 [self.navigationController setNavigationBarHidden:NO animated:YES];}else { //上滑趋势,隐藏 [self.navigationController setNavigationBarHidden:YES animated:YES]; }}else {[self.navigationController setNavigationBarHidden:NO animated:YES]; }}这里的offsetY >64只是为了在视图滑过navigationBar的高度之后才开始处理,防止影响展示效果。panTranslationY是scrollView的pan手势的手指位置的y值,可能不是太好,因为panTranslationY这个值在较小幅度上下滑动时,可能都为正或都为负,这就使得这一方式不太灵敏.
效果图
NSDate与NSString的相互转化
-(NSString *)dateToString:(NSDate *)date { // 初始化时间格式控制器 NSDateFormatter *matter = [[NSDateFormatter alloc] init]; // 设置设计格式 [matter setDateFormat:@"yyyy-MM-dd hh:mm:ss zzz"]; // 进行转换 NSString *dateStr = [matter stringFromDate:date];return dateStr;}-(NSDate *)stringToDate:(NSString *)dateStr { // 初始化时间格式控制器 NSDateFormatter *matter = [[NSDateFormatter alloc] init]; // 设置设计格式 [matter setDateFormat:@"yyyy-MM-dd hh:mm:ss zzz"]; // 进行转换 NSDate *date = [matter dateFromString:dateStr];return date;
}
判断NSString中是否包含中文
-(BOOL)isChinese:(NSString *)str{ NSString *match=@"(^[\u4e00-\u9fa5]+$)"; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF matches %@", match];return [predicate evaluateWithObject:str];
}
MRC和ARC混编设置方式
在XCode中targets的build phases选项下Compile Sources下选择 不需要arc编译的文件
双击输入 -fno-objc-arc 即可
MRC工程中也可以使用ARC的类,方法如下:
在XCode中targets的build phases选项下Compile Sources下选择要使用arc编译的文件
双击输入 -fobjc-arc 即可
UIImageView填充模式
@"UIViewContentModeScaleToFill", // 拉伸自适应填满整个视图@"UIViewContentModeScaleAspectFit", // 自适应比例大小显示@"UIViewContentModeScaleAspectFill", // 原始大小显示@"UIViewContentModeRedraw", // 尺寸改变时重绘@"UIViewContentModeCenter", // 中间@"UIViewContentModeTop", // 顶部@"UIViewContentModeBottom", // 底部@"UIViewContentModeLeft", // 中间贴左@"UIViewContentModeRight", // 中间贴右@"UIViewContentModeTopLeft", // 贴左上@"UIViewContentModeTopRight", // 贴右上@"UIViewContentModeBottomLeft", // 贴左下@"UIViewContentModeBottomRight", // 贴右下
我们说的oc是动态运行时语言是什么意思?
答:多态。 主要是将数据类型的确定由编译时,推迟到了运行时。
这个问题其实浅涉及到两个概念,运行时和多态。
简单来说,运行时机制使我们直到运行时才去决定一个对象的类别,以及调用该类别对象指定方法。
多态:不同对象以自己的方式响应相同的消息的能力叫做多态。意思就是假设生物类(life)都用有一个相同的方法-eat;
那人类属于生物,猪也属于生物,都继承了life后,实现各自的eat,但是调用是我们只需调用各自的eat方法。
也就是不同的对象以自己的方式响应了相同的消息(响应了eat这个选择器)。
因此也可以说,运行时机制是多态的基础?~~~
分别讲一下你对面向对象中封装、继承、多态的理解
封装:把东西抽离出来一个类,提供api接口,封装是实现面向对象程序设计的第一步,封装就是将数据或函数等集合在一个个的单元中(我们称之为类)。被封装的对象通常被称为抽象数据类型。
继承:继承一个父类,有其父类的所有方法和属性,继承主要实现重用代码,节省开发时间。
多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。
说说响应链
答: 事件响应链。包括点击事件,画面刷新事件等。在视图栈内从上至下,或者从下之上传播。
可以说点事件的分发,传递以及处理。具体可以去看下touch事件这块。因为问的太抽象化了
严重怀疑题目出到越后面就越笼统。
可以从责任链模式,来讲通过事件响应链处理,其拥有的扩展性
分别讲一下你对面向对象中封装、继承、多态的理解
封装:把东西抽离出来一个类,提供api接口,封装是实现面向对象程序设计的第一步,封装就是将数据或函数等集合在一个个的单元中(我们称之为类)。被封装的对象通常被称为抽象数据类型。 继承:继承一个父类,有其父类的所有方法和属性,继承主要实现重用代码,节省开发时间。 多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。
关于多态性
答:多态,子类指针可以赋值给父类。
这个题目其实可以出到一切面向对象语言中,
因此关于多态,继承和封装基本最好都有个自我意识的理解,也并非一定要把书上资料上写的能背出来
C和obj-c 如何混用
答: 1).obj-c的编译器处理后缀为m的文件时,可以识别obj-c和c的代码,处理mm文件可以识别obj-c,c,c++代码,但cpp文件必须只能用c/c++代码,而且cpp文件include的头文件中,也不能出现obj-c的代码,因为cpp只是cpp
2).在mm文件中混用cpp直接使用即可,所以obj-c混cpp不是问题
3).在cpp中混用obj-c其实就是使用obj-c编写的模块是我们想要的。
如果模块以类实现,那么要按照cpp class的标准写类的定义,头文件中不能出现obj-c的东西,包括#import cocoa的。实现文件中,即类的实现代码中可以使用obj-c的东西,可以import,只是后缀是mm。
如果模块以函数实现,那么头文件要按c的格式声明函数,实现文件中,c++函数内部可以用obj-c,但后缀还是mm或m。
总结:只要cpp文件和cpp include的文件中不包含obj-c的东西就可以用了,cpp混用obj-c的关键是使用接口,而不能直接使用 实现代 码,实际上cpp混用的是obj-c编译后的o文件,这个东西其实是无差别的,所以可以用。obj-c的编译器支持cpp
功能列表:框架分类
下面是 Cocoa Touch 中一小部分可用的框架:
音频和视频:Core Audio ,OpenAL ,Media Library ,AV Foundation
数据管理 :Core Data ,SQLite
图形和动画 :Core Animation ,OpenGL ES ,Quartz 2D
网络:Bonjour ,WebKit ,BSD Sockets
用户应用:Address Book ,Core Location ,Map Kit ,Store Kit
static 关键字的作用:
答:
1).函数体内 static 变量的作用范围为该函数体,不同于 auto 变量,该变量的内存只被分配一次,
因此其值在下次调用时仍维持上次的值;
2).在模块内的 static 全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;
3).在模块内的 static 函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明
它的模块内;
4).在类中的 static 成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;
5).在类中的 static 成员函数属于整个类所拥有,这个函数不接收 this 指针,因而只能访问类的static 成员变量。
iOS的沙盒目录结构是怎样的?
沙盒结构:
1). Application:存放程序源文件,上架前经过数字签名,上架后不可修改。
2). Documents:常用目录,iCloud备份目录,存放数据。(这里不能存缓存文件,否则上架不被通过)
3). Library:
Caches:存放体积大又不需要备份的数据。(常用的缓存路径)
Preference:设置目录,iCloud会备份设置信息。
4). tmp:存放临时文件,不会被备份,而且这个文件下的数据有可能随时被清除的可能。
如何理解MVC设计模式
MVC是一种架构模式,M表示MOdel,V表示视图View,C表示控制器Controller:
Model负责存储、定义、操作数据;
View用来展示书给用户,和用户进行操作交互;
Controller是Model和View的协调者,Controller把Model中的数据拿过来给View用。Controller可以直接与Model和View进行通信,而View不能和Controller直接通信。View与Controller通信需要利用代理协议的方式,当有数据更新时,MOdel也要与Controller进行通信,这个时候就要用Notification和KVO,这个方式就像一个广播一样,MOdel发信号,Controller设置监听接受信号,当有数据更新时就发信号给Controller,Model和View不能直接进行通信,这样会违背MVC设计模式。
如何理解MVVM设计模式。
ViewModel层,就是View和Model层的粘合剂,他是一个放置用户输入验证逻辑,视图显示逻辑,发起网络请求和其他各种各样的代码的极好的地方。说白了,就是把原来ViewController层的业务逻辑和页面逻辑等剥离出来放到ViewModel层。
View层,就是ViewController层,他的任务就是从ViewModel层获取数据,然后显示。
设计模式是什么? 你知道哪些设计模式,并简要叙述?
设计模式是一种编码经验,就是用比较成熟的逻辑去处理某一种类型的事情。
1). MVC模式:Model View Control,把模型 视图 控制器 层进行解耦合编写。
2). MVVM模式:Model View ViewModel 把模型 视图 业务逻辑 层进行解耦和编写。
3). 单例模式:通过static关键词,声明全局变量。在整个进程运行期间只会被赋值一次。
4). 观察者模式:KVO是典型的通知模式,观察某个属性的状态,状态发生变化时通知观察者。
5). 委托模式:代理+协议的组合。实现1对1的反向传值操作。
6). 工厂模式:通过一个类方法,批量的根据已有模板生产对象。
MVC 和 MVVM 的区别
1). MVVM是对胖模型进行的拆分,其本质是给控制器减负,将一些弱业务逻辑放到VM中去处理。
2). MVC是一切设计的基础,所有新的设计模式都是基于MVC进行的改进。
简述类目category优点和缺点。
优点:
不需要通过增加子类而增加现有类的行为(方法),且类目中的方法与原始类方法基本没有区别;
通过类目可以将庞大一个类的方法进行划分,从而便于代码的日后的维护、更新以及提高代码的阅读性;
缺点:
无法向类目添加实例变量,如果需要添加实例变量,只能通过定义子类的方式;
类目中的方法与原始类以及父类方法相比具有更高优先级,如果覆盖父类的方法,可能导致super消息的断裂。因此,最好不要覆盖原始类中的方法。
类别的作用
给系统原有类添加方法,不能扩展属性。如果类别中方法的名字跟系统的方法名一样,在调用的时候类别中的方法优先级更高;
分散类的实现:如:
+ (NSIndexPath *)indexPathForRow:(NSInteger)row
inSection:(NSInteger)section
原本属于NSIndexPath的方法,但因为这个方法经常使用的表的时候调用、跟表的关系特别密切,因此把这个方法一类别的形式、声明在UITableView.h中。
声明私有方法,某一个方法只实现,不声明,相当于私有方法。
类别不能声明变量,类别不可以直接添加属性。property描述setter方法,就不会报错。
键值编码KVC
键值编码是一种间接访问对象的属性使用字符串来标识属性,而不是通过调用存取方法,直接或通过实例变量访问的机制,非对象类型的变量将被自动封装或者解封成对象,很多情况下会简化程序代码;
KVC的缺点:一旦使用 KVC 你的编译器无法检查出错误,即不会对设置的键、键路径进行错误检查,且执行效率要低于合成存取器方法和自定的 setter 和 getter 方法。因为使用 KVC 键值编码,它必须先解析字符串,然后在设置或者访问对象的实例变量。
键值观察KVO
KVO(key-Value-Observing):键值观察机制 他提供了观察某一属性变化的方法,极大的简化了代码。
KVO只能被KVC触发,包括使用setValue:forKey:方法和点语法。
键值观察机制是一种能使得对象获取到其他对象属性变化的通知 ,极大的简化了代码。
实现 KVO 键值观察模式,被观察的对象必须使用 KVC 键值编码来修 改它的实例变量,这样才能被观察者观察到。因此,KVC是KVO的基础。
Demo
比如我自定义的一个button
[self addObserver:self forKeyPath:@"highlighted"options:0 context:nil];
#pragma mark ===== KVO
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if([keyPath isEqualToString:@"highlighted"] ) {
[self setNeedsDisplay];
}
}
对于系统是根据keypath去取的到相应的值发生改变,理论上来说是和kvc机制的道理是一样的。
KVC机制通过key找到value的原理。
KVC(Key-Value-Coding):键值编码 是一种通过字符串间接访问对象的方式(即给属性赋值)
当通过KVC调用对象时,比如:[self valueForKey:@”someKey”]时,程序会自动试图通过下面几种不同的方式解析这个调用。首先查找对象是否带有 someKey 这个方法,如果没找到,会继续查找对象是否带有someKey这个实例变量(iVar),如果还没有找到,程序会继续试图调用 -(id) valueForUndefinedKey:这个方法。如果这个方法还是没有被实现的话,程序会抛出一个NSUndefinedKeyException异常错误。
补充:KVC查找方法的时候,不仅仅会查找someKey这个方法,还会查找getsomeKey这个方法,前面加一个get,或者_someKey以_getsomeKey这几种形式。同时,查找实例变量的时候也会不仅仅查找someKey这个变量,也会查找_someKey这个变量是否存在。
设计valueForUndefinedKey:方法的主要目的是当你使用-(id)valueForKey方法从对象中请求值时,对象能够在错误发生前,有最后的机会响应这个请求。
KVO的底层实现?
kvo:键值观察机制,他提供了观察某一属性变化的方法,极大的简化了代码。
KVO基于runtime机制实现。
在 Objective-C 中如何实现 KVO
注册观察者(注意:观察者和被观察者不会被保留也不会被释放)
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath
options:(NSKeyValueObservingOptions)options
context:(void *)context;
接收变更通知
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object change:(NSDictionary *)change context:(void *)context;
移除对象的观察者身份
- (void)removeObserver:(NSObject *)observer
forKeyPath:(NSString *)keyPath;
KVO中谁要监听谁注册,然后对响应进行处理,使得观察者与被观察者完全解耦。KVO只检测类中的属性,并且属性名都是通过NSString来查找,编译器不会检错和补全,全部取决于自己。
KVO基本原理:
1.KVO是基于runtime机制实现的
2.当某个类的属性对象第一次被观察时,系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的setter 方法。派生类在被重写的setter方法内实现真正的通知机制
3.如果原类为Person,那么生成的派生类名为NSKVONotifying_Person
4.每个类对象中都有一个isa指针指向当前类,当一个类对象的第一次被观察,那么系统会偷偷将isa指针指向动态生成的派生类,从而在给被监控属性赋值时执行的是派生类的setter方法
5.键值观察通知依赖于NSObject 的两个方法: willChangeValueForKey: 和 didChangevlueForKey:;在一个被观察属性发生改变之前, willChangeValueForKey:一定会被调用,这就 会记录旧的值。而当改变发生后,didChangeValueForKey:会被调用,继而 observeValueForKey:ofObject:change:context: 也会被调用。
KVO深入原理:
1.Apple 使用了 isa 混写(isa-swizzling)来实现 KVO 。当观察对象A时,KVO机制动态创建一个新的名为:?NSKVONotifying_A的新类,该类继承自对象A的本类,且KVO为NSKVONotifying_A重写观察属性的setter?方法,setter?方法会负责在调用原?setter?方法之前和之后,通知所有观察对象属性值的更改情况。
2.NSKVONotifying_A类剖析:在这个过程,被观察对象的 isa 指针从指向原来的A类,被KVO机制修改为指向系统新创建的子类 NSKVONotifying_A类,来实现当前类属性值改变的监听;
3.所以当我们从应用层面上看来,完全没有意识到有新的类出现,这是系统“隐瞒”了对KVO的底层实现过程,让我们误以为还是原来的类。但是此时如果我们创建一个新的名为“NSKVONotifying_A”的类(),就会发现系统运行到注册KVO的那段代码时程序就崩溃,因为系统在注册监听的时候动态创建了名为NSKVONotifying_A的中间类,并指向这个中间类了。
4.(isa 指针的作用:每个对象都有isa 指针,指向该对象的类,它告诉 Runtime 系统这个对象的类是什么。所以对象注册为观察者时,isa指针指向新子类,那么这个被观察的对象就神奇地变成新子类的对象(或实例)了。)?因而在该对象上对 setter 的调用就会调用已重写的 setter,从而激活键值通知机制。
5.子类setter方法剖析:KVO的键值观察通知依赖于 NSObject 的两个方法:willChangeValueForKey:和 didChangevlueForKey:,在存取数值的前后分别调用2个方法: 被观察属性发生改变之前,willChangeValueForKey:被调用,通知系统该 keyPath?的属性值即将变更;当改变发生后, didChangeValueForKey: 被调用,通知系统该 keyPath?的属性值已经变更;之后,?observeValueForKey:ofObject:change:context: 也会被调用。且重写观察属性的setter?方法这种继承方式的注入是在运行时而不是编译时实现的。
代理的作用
代理又叫委托,是一种设计模式,代理是对象与对象之间的通信交互,代理解除了对象之间的耦合性。
改变或传递控制链。允许一个类在某些特定时刻通知到其他类,而不需要获取到那些类的指针。可以减少框架复杂度。
另外一点,代理可以理解为java中的回调监听机制的一种类似。
代理的属性常是assign的原因:防止循环引用,以至对象无法得到正确的释放。
NSNotification、Block、Delegate和KVO的区别。
代理是一种回调机制,且是一对一的关系,通知是一对多的关系,一个对向所有的观察者提供变更通知;
效率:Delegate比NSNOtification高;
Delegate和Block一般是一对一的通信;
Delegate需要定义协议方法,代理对象实现协议方法,并且需要建立代理关系才可以实现通信;
Block:Block更加简洁,不需要定义繁琐的协议方法,但通信事件比较多的话,建议使用Delegate;
static、self、super关键字的作用
函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值.
在模块内的 static 全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问.
在模块内的static函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明.
在类中的static成员变量属于整个类所拥有,对类的所有对象只有一份拷贝.
self:当前消息的接收者。
super:向父类发送消息。
#include与#import的区别,#import 与@class 的区别
#include 和#import其效果相同,都是查询类中定义的行为(方法);
#import不会引起交叉编译,确保头文件只会被导入一次;
@class 的表明,只定 义了类的名称,而具体类的行为是未知的,一般用于.h 文件;
@class 比#import 编译效率更高。
此外@class 和#import 的主要区别在于解决引用死锁的问题。
@public、@protected、@private、 @package 它们的含义与作用
@public:对象的实例变量的作用域在任意地方都可以被访问 ;
@protected:对象的实例变量作用域在本类和子类都可以被访问 ;
@private:实例变量的作用域只能在本类(自身)中访问 .
@package 本包内使用,跨包不可以。
解释 id 类型
任意类型对象,程序运行时才决定对象的类型。
11. 谈谈id和instancetype的异同?
1、相同点
都可以作为方法的返回类型
2、不同点
(1)instancetype可以返回和方法所在类相同类型的对象,id只能返回未知类型的对象;
(2)instancetype只能作为返回值,不能像id那样作为参数,
isMemberOfClass 和 isKindOfClass 联系与区别
联系:两者都能检测一个对象是否是某个类的成员
区别:isKindOfClass 不仅用来确定一个对象是否是一个类的成员,也可以用来确定一个对象是否派生自该类的类的成员 ,而isMemberOfClass 只能做到第一点。
举例:如 ClassA派 生 自NSObject 类 , ClassA *a = [ClassA alloc] init];,[a isKindOfClass:[NSObject class]] 可以检查出 a 是否是 NSObject派生类 的成员,但 isMemberOfClass 做不到。
对于语句NSString*obj = [[NSData alloc] init]; obj在编译时和运行时分别时什么类型的对象?
编译时是NSString的类型;运行时是NSData类型的对象
常见的object-c的数据类型有那些, 和C的基本数据类型有什么区别?如:NSInteger和int
object-c的数据类型有NSString,NSNumber,NSArray,NSMutableArray,NSData等等,这些都是class,创建后便是对象,而C语言的基本数据类型int,只是一定字节的内存空间,用于存放数值;NSInteger是基本数据类型,并不是NSNumber的子类,当然也不是NSObject的子类。NSInteger是基本数据类型Int或者Long的别名(NSInteger的定义typedef long NSInteger),它的区别在于,NSInteger会根据系统是32位还是64位来决定是本身是int还是Long。
iOS 开发中数据持久性有哪几种?
数据存储的核心都是写文件。
属性列表:只有NSString、NSArray、NSDictionary、NSData可writeToFile;存储依旧是plist文件。plist文件可以存储的7中数据类型:array、dictionary、string、bool、data、date、number。
对象序列化(对象归档):对象序列化通过序列化的形式,键值关系存储到本地,转化成二进制流。通过runtime实现自动化归档/解档,请参考这个文章。实现NSCoding协议必须实现的两个方法:
1.编码(对象序列化):把不能直接存储到plist文件中得到数据,转化为二进制数据,NSData,可以存储到本地;
2.解码(对象反序列化):把二进制数据转化为本来的类型。
SQLite 数据库:大量有规律的数据使用数据库。
CoreData :通过管理对象进行增、删、查、改操作的。它不是一个数据库,不仅可以使用SQLite数据库来保持数据,也可以使用其他的方式来存储数据。如:XML。
在某个方法中 self.name = _name,name = _name 它 们有区别吗,为什么?
前者是存在内存管理的setter方法赋值,它会对_name对象进行保留或者拷贝操作
后者是普通赋值
一般来说,在对象的方法里成员变量和方法都是可以访问的,我们通常会重写Setter方法来执行某些额外的工作。比如说,外部传一个模型过来,那么我会直接重写Setter方法,当模型传过来时,也就是意味着数据发生了变化,那么视图也需要更新显示,则在赋值新模型的同时也去刷新UI。
解释self = [super init]方法
容错处理,当父类初始化失败,会返回一个nil,表示初始化失败。由于继承的关系,子类是需要拥有父类的实例和行为,因此,我们必须先初始化父类,然后再初始化子类
定义属性时,什么时候用 assign、retain、copy 以及它们的之间的区别。
assign:普通赋值,一般常用于基本数据类型,常见委托设计模式, 以此来防止循环引用。(我们称之为弱引用).
retain:保留计数,获得到了对象的所有权,引用计数在原有基础上加1.
copy:一般认为,是在内存中重新开辟了一个新的内存空间,用来 存储新的对象,和原来的对象是两个不同的地址,引用计数分别为1。但是当copy对象为不可变对象时,那么copy 的作用相当于retain。因为,这样可以节约内存空间
tableView的重用机制?
答:UITableView 通过重用单元格来达到节省内存的目的: 通过为每个单元格指定一个重用标识符,即指定了单元格的种类,当屏幕上的单元格滑出屏幕时,系统会把这个单元格添加到重用队列中,等待被重用,当有新单元格从屏幕外滑入屏幕内时,从重用队列中找看有没有可以重用的单元格,如果有,就拿过来用,如果没有就创建一个来使用。
线程与进程的区别和联系?
答:
1). 进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性
2). 进程和线程的主要差别在于它们是不同的操作系统资源管理方式。
3). 进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。
4.)线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉。所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。
5). 但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
线程与进程的区别和联系?
一个程序至少要有进城,一个进程至少要有一个线程。
进程:资源分配的最小独立单元,进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。
线程:进程下的一个分支,是进程的实体,是CPU调度和分派的基本单元,它是比进程更小的能独立运行的基本单位,线程自己基本不拥有系统资源,只拥有一点在运行中必不可少的资源(程序计数器、一组寄存器、栈),但是它可与同属一个进程的其他线程共享进程所拥有的全部资源。
进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性。
进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。
但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
多线程编程
NSThread:当需要进行一些耗时操作时会把耗时的操作放到线程中。线程同步:多个线程同时访问一个数据会出问题,NSlock、线程同步块、@synchronized(self){}。
NSOperationQueue操作队列(不需考虑线程同步问题)。编程的重点都放在main里面,NSInvocationOperation、BSBlockOperation、自定义Operation。创建一个操作绑定相应的方法,当把操作添加到操作队列中时,操作绑定的方法就会自动执行了,当把操作添加到操作队列中时,默认会调用main方法。
GCD(`Grand Central Dispatch)宏大的中央调度,串行队列、并发队列、主线程队列;
同步和异步:同步指第一个任务不执行完,不会开始第二个,异步是不管第一个有没有执行完,都开始第二个。
串行和并行:串行是多个任务按一定顺序执行,并行是多个任务同时执行;
代码是在分线程执行,在主线程嘟列中刷新UI。
多线程编程是防止主线程堵塞、增加运行效率的最佳方法。
Apple提供了NSOperation这个类,提供了一个优秀的多线程编程方法;
一个NSOperationQueue操作队列,相当于一个线程管理器,而非一个线程,因为你可以设置这个线程管理器内可以并行运行的线程数量等。
多线程是一个比较轻量级的方法来实现单个应用程序内多个代码执行路径。
iPhoneOS下的主线程的堆栈大小是1M。第二个线程开始就是512KB,并且该值不能通过编译器开关或线程API函数来更改,只有主线程有直接修改UI的能力。
定时器与线程的区别
定时器;可以执行多次,默认在主线程中。
线程:只能执行一次。
TCP和UDP的区别于联系
TCP---传输控制协议,提供的是面向连接、可靠的字节流服务。当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。
UDP---用户数据报协议,是一个简单的面向数据报的运输层协议。UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快
TCP为传输控制层协议,为面向连接、可靠的、点到点的通信;
UDP为用户数据报协议,非连接的不可靠的点到多点的通信;
TCP侧重可靠传输,UDP侧重快速传输。
TCP连接的三次握手
第一次握手:客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包,即SYN+ACK包,此时服务器进入SYN+RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次状态。
Scoket连接和HTTP连接的区别
HTTP协议是基于TCP连接的,是应用层协议,主要解决如何包装数据。Socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议。
HTTP连接:短连接,客户端向服务器发送一次请求,服务器响应后连接断开,节省资源。服务器不能主动给客户端响应(除非采用HTTP长连接技术),iPhone主要使用类NSURLConnection。
Socket连接:长连接,客户端跟服务器端直接使用Socket进行连接,没有规定连接后断开,因此客户端和服务器段保持连接通道,双方可以主动发送数据,一般多用于游戏.Socket默认连接超时时间是30秒,默认大小是8K(理解为一个数据包大小)。
HTTP协议的特点,关于HTTP请求GET和POST的区别
GET和POST的区别:
HTTP超文本传输协议,是短连接,是客户端主动发送请求,服务器做出响应,服务器响应之后,链接断开。HTTP是一个属于应用层面向对象的协议,HTTP有两类报文:请求报文和响应报文。
HTTP请求报文:一个HTTP请求报文由请求行、请求头部、空行和请求数据4部分组成。
HTTP响应报文:由三部分组成:状态行、消息报头、响应正文。
GET请求:参数在地址后拼接,没有请求数据,不安全(因为所有参数都拼接在地址后面),不适合传输大量数据(长度有限制,为1024个字节)。
GET提交、请求的数据会附在URL之后,即把数据放置在HTTP协议头中。
以?分割URL和传输数据,多个参数用&连接。如果数据是英文字母或数字,原样发送,
如果是空格,转换为+,如果是中文/其他字符,则直接把字符串用BASE64加密。
POST请求:参数在请求数据区放着,相对GET请求更安全,并且数据大小没有限制。把提交的数据放置在HTTP包的包体中.
GET提交的数据会在地址栏显示出来,而POST提交,地址栏不会改变。
传输数据的大小:
GET提交时,传输数据就会受到URL长度限制,POST由于不是通过URL传值,理论上书不受限。
安全性:
POST的安全性要比GET的安全性高;
通过GET提交数据,用户名和密码将明文出现在URL上,比如登陆界面有可能被浏览器缓存。
HTTPS:安全超文本传输协议(Secure Hypertext Transfer Protocol),它是一个安全通信通道,基于HTTP开发,用于客户计算机和服务器之间交换信息,使用安全套结字层(SSI)进行信息交换,即HTTP的安全版。
ASIHttpRequest、AFNetWorking之间的区别
ASIHttpRequest功能强大,主要是在MRC下实现的,是对系统CFNetwork API进行了封装,支持HTTP协议的CFHTTP,配置比较复杂,并且ASIHttpRequest框架默认不会帮你监听网络改变,如果需要让ASIHttpRequest帮你监听网络状态改变,并且手动开始这个功能。
AFNetWorking构建于NSURLConnection、NSOperation以及其他熟悉的Foundation技术之上。拥有良好的架构,丰富的API及模块构建方式,使用起来非常轻松。它基于NSOperation封装的,AFURLConnectionOperation子类。
ASIHttpRequest是直接操作对象ASIHttpRequest是一个实现了NSCoding协议的NSOperation子类;AFNetWorking直接操作对象的AFHttpClient,是一个实现NSCoding和NSCopying协议的NSObject子类。
同步请求:ASIHttpRequest直接通过调用一个startSynchronous方法;AFNetWorking默认没有封装同步请求,如果开发者需要使用同步请求,则需要重写getPath:paraments:success:failures方法,对于AFHttpRequestOperation进行同步处理。
性能对比:AFNetworking请求优于ASIHttpRequest;
XML数据解析方式各有什么不同,JSON解析有哪些框架?
XML数据解析的两种解析方式:DOM解析和SAX解析;
DOM解析必须完成DOM树的构造,在处理规模较大的XML文档时就很耗内存,占用资源较多,读入整个XML文档并构建一个驻留内存的树结构(节点树),通过遍历树结构可以检索任意XML节点,读取它的属性和值,通常情况下,可以借助XPath查询XML节点;
SAX与DOM不同,它是事件驱动模型,解析XML文档时每遇到一个开始或者结束标签、属性或者一条指令时,程序就产生一个事件进行相应的处理,一边读取XML文档一边处理,不必等整个文档加载完才采取措施,当在读取解析过程中遇到需要处理的对象,会发出通知进行处理。因此,SAX相对于DOM来说更适合操作大的XML文档。
JSON解析:性能比较好的主要是第三方的JSONKIT和iOS自带的JSON解析类,其中自带的JSON解析性能最高,但只能用于iOS5之后。
SVN的使用
SVN=版本控制+备份服务器,可以把SVN当成备份服务器,并且可以帮助你记住每次上服务器的档案内容,并自动赋予每次变更的版本;
SVN的版本控制:所有上传版本都会帮您记录下来,也有版本分支及合并等功能。SVN可以让不同的开发者存取同样的档案,并且利用SVN Server作为档案同步的机制,即您有档案更新时,无需将档案寄送给您的开发成员。SVN的存放档案方式是采用差异备份的方式,即会备份到不同的地方,节省硬盘空间,也可以对非文字文件进行差异备份。
SVN的重要性:备份工作档案的重要性、版本控管的重要性、伙伴间的数据同步的重要性、备份不同版本是很耗费硬盘空间的;
防止冲突:
1.防止代码冲突:不要多人同时修改同一文件,例如:A、B都修改同一个文件,先让A修改,然后提交到服务器,然后B更新下来,再进行修改;
2.服务器上的项目文件Xcodeproj,仅让一个人管理提交,其他人只更新,防止文件发生冲突。
如何进行网络消息推送
一种是Apple自己提供的通知服务(APNS服务器)、一种是用第三方推送机制。
首先应用发送通知,系统弹出提示框询问用户是否允许,当用户允许后向苹果服务器(APNS)请求deviceToken,并由苹果服务器发送给自己的应用,自己的应用将DeviceToken发送自己的服务器,自己服务器想要发送网络推送时将deviceToken以及想要推送的信息发送给苹果服务器,苹果服务器将信息发送给应用。
推送信息内容,总容量不超过256个字节;
iOS SDK本身提供的APNS服务器推送,它可以直接推送给目标用户并根据您的方式弹出提示。
优点:不论应用是否开启,都会发送到手机端;
缺点:消息推送机制是苹果服务端控制,个别时候可能会有延迟,因为苹果服务器也有队列来处理所有的消息请求;
第三方推送机制,普遍使用Socket机制来实现,几乎可以达到即时的发送到目标用户手机端,适用于即时通讯类应用。
优点:实时的,取决于心跳包的节奏;
缺点:iOS系统的限制,应用不能长时间的后台运行,所以应用关闭的情况下这种推送机制不可用。
通信底层原理(OSI七层模型)OSI采用了分层的结构化技术,共分七层: 物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。
SDWebImage原理
调用类别的方法:
从内存中(字典)找图片(当这个图片在本次程序加载过),找到直接使用;
从沙盒中找,找到直接使用,缓存到内存。
从网络上获取,使用,缓存到内存,缓存到沙盒。
LayoutSubViews在什么时候被调用?
当View本身的frame改变时,会调用这个方法。
浅拷贝和深拷贝的区别?
答:
浅拷贝:只复制指向对象的指针,而不复制引用对象本身。
深拷贝:复制引用对象本身。内存中存在了两份独立对象本身,当修改A时,A_copy不变。
深拷贝和浅拷贝
如果对象有个指针型成员变量指向内存中的某个资源,那么如何复制这个对象呢?你会只是复制指针的值传给副本的新对象吗?指针只是存储内存中资源地址的占位符。在复制操作中,如果只是将指针复制给新对象,那么底层的资源实际上仍然由两个实例在共享。
浅复制:两个实例的指针仍指向内存中的同一资源,只复制指针值而不是实际资源;
深复制:不仅复制指针值,还复制指向指针所指向的资源。如下图:
单例模式理解与使用
单例模式是一种常用设计模式,单例模式是一个类在系统中只有一个实例对象。通过全局的一个入口点对这个实例对象进行访问;
iOS中单例模式的实现方式一般分为两种:非ARC和ARC+GCD。
ViewController 的 loadView、viewDidLoad、viewDidUnload 分别是在什么时候调用的?
viewDidLoad在view从nib文件初始化时调用,loadView在controller的view为nil时调用。
此方法在编程实现view时调用,view控制器默认会注册memory warning notification,当view controller的任何view没有用的时候,viewDidUnload会被调用,在这里实现将retain的view release,如果是retain的IBOutlet view 属性则不要在这里release,IBOutlet会负责release 。
frame和bounds有什么不同?
frame指的是:该view在父view坐标系统中的位置和大小。(参照点是父亲的坐标系统)
bounds指的是:该view在本身坐标系统中的位置和大小。(参照点是本身坐标系统)
property属性的修饰符的作用
getter=getName、setter=setName:设置setter与getter的方法名;
readwrite、readonly:设置可供访问级别;
assign:方法直接赋值,不进行任何retain操作,为了解决原类型与环循引用问题;
retain:其setter方法对参数进行release旧值再retain新值,所有实现都是这个顺序;
copy:其setter方法进行copy操作,与retain处理流程一样,先对旧值release,再copy出新的对象,retainCount为1。这是为了减少对上下文的依赖而引入的机制。
nonatomic:非原子性访问,不加同步, 多线程并发访问会提高性能。注意,如果不加此属性,则默认是两个访问方法都为原子型事务访问。
RunLoop是什么?
Run loops 是线程相关的的基础框架的一部分。
一个 run loop 就是一个事件处理 的循环,用来不停的调度工作以及处理输入事件。使用 run loop的目的是让你的线程在有工作的时候忙于工作,而没工作的时候处于休眠状态。可以节省cpu资源,提高程序性能。
Runloop还可以在loop在循环中的同时响应其他输入源,比如界面控件的按钮,手势等。
什么是 Runtime
Runtime又叫运行时,是一套底层的C语言API,其为iOS内部的核心之一,我们平时编写的OC代码,底层都是基于它来实现的。
Runtime实现的机制是什么,怎么用,一般用于干嘛?
1). 使用时需要导入的头文件
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,想怎么改就怎么改。
简叙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中的函数指针跳转到对应的函数中去执行。
XIB与Storyboards的优缺点
优点:
XIB:在编译前就提供了可视化界面,可以直接拖控件,也可以直接给控件添加约束,更直观一些,而且类文件中就少了创建控件的代码,确实简化不少,通常每个XIB对应一个类。
Storyboard:在编译前提供了可视化界面,可拖控件,可加约束,在开发时比较直观,而且一个storyboard可以有很多的界面,每个界面对应一个类文件,通过storybard,可以直观地看出整个App的结构。
缺点:
XIB:需求变动时,需要修改XIB很大,有时候甚至需要重新添加约束,导致开发周期变长。XIB载入相比纯代码自然要慢一些。对于比较复杂逻辑控制不同状态下显示不同内容时,使用XIB是比较困难的。当多人团队或者多团队开发时,如果XIB文件被发动,极易导致冲突,而且解决冲突相对要困难很多。
Storyboard:需求变动时,需要修改storyboard上对应的界面的约束,与XIB一样可能要重新添加约束,或者添加约束会造成大量的冲突,尤其是多团队开发。对于复杂逻辑控制不同显示内容时,比较困难。当多人团队或者多团队开发时,大家会同时修改一个storyboard,导致大量冲突,解决起来相当困难。
队列和多线程的使用原理
在iOS中队列分为以下几种:
串行队列:队列中的任务只会顺序执行;
1dispatch_queue_t q = dispatch_queue_create("...", DISPATCH_QUEUE_SERIAL);
并行队列: 队列中的任务通常会并发执行;
1dispatch_queue_t q = dispatch_queue_create("......",DISPATCH_QUEUE_CONCURRENT);
全局队列:是系统的,直接拿过来(GET)用就可以;与并行队列类似;
1dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
主队列:每一个应用程序对应唯一主队列,直接GET即可;在多线程开发中,使用主队列更新UI;
1dispatch_queue_t q = dispatch_get_main_queue();
更多细节见下图:
内存的使用和优化的注意事项
重用问题:如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加载方式
8.Objective-C如何对内存管理的,说说你的看法和解决方法?
答:Objective-C的内存管理主要有三种方式ARC(自动内存计数)、手动内存计数、内存池。
1)自动内存计数
解决:一般是由类的静态方法创建的, 函数名中不会出现alloc或init字样, 如[NSString string]和[NSArray arrayWithObject:], 创建后引用计数+0, 在函数出栈后释放, 即相当于一个栈上的局部变量. 当然也可以通过retain延长对象的生存期.
2)手动内存计数
解决:一般是由类的静态方法创建的, 函数名中不会出现alloc或init字样, 如[NSString string]和[NSArray arrayWithObject:], 创建后引用计数+0, 在函数出栈后释放, 即相当于一个栈上的局部变量. 当然也可以通过retain延长对象的生存期.
3)内存池
解决:是由autorelease加入系统内存池, 内存池是可以嵌套的, 每个内存池都需要有一个创建释放对, 就像main函数中写的一样. 使用也很简单, 比如[[[NSString alloc]initialWithFormat:@”Hey you!”] autorelease], 即将一个NSString对象加入到最内层的系统内存池, 当我们释放这个内存池时, 其中的对象都会被释放.
Objective-C 如何对内存管理的,说说你的看法和解决方法?
答:Objective-C的内存管理主要有三种方式ARC(自动内存计数)、手动内存计数、内存池。
1). 自动内存计数ARC:由Xcode自动在App编译阶段,在代码中添加内存管理代码。
2). 手动内存计数MRC:遵循内存谁申请、谁释放;谁添加,谁释放的原则。
3). 内存释放池Release Pool:把需要释放的内存统一放在一个池子中,当池子被抽干后(drain),池子中所有的内存空间也被自动释放掉。内存池的释放操作分为自动和手动。自动释放受runloop机制影响。
自动释放池是什么,如何工作
答:当您向一个对象发送一个autorelease消息时,Cocoa就会将该对象的一个引用放入到最新的自动释放.它仍然是个正当的对象,因此自动释放池定义的作用域内的其它对象可以向它发送消息。当程序执行到作用域结束的位置时,自动释放池就会被释放,池中的所有对象也就被释放。
简述内存管理基本原则
之前:OC内存管理遵循“谁创建,谁释放,谁引用,谁管理”的机制,当创建或引用一个对象的时候,需要向她发送alloc、copy、retain消息,当释放该对象时需要发送release消息,当对象引用计数为0时,系统将释放该对象,这是OC的手动管理机制(MRC)。
目前:iOS 5.0之后引用自动管理机制——自动引用计数(ARC),管理机制与手动机制一样,只是不再需要调用retain、release、autorelease;它编译时的特性,当你使用ARC时,在适当位置插入release和autorelease;它引用strong和weak关键字,strong修饰的指针变量指向对象时,当指针指向新值或者指针不复存在,相关联的对象就会自动释放,而weak修饰的指针变量指向对象,当对象的拥有者指向新值或者不存在时weak修饰的指针会自动置为nil。
如果使用alloc、copy(mutableCopy)或者retian一个对象时,你就有义务,向它发送一条release或者autorelease消息。其他方法创建的对象,不需要由你来管理内存。
向一个对象发送一条autorelease消息,这个对象并不会立即销毁, 而是将这个对象放入了自动释放池,待池子释放时,它会向池中每一个对象发送 一条release消息,以此来释放对象.
向一个对象发送release消息,并不意味着这个对象被销毁了,而是当这个对象的引用计数为0时,系统才会调用dealloc方法,释放该对象和对象本身它所拥有的实例。
其他注意事项
如果一个对象有一个_strong类型的指针指向着,找个对象就不会被释放。如果一个指针指向超出了它的作用域,就会被指向nil。如果一个指针被指向nil,那么它原来指向的对象就被释放了。当一个视图控制器被释放时,它内部的全局指针会被指向nil。用法“:不管全局变量还是局部变量用_strong描述就行。
局部变量:出了作用域,指针会被置为nil。
方法内部创建对象,外部使用需要添加_autorelease;
连线的时候,用_weak描述。
代理使用unsafe_unretained就相当于assign;
block中为了避免循环引用问题,使用_weak描述;
声明属性时,不要以new开头。如果非要以new开头命名属性的名字,需要自己定制get方法名,如
1@property(getter=theString) NSString * newString;
如果要使用自动释放池,用@autoreleasepool{}
ARC只能管理Foundation框架的变量,如果程序中把Foundation中的变量强制换成COre Foundation中的变量需要交换管理权;
在非ARC工程中采用ARC去编译某些类:-fobjc-arc。
在ARC下的工程采用非ARC去编译某些类:-fno-fobjc-arc。
#import跟 #include 有什么区别,@class呢,#import<> 跟 #import””有什么区别?
答:
1). #import是Objective-C导入头文件的关键字,#include是C/C++导入头文件的关键字,使用#import头文件会自动只导入一次,不会重复导入。
2). @class告诉编译器某个类的声明,当执行时,才去查看类的实现文件,可以解决头文件的相互包含。
3). #import<>用来包含系统的头文件,#import””用来包含用户头文件。
#import<>和#import“”之间的区别?
#import<> 引用系统文件,它用于对系统自带的头文件的引用,编译器会在系统文件目录下去查找该文件.
#import"": 用户自定义的文件用双引号引用,编译器首先会在用户目录下查找,然后到安装目录中查
frame 和 bounds 有什么不同?
frame指的是:该view在父view坐标系统中的位置和大小。(参照点是父view的坐标系统)
bounds指的是:该view在本身坐标系统中的位置和大小。(参照点是本身坐标系统)
Objective-C的类可以多重继承么?可以实现多个接口么?Category是什么?重写一个类的方式用继承好还是分类好?为什么?
答:Objective-C的类不可以多重继承;可以实现多个接口(协议);Category是类别;一般情况用分类好,用Category去重写类的方法,仅对本Category有效,不会影响到其他类与原有类的关系。
@property 的本质是什么?ivar、getter、setter 是如何生成并添加到这个类中的
@property 的本质是什么?
@property = ivar + getter + setter;
“属性” (property)有两大概念:ivar(实例变量)、getter+setter(存取方法)
“属性” (property)作为 Objective-C 的一项特性,主要的作用就在于封装对象中的数据。 Objective-C 对象通常会把其所需要的数据保存为各种实例变量。实例变量一般通过“存取方法”(access method)来访问。其中,“获取方法” (getter)用于读取变量值,而“设置方法” (setter)用于写入变量值。
@property中有哪些属性关键字?/ @property 后面可以有哪些修饰符?
属性可以拥有的特质分为四类:
1.原子性--- nonatomic 特质
2.读/写权限---readwrite(读写)、readonly (只读)
3.内存管理语义---assign、strong、 weak、unsafe_unretained、copy
4.方法名---getter= 、setter=
5.不常用的:nonnull,null_resettable,nullable
属性关键字 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,效率高。
什么情况使用 weak 关键字,相比 assign 有什么不同?
1.在 ARC 中,在有可能出现循环引用的时候,往往要通过让其中一端使用 weak 来解决,比如: delegate 代理属性。
2.自身已经对它进行一次强引用,没有必要再强引用一次,此时也会使用 weak,自定义 IBOutlet 控件属性一般也使用 weak;当然,也可以使用strong。
IBOutlet连出来的视图属性为什么可以被设置成weak?
因为父控件的subViews数组已经对它有一个强引用。
不同点:
assign 可以用非 OC 对象,而 weak 必须用于 OC 对象。
weak 表明该属性定义了一种“非拥有关系”。在属性所指的对象销毁时,属性值会自动清空(nil)。
怎么用 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 操作”,他们有可能会在调用之前自行拷贝属性值。这种操作多余而低效。
用@property声明的 NSString / NSArray / NSDictionary 经常使用 copy 关键字,为什么?如果改用strong关键字,可能造成什么问题?
答:用 @property 声明 NSString、NSArray、NSDictionary 经常使用 copy 关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary,他们之间可能进行赋值操作(就是把可变的赋值给不可变的),为确保对象中的字符串值不会无意间变动,应该在设置新属性值时拷贝一份。
1. 因为父类指针可以指向子类对象,使用 copy 的目的是为了让本对象的属性不受外界影响,使用 copy 无论给我传入是一个可变对象还是不可对象,我本身持有的就是一个不可变的副本。
2. 如果我们使用是 strong ,那么这个属性就有可能指向一个可变对象,如果这个可变对象在外部被修改了,那么会影响该属性。
//总结:使用copy的目的是,防止把可变类型的对象赋值给不可变类型的对象时,可变类型对象的值发送变化会无意间篡改不可变类型对象原来的值。
Category(类别)、 Extension(扩展)和继承的区别
区别:
1. 分类有名字,类扩展没有分类名字,是一种特殊的分类。
2. 分类只能扩展方法(属性仅仅是声明,并没真正实现),类扩展可以扩展属性、成员变量和方法。
3. 继承可以增加,修改或者删除方法,并且可以增加属性。
我们说的OC是动态运行时语言是什么意思?
答:主要是将数据类型的确定由编译时,推迟到了运行时。简单来说, 运行时机制使我们直到运行时才去决定一个对象的类别,以及调用该类别对象指定方法。
代理委托是什么?委托和被委托方的property声明用什么属性?为什么?
委托:一个对象保存另外一个对象的引用,被引用的对象实现了事先确定的协议,该协议用于将引用对象中的变化通知给被引用对象。
委托和被委托方双方的property声明属性都是assign而不是retain,为了避免循环引用造成的内存泄漏
为什么我们常见的delegate属性都用是week而不是retain/strong?
答:是为了防止delegate两端产生不必要的循环引用。
@property (nonatomic, weak) id delegate;
什么时候用delete,什么时候用Notification?
Delegate(委托模式):1对1的反向消息通知功能。
Notification(通知模式):只想要把消息发送出去,告知某些状态的变化。但是并不关心谁想要知道这个。
delegate 和 notification 的区别
1). 二者都用于传递消息,不同之处主要在于一个是一对一的,另一个是一对多的。
2). notification通过维护一个array,实现一对多消息的转发。
3). delegate需要两者之间必须建立联系,不然没法调用代理的方法;notification不需要两者之间有联系。
简述一下消息推送的流程?
1苹果推送服务器APNs上注册deviceToken,并发送给自己的后台服务器;
2、后台服务器将deviceToken和要发送的消息打包发送给APNs(注意:这一步还需要推送证书的路径和密码)
3、APNs将消息发送给deviceToken中保存的指定设备中的指定App
<流程2>
(1) 应用程序安装后提示用户是否需要接收推送,用户确认后注册消息推送。
(2)App接收到从APNS Server获取deviceToken的信息;
(3)APP将deviceToken信息发送到自己的服务器端;
(4)当需要向用户推送消息时,自己的服务器将向苹果的推送通知服务器(Apple Push Notification Service,以下简称 APNS)发送通知;
(5)APNs将消息发送给deviceToken中保存的指定设备中的指定App
简述一下App发布到AppStore的流程?
1、首先生成发布描述文件(通过创建生产证书-‐> 创建AppId(BundleId)-‐>创建发布的描述文件)
2、然后在iTunesContent中创建一个应用并填好信息
3、在XCode中将项目打包并上传(注意:BundleId全程一致, 在buildSetting中填好刚创建的描述文件)
4最后再回到iTunesContent中选择刚才上传的项目,然后点击提交
什么是谓词?
谓词就是通过NSPredicate给定的逻辑条件作为约束条件,完成对数据的筛选。
//定义谓词对象,谓词对象中包含了过滤条件(过滤条件比较多)
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"age<%d",30];
//使用谓词条件过滤数组中的元素,过滤之后返回查询的结果
NSArray *array = [persons filteredArrayUsingPredicate:predicate];
什么是懒加载?
答:懒加载就是只在用到的时候才去初始化。也可以理解成延时加载。
我觉得最好也最简单的一个例子就是tableView中图片的加载显示了, 一个延时加载, 避免内存过高,一个异步加载,避免线程堵塞提高用户体验。
懒加载(延时加载)
答案:
(1)不必将创建对象的代码全部写在viewDidLoad方法中,代码的可读性更强
(2)每个控件的getter方法中分别负责各自的实例化处理,代码彼此之间的独立性强,松耦合
(3)只有当真正需要资源时,再去加载,节省了内存资源。
(4)在内存警告的时候将懒加载的对象置为nil,当再次用到的时候又可以懒加载了
isKindOfClass、isMemberOfClass、selector作用分别是什么
isKindOfClass:作用是某个对象属于某个类型或者继承自某类型。
isMemberOfClass:某个对象确切属于某个类型。
selector:通过方法名,获取在内存中的函数的入口地址。
iOS多线程技术有哪几种方式?
答:pthread、NSThread、GCD、NSOperation
如何对iOS设备进行性能测试?
Profile-> Instruments ->Time Profiler
为什么IBOutlet修饰的UIView也适用weak关键字?
在xib或者Sb拖控件时,其实控件就加载到了父控件的subviews数组里面,进行了强引用,即使使用了weak,也不造成对象的释放。
用StoryBoard开发界面有什么弊端?如何避免?
使用简单逻辑页面的跳转是可以使用sb的,开发比较块。
但是SB对于逻辑项目比较复杂的时候,开发起来比较慢。不适合多人合作开发;也不利于版本的梗系和后期的维护。使用sb在项目变异编译的时候,也都会直接加载到内存中,造成内存的浪费。
可以使用xib来代替,编辑复杂逻辑界面时候可以使用纯码编写。
相关: 参考
MRC和ARC混编设置方式
在XCode中targets的build phases选项下Compile Sources下选择 不需要arc编译的文件
双击输入 -fno-objc-arc 即可
MRC工程中也可以使用ARC的类,方法如下:
在XCode中targets的build phases选项下Compile Sources下选择要使用arc编译的文件
双击输入 -fobjc-arc 即可
动态隐藏NavigationBar
//1.当我们的手离开屏幕时候隐藏- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset{if(velocity.y >0) {[self.navigationController setNavigationBarHidden:YES animated:YES];}else {
[self.navigationController setNavigationBarHidden:NO animated:YES];
}
}
velocity.y这个量,在上滑和下滑时,变化极小(小数),但是因为方向不同,有正负之分,这就很好处理了。
//2.在滑动过程中隐藏//像safari(1) self.navigationController.hidesBarsOnSwipe = YES;(2)- (void)scrollViewDidScroll:(UIScrollView *)scrollView{ CGFloat offsetY = scrollView.contentOffset.y + __tableView.contentInset.top; CGFloat panTranslationY = [scrollView.panGestureRecognizer translationInView:self.tableView].y;if (offsetY >64) {if (panTranslationY >0) { //下滑趋势,显示 [self.navigationController setNavigationBarHidden:NO animated:YES];}else { //上滑趋势,隐藏 [self.navigationController setNavigationBarHidden:YES animated:YES]; }}else {[self.navigationController setNavigationBarHidden:NO animated:YES]; }}这里的offsetY >64只是为了在视图滑过navigationBar的高度之后才开始处理,防止影响展示效果。panTranslationY是scrollView的pan手势的手指位置的y值,可能不是太好,因为panTranslationY这个值在较小幅度上下滑动时,可能都为正或都为负,这就使得这一方式不太灵敏.
效果图
设置字体和行间距
//设置字体和行间距 UILabel * lable = [[UILabel alloc]initWithFrame:CGRectMake(50,100,300,200)]; lable.text =@"大家好,我是Frank_chun,在这里我们一起学习新的知识,总结我们遇到的那些坑,共同的学习,共同的进步,共同的努力,只为美好的明天!!!有问题一起相互的探讨--438637472!!!"; lable.numberOfLines =0;lable.font = [UIFont systemFontOfSize:12];lable.backgroundColor = [UIColor grayColor]; [self.view addSubview:lable]; //设置每个字体之间的间距 //NSKernAttributeName 这个对象所对应的值是一个NSNumber对象(包含小数),作用是修改默认字体之间的距离调整,值为0的话表示字距调整是禁用的; NSMutableAttributedString * str = [[NSMutableAttributedString alloc]initWithString:lable.text attributes:@{NSKernAttributeName:@(5.0)}];//设置某写字体的颜色//NSForegroundColorAttributeName 设置字体颜色NSRange blueRange = NSMakeRange([[str string] rangeOfString:@"Frank_chun"].location, [[str string] rangeOfString:@"Frank_chun"].length); [str addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:blueRange]; NSRange blueRange1 = NSMakeRange([[str string] rangeOfString:@"438637472"].location, [[str string] rangeOfString:@"438637472"].length);[str addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:blueRange1];//设置每行之间的间距 //NSParagraphStyleAttributeName 设置段落的样式NSMutableParagraphStyle * par = [[NSMutableParagraphStyle alloc]init];[par setLineSpacing:20];//为某一范围内文字添加某个属性//NSMakeRange表示所要的范围,从0到整个文本的长度[str addAttribute:NSParagraphStyleAttributeName value:par range:NSMakeRange(0, lable.text.length)]; [lable setAttributedText:str];
效果图