iOS新手入門課外閱讀

...

??知識(shí)庫 iOS新手入門 iOS筆試題 iOS面試題 iOS開發(fā)知識(shí)整理合集

1年=小學(xué) 2年=初中 3年=高中 4年+=大學(xué)
年限對(duì)應(yīng)表可供參考

結(jié)合其他資料學(xué)習(xí)最佳,該知識(shí)庫只是輔助,可做‘課外閱讀’使用

截止2019年現(xiàn)有的iOS開發(fā)資料已經(jīng)超過16,800,000個(gè),這里整理的主要【不是如何寫代碼,而是iOS開發(fā)需要的一些理論知識(shí)和擴(kuò)展。】根據(jù)作者經(jīng)驗(yàn),我把他們按照小學(xué)、中學(xué)、大學(xué)來分階段整理。這樣做的好處是可以讓不同階段的程序員更好地進(jìn)步,希望這里的知識(shí)對(duì)你寫代碼有一定幫助。也可以有目的性的了解自己下一階段需要掌握的知識(shí)。

劃分年級(jí)的想法是我仿照自己人生軌跡,普通人都經(jīng)歷的過程,這個(gè)過程會(huì)讓我們更加身臨其境。有小學(xué)再開始一次的想法也可以增加我們學(xué)習(xí)的樂趣。對(duì)于iOS新手根據(jù)年級(jí)從小學(xué)一年級(jí)逐步學(xué)習(xí),在學(xué)習(xí)其他iOS資料的同時(shí)補(bǔ)充一些知識(shí)可以更快理解‘這樣寫’背后的含義,也不至于發(fā)現(xiàn)需要學(xué)習(xí)的太多而無從下手。如果能全部掌握小學(xué)到大學(xué)的知識(shí),那么常規(guī)的iOS問題對(duì)你已不再話下了。

開發(fā)語言O(shè)bjective-C和Swift

由于作者對(duì)Objective-C運(yùn)用更多更為熟練,所以涉及到代碼例子部分還是以O(shè)C為主,但也會(huì)盡可能附上Swift版的鏈接。
對(duì)于本文的例子和部分常用代碼已封裝到bench_ios這個(gè)庫中,所有【我的應(yīng)用】標(biāo)簽下的代碼均在這個(gè)庫中,也可通過CocoaPods快速安裝。

關(guān)于內(nèi)容的嚴(yán)謹(jǐn)性

這里的年級(jí)的劃分主要依據(jù)作者自身iOS開發(fā)成長的道路來劃分。作者本身接觸iOS開發(fā)已有5年+,從iOS4時(shí)代開始投入學(xué)習(xí),起源是被一個(gè)ipod touch上的劃水果app吸引。消耗業(yè)余時(shí)間整理了這些知識(shí)點(diǎn),限于作者能力,有些知識(shí)點(diǎn)的年級(jí)劃分或者整理可能有所偏差和不準(zhǔn)確,但每一篇都是經(jīng)過認(rèn)真審核和查閱大量資料整理,也希望有大神幫忙指點(diǎn)完善。

開始

我們把這個(gè)合集看成一個(gè)游戲,從小學(xué)到大學(xué)讀完就通關(guān)了。下面一個(gè)小測驗(yàn)可快速定位新來的你在哪一個(gè)層級(jí)。如果您都能自信地回答,請(qǐng)點(diǎn)擊左上角關(guān)閉按鈕離開??,答案在各年級(jí)的文章中~

1、以下代碼輸出什么?

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

    Person *person = [[Person alloc] init];
    person.age = 10;

    __weak Person *weakPerson = person;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4.0 * NSEC_PER_SEC)),
                   dispatch_get_main_queue(), ^{

        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"2-----age:%p",person);
        });
        NSLog(@"1-----age:%p",weakPerson);
    });

    NSLog(@"touchesBegan");
}

2、下面的代碼輸出什么?

@implementation Son : Father
- (id)init
{
    self = [super init];
    if (self) {
        NSLog(@"%@", NSStringFromClass([self class]));
        NSLog(@"%@", NSStringFromClass([super class]));
    }
    return self;
}
@end

3、#import跟 #include 有什么區(qū)別,@class呢,#import<> 跟 #import””有什么區(qū)別?

4、RSA的兩個(gè)常用用途?

5、堆排序的排序過程?

6、以下代碼的時(shí)間復(fù)雜度各多少?
A

for (i=1; i<=n; i++)  
       x++;  
for (i=1; i<=n; i++)  
     for (j=1; j<=n; j++)  
          x++;  

B

x=1;
for(i=1;i<=n;i++)
       for(j=1;j<=i;j++)
          for(k=1;k<=j;k++)
              x++;  

答案在

iOSStudyProject

歡迎fork修改,如果有幫助,希望給顆小星星~

小學(xué)知識(shí)點(diǎn)合集+如何開始iOS開發(fā)

小學(xué)

什么是程序員???

什么是程序員??????

如何開始iOS開發(fā)學(xué)習(xí)

首先需要準(zhǔn)備必要的工具,嗯,mac的電腦,然后安裝Xcode。
一切準(zhǔn)備就緒后,就可以網(wǎng)上搜一些最簡單的例子,了解Xcode的工作流程。
使用XCode開發(fā)第一個(gè)IOS程序
對(duì)照教程自己搞一遍就差不多了解開發(fā)流程了。
這之后的學(xué)習(xí)會(huì)相對(duì)困難起來,你可以逐步來學(xué)習(xí)UIKit中的各種控件。如果你的英文ok,強(qiáng)烈推薦看斯坦福公開課的教程。
斯坦福大學(xué)公開課:iOS 8開發(fā)
斯坦福大學(xué)公開課:iOS 7應(yīng)用開發(fā)
這個(gè)教程至少需要看兩遍,第一遍是入門時(shí)看,肯定會(huì)看的云里霧里,然后需要你開發(fā)一段時(shí)間,就是親自打代碼做一些demo之后,再去看一遍,會(huì)有不同體驗(yàn)。實(shí)際上還可以看更多便,因?yàn)槟阍趇OS上投入一定時(shí)間后會(huì)對(duì)之前的知識(shí)有更深的理解。
那么其他的就交給時(shí)間了,你需要投入一定時(shí)間的學(xué)習(xí),寫代碼,思考。如果你聰明,那么進(jìn)步也會(huì)很快。

一些常用網(wǎng)站

文章

cocoachina
segmentfault
objc
talk
掘金

教程

raywenderlich
raywenderlich store

demo

code4app

論壇

cocoachina bbs

提問

stackoverflow

博客

我的CSDN
ibireme的博客
美團(tuán)技術(shù)團(tuán)隊(duì)
OneV's Den
唐巧的博客

#import和#include和@class的區(qū)別

import一個(gè)文件只能被導(dǎo)入一次,因此不會(huì)引起交叉編譯。包含c/c++頭文件時(shí)用include。
import會(huì)包含這個(gè)類的所有信息,包括實(shí)體變量和方法(.h文件中),而@class只是告訴編譯器,其后面聲明的名稱是類的名稱。
在編譯效率方面考慮,如果你有100個(gè)頭文件都#import了同一個(gè)頭文件,或者這些文件是依次引用的,如A–>B, B–>C, C–>D這樣的引用關(guān)系。當(dāng)最開始的那個(gè)頭文件有變化的話,后面所有引用它的類都需要重新編譯,如果你的類有很多的話,這將耗費(fèi)大量的時(shí)間。而是用@class則不會(huì)。
如果有循環(huán)依賴關(guān)系,如:A–>B, B–>A這樣的相互依賴關(guān)系,如果使用#import不會(huì)出現(xiàn)編譯錯(cuò)誤。
'#import<>'用于包含系統(tǒng)文件 會(huì)掃描系統(tǒng)文件目錄
'#import""'用于包含本項(xiàng)目中的文件 掃描項(xiàng)目文件目錄

Property屬性

成員變量

一般而言,成員變量用于類內(nèi)部,天生私有屬性,因?yàn)槌蓡T變量本身不能生成set和get方法,不能被外界訪問。

屬性變量

鑒于成員變量的私有屬性,不便于與外界溝通,于是便有了屬性變量;它存在的意義是允許其他對(duì)象訪問到該變量(因?yàn)樗詣?dòng)生成了set和get方法)。

實(shí)例變量

如果變量的數(shù)據(jù)類型是一個(gè)類則稱這個(gè)變量為實(shí)例變量。因?yàn)閷?shí)例變量是成員變量的一種特殊情況。
【成員變量】=【實(shí)例變量】+【基本數(shù)據(jù)類型的變量】

成員變量的權(quán)限修飾符

public:公開型,外界可以通過“->”方式直接使用。
protected:受保護(hù)型,只能被本類和子類訪問;(默認(rèn)類型)。
private:私有型,只能本類使用,子類也不可以訪問。

屬性修飾符

readonly:相當(dāng)于.h文件中只有g(shù)et方法的聲明,沒有聲明set方法,不接搜寫入操作。
readwrite:默認(rèn)類型,set和get方法都有聲明,接受讀寫操作。
nonatomic:非原子性,不涉及加鎖和解鎖。
atomic:原子性,屬性讀寫層次上的線程安全(默認(rèn)類型。
assign:值傳遞
copy:也是值傳遞,不改變對(duì)象的引用計(jì)數(shù)(拷貝對(duì)象內(nèi)容,另存他處,返回新地址)。assign可以用非OC對(duì)象,而weak 必須用于OC對(duì)象。
assign:不改變對(duì)象的引用計(jì)數(shù),對(duì)象被釋放后成為野指針(即仍舊指向該對(duì)象,所以盡量不要用assign修飾對(duì)象類型)
weak:不該變對(duì)象的引用計(jì)數(shù),對(duì)象被釋放后會(huì)被自動(dòng)設(shè)置為nil。
strong:將對(duì)象的地址賦給變量,同時(shí)使對(duì)象的引用計(jì)數(shù)加1。
retain:將對(duì)象的地址賦給變量,同時(shí)使對(duì)象的引用計(jì)數(shù)加1。

注意點(diǎn)

當(dāng)我們訪問對(duì)象類型的時(shí)候,可能訪問的是指針本身,也有可能訪問的是指針?biāo)赶虻膬?nèi)存區(qū)域(對(duì)象);對(duì)于前者,往往是賦值操作,意在改變指針的指向;對(duì)于后者,我們常常是訪問(讀寫)內(nèi)存區(qū)域本身。
因而,Property可以分為三類:

  1. Primitive Property
  2. Pointer Property
  3. Memory

Property Attribute、內(nèi)存管理與線程安全

svn和git

通過一二年級(jí)的學(xué)習(xí),掌握了一些寫代碼的基本要素。如果你使用的電腦壞了,并且(最壞的情況??)硬盤也損壞了,那么你的代碼將全部白費(fèi)了。如果從公司角度看,一位員工帶著他的電腦離職了,公司沒有備份,那將是一筆巨大損失。所以我們會(huì)將代碼托管到服務(wù)器。那么最常使用的代碼管理方式就是svn和git了。

svn和git的區(qū)別

SVN是Subversion的簡稱,是一個(gè)開放源代碼的版本控制系統(tǒng)。
Git是一個(gè)開源的分布式版本控制系統(tǒng)

對(duì)比

svn是將代碼保存在中央服務(wù)器,所有人向中央提交代碼、下載代碼。缺點(diǎn)就是對(duì)中央服務(wù)器的依賴較高。
git的記錄版本歷史只關(guān)心文件是否發(fā)生變化,而不關(guān)心具體變化的內(nèi)容。每一個(gè)克隆的版本庫權(quán)限相等。每個(gè)人都有自己獨(dú)立的倉庫(在本地),當(dāng)你完成后再push給中央服務(wù)器,其他人通過pull拉取你提交的代碼。所以git脫離網(wǎng)絡(luò)你也可以提交代碼。
SVN與Git比較的優(yōu)缺點(diǎn)差異
90%人都不知道:SVN 和 Git 的一些誤解和真相

從github使用git clone和download zip的區(qū)別

采用git clone的項(xiàng)目包含.git目錄,這里面有歷史版本信息
采用下載zip文件的是沒有版本歷史信息的。只是當(dāng)前分支的最新版本

github中fork和clone

fork和clone的區(qū)別在于:fork是GitHub操作,是復(fù)制一個(gè)倉庫(包括文件,提交歷史,issues,和其余一些東西),復(fù)制后的倉庫在你自己的GitHub帳號(hào)下。clone是Git操作,是發(fā)送"復(fù)制倉庫"的命令給GitHub,復(fù)制后的倉庫在你本地計(jì)算機(jī)上。
那么如果你需要將代碼提交給原作者,你就需要使用fork。在完成常規(guī)的commit后,你Github上的倉庫更新了,這時(shí)還沒有提交給原作者,當(dāng)你發(fā)送Pull Request時(shí)(在github頁面Pull Request選項(xiàng)可操作),原作者就會(huì)收到你提交的代碼,并由他/她決定是否合并。
GitHub的Fork 是什么意思

self.和_的區(qū)別

self.會(huì)調(diào)用set/get方法,會(huì)使引用計(jì)數(shù)加一。在類外部也就是其他類里訪問這個(gè)類的變量時(shí)用。
而_是直接訪問成員變量。在類內(nèi)部訪問變量的時(shí)候用。
更多解釋

懶加載

懶加載就是只在用到的時(shí)候才去初始化。也可以理解成延時(shí)加載。
我覺得最好也最簡單的一個(gè)例子就是tableView中圖片的加載顯示了, 一個(gè)延時(shí)加載, 避免內(nèi)存過高,一個(gè)異步加載,避免線程堵塞提高用戶體驗(yàn)。

中文本地化

如果默認(rèn)的語言是英文,那么在鍵盤彈出后的Select、Copy、Paste都是英文顯示,如果需要中文,只需在PROJECT中的Localizations里添加中文,添加后如果手機(jī)默認(rèn)語言是中文,就會(huì)顯示中文選擇、復(fù)制、粘貼。

isKindOfClass和isMemberOfClass

isKindOfClass:作用是某個(gè)對(duì)象屬于某個(gè)類型或者繼承自某類型。
isMemberOfClass:某個(gè)對(duì)象確切屬于某個(gè)類型。

lldb(gdb)常用的控制臺(tái)調(diào)試命令

  1. p 輸出基本類型。是打印命令,需要指定類型。是print的簡寫
    p (int)[[[self view] subviews] count]
  2. po 打印對(duì)象,會(huì)調(diào)用對(duì)象description方法。是print-object的簡寫
    po [self view]
  3. expr 可以在調(diào)試時(shí)動(dòng)態(tài)執(zhí)行指定表達(dá)式,并將結(jié)果打印出來。常用于在調(diào)試過程中修改變量的值。
  4. bt:打印調(diào)用堆棧,是thread backtrace的簡寫,加all可打印所有thread的堆棧
  5. br l:是breakpoint list的簡寫

iOS的沙盒目錄結(jié)構(gòu)

  1. Application:存放程序源文件,上架前經(jīng)過數(shù)字簽名,上架后不可修改。
  2. Documents:常用目錄,iCloud備份目錄,存放數(shù)據(jù)。(這里不能存緩存文件,否則上架不被通過)
  3. Library:
    1. Caches:存放體積大又不需要備份的數(shù)據(jù)。(常用的緩存路徑)
    2. Preference:設(shè)置目錄,iCloud會(huì)備份設(shè)置信息。
  4. tmp:存放臨時(shí)文件,不會(huì)被備份,而且這個(gè)文件下的數(shù)據(jù)有可能隨時(shí)被清除的可能。

GCD 與 NSOperation

GCD 基于C語言的底層API,GCD主要與block結(jié)合使用,代碼簡潔高效。
NSOperation 屬于Objective-C類,是基于GCD更高一層的封裝。復(fù)雜任務(wù)一般用NSOperation實(shí)現(xiàn)。

反射是什么?

反射是指計(jì)算機(jī)程序在運(yùn)行時(shí)(Runtime)可以訪問、檢測和修改它本身狀態(tài)或行為的一種能力。用比喻來說,反射就是程序在運(yùn)行的時(shí)候能夠“觀察”并且改變自己的而行為。
比如通過類名,生成類 Class * tempClass = NSClassFromString(str);

RunLoop 與 Runtime

從字面上講就是運(yùn)行循環(huán),它內(nèi)部就是do-while循環(huán),在這個(gè)循環(huán)內(nèi)部不斷地處理各種任務(wù)。
一個(gè)線程對(duì)應(yīng)一個(gè)RunLoop,基本作用就是保持程序的持續(xù)運(yùn)行,處理app中的各種事件。通過runloop,有事運(yùn)行,沒事就休息,可以節(jié)省cpu資源,提高程序性能。
Runtime又叫運(yùn)行時(shí),是一套底層的C語言API,其為iOS內(nèi)部的核心之一,我們平時(shí)編寫的OC代碼,底層都是基于它來實(shí)現(xiàn)的。

xml中SAX和DOM區(qū)別

SAX和DOM是兩種解析方式。

SAX

SAX解析方式:逐行掃描文檔,一遍掃描一遍解析。相比于DOM,SAX可以在解析文檔的任意時(shí)刻停止解析解析,是一種速度更快,更高效的方法。
優(yōu)點(diǎn):解析可以立即開始,速度快,沒有內(nèi)存壓力。
缺點(diǎn):不能對(duì)結(jié)點(diǎn)做修改。

DOM

DOM解析方式:DOM解析器在解析XML文檔時(shí),會(huì)把文檔中的所有元素,按照其出現(xiàn)的層次關(guān)系,解析成一個(gè)個(gè)Node對(duì)象(節(jié)點(diǎn))。
優(yōu)點(diǎn):把XML文件在內(nèi)存中構(gòu)建屬性結(jié)構(gòu),可以遍歷和修改節(jié)點(diǎn)。
缺點(diǎn):如果文件比較大,內(nèi)存有壓力,解析的時(shí)間會(huì)比較長。

XMPP

XMPP是一種以XML為基礎(chǔ)的開放式實(shí)時(shí)通信協(xié)議。
簡單的說,XMPP就是一種協(xié)議,一種規(guī)定。就是說,在網(wǎng)絡(luò)上傳東西,XMM就是規(guī)定你上傳大小的格式。
xmpp有很多語言寫的庫,根據(jù)需要可以在這里xmpp org尋找你需要的。作者只使用過XMPPFrameworkgloox,在使用gloox時(shí)高版本在iOS上會(huì)編譯不通過只能使用低版本,后續(xù)因?yàn)轫?xiàng)目時(shí)間原因取消使用gloox了。報(bào)的編譯錯(cuò)誤如下,這里希望有高手能提供幫助。
[圖片上傳失敗...(image-d64bcf-1553653996662)]{:height="100"}
對(duì)于XMPPFramework,本人使用時(shí)遇到幾個(gè)小問題,但整體感覺還是不錯(cuò)的,滿足基本需求沒問題,可以放心使用。
XMPP是基于XML的協(xié)議,用于即時(shí)消息(IM)以及在線現(xiàn)場探測。最初,XMPP作為一個(gè)框架開發(fā),目標(biāo)是支持企業(yè)環(huán)境內(nèi)的即時(shí)消息傳遞和聯(lián)機(jī)狀態(tài)應(yīng)用程序。
XMPP的前身是Jabber(1998年),是一個(gè)開源組織定義的網(wǎng)絡(luò)即時(shí)通信協(xié)議。
XMPP是一個(gè)分散型通信網(wǎng)絡(luò)。
XMPP是一個(gè)典型的C/S架構(gòu)。而不是像大多數(shù)即時(shí)通訊軟件一樣,使用P2P客戶端到客戶端的架構(gòu)。也就是說在大多數(shù)情況下,當(dāng)兩個(gè)客戶端進(jìn)行通訊時(shí), 他們的消息都是通過服務(wù)器傳遞的。

優(yōu)點(diǎn):

  1. 采用這種架構(gòu),主要是為了簡化客戶端,將大多數(shù)工作放在服務(wù)器端進(jìn)行。
  2. 可擴(kuò)展。
  3. 安全。

缺點(diǎn):

  1. 數(shù)據(jù)負(fù)載過重XML。
  2. 沒有二進(jìn)制傳輸。

擴(kuò)展

大致說一下使用流程和一些必要的方法。

@interface XMPPHelper : NSObject

//@property(nonatomic,assign) BOOL isConnecting;

+ (instancetype)getInstance;

- (BOOL)initWithUserName:(NSString *)userName andPassword:(NSString *)password andHostName:(NSString *)hostName andDomain:(NSString*)domain andHostPort:(UInt16)hostPort andInfoDic:(NSDictionary *)infoDic;

- (BOOL)connect;

- (void)disconnect;

- (BOOL)isConnected;

@end

我在使用時(shí)創(chuàng)建了一個(gè)類,主要有初始化、連接、主動(dòng)斷開、連接狀態(tài)監(jiān)測這幾個(gè)方法。
它的使用過程是 連接-認(rèn)證-發(fā)送消息
初始化發(fā)送連接請(qǐng)求

- (BOOL)connect
{

    if ([self isConnected]) {
        [ShareCallback xmppCallback:@"success"];
        return ;
    }
    if (_jid) {
        [self.xmppStream connectWithTimeout:5 error:nil];
        return ;
    }
    _storage = [XMPPStreamManagementMemoryStorage new];

    self.xmppStream = [[XMPPStream alloc] init];
    [self.xmppStream addDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)];
    //設(shè)置聊天服務(wù)器地址
    self.xmppStream.hostName = _hostName;
    //設(shè)置聊天服務(wù)器端口 默認(rèn)是5222
    self.xmppStream.hostPort = _hostPort;
    //設(shè)置Jid 就是用戶名
     _jid = [XMPPJID jidWithUser:_userName domain:_domain resource:@"smack"];
    self.xmppStream.myJID = _jid;

    //接入斷線重連模塊
    _xmppReconnect = [[XMPPReconnect alloc] init];
    _xmppReconnect.reconnectTimerInterval=5;
    _xmppReconnect.reconnectDelay=0;
    [_xmppReconnect setAutoReconnect:YES];
    [_xmppReconnect activate:self.xmppStream];
    [_xmppReconnect addDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)];

    //接入流管理模塊,用于流恢復(fù)跟消息確認(rèn),在移動(dòng)端很重要
    _xmppStreamManagement = [[XMPPStreamManagement alloc] initWithStorage:_storage];
    _xmppStreamManagement.autoResume = YES;
    [_xmppStreamManagement addDelegate:self delegateQueue:dispatch_get_main_queue()];
    [_xmppStreamManagement activate:self.xmppStream];

    NSError * error = nil;
    //驗(yàn)證連接
    [self.xmppStream connectWithTimeout:5 error:&error];
    if (error) {
        NSLog(@"連接失?。?@",error);
        return NO;
    }
    else
    {
        NSLog(@"連接成功!");
        return  YES;
    }
}

注意是先建立連接,如果連接成功,在xmppStreamDidConnect回調(diào)中驗(yàn)證用戶名和密碼。

//輸入密碼驗(yàn)證登陸
- (void)xmppStreamDidConnect:(XMPPStream *)sender
{
    NSError *error = nil;
    [[self xmppStream] authenticateWithPassword:_password error:&error];
}

錯(cuò)誤的回調(diào)這里忽略了,如果驗(yàn)證成功,也會(huì)有回調(diào),在回調(diào)里發(fā)送ping,這樣服務(wù)端就可以標(biāo)記已經(jīng)連接了。

//登錄成功
- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender
{
    NSLog(@"xmpp登錄成功%s",__func__);
    //發(fā)送在線通知給服務(wù)器,服務(wù)器才會(huì)將離線消息推送過來
//    [XMPPPresence alloc]initWithType:@"" to:<#(XMPPJID *)#>
    XMPPPresence *presence = [XMPPPresence presence]; // 默認(rèn)"available"
    [[self xmppStream] sendElement:presence];
    [_xmppStreamManagement enableStreamManagementWithResumption:YES maxTimeout:10];

    XMPPAutoPing *xmppAutoPing =  [[XMPPAutoPing alloc] init];
    xmppAutoPing.pingInterval = 10.0;
    [xmppAutoPing activate:_xmppStream];
    [xmppAutoPing addDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)];
//    NSXMLElement *enable = [NSXMLElement elementWithName:@"r" xmlns:@"urn:xmpp:sm:3"];
//    [[self xmppStream] sendElement:enable];
    //啟用流管理
//    [_xmppStreamManagement enableStreamManagementWithResumption:YES maxTimeout:0];
}

這里連接成功后就可以在代理里接收消息了。

- (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message{
    NSString *messageBody = [[message elementForName:@"body"] stringValue];
    [ShareCallback xmppPushMsg:messageBody];
}

注意其實(shí)所有的連接,認(rèn)證,發(fā)送XMPPPresence都是以xml的格式進(jìn)行的,通過解析xml標(biāo)簽來判斷消息體的類型。所以如果需要調(diào)試,可以去找到接收元數(shù)據(jù)xml的地方,看解析有沒有正確處理。

字典的原理

NSDictionary是使用hash表來實(shí)現(xiàn)key和value之間的映射和存儲(chǔ)的,底層是一個(gè)哈希表。
哈希表的本質(zhì)是一個(gè)數(shù)組,數(shù)組中每一個(gè)元素稱為一個(gè)箱子(bin),箱子中存放的是鍵值對(duì)。在使用拉鏈法解決哈希沖突時(shí),每個(gè)箱子其實(shí)是一個(gè)鏈表,屬于同一個(gè)箱子的所有鍵值對(duì)都會(huì)排列在鏈表中。

擴(kuò)展

假設(shè)我們要做個(gè)存儲(chǔ)結(jié)構(gòu),需要存儲(chǔ)下來三國中的人物,以及他們的詳細(xì)信息。我們用他們的名字來作為存儲(chǔ) 的關(guān)鍵值,例如:劉備,曹操,孫權(quán),關(guān)羽,張飛……等等。這個(gè)時(shí)候我們?nèi)绻胗靡话愕姆椒▉聿檎疫@些英雄豪杰,需要遍歷整個(gè)存儲(chǔ)空間,如果這些英雄豪杰一共有n個(gè),那么這時(shí)候的時(shí)間算法復(fù)雜度為O(n)。顯然如果n值很大,每次想要找到某個(gè)英雄就需要比較長的時(shí)間。
此時(shí)我們先定義一個(gè)大的有序結(jié)構(gòu)數(shù)組HashValue[m],用來存放各位英雄豪杰的信息(value值,劉備,曹操...等信息)。然后編寫一個(gè)哈希函數(shù)ChangeToHashValue (name),函數(shù)的具體內(nèi)容就不細(xì)說了,反正這個(gè)函數(shù)會(huì)將這些做為關(guān)鍵值的名字轉(zhuǎn)換為HashValue[m]中的某個(gè)下標(biāo)值x。然后可以將英雄的信息放進(jìn)HashValue[x]中去。這樣,可以將所有英雄的信息存儲(chǔ)起來。當(dāng)查詢的時(shí)候再使用哈希函數(shù)ChangeToHashValue(name)得到這個(gè)下標(biāo)值,這樣就很容易得到了這個(gè)英雄的信息。例如:ChangeToHashValue(劉備)為10,那么就將劉備存儲(chǔ)到HashValue [10]里面。當(dāng)查詢的時(shí)候再次使用ChangeToHashValue(劉備)得到10,這個(gè)時(shí)候我們就可以很容易找到劉備的所有信息。在實(shí)際應(yīng)用中如果我們想把所有的英雄豪杰都存儲(chǔ)進(jìn)系統(tǒng)時(shí),需要定義m>n。就是數(shù)組的大小要大于需要存儲(chǔ)的信息量,所以說哈希表是一個(gè)以空間換取時(shí)間的數(shù)據(jù)結(jié)構(gòu)。
這個(gè)時(shí)候問題來了,出現(xiàn)了這種情況ChangeToHashValue(關(guān)羽)和ChangeToHashValue(張飛)得到的值是一樣的,都是250,我們豈不是在存儲(chǔ)過程中會(huì)遇到麻煩,怎么安排他們二位的地方呢(總不能讓二位打一架,誰贏了誰呆在那吧),這就需要一個(gè)解決沖突的方法。當(dāng)遇到這 種情況時(shí)我們可以這樣處理,先存儲(chǔ)好了關(guān)羽,當(dāng)張飛進(jìn)入系統(tǒng)時(shí)會(huì)發(fā)現(xiàn)關(guān)羽已經(jīng)是250了,那咱就加一位,251得了,這不就解決了。我們查找張飛的時(shí)候也 是,一看250不是張飛,那就加個(gè)1,就找到了。這時(shí)還存在一個(gè)問題。直接用ChangeToHashValue(趙云)為251,張飛已經(jīng)早早占了他的 地方,那就再加1存到252唄。呵呵,這時(shí)我們會(huì)發(fā)現(xiàn),當(dāng)哈希函數(shù)沖突發(fā)生的機(jī)率很高時(shí),可能會(huì)有一群英雄豪杰在250這個(gè)值后面扎堆排隊(duì)。要命的是查找 的時(shí)候,時(shí)間算法復(fù)雜度早已不是O(1)了(所以我們說理想情況下哈希表的時(shí)間算法復(fù)雜度為O(1))。
這就是說哈希函數(shù)的編寫是哈希表的一個(gè)關(guān)鍵問題,會(huì)涉及到一個(gè)存儲(chǔ)值在哈希表中的統(tǒng)計(jì)分布。如果哈希函數(shù)已經(jīng)定義好了,沖突的解決就成為了改變系統(tǒng)性能的關(guān)鍵因素。其實(shí)還有很多種方法來解決沖突情況下的存儲(chǔ)和查找問題,不一定非要線性向后排隊(duì),如果有好的哈希表沖突的解決方法也能很大程度上提高系統(tǒng)的效率。
深入理解哈希表
從根源揭秘HashMap的數(shù)據(jù)存儲(chǔ)過程
什么是哈希表

注意點(diǎn)

iOS中的setObject: forKey: 函數(shù)在key為空時(shí)會(huì)crash。
如果要存入plist,NSDictionary的key只能為NSString。
通過[NSJSONSerialization JSONObjectWithData: data options: NSJSONReadingMutableLeaves error: nil];來獲得json的解析字典時(shí)會(huì)有精度丟失問題,如8.37會(huì)轉(zhuǎn)成8.369999999999999。通常是在取值的時(shí)候通過[NSString stringWithFormat:@"%.2f",v]這樣來定義小數(shù)后位數(shù)。iOS 處理浮點(diǎn)類型精度丟失問題

我的應(yīng)用:

bench_ios庫為NSMutableDictionary寫了一個(gè)category,通過safeSetObject來阻止key為空的crash。
通過遍歷字典每一個(gè)value的類型,來解決精度丟失問題

/**
 *  key or v空時(shí)不閃退
 */
- (void)safeSetObject:(id)anObject forKey:(id<NSCopying>)aKey;
/**
 *  修正使用NSJSONSerialization將NSString轉(zhuǎn)換為Dictionary后 有小數(shù)部分出現(xiàn)如8.369999999999999問題
 例子:
 NSString *html = @"{\"71.40\":71.40,\"8.37\":8.37,\"80.40\":80.40,\"188.40\":188.40}";此段html轉(zhuǎn)換成NSMutableDictionary后使用correctNumberLoss處理耗時(shí)0.000379秒
 */
- (NSMutableDictionary *)correctDecimalLoss:(NSMutableDictionary *)dic;

通常在接收服務(wù)端的數(shù)據(jù)時(shí),會(huì)有很多map的出現(xiàn),我們需要通過list里的某個(gè)key去map里取對(duì)應(yīng)數(shù)據(jù),對(duì)于這種取法會(huì)使我們app使用數(shù)據(jù)時(shí)代碼變得非常不雅,所以通過CC_Parser對(duì)原始數(shù)據(jù)做了處理,為了是消除map

/**
 * 將map的數(shù)據(jù)移置list中
 *
 * NSMutableArray *parr=[CC_Parser getMapParser:result[@"response"][@"purchaseOrders"] idKey:@"order" keepKey:YES pathMap:result[@"response"][@"paidFeeMap"]];
 * parr=[CC_Parser addMapParser:parr idKey:@"prize" keepKey:NO map:result[@"response"][@"prizeFeeMap"]];
 *
 * pathArr 需要獲取的list路徑 如result[@"response"][@"purchaseOrders"]
 * idKey 要取的map字段key 如purchseNo
 * keepKey 是否保留原字段 如purchseNo 本身含有意義要保留 會(huì)在key最后添加_map區(qū)分" 如xxxid 本身沒有意義,為了取值而生成的id不保留 被map相應(yīng)id的數(shù)據(jù)替換
 * mapPath 要取的map的路徑 如result[@"response"][@"paidFeeMap"] map中可以是nsstring 也可以是nsdictionary
 */
+ (NSMutableArray *)getMapParser:(NSArray *)pathArr idKey:(NSString *)idKey keepKey:(BOOL)keepKey pathMap:(NSDictionary *)pathMap;

/**
 * 將map的數(shù)據(jù)移置list中 多個(gè)map時(shí)添加
 */
+ (NSMutableArray *)addMapParser:(NSMutableArray *)pathArr idKey:(NSString *)idKey keepKey:(BOOL)keepKey map:(NSDictionary *)getMap;

block

block本質(zhì)上也是一個(gè)OC對(duì)象,它內(nèi)部也有個(gè)isa指針。
block是封裝了函數(shù)調(diào)用以及函數(shù)調(diào)用環(huán)境的OC對(duì)象。
block是封裝函數(shù)及其上下文的OC對(duì)象。

void (^block1)(void) = ^{
    NSLog(@"block1");
};
NSLog(@"%@",[block1 class]);
NSLog(@"%@",[[block1 class] superclass]);
NSLog(@"%@",[[[block1 class] superclass] superclass]);
NSLog(@"%@",[[[[block1 class] superclass] superclass] superclass]);
NSLog(@"%@",[[[[[block1 class] superclass] superclass] superclass] superclass]);

輸出結(jié)果:

NSGlobalBlock
__NSGlobalBlock
NSBlock
NSObject
null

上述代碼輸出了block1的類型,也證實(shí)了block是對(duì)象,最終繼承NSObject。

block捕獲變量

這兩個(gè)例子很好助于理解:
以下代碼輸出什么?

int age=10;
void (^Block)(void) = ^{
    NSLog(@"age:%d",age);
};
age = 20;
Block();

輸出值為 age:10
原因:創(chuàng)建block的時(shí)候,已經(jīng)把a(bǔ)ge的值存儲(chǔ)在里面了。

以下代碼輸出什么?

auto int age = 10;
static int num = 25;
void (^Block)(void) = ^{
    NSLog(@"age:%d,num:%d",age,num);
};
age = 20;
num = 11;
Block();

輸出結(jié)果為:age:10,num:11
愿意:auto變量block訪問方式是值傳遞,static變量block訪問方式是指針傳遞
auto自動(dòng)變量可能會(huì)銷毀的,內(nèi)存可能會(huì)消失,不采用指針訪問;static變量一直保存在內(nèi)存中,指針訪問即可。

堆和棧的區(qū)別

堆:動(dòng)態(tài)分配內(nèi)存,需要程序員自己申請(qǐng),程序員自己管理。
棧:自動(dòng)分配內(nèi)存,自動(dòng)銷毀,先入后出,棧上的內(nèi)容存在自動(dòng)銷毀的情況。

block有哪幾種類型

__NSGlobalBlock __ 在數(shù)據(jù)區(qū)
__NSMallocBlock __ 在堆區(qū)
__NSStackBlock __ 在棧區(qū)

block的強(qiáng)引用現(xiàn)象

棧block

  1. 如果block是在棧上,將不會(huì)對(duì)auto變量產(chǎn)生強(qiáng)引用。
  2. 棧上的block隨時(shí)會(huì)被銷毀,也沒必要去強(qiáng)引用其他對(duì)象。

如果block在棧空間,不管外部變量是強(qiáng)引用還是弱引用,block都會(huì)弱引用訪問對(duì)象。
如果block在堆空間,如果外部強(qiáng)引用,block內(nèi)部也是強(qiáng)引用;如果外部弱引用,block內(nèi)部也是弱引用。

兩個(gè)例子:
以下代碼輸出什么?

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

    Person *person = [[Person alloc] init];
    person.age = 10;

    __weak Person *weakPerson = person;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"age:%p",weakPerson);
    });

    NSLog(@"touchesBegan");
}

輸出結(jié)果:

14:38:42.996990+0800 test[1104:347260] touchesBegan
14:38:42.997481+0800 test[1104:347260] Person-dealloc
14:38:44.997136+0800 test[1104:347260] age:0x0

原因:使用__weak修飾過后的對(duì)象,堆block會(huì)采用弱引用,無法延時(shí)Person的壽命,所以在touchesBegan函數(shù)結(jié)束后,Person就會(huì)被釋放,gcd就無法捕捉到Person。

以下代碼輸出什么?

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

    Person *person = [[Person alloc] init];
    person.age = 10;

    __weak Person *weakPerson = person;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4.0 * NSEC_PER_SEC)),
                   dispatch_get_main_queue(), ^{

        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"2-----age:%p",person);
        });
        NSLog(@"1-----age:%p",weakPerson);
    });

    NSLog(@"touchesBegan");
}

輸出結(jié)果:

14:48:01.293818+0800 test[1199:403589] touchesBegan
14:48:05.294127+0800 test[1199:403589] 1-----age:0x604000015eb0
14:48:08.582807+0800 test[1199:403589] 2-----age:0x604000015eb0
14:48:08.583129+0800 test[1199:403589] Person-dealloc

原因:gcd內(nèi)部只要有強(qiáng)引用Person,Person就會(huì)等待執(zhí)行完再銷毀!所以Person銷毀時(shí)間為7秒。

block能否修改變量值

auto修飾變量,block無法修改,因?yàn)閎lock使用的時(shí)候是內(nèi)部創(chuàng)建了變量來保存外部的變量的值,block只有修改內(nèi)部自己變量的權(quán)限,無法修改外部變量的權(quán)限。
static修飾變量,block可以修改,因?yàn)閎lock把外部static修飾變量的指針存入,block直接修改指針指向變量值,即可修改外部變量值。
全局變量值,全局變量無論哪里都可以修改,當(dāng)然block內(nèi)部也可以修改。

__block 修飾符

__block可以用于解決block內(nèi)部無法修改auto變量值的問題
__block不能修飾全局變量、靜態(tài)變量(static)
編譯器會(huì)將__block變量包裝成一個(gè)對(duì)象
__block修改變量:age->__forwarding->age

__Block_byref_age_0結(jié)構(gòu)體內(nèi)部地址和外部變量age是同一地址

block可以向NSMutableArray添加元素么?

NSMutableArray *arr = [NSMutableArray array];

Block block = ^{
    [arr addObject:@"123"];
    [arr addObject:@"2345"];
};

答案:可以,因?yàn)槭莂ddObject是使用NSMutableArray變量,而不是通過指針改變NSMutableArray,如果是arr = nil,這就是改變了NSMutableArray變量,會(huì)報(bào)錯(cuò)。

解決block循環(huán)引用

1.__weak修飾
不會(huì)產(chǎn)生強(qiáng)引用,指向的對(duì)象銷毀時(shí),會(huì)自動(dòng)讓指針置為nil。

Person *person = [[Person alloc] init];
//        __weak Person *weakPerson = person;
__weak typeof(person) weakPerson = person;

person.block = ^{
    NSLog(@"age is %d", weakPerson.age);
};

缺陷:

- (void)viewDidLoad {
     [super viewDidLoad];
     MitPerson*person = [[MitPerson alloc]init];
     __weak MitPerson * weakPerson = person;
     person.mitBlock = ^{
         dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
             [weakPerson test];
         });
     };
     person.mitBlock();
 }

直接運(yùn)行這段代碼會(huì)發(fā)現(xiàn)[weakPerson test];并沒有執(zhí)行,打印一下會(huì)發(fā)現(xiàn),weakPerson已經(jīng)是 Nil 了,這是由于當(dāng)我們的viewDidLoad方法運(yùn)行結(jié)束,由于是局部變量,無論是MitPerson和weakPerson都會(huì)被釋放掉,那么這個(gè)時(shí)候在Block中就無法拿到正真的person內(nèi)容了。
解決:

- (void)viewDidLoad {
      [super viewDidLoad];
      MitPerson*person = [[MitPerson alloc]init];
      __weak MitPerson * weakPerson = person;
      person.mitBlock = ^{
          __strong MitPerson * strongPerson = weakPerson;
          dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
              [strongPerson test];
          });
      };
      person.mitBlock();
  }

2.__block
必須手動(dòng)把引用對(duì)象置位nil,并且要調(diào)用該block。

__block Person *person = [[Person alloc] init];
person.block = ^{
    NSLog(@"age is %d", person.age);
    person = nil;
};
person.block();

3.將變量傳入block中
將self傳入block,在block中使用傳入的self。

4.__unsafe_unretained
不會(huì)產(chǎn)生強(qiáng)引用,不安全,指向的對(duì)象銷毀時(shí),指針存儲(chǔ)的地址值不變

__block Person *person = [[Person alloc] init];
person.block = ^{
    NSLog(@"age is %d", person.age);
    person = nil;
};
person.block();

5.控制器內(nèi)部
block是控制器對(duì)象的一個(gè)【屬性】,則在block內(nèi)部使用self將會(huì)引起循環(huán)應(yīng)用。

typedef void(^TestBlock)();
@interface SecondViewController ()
@property (nonatomic, copy)TestBlock testBlock;
@end

self.testBlock = ^()
  {
      NSLog(@"%@",self.mapView);
  };
  self.testBlock();

當(dāng)block不是self的【屬性】時(shí),block內(nèi)部使用self也不會(huì)造成內(nèi)存泄露。

TestBlock testBlock = ^()
{
      NSLog(@"%@",self.mapView);
};
[self test:testBlock];

iOS-Block本質(zhì)
iOS中block循環(huán)引用問題

KVO和KVC

KVO

當(dāng)類A的對(duì)象第一次被觀察的時(shí)候,系統(tǒng)會(huì)在運(yùn)行期動(dòng)態(tài)創(chuàng)建類A的派生類。我們稱為B。
在派生類B中重寫類A的setter方法,B類在被重寫的setter方法中實(shí)現(xiàn)通知機(jī)制。
類B重寫會(huì) class方法,將自己偽裝成類A。類B還會(huì)重寫dealloc方法釋放資源。
系統(tǒng)將所有指向類A對(duì)象的isa指針指向類B的對(duì)象。

KVC

KVC運(yùn)用了isa-swizzing技術(shù)。isa-swizzing就是類型混合指針機(jī)制。
比如:

[site setValue:@"sitename" forKey:@"name"];  
//會(huì)被編譯器處理成
SEL sel = sel_get_uid(setValue:forKey);
IMP method = objc_msg_loopup(site->isa,sel);
method(site,sel,@"sitename",@"name");

每個(gè)類都有一張方法表,是一個(gè)hash表,值是還書指針I(yè)MP,SEL的名稱就是查表時(shí)所用的鍵。
SEL數(shù)據(jù)類型:查找方法表時(shí)所用的鍵。定義成char*,實(shí)質(zhì)上可以理解成int值。
IMP數(shù)據(jù)類型:他其實(shí)就是一個(gè)編譯器內(nèi)部實(shí)現(xiàn)時(shí)候的函數(shù)指針。當(dāng)Objective-C編譯器去處理實(shí)現(xiàn)一個(gè)方法的時(shí)候,就會(huì)指向一個(gè)IMP對(duì)象,這個(gè)對(duì)象是C語言表述的類型。

OC和JS的交互

OC 調(diào) JS

UIWebView 方式
這種?方式說?白了了就是使?用 JSCore ,通過 UIWebView 來獲取 JSContext
WKWebView 方式
WKWebView 沒有提供獲取 JSContext 的方法,但是它提供了執(zhí)行 JS 的方法 evaluateJS

JS 調(diào) OC

通過 JSCore 中的 block
通過 JSCore 中的 JSExport

冒泡排序

iOS開發(fā)中會(huì)用到的排序?qū)崿F(xiàn)
根據(jù)序列中兩個(gè)元素的比較結(jié)果來對(duì)換這兩個(gè)記錄在序列中的位置,將鍵值較大的記錄向序列的尾部移動(dòng),鍵值較小的記錄向序列的前部移動(dòng)。因此,每一趟都將較小的元素移到前面,較大的元素自然就逐漸沉到最后面了,也就是說,最大的元素最后才能確定,這就是冒泡。冒泡排序是一種穩(wěn)定的排序算法。

NSMutableArray *dataArr = [NSMutableArray arrayWithObjects:@1,@19,@2,@65,@876,@0,@63,@-1,@87,@100,@-5,@100,@333, nil];
for (int i = 0; i < dataArr.count; ++i) {
    //遍歷數(shù)組的每一個(gè)`索引`(不包括最后一個(gè),因?yàn)楸容^的是j+1)
    for (int j = 0; j < dataArr.count-1; ++j) {
        //根據(jù)索引的`相鄰兩位`進(jìn)行`比較`
        if ([dataArr[j] integerValue] > [dataArr[j+1] integerValue]) {
            [dataArr exchangeObjectAtIndex:j withObjectAtIndex:j+1];
        }
    }
}
NSLog(@"冒泡排序%@",dataArr);

直接插入排序

當(dāng)插入第i(i>=1)個(gè)元素時(shí),前面的V[0],...,V[i-1]個(gè)元素已經(jīng)有序。這時(shí),將第i個(gè)元素和前i-1個(gè)元素V[i-1],...,V[0]依次比較,找到插入的位置即將V[i]插入,同時(shí)原來位置上的元素向后順移。

NSMutableArray *dataArr = [NSMutableArray arrayWithObjects:@1,@19,@2,@65,@876,@0,@63,@-1,@87,@100,@-5,@100, nil];
// 時(shí)間復(fù)雜度O(n^2)
// 控件復(fù)雜度O(1)
// 穩(wěn)定性: 穩(wěn)定
// 內(nèi)部排序
for (int i = 0; i < dataArr.count; i++) {
    for (int j = i; j > 0; j--) {
        if ([dataArr[j] intValue] < [dataArr[j - 1] intValue]) {
            [dataArr exchangeObjectAtIndex:j withObjectAtIndex:j-1];
        }
    }
}
NSLog(@"直接插入排序結(jié)果----%@",dataArr);

希爾排序

假設(shè)待排序的元素共 N 個(gè)元素,首先取一個(gè)整數(shù) i<n 作為間隔,將全部元素分為間隔為 i 的 i 個(gè)子序列并對(duì)每個(gè)子序列進(jìn)行直接插入排序。然后縮小間隔 i ,重復(fù)上述操作,直至 i 縮小為1,此時(shí)所有的元素位于同一個(gè)序列且有序。由于剛開始時(shí) i 較大, 每個(gè)子序列元素較少,排序速度較快。后期 i 變小,每個(gè)子序列元素較多,但大部分元素有序,所以排序速度仍然較快。一般 i 取 i/2。 希爾排序是一種不穩(wěn)定的排序算法。

NSMutableArray *dataArr = [NSMutableArray arrayWithObjects:@1,@19,@2,@65,@876,@0,@63,@-1,@87,@100,@-5,@100,@333, nil];  
//時(shí)間復(fù)雜度:O(n) ~ O(n^2)
//空間復(fù)雜度:O(1)
//穩(wěn)定性:不穩(wěn)定
//內(nèi)部排序
int n = (int)dataArr.count;
for (int gap = n / 2; gap > 0; gap /= 2){
    for (int i = gap; i < n; i++){
        for (int j = i - gap; j >= 0 && [dataArr[j] intValue] > [dataArr[j + gap] intValue]; j -= gap){
            [dataArr exchangeObjectAtIndex:j withObjectAtIndex:(j + gap)];
        }
    }
}
NSLog(@"----%@", dataArr);

堆排序

堆排序是借助堆來實(shí)現(xiàn)的選擇排序,思想同簡單的選擇排序,以下以大頂堆為例。注意:如果想升序排序就使用大頂堆,反之使用小頂堆。原因是堆頂元素需要交換到序列尾部。堆排序是不穩(wěn)定排序。

NSMutableArray *dataArr = [NSMutableArray arrayWithObjects:@1,@19,@2,@65,@876,@0,@63,@-1,@87,@100,@-5,@100,@333, nil];
/*
  從最后一個(gè)非葉子節(jié)點(diǎn)開始 自下而上進(jìn)行調(diào)整堆
 */
for (NSInteger i=(dataArr.count/2-1); i >= 0; --i) {
    dataArr = [self maxHeapAdjust:dataArr index:i length:dataArr.count] ;
}
NSInteger num = dataArr.count;
/*
  剩余的元素個(gè)數(shù)不為1時(shí)則繼續(xù)調(diào)整,取出元素。取出的元素放在最后的一個(gè)節(jié)點(diǎn)。然后減小堆的元素的個(gè)數(shù)。所以大頂堆排序出來的是升序的。
*/
while (num > 1) {
    [dataArr exchangeObjectAtIndex:0 withObjectAtIndex:num-1];
    dataArr=[self maxHeapAdjust:dataArr index:0 length:num-1];
    num--;
}
NSLog(@"堆排序-----%@",dataArr);

圖解排序算法(三)之堆排序

哈夫曼樹(最優(yōu)二叉樹)

哈夫曼樹(Huffman)樹又稱最優(yōu)二叉樹,是指對(duì)于一組帶有確定權(quán)值的葉子結(jié)點(diǎn)所構(gòu)造的具有帶權(quán)路徑長度最短的二叉樹。
霍夫曼編碼就是尋找這個(gè)最優(yōu)路徑的過程。

霍夫曼編碼的步驟
  1. 將信源符號(hào)的概率按減小的順序排隊(duì)。
  2. 把兩個(gè)最小的概率相加,并繼續(xù)這一步驟,始終將較高的概率分支放在右邊,直到最后變成概率1。
  3. 畫出由概率1處到每個(gè)信源符號(hào)的路徑,順序記下沿路徑的0和1,所得就是該符號(hào)的霍夫曼碼字。
  4. 將每對(duì)組合的左邊一個(gè)指定為0,右邊一個(gè)指定為1(或相反)。

哈夫曼(huffman)樹和哈夫曼編碼

簡單動(dòng)畫

CABasicAnimation
CABasicAnimation是核心動(dòng)畫類簇中的一個(gè)類,其父類是CAPropertyAnimation,其子類是CASpringAnimation,它的祖父是CAAnimation。它主要用于制作比較單一的動(dòng)畫,例如,平移、縮放、旋轉(zhuǎn)、顏色漸變、邊框的值的變化等,也就是將layer的某個(gè)屬性值從一個(gè)值到另一個(gè)值的變化。
本人比較常用的動(dòng)畫是UIView的animateWithDuration,做一些顏色漸變,位置移動(dòng)。

[UIView animateWithDuration:.5f animations:^{
    //這里實(shí)現(xiàn)動(dòng)畫 如alpha,frame
} completion:^(BOOL finished) {

}];

我的應(yīng)用

在CC_Animation里添加了閃爍和按鈕放大動(dòng)畫,更多自定義動(dòng)畫可百度CABasicAnimation這個(gè)類

/**
 * 不停閃爍
 * [noteTextV.layer addAnimation:[CC_Animation opacityForever_Animation:.5] forKey:nil];
 */
+ (CABasicAnimation *)opacityForever_Animation:(float)time;
/**
 * 按鈕點(diǎn)擊放大動(dòng)畫
 * [CC_Animation buttonClick:checkBt];
 */
+ (void)buttonClick:(UIButton *)button;

GBK和UTF-8

GBK

GBK是在國家標(biāo)準(zhǔn)GB2312基礎(chǔ)上擴(kuò)容后兼容GB2312的標(biāo)準(zhǔn)(好像還不是國家標(biāo)準(zhǔn))。GBK編碼專門用來解決中文編碼的,是雙字節(jié)的。不論中英文都是雙字節(jié)的。

unicode

unicode 是一種包含所有字符的編碼表格,例如,給一個(gè)漢字規(guī)定一個(gè)代碼,一個(gè)字母也一個(gè)代碼。它可以廣泛地在全世界使用。

UTF8

UTF8 編碼是用以解決國際上字符的一種多字節(jié)編碼,它對(duì)英文使用8位(即一個(gè)字節(jié)),中文使用24位(三個(gè)字節(jié))來編碼。對(duì)于英文字符較多的論壇則用UTF8 節(jié)省空間。UTF8是為傳送unicode而想出來的“再編碼”方法,將unicode編碼之后再在網(wǎng)絡(luò)傳輸。
最為透徹的utf-8、unicode詳解
使用UTF8

NSString *resultStr= [NSString stringWithContentsOfURL:location encoding:NSUTF8StringEncoding error:&error];

使用GBK

NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
                resultStr= [NSString stringWithContentsOfURL:location encoding:enc error:&error];

我的應(yīng)用

在CC_GHttpSessionTask網(wǎng)絡(luò)請(qǐng)求結(jié)果中加入對(duì)UTF8和GBK的同時(shí)支持。

/**
 * url NSString 或者 NSURL
 * paramsDic的關(guān)鍵字
 * getDate 可以獲取時(shí)間
 */
- (void)post:(id)url params:(id)paramsDic model:(ResModel *)model finishCallbackBlock:(void (^)(NSString *, ResModel *))block;
- (void)get:(id)url params:(id)paramsDic model:(ResModel *)model finishCallbackBlock:(void (^)(NSString *, ResModel *))block;

未完待續(xù)

更多請(qǐng)看

初中知識(shí)點(diǎn)合集

高中知識(shí)點(diǎn)合集

大學(xué)知識(shí)點(diǎn)合集

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

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