Open FrizzleFur opened 5 years ago
@property(nonatomic, copy) NSString *name;
- (void)testMethod{
__weak __typeof(self)weakSelf = self;
self.name = @"testName";
self.block = ^(UIViewController *vc) {
NSLog(@"weakSelf.name = %@", weakSelf.name);
NSLog(@"self.name = %@", self.name);
};
}
// NextVC.m
/* log:
weakSelf.name = testName
-[NextVC dealloc]
*/
- (void)testMethod{
__weak __typeof(self)weakSelf = self;
self.name = @"testName";
self.block = ^(UIViewController *vc) {
// 延时操作,然后pop,vcu出栈,被释放
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// pop回去后nextVC已经被释放,所以打印是空值
// NSLog(@"weakSelf.name = %@", weakSelf.name);
// vc被block存储,拷贝到堆上,所以可以获取打印的值
NextVC *nextVC = (NextVC *)vc;
NSLog(@"weakSelf.name = %@", nextVC.name);
});
// 强引用,导致循环链
// NSLog(@"self.name = %@", self.name);
// 使用弱引用,避免循环链
// NSLog(@"weakSelf.name = %@", weakSelf.name);
};
self.block(self);
}
对于初、中级的一般我建议根据情况,了解下面试者对自己的职业规划,可以谈谈技术外的,不限于技术。例如: 你对自己的职业是怎么规划的? 你觉得理想的团队应该如何?
比如,为了寻找所有 100 以内的偶平方数,我们可以对 0..<10 进行 map 来得到所有平方数,然后再过滤掉所有奇数:
(1..<10).map { $0 * $0 }.filter { $0 % 2 == 0 } // [4, 16, 36, 64]
通过组合使用 map 和 filter,我们现在可以轻易完成很多数组操作,而不需要引入中间变量。 这会使得最终的代码变得更短更易读。
UIScrollView在滚动过程当中,其实是在修改原点坐标。当手指触摸后, scroll view会暂时拦截触摸事件,使用一个计时器。假如在计时器到点后没有发生手指移动事件,那么 scroll view 发送 tracking events 到被点击的 subview。假如在计时器到点前发生了移动事件,那么 scroll view 取消 tracking 自己发生滚动。
首先了解下UIScrollView对于touch事件的接收处理原理:
UIScrollView应该是重载了hitTest 方法,并总会返回itself 。所以所有的touch 事件都会进入到它自己里面去了。内部的touch事件检测到这个事件是不是和自己相关的,或者处理或者除递给内部的view。 为了检测touch是处理还是传递,UIScrollView当touch发生时会生成一个timer。 如果150ms内touch未产生移动,它就把这个事件传递给内部view 如果150ms内touch产生移动,开始scrolling,不会传递给内部的view。(例如, 当你touch一个table时候,直接scrolling,你touch的那行永远不会highlight。) 如果150ms内touch未产生移动并且UIScrollView开始传递内部的view事件,但是移动足够远的话,且canCancelContentTouches = YES,UIScrollView会调用touchesCancelled方法,cancel掉内部view的事件响应,并开始scrolling。(例如, 当你touch一个table, 停止了一会,然后开始scrolling,那一行就首先被highlight,但是随后就不在高亮了)
GET和POST的区别:
HTTP超文本传输协议,是短连接,是客户端主动发送请求,服务器做出响应,服务器响应之后,链接断开。HTTP是一个属于应用层面向对象的协议,HTTP有两类报文:请求报文和响应报文。
HTTP请求报文:一个HTTP请求报文由请求行、请求头部、空行和请求数据4部分组成。
HTTP响应报文:由三部分组成:状态行、消息报头、响应正文。
GET请求:参数在地址后拼接,没有请求数据,不安全(因为所有参数都拼接在地址后面),不适合传输大量数据(长度有限制,为1024个字节)。
GET提交、请求的数据会附在URL之后,即把数据放置在HTTP协议头
POST请求:参数在请求数据区放着,相对GET请求更安全,并且数据大小没有限制。把提交的数据放置在HTTP包的包体
GET提交的数据会在地址栏显示出来,而POST提交,地址栏不会改变。
传输数据的大小:
GET提交时,传输数据就会受到URL长度限制,POST由于不是通过URL传值,理论上书不受限。 安全性:
POST的安全性要比GET的安全性高;
通过GET提交数据,用户名和密码将明文出现在URL上,比如登陆界面有可能被浏览器缓存。
HTTPS:安全超文本传输协议(Secure Hypertext Transfer Protocol),它是一个安全通信通道,基于HTTP开发,用于客户计算机和服务器之间交换信息,使用安全套结字层(SSI)进行信息交换,即HTTP的安全版。
既然直接对 CALayer 的contents属性赋值一个CGImage便能显示图片,所以 UIImageView 就顺利成章地诞生了。实际上 UIImage 就是对 CGImage(或者 CIImage) 的一个轻量封装。
每一个View都和视图结构以及响应者链有直接的关系,但是这篇文章不打算着重的讲这两个方面,主要讲removeFromSuperview方法。将当前视图从其父视图移除,需要调用removeFromSuperview方法。下面是苹果对于这个API的官方定义:
Unlinks the receiver from its superview and its window, and removes it from the responder chain. 译:把当前View从它的父View和窗口中移除,同时也把它从响应事件操作的响应者链中移除。
removeFromSuperview就是一个视图节点删除的操作,执行这个方法,就等于在树形结构中找到该节点,从树型数据结构中删除该节点及其子节点,而并非只是删除该节点自己。同时,另一个操作就是把该对象从响应者链中移除。
执行removeFromSuperview方法后,会从父视图中移除,并且将Superview对视图的强引用删除,此时如果没有其他地方再对视图进行强引用,则会从内存中移除。如果还存在其他强引用,视图只是不在屏幕中显示,并没有将该视图从内存中移除。所以如果需要使用该视图,不需要再次创建,而是直接addSubview就可以了。
多次执行addSubview:操作 假设现在有ViewA、ViewB、ViewC三个视图,ViewA添加到ViewB之后又要添加到ViewC上面,此时ViewA同时执行了向ViewB、ViewC两个视图addSubview:的操作。但是因为只有一个视图对象,所以只会以最后一次添加的为准,第一次执行的添加到ViewB的操作是无效的。通过打印两个View的子视图可以看到,只有最后执行的添加到ViewC上的操作才是有效的,ViewC才真正拥有了ViewA,而ViewB的子视图是空的。
一个视图不只是向其他多个页面进行添加操作不会出现问题,而且向同一个视图上执行多次添加操作也是没有问题的,并不会导致视图被多次添加的问题,也不需要在添加之前进行removeFromSuperview操作,这个是在MRC和ARC都是有效的。因为系统在addSubview:方法中进行了一些判断操作,如果当前视图已经添加到其他视图,会将当前视图从其他视图中移除,然后执行添加操作。如果当前视图已经添加到这个视图中,就不会再次执行添加操作。
其实也说不上是坑,可以算是一个了解的知识点吧。在ARC或MRC的情况下,调用removeFromSuperview和addSubview:方法其中之一,都需要在另一个方法已经执行的情况下才会有效,对于多次执行一个同方法系统也是有判断操作的,并不会被执行多次。
例如调用remove方法之后,此时视图已经不在父视图之上了,在多次调用这个方法是不起作用的,而且MRC下引用计数也不会被减少多次。对于addSubview:方法也是一样的,向同一个父视图上添加子视图,不会被重复添加,添加之后引用计数也不会多次+1。
无论是ARC还是MRC中多次调用removeFromSuperview和addSubview:方法,都不会造成造成重复释放和添加。 苹果的官方API注明:Never call this method from inside your view’s drawRect: method. 译:永远不要在你的View的drawRect:方法中调用removeFromSuperview。
HTTP协议是基于TCP连接的,是应用层协议,主要解决如何包装数据。Socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议。
HTTP连接:短连接,客户端向服务器发送一次请求,服务器响应后连接断开,节省资源。服务器不能主动给客户端响应(除非采用HTTP长连接技术),iPhone主要使用类NSURLConnection。
Socket连接:长连接,客户端跟服务器端直接使用Socket进行连接,没有规定连接后断开,因此客户端和服务器段保持连接通道,双方可以主动发送数据,一般多用于游戏.Socket默认连接超时时间是30秒,默认大小是8K(理解为一个数据包大小)。
这个问题很重要,如果各位老哥是对整个技术了如指掌的,可以忽略这个问题,碰到过一家公司,老人已经都走了,没留下文档,自然也没人带,一个维护项目直接丢给我,面试官也就是我组长整天嫌我菜(没技术面,建议没技术面或太水的公司也别去,画了一个多小时的饼),后来请教了些前辈,基本正规公司,不论是校招还是社招,都会有人带的,放心问,不用担心社招公司不会培养这种
主要是想看下工作难度,如果希望寻求技术突破,但项目主要是CRUD,那可能就要认真考虑下了,同时也能问问有什么代表性的难点吗,看下和自己合不合适,另外,一定要问清楚加班的问题,有幸加过一年半,加班是很痛苦,可以完全打乱自己生活节奏的事,建议各位考虑好,仔细考虑自己的加班承受能力
其实是怕人太少,整个项目组没几个同行,很虚
主要是对项目负责人问,一般是二面,仔细想想自己想要的工作环境,看看是不是合适,上班嘛,干的开心才能做的长远
挺滑头的一个问题,主要想旁侧敲击下项目整体怎么样,加不加班啊,早上可不可以晚到会啊,中午快到吃饭可不可以早走几分钟啊,我待过两家公司,第一家外包,管理比较自由,吃饭可以早走几分钟,规定是13点40上班但基本都是14点才开始,另外一家公司感觉跟进了军队一样,13:28打铃过了两分钟所有人就全坐好了,这种说实话,有点难受
主要想到的就是上边的问题,像一些企业文化这种有点虚的,可以酌情考虑,毕竟问太多也不太好 比如说开发流程,整个项目组怎么做事
一般是三个月,少数公司会一到两个月,可以问问,另外有试岗期的公司,也算是稀有,建议背一遍爱莲说
侧面反应出这公司怎么样,10号以前发的基本都是好公司,10号到20号的,会很难受,假设你现在发工资的时间往后推迟10天会发生什么
这个没什么,越多越好,去找下同学朋友问下,就知道这公司叫的公积金大概什么水平了,按最低标准交的,建议开溜
先大概解释一下self和super。self是对象指针,指向当前消息接收者。super是编译器指令,使用super调用方法是从当前消息接收者类的父类中开始查找方法的实现,但消息接收者还是子类。有关self和super的详细解释可以参阅《深入浅出 Runtime(四):super 的本质》。 调用[super init],是子类去调用父类的init方法,先完成父类的初始化工作。要注意调用过程中,父类的init方法中的self还是子类。 执行self = [super init], 如果父类初始化成功,接下来就进行子类的初始化; 如果父类初始化失败,则[super init]会返回nil并赋值给self,接下来if (self)语句的内容将不被执行,子类的init方法也返回nil。这样做可以防止因为父类初始化失败而返回了一个不可用的对象。如果你不是这样做,你可能你会得到一个不可用的对象,并且它的行为是不可预测的,最终可能会导致你的程序发生Crash。
super is a flag that tells the compiler to search for the method implementation in a very different place. It begins in the superclass of the class that defines the method where super appears.
要回答这个问题,我们可以参考一下刚刚提到的面向对象编程,在面向对象编程里,是从一个 class 开始的,那要是照这样说,在面向协议编程里就是从一个 protocol 了吗?这样解释对不对呢?我们可以在刚刚提到视频里找找答案,如果看过上面的视频,你会发现在上面的视频中 Apple 自己都说:
"从一个 protocol 开始,别从 class 开始。" ——Dave Abrahams: 毁你三观教授
protocol 就是协议的意思。当然,可以从protocol 开始,但是从 protocol 开始了之后,该怎么做呢?
是的,这也是我们该思考的问题,我这里不会太着重去介绍 Swift 的基础,因为我默认看我视频的同学都已经掌握了 Swift 的基础了,所以关于 protocol 的概念我也不在详细介绍了,回到我们刚才的问题,现在我们已经有了 protocol,接下来我们要做的就是使用非常强大的 extension 了,额…,关于 extension 的概念我也不再详细介绍了,如果感觉基础不好的同学可以先去看一下基础,然后再来看我的视频吧,关于 extension,可以为现有的 class,struct,enum,protocol 添加新功能,注意刚刚我提到了 protocol,所以我们先现在可以在 protocol 的extension 里添加任何你需要添加的东西了。
那好,功能也添加了,那怎么该怎么使用这个 protocol 呢?
这也是个问题,让我们再分析一下,protocol 不同于 class 或者 struct,因为后两者可以各自调用它们的类型方法或者实例方法,但是 protocol 却不能直接使用,也不能实例化,既然都不行,那该怎么做啊?别着急,既然不能直接用,那我们就要考虑用上面提到的 class 或者 struct 了
iOS内存管理机制的原理是引用计数,当这块内存被创建后,它的引用计数0->1,表示有一个对象或指针持有这块内存,拥有这块内存的所有权,如果这时候有另外一个对象或指针指向这块内存,那么为了表示这个后来的对象或指针对这块内存的所有权,引用计数1->2,之后若有一个对象或指针不再指向这块内存时,引用计数-1,表示这个对象或指针不再拥有这块内存的所有权,当一块内存的引用计数变为0,表示没有任何对象或指针持有这块内存,系统便会立刻释放掉这块内存。
alloc、new :类初始化方法,开辟新的内存空间,引用计数+1; retain :实例方法,不会开辟新的内存空间,引用计数+1; copy : 实例方法,把一个对象复制到新的内存空间,新的内存空间引用计数+1,旧的不会;其中分为浅拷贝和深拷贝,浅拷贝只是拷贝地址,不会开辟新的内存空间;深拷贝是拷贝内容,会开辟新的内存空间; strong :强引用; 引用计数+1; release :实例方法,释放对象;引用计数-1; autorelease : 延迟释放;autoreleasepool自动释放池;当执行完之后引用计数-1; 还有是initWithFormat和stringWithFormat 字符串长度大于9时,引用计数+1; assign : 弱引用 ;weak也是弱引用,两者区别:assign不但能作用于对象还能作用于基本数据类型,但是所指向的对象销毁时不会将当前指向对象的指针指向nil,有野指针的生成;weak只能作用于对象,不能作用于基本数据类型,所指向的对象销毁时会将当前指向对象的指针指向nil,防止野指针的生成。
NSThread是封装程度最小最轻量级的,使用更灵活,但要手动管理线程的生命周期、线程同步和线程加锁等,开销较大;
1|[NSThread isMultiThreaded];//BOOL 是否开启了多线程
2|[NSThread currentThread];//NSThread 获取当前线程
3|[NSThread mainThread];//NSThread 获取主线程
4|[NSThread sleepForTimeInterval:1];//线程睡眠1s
5|
GCD基于C语言封装的,遵循FIFO
1dispatch_sync与dispatch_async//同步和异步操作
2
3dispatch_queue_t;//主要有串行和并发两种;
4 其中:
5 dispatch_queue_create("concurrent_queue", DISPATCH_QUEUE_CONCURRENT)并发;
6 dispatch_queue_create("serial_queue", DISPATCH_QUEUE_SERIAL)串行;
7
8dispatch_once_t;//代码只会被执行一次,用于单例
9dispatch_after;//延迟操作
10dispatch_get_main_queue;//回到主线程操作
11
12
13//Demo单例
14+ (instancetype)sharedInstance {
15 static ZZScreenshotsMonitor *instance = nil;
16 static dispatch_once_t onceToken;
17 dispatch_once(&onceToken, ^{
18 instance = [[self alloc] init];
19 });
20 return instance;
21}
22
23
24
25//Demo:执行顺序
26- (void)viewDidLoad {
27 [super viewDidLoad];
28 dispatch_async(dispatch_get_main_queue(), ^{
29 NSLog(@"1");
30 });
31
32 NSLog(@"2");33
34 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0);
35
36 dispatch_sync(queue, ^{
37 NSLog(@"3");
38 });
39
40 dispatch_async(dispatch_get_main_queue(), ^{
41
NSLog(@"4");
42 });
43
44 dispatch_async(queue, ^{
45 NSLog(@"5");
46 });
47
48 NSLog(@"6");
49
50 [self performSelector:@selector(delayMethod) withObject:nil afterDelay:0];
51
52 NSLog(@"8");
53}
54
55- (void)delayMethod {
56
57}
58
59打印结果:23658147;其中5和8随机调换
60
61
NSOperation基于GCD封装的,比GCD可控性更强;可以加入操作依赖(addDependency)、设置操作队列最大可并发执行的操作个数(setMaxConcurrentOperationCount)、取消操作(cancel)等,需要使用两个它的实体子类:NSBlockOperation和NSInvocationOperation,或者继承NSOperation自定义子类;NSBlockOperation和NSInvocationOperation用法的主要区别是:前者执行指定的方法,后者执行代码块,相对来说后者更加灵活易用。NSOperation操作配置完成后便可调用start函数在当前线程执行,如果要异步执行避免阻塞当前线程则可以加入NSOperationQueue中异步执行
作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的点击加入群聊iOS交流群:789143298 进群密码123,不管你是小白还是大牛欢迎入驻 ,分享BAT,阿里面试题、面试经验,讨论技术, 大家一起交流学习成长!
1+ (BOOL)isValidIP:(NSString *)ipStr {
2 if (nil == ipStr) {
3 return NO;
4 }
5
6 NSArray *ipArray = [ipStr componentsSeparatedByString:@"."];
7 if (ipArray.count == 4) {
8 for (NSString *ipnumberStr in ipArray) {
9 if ([self isPureInt:ipnumberStr]) {
10 int ipnumber = [ipnumberStr intValue];
11 if (!(ipnumber>=0 && ipnumber<=255)) {
12 return NO;
13 }
14 }
15 }
16 return YES;
17 }
18 return NO;
19}
20//是否整形
21- (BOOL)isPureInt:(NSString*)string {
22 NSScanner* scan = [NSScanner scannerWithString:string];
23 int val;
24 return[scan scanInt:&val] && [scan isAtEnd];
25}
26//是否只含有数字
27- (BOOL)validateNumber:(NSString*)number {
28 BOOL res = YES;
29 NSCharacterSet* tmpSet = [NSCharacterSet characterSetWithCharactersInString:@"0123456789"];
30 int i = 0;
31 while (i < number.length) {
32 NSString * string = [number substringWithRange:NSMakeRange(i, 1)];
33 NSRange range = [string rangeOfCharacterFromSet:tmpSet];
34 if (range.length == 0) {
35 res = NO;
36 break;
37 }
38 i++;
39 }
40 return res;
41
42}
43
44
使用字符串实现;
1/两个大数相加算法
2-(NSString *)addTwoNumberWithOneNumStr:(NSString *)one anotherNumStr:(NSString *)another
3{
4 int i = 0;
5 int j = 0;
6 int maxLength = 0;
7 int sum = 0;
8 int overflow = 0;
9 int carryBit = 0;
10 NSString *temp1 = @"";
11 NSString *temp2 = @"";
12 NSString *sums = @"";
13 NSString *tempSum = @"";
14 int length1 = (int)one.length;
15 int length2 = (int)another.length;
16 //1.反转字符串
17 for (i = length1 - 1; i >= 0 ; i--) {
18 NSRange range = NSMakeRange(i, 1);
19 temp1 = [temp1 stringByAppendingString:[one substringWithRange:range]];
20 NSLog(@"%@",temp1);
21 }
22 for (j = length2 - 1; j >= 0; j--) {
23 NSRange range = NSMakeRange(j, 1);
24 temp2 = [temp2 stringByAppendingString:[another substringWithRange:range]];
25 NSLog(@"%@",temp2);
26 }
27
28 //2.补全缺少位数为0
29 maxLength = length1 > length2 ? length1 : length2;
30 if (maxLength == length1) {
31 for (i = length2; i < length1; i++) {
32 temp2 = [temp2 stringByAppendingString:@"0"];
33 NSLog(@"i = %d --%@",i,temp2);
34 }
35 }else{
36 for (j = length1; j < length2; j++) {
37 temp1 = [temp1 stringByAppendingString:@"0"];
38 NSLog(@"j = %d --%@",j,temp1);
39 }
40 }
41 //3.取数做加法
42 for (i = 0; i < maxLength; i++) {
43 NSRange range = NSMakeRange(i, 1);
44 int a = [temp1 substringWithRange:range].intValue;
45 int b = [temp2 substringWithRange:range].intValue;
46 sum = a + b + carryBit;
47 if (sum > 9) {
48 if (i == maxLength -1) {
49 overflow = 1;
50 }
51 carryBit = 1;
52 sum -= 10;
53 }else{
54 carryBit = 0;
55 }
56 tempSum = [tempSum stringByAppendingString:[NSString stringWithFormat:@"%d",sum]];
57 }
58 if (overflow == 1) {
59 tempSum = [tempSum stringByAppendingString:@"1"];
60 }
61 int sumlength = (int)tempSum.length;
62 for (i = sumlength - 1; i >= 0 ; i--) {
63 NSRange range = NSMakeRange(i, 1);
64 sums = [sums stringByAppendingString:[tempSum substringWithRange:range]];
65 }
66 NSLog(@"sums = %@",sums);
67 return sums;
68}
KVC : 键值编码(Key-Value Coding),它是一种通过key值访问类属性的机制,而不是通过setter/getter方法访问。其中 KVC 原理:当调用- (void)setValue:(id)value forUndefinedKey:(NSString )key时,KVC底层的执行机制如下: 首先搜索对应属性的setter方法 如果没有找到属性的setter方法,则会检查+ (BOOL)accessInstanceVariablesDirectly方法是否返回了YES(该方法默认返回YES),如果返回了YES, 则KVC机制会搜索类中是否存在该属性的成员变量,也就是_属性名,存在则对该成员变量赋值。搜索成员变量名的顺序是 _key,_isKey,key,isKey。 另外我们也可以通过重写+ (BOOL)accessInstanceVariablesDirectly方法返回NO,这个时候KVC机制就会调用 - (void)setValue:(id)value forUndefinedKey:(NSString )key。 如果没有找到成员变量,调用 - (void)setValue:(id)value forUndefinedKey:(NSString *)key。
KVO : 键值观察者 (Key-Value Observer): KVO 是观察者模式的一种实现,观察者A监听被观察者B的某个属性,当B的属性发生更改时,A就会收到通知,执行相应的方法。实现原理:基本的原理:当观察某对象A时,KVO机制动态创建一个对象A当前类的子类,并为这个新的子类重写了被观察属性keyPath的setter 方法。setter 方法随后负责通知观察对象属性的改变状况。
block本质上也是一个oc对象,他内部也有一个isa指针。block是封装了函数调用以及函数调用环境的OC对象。结构体,在栈上的情况, Block中的指针只是指向栈上的block变量, 而当Block/block变量被copy到堆上以后, 堆上Block会持有堆上block变量. 而堆上的Block再次被调用copy时, 只是Block的引用计数+1而已, 而block变量如果被多个堆上Block持有也只涉及到引用记数的变化. 一旦Block/block变量的引用计数为0, 就会自动从堆上释放内存.这里Block/block变量在堆上的内存管理与Objective-C对象完全一致.
1 Block类 原存储域 调用copy效果
2 _NSConcreteStackBlock 栈 从栈copy到堆
3 _NSConcreteGlobalBlock 数据域(.data域) 什么也不做
4 _NSConcreteMallocBlock 堆 引用计数+1
1:何为适配器模式? 适配器模式将一个类的接口适配成用户所期待的。一个适配器通常允许因为接口不兼容而不能一起工作的类能够在一起工作,做法是将类自己的接口包裹在一个已存在的类中。 2:[如何使用适配器模式?] 当你想使用一个已经存在的类,而它的接口不符合你的需求; 你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类协同工作; 你想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口,对象适配器可以适配它的父亲接口。 3:[适配器模式的优缺点?] 优点:降低数据层和视图层(对象)的耦合度,使之使用更加广泛,适应复杂多变的变化。 缺点:降低了可读性,代码量增加,对于不理解这种模式的人来说比较难看懂。
1:何为策略模式?策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。 2:如何使用策略模式? 在有多种算法相似的情况下,使用 if…else 所带来的复杂和难以维护。 如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。 一个系统需要动态地在几种算法中选择一种。 如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。 注意事项:如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。 3:策略模式的优缺点? 优点:简化操作,提高代码维护性。算法可以自由切换,避免使用多重条件判断,扩展性良好。 缺点:在使用之前就要确定使用某种策略,而不是动态的选择策略。策略类会增多,所有策略类都需要对外暴露。
1:[何为观察者模式?] 当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知它的依赖对象。观察者模式属于行为型模式。 2:如何使用观察者模式? 一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。 3:观察者模式的优缺点? 优点:观察者和被观察者是抽象耦合的。建立一套触发机制。缺点:如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
1:何为原型/外观模式? 原型模式:(Prototype Pattern)用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。 外观模式:(Facade Pattern)隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性。这种模式涉及到一个单一的类,该类提供了客户端请求的简化方法和对现有系统类方法的委托调用。 2:如何使用原型/外观模式? 原型模式: 当一个系统应该独立于它的产品创建,构成和表示时。 当要实例化的类是在运行时刻指定时,例如,通过动态装载。 为了避免创建一个与产品类层次平行的工厂类层次时。 当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。 外观模式: 客户端不需要知道系统内部的复杂联系,整个系统只需提供一个"接待员"即可。 定义系统的入口。 3:原型/外观模式的优缺点? 原型模式: 优点:性能提高,逃避构造函数的约束。 缺点: 配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易。 必须实现 Cloneable 接口。 逃避构造函数的约束。 外观模式 优点:减少系统相互依赖、提高灵活性、提高了安全性。 缺点:不符合开闭原则,如果要改东西很麻烦,继承重写都不合适。
1:何为工厂模式? 这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。 2:如何使用工厂模式? 我们明确地计划不同条件下创建不同实例时。 作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。 3:工厂模式的优缺点? 优点: 一个调用者想创建一个对象,只要知道其名称就可以了。 扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。 屏蔽产品的具体实现,调用者只关心产品的接口。 缺点: 每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。
1:何为桥接模式? 桥接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。 这种模式涉及到一个作为桥接的接口,使得实体类的功能独立于接口实现类。这两种类型的类可被结构化改变而互不影响。 2:如何使用桥接模式? 在有多种可能会变化的情况下,用继承会造成类爆炸问题,扩展起来不灵活。 实现系统可能有多个角度分类,每一种角度都可能变化。 把这种多角度分类分离出来,让它们独立变化,减少它们之间耦合。 3:桥接模式的优缺点? 优点 :抽象和实现的分离、优秀的扩展能力、实现细节对客户透明。 缺点:桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
1:何为代理模式? 在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。 在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。 2:如何使用代理模式? 在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。 想在访问一个类时做一些控制。 3:代理模式的优缺点? 优点: 职责清晰、高扩展性、智能化。 缺点: 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。 实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
1:何为单例模式? 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。 注意: 单例类只能有一个实例。 单例类必须自己创建自己的唯一实例。 单例类必须给所有其他对象提供这一实例。 2:如何使用单例模式? 当您想控制实例数目,节省系统资源的时候。 3:单例模式的优缺点? 优点: 在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。 避免对资源的多重占用比如写文件操作。 缺点: 没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
1:何为备忘录模式? 备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。备忘录模式属于行为型模式。 2:如何使用备忘录模式? 很多时候我们总是需要记录一个对象的内部状态,这样做的目的就是为了允许用户取消不确定或者错误的操作,能够恢复到他原先的状态,使得他有"后悔药"可吃。 3:备忘录模式的优缺点? 优点: 给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态。 实现了信息的封装,使得用户不需要关心状态的保存细节。 缺点: 消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。
1:何为送生成器模式? 建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 2:如何使用生成器模式? 主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。 一些基本部件不会变,而其组合经常变化的时候。 3:生成器模式的优缺点? 优点: 建造者独立,易扩展。 便于控制细节风险。 缺点: 产品必须有共同点,范围有限制。 如内部变化复杂,会有很多的建造类。
1:何为命令模式? 命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。 主要解决的问题? 在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。 2:如何使用命令模式? 在某些场合,比如要对行为进行"记录、撤销/重做、事务"等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将"行为请求者"与"行为实现者"解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。 3:命令模式的优缺点? 优点:降低了系统耦合度,新的命令可以很容易添加到系统中去。 缺点:使用命令模式可能会导致某些系统有过多的具体命令类。
NSThread GCD NSOperation
iOS 实现线程加锁有很多种方式。@synchronized、 NSLock、NSRecursiveLock、NSCondition、NSConditionLock、pthread_mutex、dispatch_semaphore、OSSpinLock、atomic(property) set/ge等等各种方式。
先来看看删除的原理:因为数据结构是单链表,要想删除第i个节点,就要找到第i个节点;要想找到第i个节点,就要找到第i-1个节点;要想找到第i-1个节点,就要找到第i-2个节点…于是就要从第一个节点开始找起,一直找到第i-1个节点。如何找?让一个指针从头结点开始移动,一直移动到第i-1个节点为止。这个过程中可以用一个变量j从0开始计数,一直自增到i-1。之后呢?我们把第i-1个节点找到了,就让它的指针域指向第i+1个节点,这样就达到了删除的目的。而第i+1个节点的地址又从第i个节点获得,第i个节点的地址又是第i-1个节点的后继。因此我们可以这样做:先让一个指针指向第i-1个节点的后继,(保存i+1节点的地址),再让i-1节点的后继指向第i个节点的后继,这样就将第i个节点删除了。(p->next=q->next;)
1Status ListDelete(LinkList *L,int i,ElemType *e){
2 int j;
3 LinkList p,q;
4 p = *L; // 声明一结点p指向链表第一个结点
5 j = 1;
6 while (p->next && j < i) /* 遍历寻找第i个元素 */
7 {
8 p = p->next;
9 ++j;
10 }
11 if (!(p->next) || j > i)
12 return ERROR; /* 第i个元素不存在 */
13 q = p->next;
14 p->next = q->next; /* 将q的后继赋值给p的后继 */
15 *e = q->data; /* 将q结点中的数据给e */
16 free(q); /* 让系统回收此结点,释放内存 */
17 return OK;
18 }
19
NSNotificationCenter是类似一个广播中心站,使用defaultCenter来获取应用中的通知中心,它可以向应用任何地方发送和接收通知。在通知中心注册观察者,发送者使用通知中心广播时,以NSNotification的name和object来确定需要发送给哪个观察者。为保证观察者能接收到通知,所以应先向通知中心注册观察者,接着再发送通知这样才能在通知中心调度表中查找到相应观察者进行通知。
1-(void)postNotification:(NSNotification *)notification;
2-(void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject;
3-(void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject userInfo:(nullable NSDictionary *)aUserInfo;
发送通知通过name和object来确定来标识观察者,name和object两个参数的规则相同即当通知设置name为kChangeNotifition时,那么只会发送给符合name为kChangeNotifition的观察者,同理object指发送给某个特定对象通知,如果只设置了name,那么只有对应名称的通知会触发。如果同时设置name和object参数时就必须同时符合这两个条件的观察者才能接收到通知。
1- (void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable NSNotificationName)aName object:(nullable id)anObject;
2- (id <NSObject>)addObserverForName:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block NS_AVAILABLE(10_6, 4_0);
第一种方式是比较常用的添加Oberver的方式,接到通知时执行aSelector。 第二种方式是基于Block来添加观察者,往通知中心的调度表中添加观察者,这个观察者包括一个queue和一个block,并且会返回这个观察者对象。当接到通知时执行block所在的线程为添加观察者时传入的queue参数,queue也可以为nil,那么block就在通知所在的线程同步执行。 这里需要注意的是如果使用第二种的方式创建观察者需要弱引用可能引起循环引用的对象,避免内存泄漏。
NSNotificatinonCenter实现原理: NSNotificatinonCenter是使用观察者模式来实现的用于跨层传递消息,用来降低耦合度。 NSNotificatinonCenter用来管理通知,将观察者注册到NSNotificatinonCenter的通知调度表中,然后发送通知时利用标识符name和object识别出调度表中的观察者,然后调用相应的观察者的方法,即传递消息(在Objective-C中对象调用方法,就是传递消息,消息有name或者selector,可以接受参数,而且可能有返回值),如果是基于block创建的通知就调用NSNotification的block。
SEL 类成员方法的指针 可以理解 @selector()就是取类方法的编号,他的行为基本可以等同C语言的中函数指针,只不过C语言中,可以把函数名直接赋给一个函数指针,而Object-C的类不能直接应用函数指针,这样只能做一个@selector语法来取. 它的结果是一个SEL类型。这个类型本质是类方法的编号(函数地址)
1- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
2 UIView *hitView = [super hitTest:point withEvent:event];
3 if(hitView == self){
4 return nil;
5 }
6 return hitView;
7 }
RunLoop实际上是一个对象,这个对象在循环中用来处理程序运行过程中出现的各种事件(比如说触摸事件、UI刷新事件、定时器事件、Selector事件)和消息,从而保持程序的持续运行,而且在没有事件处理的时候,会进入睡眠模式,从而节省CPU资源,提高程序性能。
动态的添加对象的成员变量和方法;动态交换两个方法的实现;拦截并替换方法;在方法上增加额外功能;实现NSCoding的自动归档和解档;实现字典转模型的自动转换;
clang -rewrite-objc main.m 查看最终生成代码
1+ (BOOL)resolveInstanceMethod:(SEL)selector;
2+ (BOOL)resolveClassMethod:(SEL)selector;
1- (id)forwardingTargetForSelector:(SEL)selector;
2
1- (void)forwardInvocation:(NSInvocation *)invocation;
2
weak 关键字的作用弱引用,所引用对象的计数器不会加一,并在引用对象被释放的时候自动被设置为 nil; weak是有Runtime维护的weak表; 3.weak释放为nil过程 weak被释放为nil,需要对对象整个释放过程了解,如下是对象释放的整体流程: 1、调用objc_release 2、因为对象的引用计数为0,所以执行dealloc 3、在dealloc中,调用了_objc_rootDealloc函数 4、在_objc_rootDealloc中,调用了object_dispose函数 5、调用objc_destructInstance 6、最后调用objc_clear_deallocating。 对象准备释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。 其实Weak表是一个hash(哈希)表,然后里面的key是指向对象的地址,Value是Weak指针的地址的数组 总结 weak是Runtime维护了一个hash(哈希)表,用于存储指向某个对象的所有weak指针。weak表其实是一个hash(哈希)表,Key是所指对象的地址,Value是weak指针的地址(这个地址的值是所指对象指针的地址)数组。
1+ (instancetype)sharedInstance {
2 static RMF *instance = nil;
3 static dispatch_once_t onceToken;
4 dispatch_once(&onceToken, ^{
5 instance = [[self alloc] init];
6 });
7
8 return instance;
9 }
1- (BOOL)isPureNumandCharacters:(NSString *)text
2{
3 for(int i = 0; i < [text length]; ++i) {
4 int a = [text characterAtIndex:i];
5 if ([self isNum:a]){
6 continue;
7 } else {
8 return NO;
9 }
10 }
11 return YES;
12}
1for (NSString *item in originalArr) {
2 if (![resultArrM containsObject:item]) {
3 [resultArrM addObject:item];
4 }
5}
1for (NSNumber *n in originalArr) {
2 [dict setObject:n forKey:n];
3}
1 NSSet *set = [NSSet setWithArray:originalArr];
主要是对比一下常用的几种
方案1:使用group和semaphore 方案2:group_enter和group_leave也可以实现 下面使用方案1实现例子
1 dispatch_group_t group = dispatch_group_create();
2 dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
3 dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
4 dispatch_group_async(group, queue, ^{
5 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
6 //异步执行A
7 dispatch_semaphore_signal(semaphore);
8 });
9 dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
10 });
11
12 dispatch_group_async(group, queue, ^{
13 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
14 //异步执行B
15 dispatch_semaphore_signal(semaphore);
16 });
17 dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
18 });
19
20 dispatch_group_async(group, queue, ^{
21 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
22 //异步执行C
23 dispatch_semaphore_signal(semaphore);
24 });
25 dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
26 });
27
28 dispatch_group_notify(group, queue, ^{
29 //执行D
30 });
1)Instruments中:Core Animation; 2)FPS:CADisplayLink
1、文本、布局计算,提前计算缓存; 2、对象创建;CALayer代替UIView; 3、离屏渲染; 4、图片解码;
(离屏渲染是指图层在被显示之前是在当前屏幕缓冲区以外开辟的一个缓冲区进行渲染操作。 离屏渲染需要多次切换上下文环境:先是从当前屏幕(On-Screen)切换到离屏(Off-Screen);等到离屏渲染结束以后,将离屏缓冲区的渲染结果显示到屏幕上又需要将上下文环境从离屏切换到当前屏幕,而上下文环境的切换是一项高开销的动作。) 1.阴影(UIView.layer.shadowOffset/shadowRadius/…) 2.圆角(当 UIView.layer.cornerRadius 和 UIView.layer.maskToBounds 一起使用时) 3.图层蒙板 4.开启光栅化(shouldRasterize = true)
1、使用CAShapeLayer和UIBezierPath设置圆角; 2、UIBezierPath和Core Graphics框架画出一个圆角;
1//1、使用CAShapeLayer和UIBezierPath设置圆角;
2UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:imageView.bounds byRoundingCorners:UIRectCornerAllCorners cornerRadii:imageView.bounds.size];
3CAShapeLayer *maskLayer = [[CAShapeLayer alloc]init];
4maskLayer.frame = imageView.bounds;
5maskLayer.path = maskPath.CGPath;
6imageView.layer.mask = maskLayer;
7
8//2、UIBezierPath和Core Graphics框架画出一个圆角
9UIGraphicsBeginImageContextWithOptions(imageView.bounds.size,NO,1.0);
10[[UIBezierPathbezierPathWithRoundedRect:imageView.boundscornerRadius:imageView.frame.size.width]addClip];
11[imageView drawRect:imageView.bounds];
12imageView.image=UIGraphicsGetImageFromCurrentImageContext();
13UIGraphicsEndImageContext();
14[self.view addSubview:imageView];
HTTP+对称加密和非对称加密+证书认证
采集视频,音频–》使用iOS原生框架 AVFoundation.framework 视频滤镜处理–》使用iOS原生框架 CoreImage.framework;使用第三方框架 GPUImage.framework
CoreImage 与 GPUImage 框架比较: 在实际项目开发中,开发者更加倾向使用于GPUImage框架. 首先它在使用性能上与iOS提供的原生框架,并没有差别;其次它的使用便利性高于iOS原生框架,最后也是最重要的GPUImage框架是开源的.而大家如果想要学习GPUImage框架,建议学习OpenGL ES,其实GPUImage的封装和思维都是基于OpenGL ES.
void main()
{
char str[20];
int i,num[256]={0};
printf("please input string:");
scanf("%s",str);
for(i=0;i<strlen(str);i++)
num[(int)str[i]]++;
for(i=0;i<256;i++)
if(num[i]!=0)
printf("字符%c出现%d次\n",(char)i,num[i]);
}
@interface TreeNode : NSObject
@property (nonatomic, assign) NSInteger val;
@property (nonatomic, strong) TreeNode *left;
@property (nonatomic, strong) TreeNode *right;
@end
- (void)exchangeNode:(TreeNode *)node {
//判断是否存在node节点
if(node) {
//交换左右节点
TreeNode *temp = node.left;
node.left = node.right;
node.right = temp;
}
}
- (TreeNode *)invertTree:(TreeNode *)root
{
//边界条件 递归结束或输入为空情况
if(!root) {
return root;
}
//递归左右子树
[self invertTree:root.left];
[self invertTree:root.right];
//交换左右子节点
[self exchangeNode:root];
return root;
}
递归
冒泡、快速、插入、选择、希尔、堆等等
self调用自己方法,super调用父类方法 self是类,super是预编译指令 【self class】和【super class】输出是一样的
1.当使用 self 调用方法时,会从当前类的方法列表中开始找,如果没有,就从父类中再找;而当使用 super 时,则从父类的方法列表中开始找,然后调用父类的这个方法。 2.当使用 self 调用时,会使用 objc_msgSend 函数: id objc_msgSend(id theReceiver, SEL theSelector, …)。第 一个参数是消息接收者,第二个参数是调用的具体类方法的 selector,后面是 selector 方法的可变参数。以 [self setName:] 为例,编译器会替换成调用 objc_msgSend 的函数调用,其中 theReceiver 是 self,theSelector 是 @selector(setName:),这个 selector 是从当前 self 的 class 的方法列表开始找的 setName,当找到后把对应的 selector 传递过去。 3.当使用 super 调用时,会使用 objc_msgSendSuper 函数:id objc_msgSendSuper(struct objc_super
struct objc_super {
id receiver;
Class superClass;
};
当编译器遇到 [super setName:] 时,开始做这几个事: 1)构 建 objc_super 的结构体,此时这个结构体的第一个成员变量 receiver 就是 子类,和 self 相同。而第二个成员变量 superClass 就是指父类 调用 objc_msgSendSuper 的方法,将这个结构体和 setName 的 sel 传递过去。 2)函数里面在做的事情类似这样:从 objc_super 结构体指向的 superClass 的方法列表开始找 setName 的 selector,找到后再以 objc_super->receiver 去调用这个 selector
NSTimer准吗?
定时器被添加在主线程中,由于定时器在一个RunLoop中被检测一次,所以如果在这一次的RunLoop中做了耗时的操作,当前RunLoop持续的时间超过了定时器的间隔时间,那么下一次定时就被延后了。
请你出一套iOS面试题
底层
参考解答: self.name ="object":会调用对象的setName()方法; name =“object":会直接把object赋值给当前对象的name属性。
NSString*obj = [[NSData alloc] init];
obj在编译时和运行时分别时什么类型的对象?参考解答: 编译时是NSString的类型, 运行时是NSData类型的对象
Runloop
的面试题: 在程序通过main()
函数启动后,在主线程开启UIApplicationMain
的Runloop
后,在下面的main()
函数里创建了一个autoreleasepool
,请问如果程序不退出,main()
函数永远不会返回,那么程序中(比如一个VC中)创建的对象是否就永远不会被释放?参考解答: 在程序创建的对象,不是被
main()
函数中的autoreleasepool
管理的, 而是由主线程的Runloop
管理,在某次Runloop
循环中,Runloop
休眠之前调用了对象的release
方法释放。在每次
Runloop
循环中,Runloop
休眠之前会调用了对象的release
方法释放AutoreleasePoolPage
中边界内的对象。Cocoa
参考解答: 两个问题: 1、添加,删除,修改数组内的元素的时候程序会因为找不到对应的方法而崩溃.因为copy就是复制一个不可变NSArray的对象,使用了atomic属性会严重影响性能。
参考解答: 我们在声明一个NSString属性时,对于其内存相关特性,通常有两种选择(基于ARC环境): strong与copy。那这两者有什么区别呢?什么时候该用strong,什么时候该用copy呢?
由于NSMutableString是NSString的子类,所以一个NSString指针 可以指向NSMutableString对象,让我们的strongString指针指向一 个可变字符串是OK的。
而上面的例子可以看出,当源字符串是NSString时,由于字符串是不可变的,所以,不管是strong还是copy属性的对象,都是指向源对象,copy操作只是做了次浅拷贝。当源字符串是NSMutableString时,strong属 性只是增加了源字符串的引用计数,而copy属性则是对源字符串做了次深拷贝,产生一个新的对象,且copy属性对象指向这个新的对象。另外需要注意的是,这个copy属性对象的类型始终是NSString,而不是NSMutableString,因此其是不可变的。
这里还有一个性能问题,即在源字符串是NSMutableString, strong是单纯的增加对象的用计数,而copy操作是执行了一次深拷贝,所以性能上会有所差异。而如果源字符串是NSString时,则没有这个问题。
所以,在声明NSString属性时,到底是选择strong还是copy,可以根据实际情况来定。不过,一般我们将对象声明为NSString时,都不希望它改变,所以大多数情况下,我们建议用copy,以免因可变字符串的修改导致的一些非预期问题。
2.3 __strong的使用场景?
参考解答:
AutoLayout
,是苹果公司提供的一个基于约束布局,动态计算视图大小和位置的库,并且已经集成到了Xcode开发环境里。AutoLayout的实现原理
AutoLayout性能
AutoLayout关于更新的几个方法的区别
多线程Demo学习 · Issue #21 · FrizzleFur/DailyLearning
性能优化
TableView性能优化
UITableView核心思想
tableView:cellForRowAtIndexPath:
和tableView:heightForRowAtIndexPath:
.UlTableView的优化总结
提前计算并缓存好高度(布局) , 因heightForRowAtIndexPath:是调用最频繁的方法;
异步绘制,遇到复杂界面,遇到性能瓶颈时,可能就是突破口;
滑动时按需加载,这个在大量图片展示,网络加载的时候很管用! (SDWeblmage已经实现异步加载,配合这条性能杠杠的)。正确使用reuseldentifier来重用Cells
使用cell重用机制,尽可能快地返回重用cell实例这点大家都应该比较清楚,使用reuse机制能大幅降低创建cell所带来的损耗,这就要各位在UITableView的dataSource中实现的
tableView:cellForRowAtIndexPath:
方法。只是有一点,尽量不要在此时绑定数据,因为目前在屏幕上还没有cell,可以在UITableView的delegate方法tableView:willDisplayCell:forRowAtIndexPath:
中进行数据的填充。需要说明的是,你可能会动态计算cel高度,但最好是不要选择Autolayout。使用Autolayout后,会根据cel的子视图使得求解的约束也越多,从而降低计算速度,影响滑动时FPS。所以,为了使tableview平滑滚动,请使用动态计算高度,不要选择Autolayout。cell的subViews的各级opaque值要设成YES,opaque用于辅助绘图系统,表示UIView是否透明。 在不透明的情况下,渲染视图时需要快速地渲染,以提高性能。渲染最慢的操作之一是混合(blending)。提高性能的方法是减少混合操作的次数,其实就是GPU的不合理使用,这是硬件来完成的(混合操作由GPU来执行,因为这个硬件就是用来做混合操作的,当然不只是混合)。优化混合操作的关键点是在平衡CPU和GPU的负载。还有就是cell的layer的
shouldRasterize
要设成YES。尽量少用addView给Cell动态添加View,可以初始化时就添加,然后通过hide来控制是否显示在使用UITableView的时候,有的时候你会碰到Cell卡顿,图片加载慢,使得滑动cel时变得不那么流畅,这些都会影响用户体验,拉低整体app的效果。
在绘制字符串时,尽可能使用drawAtPoint:withFont: , 在绘制图片,尽量使用
drawAtPoint
drawAtPoint:(CGPoint)point forWidth:(CGFloat)width withFont:(UIFont*)font
cell异步加载图片以及缓存
cellForRow
方法里面设置cell的图片数据源,也就是说如果一个cell的imageview对象开启了一个下载任务,这个时候该cell对象发生了重用,新的image数据源会开启。imageview
对象,就会发生2个下载任务回调给同一个imageview对象。这个时候就有必要做一些处理,避免回调发生时,错误的image数据源刷新了UI。tableview
的时候我们需要手动去取消掉下载操作,当用户停止滑动,再去执行下载操作网络请求
网络Http请求 · Issue #9 · FrizzleFur/DailyLearning
架构设计
设计模式
组件化
知名框架原理
CS基础
职业