一.在查看优化经验文章的时候,经常会看到关于+load和initialize两个方法,其中+load方法,是只要程序启动了,程序就会将所有代码加载到内存的代码区,此时,这个+load方法就会执行,同时,此方法会在main函数执行前就调用,而 initialize是该类初始化时候才调用。所以推荐在做启动优化的时候,要检查系统中的无用类、过期类,合并多余类、中间类,因为每次启动app,这些都会加载一遍。
二.启动时间大体可分为两步(冷启动热启动就算了,不区分了):
第一步:main函数加载之前,系统会加载动态库、引入类的+load方法等,所以需要:删减不必要的动态库,定期检查系统中无用类,过期类,合并多余中间类(多说一嘴,中小项目没必要用MVVM),除了系统动态库,个人创建的动态库多常见于一些自己封装的UIKit,可以考虑封装成静态库,或者合并。
第二步:main函数加载之后,此时,主要是针对业务模块进行优化,
1.日志、统计、广告业等必须一开始加载的功能,保留在didFinishLaunchingWithOptions中。
2.推送、项目配置、环境配置、信息初始化等必须在主页面进入前完成的工作,放到广告页的viewDidAppear里进行。
3.其他SDK和配置事件,由于启动时间不是必须的,所以可以放到首页的viewDidAppear里进行。
4.初始化tabbar的时候,除了首页外,延迟其他几个控制器的创建时间,比如“个人中心”,“订单”等模块,用空的viewController占位,第一次点击的时候再初始化加载。
其他更厉害的优化,可以参考抖音团队分享的:基于二进制文件重排的解决方案。http://www.zyiz.net/tech/detail-127196.html
三:KVO是如何实现监听的
KVO底层使用了 isa-swizling的技术.
OC中每个对象/类都有isa指针, isa 表示这个对象是哪个类的对象.
当使用KVO给对象的某个属性注册了一个 observer,系统会创建一个新的中间类(intermediate class)继承原来的class,把该对象的isa指针指向中间类(这样,这个对象的类其实就改变为这个新创建的中间类)。
然后中间类会重写该属性的setter方法,当这个属性的值更改的时候,会在调用setter之前调用willChangeValueForKey, 调用setter之后调用didChangeValueForKey,以此通知所有观察者值发生更改。
之后重写了 -class 方法,企图欺骗我们这个类没有变,就是原本那个类。
四:NSTimer的循环引用和Block循环引用有什么区别?
__weak typeof(self) weakSelf = self;
self.timer = [NSTimer timerWithTimeInterval:1 target:weakSelf selector:@selector(timerAction) userInfo:nil repeats:YES];
同样是用__weak修饰self,为什么NSTimer会导致循环引用,而block不会呢?
因为block中,引用的是weakSelf这个指针,而NSTimer引用的是weakSelf指针指向的内存地址,所以只要NSTimer不释放,就会一直持有weakSelf指向的self,而因为timer被runloop强持有,runloop又是常驻的,所以timer一直没有被释放。
NSTimer解决循环引用的几种方式:
1.离开页面的时候,置空timer
2.ios 10之后,系统帮我们对NSTimer进行了block的封装,防止block循环引用即可
3.做个中间类,帮助控制器持有NSTimer
4.用GCD的定时器
五:GCD什么情况下会开启多线程?
1.同步函数串行队列:
不会开启新线程,在当前线程执行任务
任务串行执行,一个接着一个
会产生死锁
2.同步函数并行队列:
不会开启新线程,在当前线程执行任务
任务依然一个接一个
3.异步函数串行队列:
开启一个新线程,任务一个接一个执行
4.异步函数并行队列:
开启多个线程,任务并发执行
5.创建队列的方式:
- (void)test {
//串行队列
dispatch_queue_t serial = dispatch_queue_create("com", DISPATCH_QUEUE_SERIAL);
//并发队列
dispatch_queue_t concurrent = dispatch_queue_create("com", DISPATCH_QUEUE_CONCURRENT);
//主队列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
//全局队列
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
}
根据官网介绍,多线程创建需要耗费一定的内核空间,主线程一般是1MB,非主线程一般是16Kb到512KB,创建线程的时间大概是90微秒,切换线程,也会消耗一定的资源时间
六:栅栏函数
1.dispatch_barrier_async: 当前任务队列中,前面的任务执行完毕才会执行barrier中的逻辑,以及barrier后加入该队列的任务,但是不影响该栅栏函数所在队列后面函数的执行。
2.dispatch_barrier_sync:作用相同,区别是会堵塞队列后面的函数执行。
举例:
dispatch_queue_t concurrentQueue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentQueue, ^{
NSLog(@"1");
});
dispatch_async(concurrentQueue, ^{
NSLog(@"2");
});
dispatch_barrier_async(concurrentQueue, ^{
NSLog(@"3:%@",[NSThread currentThread]);
});
dispatch_async(concurrentQueue, ^{
NSLog(@"4");
});
NSLog(@"5");
}
上面代码打印出来的数据,1,2,5的排序是不固定的,,但是3、4一定是最后两个,3一定在4前面。
3.栅栏函数在全局队列中是不生效的,因为全局队列中,不仅有你的任务,还有其他系统的任务,如果加barrier,不仅会影响你自己的任务,还会影响系统的任务,对于全局队列来说,barrier相当于普通的异步函数。
4.栅栏函数可以当成锁来用:
NSMutableArray *array = [NSMutableArray array];
dispatch_queue_t concurrentQueue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 1000; i++) {
dispatch_async(concurrentQueue, ^{
[array addObject:@(i)];
});
}
上面代码中,多线程操作一个数组array,在addObject的时候有可能存在同一时间对同一块内存空间写入数据(因为多线程是无序的,i=4的时候和i=100的时候,有可能i=100先运行,那么这时候往数组里插数据,就有可能数据错误,此时就会崩溃报错)。
比如写第3个数据的时候,当前数组中数据是(1,2)这个时候有2个线程同时写入数据就存在了(1,2,3)和(1,2,4)这个时候数据就发生了混乱造成了错误。
将数组添加元素的操作放入dispatch_barrier_async中:
dispatch_queue_t concurrentQueue = dispatch_queue_create("Hotpot", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 1000; i++) {
dispatch_async(concurrentQueue, ^{
dispatch_barrier_async(concurrentQueue , ^{
[array addObject:@(i)];
});
});
}
七.信号量
dispatch_semaphore_create(long value);
初始化一个值为value的信号量,value等于几,就支持最多几个线程并发访问。
dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
此函数会让信号量-1(通常来讲,我们会将信号量初始化为1),信号量小于0,则线程进入堵塞、等待状态。
dispatch_semaphore_signal(dispatch_semaphore_t dsema);
此函数会释放信号量,让信号量+1,信号量不为1,则该线程继续执行
信号量的作用:
1.可以限制并发队列里最大并发数
2.配合线程组,解决多个网络请求后,统一刷新页面、处理数据的问题
3.保护数据上锁
dispatch_queue_t queue = dispatch_queue_create("并发队列", DISPATCH_QUEUE_CONCURRENT);
NSMutableArray *array = [NSMutableArray array];
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
for (int i = 0; i < 10000; i++) {
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);//此时当前并发队列,只有一个线程能访问
NSLog(@"添加的值:%d 当前线程%@",i,[NSThread currentThread]);
[array addObject:@(i)];
dispatch_semaphore_signal(semaphore);
});
}
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
dispatch_async(queue, ^{
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
NSLog(@"1 start");
NSLog(@"1 end");
});//由于初始值为0,wait后,信号量-1,小于0,所以后面的打印方法不执行
dispatch_async(queue, ^{
sleep(2);
NSLog(@"2 start");
NSLog(@"2 end");
dispatch_semaphore_signal(sem);
});//此异步任务先执行
打印出来的值:
2 start
2 end
1 start
1 end
八:线程组:
控制线程任务执行顺序,类似于栅栏函数的作用
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_queue_t queue1 = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_async(group, queue, ^{
sleep(3);
NSLog(@"1");
});
dispatch_group_async(group, queue1, ^{
sleep(2);
NSLog(@"2");
});
dispatch_group_async(group, queue1, ^{
sleep(1);
NSLog(@"3");
});
dispatch_group_async(group, queue, ^{
NSLog(@"4");
});
dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{
NSLog(@"5");
});
任务5永远在任务1、2、3、4之后执行。
Sinatra新手;我正在运行一些rspec测试,但在日志中收到了一堆不需要的噪音。如何消除日志中过多的噪音?我仔细检查了环境是否设置为:test,这意味着记录器级别应设置为WARN而不是DEBUG。spec_helper:require"./app"require"sinatra"require"rspec"require"rack/test"require"database_cleaner"require"factory_girl"set:environment,:testFactoryGirl.definition_file_paths=%w{./factories./test/
有没有办法在这个简单的get方法中添加超时选项?我正在使用法拉第3.3。Faraday.get(url)四处寻找,我只能先发起连接后应用超时选项,然后应用超时选项。或者有什么简单的方法?这就是我现在正在做的:conn=Faraday.newresponse=conn.getdo|req|req.urlurlreq.options.timeout=2#2secondsend 最佳答案 试试这个:conn=Faraday.newdo|conn|conn.options.timeout=20endresponse=conn.get(url
这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下
我有两个Rails模型,即Invoice和Invoice_details。一个Invoice_details属于Invoice,一个Invoice有多个Invoice_details。我无法使用accepts_nested_attributes_forinInvoice通过Invoice模型保存Invoice_details。我收到以下错误:(0.2ms)BEGIN(0.2ms)ROLLBACKCompleted422UnprocessableEntityin25ms(ActiveRecord:4.0ms)ActiveRecord::RecordInvalid(Validationfa
我正在尝试解析一个文本文件,该文件每行包含可变数量的单词和数字,如下所示:foo4.500bar3.001.33foobar如何读取由空格而不是换行符分隔的文件?有什么方法可以设置File("file.txt").foreach方法以使用空格而不是换行符作为分隔符? 最佳答案 接受的答案将slurp文件,这可能是大文本文件的问题。更好的解决方案是IO.foreach.它是惯用的,将按字符流式传输文件:File.foreach(filename,""){|string|putsstring}包含“thisisanexample”结果的
我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b
我意识到这可能是一个非常基本的问题,但我现在已经花了几天时间回过头来解决这个问题,但出于某种原因,Google就是没有帮助我。(我认为部分问题在于我是一个初学者,我不知道该问什么......)我也看过O'Reilly的RubyCookbook和RailsAPI,但我仍然停留在这个问题上.我找到了一些关于多态关系的信息,但它似乎不是我需要的(尽管如果我错了请告诉我)。我正在尝试调整MichaelHartl'stutorial创建一个包含用户、文章和评论的博客应用程序(不使用脚手架)。我希望评论既属于用户又属于文章。我的主要问题是:我不知道如何将当前文章的ID放入评论Controller。
我的工作要求我为某些测试自动生成电子邮件。我一直在四处寻找,但未能找到可以快速实现的合理解决方案。它需要在outlook而不是其他邮件服务器中,因为我们有一些奇怪的身份验证规则,我们需要保存草稿而不是仅仅发送邮件的选项。显然win32ole可以做到这一点,但我找不到任何相当简单的例子。 最佳答案 假设存储了Outlook凭据并且您设置为自动登录到Outlook,WIN32OLE可以很好地完成此操作:require'win32ole'outlook=WIN32OLE.new('Outlook.Application')message=
1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里
//1.验证返回状态码是否是200pm.test("Statuscodeis200",function(){pm.response.to.have.status(200);});//2.验证返回body内是否含有某个值pm.test("Bodymatchesstring",function(){pm.expect(pm.response.text()).to.include("string_you_want_to_search");});//3.验证某个返回值是否是100pm.test("Yourtestname",function(){varjsonData=pm.response.json