学习总结

一.内存分配

占用内存的行为

1.创建对象
2.定义变量、常量
3.调用函数或者方法

内存管理范围

对OC对象需要进行内存管理,非OC对象如基本数据类型不需要进行内存管理。

为什么只有OC对象才需要进行内存管理

1.OC的对象在内存中是以堆的方式存储在内存空间的,并且堆内存是由开发者释放(release)。
2.堆里面的内存是动态分配的,所以需要开发者手动添加和释放内存。
3.栈有两种分配方式:静态分配和动态分配
静态分配是由系统编译器完成的,比如局部变量的分配。
动态分配是由alloc函数进行分配的,但是栈的动态分配和堆(stack)不同,它的动态分配也由系统编译器进行释放,不需要开发者管理。
总结:OC对象存放于堆里面(堆内存要程序员手动回收),非OC对象一般放在栈里面(栈内存会被系统自动回收)。

二.block

block的本质是什么?为啥在block里面更改外面变量的值,要给外面的变量加_block修饰,加_block修饰的原理是什么?

答:
(1) block本质是一个数据类型,多用于参数传递,代替代理方法, (有多个参数需要传递或者多个代理方法需要实现还是推荐使用代理方法),少用于当做返回值传递. block是一个OC对象,它的功能是保存代码片段,预先准备好代码,并在需要的时候执行.
(2)因为使用block代码块可能会引起内部循坏引用,所以应在block定义前加上修饰

block和闭包

1.Block的声明:
Block的定义和函数的声明差不多,就是把函数名改为(^blockName)即可.以下为Block的声明代码:
2.闭包可以 捕获 和存储其所在上下文中任意常量和变量的引用。 这就是所谓的闭合并包裹着这些常量和变量,俗称闭包。Swift会为您管理在 捕获 过程中涉及到的内存操作。

OC中的block是匿名的函数
Swift中的闭包是一个特殊的函数
block和闭包都经常用于回调

思考:为什么masonry中不用担心block的循环引用?

查看masonry源码可以看到究竟:masonry中设置布局的方法中的block对象并没有被View所引用,而是直接在方法内部同步执行,执行完以后block将释放,其中捕捉的外部变量的引用计数也将还原到之前。

//
//  UIView+MASAdditions.m
//  Masonry
//
//  Created by Jonas Budelmann on 20/07/13.
//  Copyright (c) 2013 cloudling. All rights reserved.
//

#import "View+MASAdditions.h"
#import <objc/runtime.h>

@implementation MAS_VIEW (MASAdditions)

- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
    self.translatesAutoresizingMaskIntoConstraints = NO;
    MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
    block(constraintMaker);
    return [constraintMaker install];
}

- (NSArray *)mas_updateConstraints:(void(^)(MASConstraintMaker *))block {
    self.translatesAutoresizingMaskIntoConstraints = NO;
    MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
    constraintMaker.updateExisting = YES;
    block(constraintMaker);
    return [constraintMaker install];
}

- (NSArray *)mas_remakeConstraints:(void(^)(MASConstraintMaker *make))block {
    self.translatesAutoresizingMaskIntoConstraints = NO;
    MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
    constraintMaker.removeExisting = YES;
    block(constraintMaker);
    return [constraintMaker install];
}
Block在ARC和MRC分布情况

总结:
在ARC中:
*1.return在栈中的block,无论是否有赋值操作,最终获得block都存在堆中。
*2.return在全局区的block,无论是否有赋值操作,最终获得block都全局区。
*3.return数组,数组中如果是全局区的block,最终获得block都全局区。
*4.return数组,数组中如果是栈中的block,arr[0]保存block在堆中,arr[1]会报错。
在MRC中:
*1.return在栈中的block,编译报错,提示无法返回本地block。
*2.return在全局区的block,无论是否有赋值操作,最终获得block都全局区。
*3.return数组,数组中如果是全局区的block,最终获得block都全局区。
*4.return数组,数组中如果是栈中的block,打印block会报错。

三.深拷贝和浅拷贝

1.深拷贝(内容拷贝)单独复制内容,和原来的相互独立,是重新开辟一块独立的内存空间,并拷贝对象的具体内容。两个对象虽然存的值是相同的,但是内存地址不一样,两个对象也互不影响,互不干涉。只拷贝空间内容,不拷贝对象地址,重新开辟一块新的内存空间。
常用:copy NSMutableCopy
2.浅拷贝(指针拷贝,对象地址的拷贝)对对象地址的拷贝,让几个指针变量共用同一块内存空间,随着源对象的内容改变而改变。常用 retain srong.(只是拷贝对象地址,还指向原对象)


lALPBG1Q4qxySOLNARPNA1k_857_275.png_720x720q90g.jpg
property里的copy、strong区别

1.NSMutableArray、NSMutableDictionary、NSMutableString,用strong
用copy为关键字的话,调用setter方法后。是对赋值对象进行深拷贝。并且拷贝的对象是copy的(不可变的),而不是mutableCopy的(可变的)。所以用copy修饰的mutableArray也被视为Array了,所以再用mutableArray的方法就会发生崩溃

2.NSArray、NSDictionary、NSString,用copy
被strong修饰之后,由于只是强引用,所以副本对象数组和源对象数组只是指向同一个内存区域,这样就会造成副本对象数组会随着源对象数组的改变而改变,即便有时候你并不想让副本对象跟着改变。

四. CPU、GPU的性能优化

CPU(中央处理器)
对象的创建和销毁,对象属性的调整、布局计算、文本的计算和排版、图片格式转码和解码、图像的绘制(Core Graphics)
GPU(图形处理器)
纹理的渲染(OpenGL)

CPU和GPU的协作过程:控件的位置大小、颜色都是交由CPU计算,计算完成之后会提交给CPU进行渲染,GPU做的操作则是:将收到的数据转成屏幕能显示的数据格式,所以要进行渲染的操作。渲染的过程是是直接放在帧缓存的(这个就是个缓存区),然后视频控制器从缓存区读取出来展示在屏幕上,这就是一个渲染的过程。
在iOS中是双缓存机制,有前帧缓存、后帧缓存
前帧缓存:GPU会预先渲染好一帧放入一个缓冲区内
后帧缓存:让视频控制器读取,当下一帧渲染好后,GPU会直接把视频控制器的指针指向第二个缓冲器

五.卡顿解决的主要思路:

尽可能减少CPU、GPU资源的消耗。
按照60FPS的刷帧率,每隔16ms就会有一次VSync信号。

性能相关注意事项:
1.尽量用轻量级的对象,比如用不到事件处理的地方,可以考虑使用CAlayer取代UIView;能用基本数据类型,就别用NSNumber类型。
2.不要频繁地跳用UIVIew的相关属性,比如frame、bounds、transform等属性,尽量减少不必要的修改
3.尽量提前计算好布局,在有需要时一次性调整对应的布局,不要多次修改属性
4.Autolayout会比直接设置frame消耗更多的CPU资源
5.图片的size最好刚好跟UIImageView的size保持一致
6.控制一下线程的最大并发数量
7.尽量把耗时的操作放到子线程
8.尽量减少视图数量和层次
9.GPU能处理的最大纹理尺寸是4096x4096,一旦超过这个尺寸,就会占用CPU资源进行处理,所以纹理尽量不要超过这个尺寸
10.尽量避免段时间内大量图片的显示,尽可能将多张图片合成一张图片显示
11.减少透明的视图(alpha<1),不透明的就设置opaque为yes
12.尽量避免出现离屏渲染

六. 耗电优化

1.尽量减少定时器操作
2.降低CPU、GPU的功耗
3.不要频繁写入小数据,尽可能一次性操作数据
4.减少网络请求次数,尽量做好本地缓存
5.数据量大的时候尽量使用数据库
6.定位优化(持续定位的优化)

七.启动优化

1.减少动态库
2.减少Objc类、分类、方法的数量
3.用+initialize方法替代+load
4.尽量在finishLaunching方法中不要做太多操作(创建太多对象),尽量做到分批加载、创建。

八.TCP与UDP原理

UDP:用户数据报协议,是一个非连接的协议。传输数据之前源端和终端不建立连接,当它想传送时就简单地去抓取应用程序的数据,并尽可能的把它扔到网络上。
TCP:面向连接、传输可靠(保证数据正确性,保证数据顺序)、用于传输大量数据(流模式)、速度慢,建立连接需要开销较多(时间,系统资源)。

TCP与UDP的区别:

1.基于连接与无连接;
2.对系统资源的要求(TCP较多,UDP少);
3.UDP程序结构较简单;
4.流模式与数据报模式 ;
5.TCP保证数据正确性,UDP可能丢包,TCP保证数据顺序,UDP不保证

九.常用的设计模式

观察者模式

-何为观察者模式?
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知它的依赖对象。观察者模式属于行为型模式。
-如何使用观察者模式?
一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
观察者模式的优缺点?
优点:
1.观察者和被观察者是抽象耦合的。
2.建立一套触发机制。
缺点:
1.如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
2.如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
3.观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

工厂模式

-何为工厂模式?
1.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
2.在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
-如何使用工厂模式?
1.我们明确地计划不同条件下创建不同实例时。
2.作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。
工厂模式的优缺点?
优点:
1.一个调用者想创建一个对象,只要知道其名称就可以了。
2.扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。
3.屏蔽产品的具体实现,调用者只关心产品的接口。
缺点:

1.每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。

单例模式
代理模式
桥接模式
适配器模式
策略模式
组合模式

其余多种模式待学习更新

十.线程锁

dispatch_semaphore_t(信号量)用法
dispatch_semaphore_create
dispatch_semaphore_wait
dispatch_semaphore_signal

优缺点
要设置好信号量的数值,当wait的时候,信号量会减一,当signal的时候,信号量会加一,当为0的时候,线程会一直处于等待状态,如果dispatch_semaphore_wait(signal, overTime);后面的overTime给了一定的数值,那么会等待这个时间之后,去释放线程继续执行,如果给的是forever,那么线程会一直卡在这里.

示例代码
- (void)viewDidLoad {
    self.tickets = 10;
    [super viewDidLoad];
    self.lock = [[NSLock alloc] init];
    self.semaphore = dispatch_semaphore_create(1);
    // 线程1
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [self semSale];
    });
    // 线程2
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [self semSale];
    });
}
- (void)semSale{
    while (true) {
        dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
        if (self.tickets > 0) {
            [NSThread sleepForTimeInterval:0.5];
            self.tickets --;
            NSLog(@"剩余票数---%ld张",self.tickets);
        }
        else{
            NSLog(@"卖光了");
            break;
        }
        NSLog(@"%@",[NSThread currentThread]);
        dispatch_semaphore_signal(self.semaphore);
    }
}
原理

我们一开始给的信号量为1,当有一个线程进来买票的时候,这个时候执行了wait,这个时候信号量会减一,然后自己开始买票,假设这时候,又有一个线程进来买票了,有执行到wait了,发现这时候信号量为0,就一直等待,知道上一个人买到票了,买到票之后把信号量加一,执行signal,这个人才可以买。

看一下输出和线程

2018-03-20 13:50:43.325467+0800 lock[65191:2429714] 剩余票数---9张
2018-03-20 13:50:43.325657+0800 lock[65191:2429714] <NSThread: 0x60400027dc00>{number = 3, name = (null)}
2018-03-20 13:50:43.829059+0800 lock[65191:2429712] 剩余票数---8张
2018-03-20 13:50:43.829512+0800 lock[65191:2429712] <NSThread: 0x60c000066640>{number = 4, name = (null)}
2018-03-20 13:50:44.332722+0800 lock[65191:2429714] 剩余票数---7张
2018-03-20 13:50:44.333094+0800 lock[65191:2429714] <NSThread: 0x60400027dc00>{number = 3, name = (null)}
2018-03-20 13:50:44.837398+0800 lock[65191:2429712] 剩余票数---6张
2018-03-20 13:50:44.837762+0800 lock[65191:2429712] <NSThread: 0x60c000066640>{number = 4, name = (null)}
2018-03-20 13:50:45.343206+0800 lock[65191:2429714] 剩余票数---5张
2018-03-20 13:50:45.343577+0800 lock[65191:2429714] <NSThread: 0x60400027dc00>{number = 3, name = (null)}
2018-03-20 13:50:45.848952+0800 lock[65191:2429712] 剩余票数---4张
2018-03-20 13:50:45.849332+0800 lock[65191:2429712] <NSThread: 0x60c000066640>{number = 4, name = (null)}
2018-03-20 13:50:46.353070+0800 lock[65191:2429714] 剩余票数---3张
2018-03-20 13:50:46.353349+0800 lock[65191:2429714] <NSThread: 0x60400027dc00>{number = 3, name = (null)}
2018-03-20 13:50:46.854531+0800 lock[65191:2429712] 剩余票数---2张
2018-03-20 13:50:46.854909+0800 lock[65191:2429712] <NSThread: 0x60c000066640>{number = 4, name = (null)}
2018-03-20 13:50:47.355823+0800 lock[65191:2429714] 剩余票数---1张
2018-03-20 13:50:47.356235+0800 lock[65191:2429714] <NSThread: 0x60400027dc00>{number = 3, name = (null)}
2018-03-20 13:50:47.857671+0800 lock[65191:2429712] 剩余票数---0张
2018-03-20 13:50:47.858070+0800 lock[65191:2429712] <NSThread: 0x60c000066640>{number = 4, name = (null)}
2018-03-20 13:50:47.858392+0800 lock[65191:2429714] 卖光了

十一.自动释放池和runloop的关系,何时释放?

什么时候用AutoreleasePool

1.写给予命令行的程序时,就是没有UI框架;
2.写循环,循环里边包含了大量临时创建的对象;
3.创建了新的线程;
4.长时间在后台运行的任务;
5.合理运用自动释放池,可以降低程序的内存峰值,异步的方式将文件保存在磁盘(SDWebimage里边异步保存图片到磁盘,类似的占用内存的操作);

RunLoop 是怎样让autorelease释放的呢?

系统在主线程的RunLoop里注册了两个Observer,回调都是_wrapRunLoopWithAutoreleasePoolHandler,第一个Observer的状态是activities = 0x1,第二个Observer的状态是activities = 0xa0,这两种状态代表什么意思呢?

0x1代表kCFRunLoopEntry, Observer监听的第一个事件Entry (即将进入Loop时)回调内会调用_objc_autoreleasePoolPush()创建一个自动释放池 ,其order优先级是-2147483647, 优先级最高,保证创建自动释放池发生在其他所有回调之前

0xa0代表的是kCFRunLoopBeforeWaiting和kCFRunLoopExit,第二个Observer监听了两个事件:kCFRunLoopBeforeWaiting准备进入休眠,kCFRunLoopExit即将退出RunLoop。在kCFRunLoopBeforeWaiting事件时调用 _objc_autoreleasePoolPop()和_objc_autoreleasePoolPush() 释放旧的自动释放池并创建新的自动释放池;同时这个Observer的order优先级是 2147483647,优先级最低,保证其释放自动释放池的操作发生在其他所有回调之后。

所以在没有手动增加AutoreleasePool的情况下,Autorelease对象都是在当前的runloop迭代结束时释放的,而它能够释放的原因是系统在每个runloop迭代中都加入了自动释放池push和pop操作。

参考:http://www.mamicode.com/info-detail-1834807.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335