學(xué)習(xí)總結(jié)

一.內(nèi)存分配

占用內(nèi)存的行為

1.創(chuàng)建對象
2.定義變量、常量
3.調(diào)用函數(shù)或者方法

內(nèi)存管理范圍

對OC對象需要進(jìn)行內(nèi)存管理,非OC對象如基本數(shù)據(jù)類型不需要進(jìn)行內(nèi)存管理。

為什么只有OC對象才需要進(jìn)行內(nèi)存管理

1.OC的對象在內(nèi)存中是以堆的方式存儲在內(nèi)存空間的,并且堆內(nèi)存是由開發(fā)者釋放(release)。
2.堆里面的內(nèi)存是動態(tài)分配的,所以需要開發(fā)者手動添加和釋放內(nèi)存。
3.棧有兩種分配方式:靜態(tài)分配和動態(tài)分配
靜態(tài)分配是由系統(tǒng)編譯器完成的,比如局部變量的分配。
動態(tài)分配是由alloc函數(shù)進(jìn)行分配的,但是棧的動態(tài)分配和堆(stack)不同,它的動態(tài)分配也由系統(tǒng)編譯器進(jìn)行釋放,不需要開發(fā)者管理。
總結(jié):OC對象存放于堆里面(堆內(nèi)存要程序員手動回收),非OC對象一般放在棧里面(棧內(nèi)存會被系統(tǒng)自動回收)。

二.block

block的本質(zhì)是什么?為啥在block里面更改外面變量的值,要給外面的變量加_block修飾,加_block修飾的原理是什么?

答:
(1) block本質(zhì)是一個數(shù)據(jù)類型,多用于參數(shù)傳遞,代替代理方法, (有多個參數(shù)需要傳遞或者多個代理方法需要實現(xiàn)還是推薦使用代理方法),少用于當(dāng)做返回值傳遞. block是一個OC對象,它的功能是保存代碼片段,預(yù)先準(zhǔn)備好代碼,并在需要的時候執(zhí)行.
(2)因為使用block代碼塊可能會引起內(nèi)部循壞引用,所以應(yīng)在block定義前加上修飾

block和閉包

1.Block的聲明:
Block的定義和函數(shù)的聲明差不多,就是把函數(shù)名改為(^blockName)即可.以下為Block的聲明代碼:
2.閉包可以 捕獲 和存儲其所在上下文中任意常量和變量的引用。 這就是所謂的閉合并包裹著這些常量和變量,俗稱閉包。Swift會為您管理在 捕獲 過程中涉及到的內(nèi)存操作。

OC中的block是匿名的函數(shù)
Swift中的閉包是一個特殊的函數(shù)
block和閉包都經(jīng)常用于回調(diào)

思考:為什么masonry中不用擔(dān)心block的循環(huán)引用?

查看masonry源碼可以看到究竟:masonry中設(shè)置布局的方法中的block對象并沒有被View所引用,而是直接在方法內(nèi)部同步執(zhí)行,執(zhí)行完以后block將釋放,其中捕捉的外部變量的引用計數(shù)也將還原到之前。

//
//  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分布情況

總結(jié):
在ARC中:
*1.return在棧中的block,無論是否有賦值操作,最終獲得block都存在堆中。
*2.return在全局區(qū)的block,無論是否有賦值操作,最終獲得block都全局區(qū)。
*3.return數(shù)組,數(shù)組中如果是全局區(qū)的block,最終獲得block都全局區(qū)。
*4.return數(shù)組,數(shù)組中如果是棧中的block,arr[0]保存block在堆中,arr[1]會報錯。
在MRC中:
*1.return在棧中的block,編譯報錯,提示無法返回本地block。
*2.return在全局區(qū)的block,無論是否有賦值操作,最終獲得block都全局區(qū)。
*3.return數(shù)組,數(shù)組中如果是全局區(qū)的block,最終獲得block都全局區(qū)。
*4.return數(shù)組,數(shù)組中如果是棧中的block,打印block會報錯。

三.深拷貝和淺拷貝

1.深拷貝(內(nèi)容拷貝)單獨復(fù)制內(nèi)容,和原來的相互獨立,是重新開辟一塊獨立的內(nèi)存空間,并拷貝對象的具體內(nèi)容。兩個對象雖然存的值是相同的,但是內(nèi)存地址不一樣,兩個對象也互不影響,互不干涉。只拷貝空間內(nèi)容,不拷貝對象地址,重新開辟一塊新的內(nèi)存空間。
常用:copy NSMutableCopy
2.淺拷貝(指針拷貝,對象地址的拷貝)對對象地址的拷貝,讓幾個指針變量共用同一塊內(nèi)存空間,隨著源對象的內(nèi)容改變而改變。常用 retain srong.(只是拷貝對象地址,還指向原對象)


lALPBG1Q4qxySOLNARPNA1k_857_275.png_720x720q90g.jpg
property里的copy、strong區(qū)別

1.NSMutableArray、NSMutableDictionary、NSMutableString,用strong
用copy為關(guān)鍵字的話,調(diào)用setter方法后。是對賦值對象進(jìn)行深拷貝。并且拷貝的對象是copy的(不可變的),而不是mutableCopy的(可變的)。所以用copy修飾的mutableArray也被視為Array了,所以再用mutableArray的方法就會發(fā)生崩潰

2.NSArray、NSDictionary、NSString,用copy
被strong修飾之后,由于只是強(qiáng)引用,所以副本對象數(shù)組和源對象數(shù)組只是指向同一個內(nèi)存區(qū)域,這樣就會造成副本對象數(shù)組會隨著源對象數(shù)組的改變而改變,即便有時候你并不想讓副本對象跟著改變。

四. CPU、GPU的性能優(yōu)化

CPU(中央處理器)
對象的創(chuàng)建和銷毀,對象屬性的調(diào)整、布局計算、文本的計算和排版、圖片格式轉(zhuǎn)碼和解碼、圖像的繪制(Core Graphics)
GPU(圖形處理器)
紋理的渲染(OpenGL)

CPU和GPU的協(xié)作過程:控件的位置大小、顏色都是交由CPU計算,計算完成之后會提交給CPU進(jìn)行渲染,GPU做的操作則是:將收到的數(shù)據(jù)轉(zhuǎn)成屏幕能顯示的數(shù)據(jù)格式,所以要進(jìn)行渲染的操作。渲染的過程是是直接放在幀緩存的(這個就是個緩存區(qū)),然后視頻控制器從緩存區(qū)讀取出來展示在屏幕上,這就是一個渲染的過程。
在iOS中是雙緩存機(jī)制,有前幀緩存、后幀緩存
前幀緩存:GPU會預(yù)先渲染好一幀放入一個緩沖區(qū)內(nèi)
后幀緩存:讓視頻控制器讀取,當(dāng)下一幀渲染好后,GPU會直接把視頻控制器的指針指向第二個緩沖器

五.卡頓解決的主要思路:

盡可能減少CPU、GPU資源的消耗。
按照60FPS的刷幀率,每隔16ms就會有一次VSync信號。

性能相關(guān)注意事項:
1.盡量用輕量級的對象,比如用不到事件處理的地方,可以考慮使用CAlayer取代UIView;能用基本數(shù)據(jù)類型,就別用NSNumber類型。
2.不要頻繁地跳用UIVIew的相關(guān)屬性,比如frame、bounds、transform等屬性,盡量減少不必要的修改
3.盡量提前計算好布局,在有需要時一次性調(diào)整對應(yīng)的布局,不要多次修改屬性
4.Autolayout會比直接設(shè)置frame消耗更多的CPU資源
5.圖片的size最好剛好跟UIImageView的size保持一致
6.控制一下線程的最大并發(fā)數(shù)量
7.盡量把耗時的操作放到子線程
8.盡量減少視圖數(shù)量和層次
9.GPU能處理的最大紋理尺寸是4096x4096,一旦超過這個尺寸,就會占用CPU資源進(jìn)行處理,所以紋理盡量不要超過這個尺寸
10.盡量避免段時間內(nèi)大量圖片的顯示,盡可能將多張圖片合成一張圖片顯示
11.減少透明的視圖(alpha<1),不透明的就設(shè)置opaque為yes
12.盡量避免出現(xiàn)離屏渲染

六. 耗電優(yōu)化

1.盡量減少定時器操作
2.降低CPU、GPU的功耗
3.不要頻繁寫入小數(shù)據(jù),盡可能一次性操作數(shù)據(jù)
4.減少網(wǎng)絡(luò)請求次數(shù),盡量做好本地緩存
5.數(shù)據(jù)量大的時候盡量使用數(shù)據(jù)庫
6.定位優(yōu)化(持續(xù)定位的優(yōu)化)

七.啟動優(yōu)化

1.減少動態(tài)庫
2.減少Objc類、分類、方法的數(shù)量
3.用+initialize方法替代+load
4.盡量在finishLaunching方法中不要做太多操作(創(chuàng)建太多對象),盡量做到分批加載、創(chuàng)建。

八.TCP與UDP原理

UDP:用戶數(shù)據(jù)報協(xié)議,是一個非連接的協(xié)議。傳輸數(shù)據(jù)之前源端和終端不建立連接,當(dāng)它想傳送時就簡單地去抓取應(yīng)用程序的數(shù)據(jù),并盡可能的把它扔到網(wǎng)絡(luò)上。
TCP:面向連接、傳輸可靠(保證數(shù)據(jù)正確性,保證數(shù)據(jù)順序)、用于傳輸大量數(shù)據(jù)(流模式)、速度慢,建立連接需要開銷較多(時間,系統(tǒng)資源)。

TCP與UDP的區(qū)別:

1.基于連接與無連接;
2.對系統(tǒng)資源的要求(TCP較多,UDP少);
3.UDP程序結(jié)構(gòu)較簡單;
4.流模式與數(shù)據(jù)報模式 ;
5.TCP保證數(shù)據(jù)正確性,UDP可能丟包,TCP保證數(shù)據(jù)順序,UDP不保證

九.常用的設(shè)計模式

觀察者模式

-何為觀察者模式?
當(dāng)對象間存在一對多關(guān)系時,則使用觀察者模式(Observer Pattern)。比如,當(dāng)一個對象被修改時,則會自動通知它的依賴對象。觀察者模式屬于行為型模式。
-如何使用觀察者模式?
一個對象狀態(tài)改變給其他對象通知的問題,而且要考慮到易用和低耦合,保證高度的協(xié)作。一個對象(目標(biāo)對象)的狀態(tài)發(fā)生改變,所有的依賴對象(觀察者對象)都將得到通知,進(jìn)行廣播通知。
觀察者模式的優(yōu)缺點?
優(yōu)點:
1.觀察者和被觀察者是抽象耦合的。
2.建立一套觸發(fā)機(jī)制。
缺點:
1.如果一個被觀察者對象有很多的直接和間接的觀察者的話,將所有的觀察者都通知到會花費很多時間。
2.如果在觀察者和觀察目標(biāo)之間有循環(huán)依賴的話,觀察目標(biāo)會觸發(fā)它們之間進(jìn)行循環(huán)調(diào)用,可能導(dǎo)致系統(tǒng)崩潰。
3.觀察者模式?jīng)]有相應(yīng)的機(jī)制讓觀察者知道所觀察的目標(biāo)對象是怎么發(fā)生變化的,而僅僅只是知道觀察目標(biāo)發(fā)生了變化。

工廠模式

-何為工廠模式?
1.這種類型的設(shè)計模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對象的最佳方式。
2.在工廠模式中,我們在創(chuàng)建對象時不會對客戶端暴露創(chuàng)建邏輯,并且是通過使用一個共同的接口來指向新創(chuàng)建的對象。
-如何使用工廠模式?
1.我們明確地計劃不同條件下創(chuàng)建不同實例時。
2.作為一種創(chuàng)建類模式,在任何需要生成復(fù)雜對象的地方,都可以使用工廠方法模式。有一點需要注意的地方就是復(fù)雜對象適合使用工廠模式,而簡單對象,特別是只需要通過 new 就可以完成創(chuàng)建的對象,無需使用工廠模式。如果使用工廠模式,就需要引入一個工廠類,會增加系統(tǒng)的復(fù)雜度。
工廠模式的優(yōu)缺點?
優(yōu)點:
1.一個調(diào)用者想創(chuàng)建一個對象,只要知道其名稱就可以了。
2.擴(kuò)展性高,如果想增加一個產(chǎn)品,只要擴(kuò)展一個工廠類就可以。
3.屏蔽產(chǎn)品的具體實現(xiàn),調(diào)用者只關(guān)心產(chǎn)品的接口。
缺點:

1.每次增加一個產(chǎn)品時,都需要增加一個具體類和對象實現(xiàn)工廠,使得系統(tǒng)中類的個數(shù)成倍增加,在一定程度上增加了系統(tǒng)的復(fù)雜度,同時也增加了系統(tǒng)具體類的依賴。這并不是什么好事。

單例模式
代理模式
橋接模式
適配器模式
策略模式
組合模式

其余多種模式待學(xué)習(xí)更新

十.線程鎖

dispatch_semaphore_t(信號量)用法
dispatch_semaphore_create
dispatch_semaphore_wait
dispatch_semaphore_signal

優(yōu)缺點
要設(shè)置好信號量的數(shù)值,當(dāng)wait的時候,信號量會減一,當(dāng)signal的時候,信號量會加一,當(dāng)為0的時候,線程會一直處于等待狀態(tài),如果dispatch_semaphore_wait(signal, overTime);后面的overTime給了一定的數(shù)值,那么會等待這個時間之后,去釋放線程繼續(xù)執(zhí)行,如果給的是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(@"剩余票數(shù)---%ld張",self.tickets);
        }
        else{
            NSLog(@"賣光了");
            break;
        }
        NSLog(@"%@",[NSThread currentThread]);
        dispatch_semaphore_signal(self.semaphore);
    }
}
原理

我們一開始給的信號量為1,當(dāng)有一個線程進(jìn)來買票的時候,這個時候執(zhí)行了wait,這個時候信號量會減一,然后自己開始買票,假設(shè)這時候,又有一個線程進(jìn)來買票了,有執(zhí)行到wait了,發(fā)現(xiàn)這時候信號量為0,就一直等待,知道上一個人買到票了,買到票之后把信號量加一,執(zhí)行signal,這個人才可以買。

看一下輸出和線程

2018-03-20 13:50:43.325467+0800 lock[65191:2429714] 剩余票數(shù)---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] 剩余票數(shù)---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] 剩余票數(shù)---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] 剩余票數(shù)---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] 剩余票數(shù)---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] 剩余票數(shù)---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] 剩余票數(shù)---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] 剩余票數(shù)---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] 剩余票數(shù)---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] 剩余票數(shù)---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的關(guān)系,何時釋放?

什么時候用AutoreleasePool

1.寫給予命令行的程序時,就是沒有UI框架;
2.寫循環(huán),循環(huán)里邊包含了大量臨時創(chuàng)建的對象;
3.創(chuàng)建了新的線程;
4.長時間在后臺運行的任務(wù);
5.合理運用自動釋放池,可以降低程序的內(nèi)存峰值,異步的方式將文件保存在磁盤(SDWebimage里邊異步保存圖片到磁盤,類似的占用內(nèi)存的操作);

RunLoop 是怎樣讓autorelease釋放的呢?

系統(tǒng)在主線程的RunLoop里注冊了兩個Observer,回調(diào)都是_wrapRunLoopWithAutoreleasePoolHandler,第一個Observer的狀態(tài)是activities = 0x1,第二個Observer的狀態(tài)是activities = 0xa0,這兩種狀態(tài)代表什么意思呢?

0x1代表kCFRunLoopEntry, Observer監(jiān)聽的第一個事件Entry (即將進(jìn)入Loop時)回調(diào)內(nèi)會調(diào)用_objc_autoreleasePoolPush()創(chuàng)建一個自動釋放池 ,其order優(yōu)先級是-2147483647, 優(yōu)先級最高,保證創(chuàng)建自動釋放池發(fā)生在其他所有回調(diào)之前

0xa0代表的是kCFRunLoopBeforeWaiting和kCFRunLoopExit,第二個Observer監(jiān)聽了兩個事件:kCFRunLoopBeforeWaiting準(zhǔn)備進(jìn)入休眠,kCFRunLoopExit即將退出RunLoop。在kCFRunLoopBeforeWaiting事件時調(diào)用 _objc_autoreleasePoolPop()和_objc_autoreleasePoolPush() 釋放舊的自動釋放池并創(chuàng)建新的自動釋放池;同時這個Observer的order優(yōu)先級是 2147483647,優(yōu)先級最低,保證其釋放自動釋放池的操作發(fā)生在其他所有回調(diào)之后。

所以在沒有手動增加AutoreleasePool的情況下,Autorelease對象都是在當(dāng)前的runloop迭代結(jié)束時釋放的,而它能夠釋放的原因是系統(tǒng)在每個runloop迭代中都加入了自動釋放池push和pop操作。

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容