1.Objective-C語言特性
2.static __block const
3.Object-C的內存管理
4.RunLoop
5.iOS消息傳遞機制
6.iOS程序生命週期
7.MVC設計模式MVVM
8.UIView CALayer Frame 與bounds
9.
根類 NSObject
10.多線程簡介
11.數據持久化
12.JSON和XML HTML 自定義報文
13.網絡編程 HTTP TCP/IP Socket ASI AFNetwork
14.AppStore上傳 及遠程推送
15.XMPP
16.Block
===========================================================
Objective-C是面向對象的語言
Objective-C和Java C++同樣,有封裝,繼承,多態,重用。
可是它不像C++那樣有重載操做法、模版和多繼承,也沒有Java的垃圾回收機制。但
Objective-C的優勢
Objective-C語言有C++ Java等面向對象的特色。Objective-C的優勢是它是動態的。動態能力有三種:
動態類-運行時肯定類的對象
動態綁定-運行時肯定要調用的方法
動態加載--運行時爲程序加載新的模塊
動態類型(Dynamic typing)
運行時決定對象類型,id類型
id obj = someInstance;
if ([obj isKindOfClass:someclass])
{
someclass*classInstance = (someclass *)obj
}
動態綁定
動態綁定基於動態類型,某個實例對象類型被肯定後,將某些屬性和響應的方法綁定到實例上。該隊相對應的屬性和響應者消息也被徹底肯定。
在OC中最經常使用的是消息傳遞機制。調用實例的方法,所作的是向該實例對象的指針發送消息,實例在收到消息後,
從自身實現中尋找響應這條消息的方法。
在Cocoa層,咱們通常向一個NSObject對象發送 -respondsToSelector活着instanceRespondToSelector,在消息機制被觸發前
+resolveClasssMethod 和+resolveInstanceMethod將會被調用,此時有機會動態的響雷或者實例添加新方法
動態加載
在Retina設備上加載@2x的圖片
基本動態特性在常規的Cocoa開發中很是經常使用,因爲Cocoac程序大量的使用Protocol-Delegate中的設計模式,所以大部分delegate指針類型必須是id
以知足運行時delegate的動態轉換(Java裏這種設計模式被稱爲Strategy?)
OC的動態性表現爲三個方面:動態類型,動態綁定,動態加載。之因此叫動態,是由於必須到運行時(runtime )纔會作一些事
面向對象的三個基本特徵是:封裝、繼承、多態。
封裝是面向對象的特徵之一,是對象和類概念的主要特性。 封裝,也就是把客觀事物封裝成抽象的類,而且類能夠把本身的 數據和方法只讓可信的類或者對象操做,對不可信的進行信息隱藏。隱藏對象的屬性和實現細節,僅對外公開接口,提升代 碼安全性,封轉程度越高,獨立性越強,使用越方便。 繼承是指這樣一種能力:它能夠使用現有類的全部功能,並在無需從新編寫原來的類的狀況下對這些功能進行擴展。 經過 繼承建立的新類稱爲「子類」或「派生類」。 被繼承的類稱爲「基類」、「父類」或「超類」 多態性:容許你將父對象設置成爲和一個或更多的他的子對象相等的技術,賦值以後,父對象就能夠根據當前賦值給它的子 對象的特性以不一樣的方式運做。簡單的說,就是一句話:容許將子類類型的指針賦值給父類類型的指針
咱們說的obc是動態運行時語言是什麼意思? 多態。主要是將數據類型的肯定由編譯時,推遲到了運行時。這個問題其實淺涉及到兩個概念,運行時和多態。簡單來講, 運行時機制使咱們直到運行時纔去決定一個對象的類別,以及調用該類別對象指定方法。多態:不一樣對象以本身的方式響應 相同的消息的能力叫作多態。意思就是假設生物類(life)都用有一個相同的方法-eat;那人類屬於生物,豬也屬於生物,都 繼承了life後,實現各自的eat,可是調用是咱們只需調用各自的eat方法。也就是不一樣的對象以本身的方式響應了相同的消 息(響應了eat這個選擇器)。所以也能夠說,運行時機制是多態的基礎
有時咱們須要在一個已經定義好的類中增長一些方法,而不想去重寫該類。好比,當工程已經很大,代碼量比較多,或者類中已經包住不少方法,已經有其餘代碼調用了該類建立對象並使用該類的方法時,能夠使用類別對該類擴充新的方法。
category extension protrol
分別描述類別(categories)和延展(extensions)是什麼?以及二者的區別?繼承和類別在實現中有何區html
別?爲何Category只能爲對象添加方法,卻不能添加成員變量?java
類別:在沒有原類.m文件的基礎上,給該類添加方法; 延展:一種特殊形式的類別,主要在一個類的.m文件裏聲明和實現延展的做用,就是給某類添加私有方法或是私有變量。 兩個的區別:延展能夠添加屬性而且它添加的方法是必需要實現的。延展能夠認爲是一個私有的類目。 繼承和類別在實現中的區別:類別能夠在不獲悉,不改變原來代碼的狀況下往裏面添加新的方法,只能添加,不能刪除修 改。而且若是類別和原來類中的方法產生名稱衝突,則類別將覆蓋原來的方法,由於類別具備更高的優先級。 Category只能爲對象添加方法,卻不能添加成員變量的緣由:若是能夠添加成員變量,添加的成員變量沒有辦法初始 化----這是語言規則node
什麼是Protocol?什麼是代理?寫一個委託的interface?委託的property聲明用什麼屬性?爲何? Protocol:一個方法簽名的列表,在其中能夠定義若干個方法。根據配置,遵照協議的類,會實現這個協議中的若干個方 法。
Objective-C 2.0也是有垃圾回收機制的,可是隻能在Mac OS X Leopard10.5 以上的版本使用。
IPhone iOS有沒有垃圾回收?autorelease 和垃圾回收制(gc)有什麼關係?
沒有。autorelease只是延遲釋放,gc是每隔一段時間詢問程序,看是否有無指針指向的對象,如有,就將它回收。他們 二者沒有什麼關係。
What is lazy loading?
就是懶加載,只在用到的時候纔去初始化。也能夠理解成延時加載。 我以爲最好也最簡單的一個列子就是tableView中圖片的加載顯示了。 一個延時載,避免內存太高,一個異步加載,避免線程堵塞。
Object-C有多繼承嗎?沒有的話用什麼代替?
沒有,多繼承在這裏是用protocol 委託代理來實現的
cocoa 中全部的類都是NSObject 的子類,你不用去考慮繁瑣的多繼承 ,虛基類的概念.ood的多態特性 在 obj-c 中經過委託來實現.
ARC
iOS8 新特性
什麼是動態連接庫和靜態連接庫?調用一個類的靜態方法需不須要release?程序的編譯過程--連接--- 靜態鏈接庫就是把(lib)文件中用到的函數代碼直接連接進目標程序,程序運行的時候再也不須要其它的庫文件;動態連接就是 把調用的函數所在文件模塊 (DLL)和調用函數在文件中的位置等信息連接進目標程序,程序運行的時候再從DLL中尋找 相應函數代碼,所以須要相應DLL文件的支持。 靜態連接庫和動態連接庫的另一個區別在於靜態連接庫中不能再包含其餘的動態連接庫或者靜態庫,而在動態連接庫中還 能夠再包含其餘的動態或靜態連接庫。
動態的是:運行時才加載到內存中,
靜態:編譯時就加載到內存中 靜態方法也就是類方法,不須要release
Object-C有私有方法嗎?私有變量呢?
objective-c 類裏面的方法只有兩種, 靜態方法和實例方法. --就是類方法和對象方法,
這彷佛就不是完整的面向對象了,按照OO的原則就是一個對象只暴露有用的東西. 若是沒有了私有方法的話, 對於一些小範圍的代碼重用就不那麼順手了. 在類裏面聲名一個私有方法
@interface Controller : NSObject {
NSString *something;
}
+ (void)thisIsAStaticMethod;
– (void)thisIsAnInstanceMethod;
@end
@interface Controller (private)
- (void)thisIsAPrivateMethod;
@end
@private能夠用來修飾私有變量
在Objective‐C中,全部實例變量默認都是私有的,全部實例方法默認都是公有的ios
Swfit與Objective-C相比
*從語法上來講,Swift更簡單,更簡潔
*開發難度上,Swfit比OC可貴多,開源框架比較少,會CocOaTouch開發上手簡單,若是之間上手Swfit,遇到問題一查資料都是OC的例子
*swift在arc的objc上架了類型導倒,泛型,運算符重載,沒有異常捕捉 oc上也不怎麼使用
swift中對象和struct的區別.
OC中的數字對象都有哪些,簡述它們與基本數據類型的區別是什麼
Objective-C中的數字對象NSNumber; Objective-C中的基本類型和C語言中的基本類型同樣.主要有:int,long,float,double,char,void,bool等. 對於基本類型變量,不須要用指針,也不用手動回收,方法執行結束會自動回收.數字對象須要指針,也須要手動回收內存。
什麼是動態識別,動態綁定?延展--程序的編譯過程
編譯時和運行時對象分別是什麼數據類型
對於語句NSString * testObject = [[NSData alloc]init];testObject在編譯時和運行時分別是什麼數據類型? 編譯時是nsstring, 運行時是nsdata的一個實例,沒有類型之分。
編譯時被當作NSString 的實例來處理,運行時其isa指向的是[NSData Class],故運行時該實例只能收到NSData的固有實例方法(或者本身寫的Category),若是對該實例發送諸如 testObject stringByAppendingString:@"哈哈哈" ,編譯時不會報錯只會給出黃色警告,可是運行時會直接崩潰,錯誤信息多是
-[_NSZeroData stringByAppendingString:]: unrecognized selector sent to instance 0x8946440
故能夠看出NSData 在運行時的真實類型是_NSZeroData(這裏用的NSData alloc init,這裏是個空的NSData,因此是ZeroData,不是說全部的)
若是對該實例發送諸如 testObject
base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength:這類消息 編譯器會直接給出紅色錯誤,沒法運行,但其實這個實例是有這個方法的。
這裏若是改成 id testObject=[[NSData alloc]init];
那麼發送NSString 的方法仍然會崩潰,發送NSData的實例方法則徹底正常。而且兩種狀況編譯器都不會再給出警告或錯誤。 這就是運行時語言的特色
//1 繼承有傳遞性
//父類繼承過來的變量能夠直接使用.繼承於nsobject 改爲父類;
//super (關鍵字,相似consit static); self是一個對象
//子類特性 重寫父類函數sayHi,優先執行子類的函數;
//2 初始化的時候,優先調用父類的初始化方法,初始化父類中的變量,而後再初始化本身獨有的變量。
//每一個類都清楚本身獨有的變量所以初始化方法裏僅僅初始化本身獨有的變量,經過調用父類初始化方法,初始化繼承過來的變量;
//最不科學的初始化方法:在子類裏面,爲每一個變量賦值,由於,你須要本身查閱都繼承了那些變量信息;
//指定初始化,一般選參數最多的做爲指定初始化方法;
//初始化方法特別多的時候,指定必須執行的初始化方法;//方便調用時賦值,不管改什麼變量,都用變量最多的那個初始化方法
什麼是安全釋放?
[_instance release],_instance = nil;
OC中異常exception 怎麼捕獲?不一樣的CPU結構上開銷怎樣?C中又什麼相似的方法? 瞭解一下異常捕獲
CPU的開銷:關於Objective-C++中的異常處理,能夠相互捕獲到嗎?不能夠;
run loop,正如其名稱所示,是線程進入和被線程用來響應事件以及調用事件處理函數的地方。須要在代碼中使用控制語 句實現run loop的循環,也就是說,須要代碼提供while 或者 for循環來驅動run loop。在這個循環中,使用一個 runloop對象[NSRunloop currentRunloop]執行接收消息,調用對應的處理函數。
談談對性能優化的見解,如何作? 控制好內存,不用的內存實時釋放;冗餘代碼;用戶體驗度;耗時操做,開線程進行處理
簡述值傳遞和引用傳遞的區別? 所謂值傳遞,就是說僅將對象的值傳遞給目標對象,就至關於copy;系統將爲目標對象從新開闢一個徹底相同的內存空間。 所謂引用傳遞,就是說將對象在內存中的地址傳遞給目標對象,就至關於使目標對象和原始對象對應同一個內存存儲空間。此時,如 果對目標對象進行修改,內存中的數據也會改變。
NSArray和NSMutableArray的區別,多線程操做哪一個更安全? NSArray更安全,當同時被訪問時,NSArray是不可改變
以.mm爲拓展名的文件裏,能夠包含的代碼有哪些? .mm是oc和C++混編類型文件後綴,給編譯器識別的。
說說如何進行後臺運行程序?
答:判斷是否支持多線程
UIDevice* device = [UIDevice currentDevice];
BOOL backgroundSupported = NO;
if ([device respondsToSelector:@selector(isMultitaskingSupported)])
backgroundSupported = device.multitaskingSupported;
聲明你須要的後臺任務Info.plist中添加UIBackgroundModes鍵值,它包含一個或多個string的值,包括audio:在後臺提供聲音 播放功能,包括音頻流和播放視頻時的聲音 location:在後臺能夠保持用戶的位置信息 voip:在後臺使用VOIP功能 前面的每一個value讓系統知道你的應用程序應該在適當的時候被喚醒。例如,一個應用程序,開始播放音樂,而後移動到後臺仍然須要 執行時間,以填補音頻輸出緩衝區。添加audio鍵用來告訴系統框架,須要繼續播放音頻,而且能夠在合適的時間間隔下回調應用程 序;若是應用程序不包括此項,任何音頻播放在移到後臺後將中止運行。除了添加鍵值的方法,IOS還提供了兩種途徑使應用程序在後 臺工做:
Task completion—應用程序能夠向系統申請額外的時間去完成給定的任務
Local notifications—應用程序能夠預先安排時間執行local notifications 傳遞實現長時間的後臺任務:應用程序能夠請求在後臺 運行以實現特殊的服務。這些應用程序並不連續的運行,可是會被系統框架在合適的時間喚醒,以實現這些服務 91
你瞭解svn,cvs等版本控制工具麼?
版本控制 svn,cvs 是兩種版控制的器,須要配套相關的svn,cvs服務器。scm是xcode裏配置版本控制的地方。版本控制 的原理就是a和b同時開發一個項目,a寫完當天的代碼以後把代碼提交給服務器,b要作的時候先從服務器獲得最新版本, 就能夠接着作。 若是a和b都要提交給服務器,而且同時修改了同一個方法,就會產生代碼衝突,若是a先提交,那麼b提交 時,服務器能夠提示衝突的代碼,b能夠清晰的看到,並作出相應的修改或融合後再提交到服務器。
目標-動做機制 目標是動做消息的接收者。一個控件,或者更爲常見的是它的單元,以插座變量的形式保有其動做消息的目標。c++
動做是控件發送給目標的消息,或者從目標的角度看,它是目標爲了響應動做而實現 的方法。 程序須要某些機制來進行事件和指令的 翻譯。這個機制就是目標-動做機制。
參考target-action
深層複製(mutableCopy):複製引用對象自己---再建立一個對象 獲得一個新的可變對象,能夠看到它的地址和原來對象的地址是不一樣的,也就是新對象的retainCount從0-1。
copy獲得的是一個不可變對象,這裏分爲兩種狀況:
一、若是原來的對象也是一個不可變的,那麼他們的地址指向同一個地址,也就是說它們同一個對象,只是把retainCount加1了而已,只複製指向對象的指針,而不復制引用對象自己 淺層複製(copy)
二、原來的對象是一個可變對象,那麼它會新生成一個不可變對象,地址不一樣,也是retainCount從0-1。(和mutableCopy同樣的深拷貝)
何時用到深淺複製呢?那麼我就把我所總結的一些分享給你們,但願能幫助大家更好的理解深淺複製!
那麼先讓咱們來看一看下邊數組類型的轉換程序員
一、不可變對象→可變對象的轉換:web
NSArray *array1= [NSArray arrayWithObjects:@"a",@"b",@"c",@"d",nil];objective-c
NSMutableArray *str2=[array1 mutableCopy];sql
二、可變對象→不可變對象的轉換:
NSMutableArray *array2 = [NSMutableArray arrayWithObjects:@"aa",@"bb",@"cc",@"dd",nil];
NSArray *array1=[ array2 Copy];
三、可變對象→可變對象的轉換(不一樣指針變量指向不一樣的內存地址):
NSMutableArray *array1= [NSMutableArray arrayWithObjects:@"a",@"b",@"c",@"d",nil];
NSMutableArray *str2=[array1 mutableCopy];
經過上邊的兩個例子,咱們可輕鬆的將一個對象在可變和不可變之間轉換,而且這裏不用考慮內存使用原則(即引用計數的問題)。沒錯,這就是深拷貝的魅力了。
四、同類型對象之間的指針複製(不一樣指針變量指向同一塊內存地址):
NSMutableString *str1=[NSMutableString stringWithString:@"two day"];
NSMutableString *str2=[str1 retain];
NSArray *array1= [NSArray arrayWithObjects:@"a",@"b",@"c",@"d",nil];
NSArray *str2=[array1 Copy];
通俗的講,多個指針同時指向同一塊內存區域,那麼這些個指針同時擁有對該內存區的全部權
。全部權的瓜分過程,這時候就要用到淺拷貝了。
何時用到深淺拷貝?
深拷貝是在要將一個對象從可變(不可變)轉爲不可變(可變)或者將一個對象內容克隆一份時用到;
OC中是全部對象間的交互是如何實現的? 經過指針實現的
objective-c中的類型轉換分爲哪幾類? 可變與不可變之間的轉化; 可變與可變之間的轉化;不可變與不可變之間
編譯語言和解釋語言的區別 區別:C語言,OC語言屬於編譯語言;解釋語言:也能夠理解爲腳本文件,不須要編譯, 編譯型語言寫的程序執行以前,須要一個專門的編譯過程,把程序編譯成爲機器語言的文件,好比exe文件,之後要運行的 話就不用從新翻譯了,直接使用編譯的結果就好了(exe文件),由於翻譯只作了一次,運行時不須要翻譯,因此編譯型語 言的程序執行效率高,但也不能一律而論,部分解釋型語言的解釋器經過在運行時動態優化代碼,甚至可以使解釋型語言的 性能超過編譯型語言。 解釋則不一樣,解釋性語言的程序不須要編譯,省了道工序,解釋性語言在運行程序的時候才翻譯, 好比解釋性basic語言,專門有一個解釋器可以直接執行basic程序,每一個語句都是執行的時候才翻譯。這樣解釋性語言每 執行一次就要翻譯一次,效率比較低。解釋是一句一句的翻譯。
===============================================================
static __block const id、nil null 預處理指令#define #import和#include @class
用預處理指令#define聲明一個常數,用以代表1年中有多少秒(忽略閏年問題):
#define SECONDS_PER_YEAR (60 * 60 * 24 * 365ul)
1) #define 語法的基本知識(例如:不能以分號結束,括號的使用,等等)
2)懂得預處理器將爲你計算常數表達式的值,所以,直接寫出你是如何計算一年中有多少 秒而不是計算出實際的值,是更清晰而沒有代價的。
3) 意識到這個表達式將使一個16位機的整型數溢出-所以要用到長整型符號L,告訴編譯器這個常數是的長整型數。
4) 若是你在你的表達式中用到UL(表示無符號長整型),那麼你有了一個好的起點。
#import和#include的區別,@class表明什麼?
@class通常用於頭文件中須要聲明該類的某個實例變量的時候用到,在m文件中仍是須要使用#import
而#import比起#include的好處就是不會引發交叉編譯。
這樣會致使循環引入頭文件須要使用對方的時候,有一個文件或者兩個都改爲
@class b;在他們的.M文件引入頭文件
#import 過多會影響編譯時間 ,是編譯時間過長,用@class能夠縮短編譯時間
將註冊的通知標示符,寫成宏在pch文件裏,能夠提升可讀性
系統的通知,監測鍵盤彈出,監測視頻播放完畢;
單例很強大,用於不相關的類直之間的通信,appdelegate是系統的單例,
任何地方都用單例會下降性能 ;
uipappliction
uiWindow
UIview
cell
button
查找順序自上而下,響應順序,自下而上;
c和obj-c如何混用? 1)obj-c的編譯器處理後綴爲m的文件時,能夠識別obj-c和c的代碼,處理mm文件能夠識別obj-c,c,c++代碼,但cpp文件必須只
能用c/c++代碼,並且cpp文件include的頭文件中,也不能出現obj- c的代碼,由於cpp只是cpp。
2) 在mm文件中混用cpp直接使用便可,因此obj-c混cpp不是問題
3)在cpp中混用obj- c其實就是使用obj-c編寫的模塊是咱們想要的。若是模塊以類實現,那麼要按照cpp class的標準寫類的定 義,頭文件中不能出現obj-c的東西,包括#import cocoa的。 實現文件中,即類的實現代碼中能夠使用obj-c的東西,能夠import,只是後綴是mm。若是模塊以函數實現,那麼頭文件要按 c的格 式聲明函數,實現文件中,c++函數內部能夠用obj-c,但後綴仍是mm或m。總結:只要cpp文件和cpp include的文件中不包含 obj-c的東西就能夠用了,cpp混用obj-c的關鍵是使用接口,而不能直接使用實現代碼,實際上cpp混用的是obj-c編譯後的o文 件,這個東西實際上是無差異的,因此能夠用。obj-c的編譯器支持cpp。
舉出5個以上你所熟悉的ios sdk庫有哪些和第三方庫有哪些?
ios-sdk:
Foundation.framework,CoreGraphics.framework,UIKit.framework, MediaPlayer.framework, CoreAudio.framework
第三方庫: 1.json編碼解碼;2.ASIHTTPRequest等相關協議封裝;3.EGORefreshTableHeaderView下拉刷新代 碼;4.AsyncImageView 異步加載圖片並緩存;5.SDWebImage——簡化網絡圖片處理
如何將產品進行多語言發佈?
程序國際化;
好比:本地化應用程序名稱
(一、選中工程,Info—Localizations點擊「+」添加要國際化的語言。 (二、在InfoPlist.strings右邊會多出一個三角形,點擊展開可看到InfoPlish.strings(english)和
InfoPlish.strings(chinese)兩個版本的文件; (三、在InfoPlish.strings(english)文件中加入:
CFBundleDisplayName ="Program"; 其中「Program」爲英文應用程序名稱,同理在InfoPlish.strings(chinese)文件中加入: CFBundleDisplayName ="應用程序"; 其中「應用程序」爲中文名稱,注意:CFBundleDisplayName加不加雙引號都行;
(編輯Info.plist,添加一個新的屬性Application has localized display name, 設置其類型爲boolean,並將其 value設置爲YES便可。
在開發大型項目的時候,如何進行內存泄露檢測的? 內存泄露怎麼處理?
如何檢測內存泄露:
能夠經過xcode的自帶工具run---start with performance tool裏有instruments下有個leaks工具,啓動此工具 後,運行項目,工具裏能夠顯示內存泄露的狀況,雙擊可找到源碼位置,能夠幫助進行內存泄露的處理。 如何處理:先定位到具體位置, 再解決之。
iphone應用程序的項目基本結構?
· Classes -> 源程序文件(.h、.m)
· Other Sources-> main.m 等,不須要程序員修改 -Prefix.pch
· Resources -> 界面文件(.xib)、配置文件-info.plist
· Frameworks -> 連接的庫· Targets -> 項目的不一樣Target(資源、編譯配置不一樣)
· Executables -> 項目中全部的可執行文件 -Prefix.pch:_Prefix爲全部的項目程序文件預先配置運行環境的前綴標頭,在程序運行以前,引入所需框架中的(.h)頭文 件。這樣能夠減小每一個頭文件對程序編譯作出相同的定義,在巨型的應用程序項目開發中節省大量的時間,例如,程序有 100個根文件須要定義abc.h,只須要在_Preix.pch文件下創建一個對象,全部的根文件即可以重複地對程序編譯作出定 義。
編譯語言和解釋語言的區別 區別:C語言,OC語言屬於編譯語言;解釋語言:也能夠理解爲腳本文件,不須要編譯, 編譯型語言寫的程序執行以前,須要一個專門的編譯過程,把程序編譯成爲機器語言的文件,好比exe文件,之後要運行的 話就不用從新翻譯了,直接使用編譯的結果就好了(exe文件),由於翻譯只作了一次,運行時不須要翻譯,因此編譯型語 言的程序執行效率高,但也不能一律而論,部分解釋型語言的解釋器經過在運行時動態優化代碼,甚至可以使解釋型語言的 性能超過編譯型語言。 解釋則不一樣,解釋性語言的程序不須要編譯,省了道工序,解釋性語言在運行程序的時候才翻譯, 好比解釋性basic語言,專門有一個解釋器可以直接執行basic程序,每一個語句都是執行的時候才翻譯。這樣解釋性語言每 執行一次就要翻譯一次,效率比較低。解釋是一句一句的翻譯。
OC中是全部對象間的交互是如何實現 經過指針實現的
objective-c中的類型轉換分爲哪幾類? 可變與不可變之間的轉化; 可變與可變之間的轉化;不可變與不可變之間。
關鍵字const什麼含義
const意味着」只讀」,下面的聲明都是什麼意思?
const int a;
int const a;
const int *a;
int * const a;
int const * a const;
前兩個的做用是同樣,a是一個常整型數。第三個意味着a是一個指向常整型數的指針(也就是,整型數是不可修改的,但指針能夠)。第四個意思a是一個指向整型數的常指針(也就是說,指針指向的整型數是能夠修改的,但指針是不可修改的)。最後一個意味着a是一個指向常整型數的常指針(也就是說,指針指向的整型數是不可修改的,同時指針也是不可修改的)。
結論:
•; 關鍵字const的做用是爲給讀你代碼的人傳達很是有用的信息,實際上,聲明一個參數爲常量是爲了告訴了用戶這個參數的應用目的。若是
你曾花不少時間清理其它人留下的垃圾,你就會很快學會感謝這點多餘的信息。(固然,懂得用const的程序員不多會留下的垃圾讓別人來清
理的。)
•; 經過給優化器一些附加的信息,使用關鍵字const也許能產生更緊湊的代碼。
•; 合理地使用關鍵字const能夠使編譯器很天然地保護那些不但願被改變的參數,防止其被無心的代碼修改。簡而言之,這樣能夠減小bug的出現。
欲阻止一個變量被改變,能夠使用 const 關鍵字。在定義該const 變量時,一般須要對它進行初
始化,由於之後就沒有機會再去改變它了;
(2)對指針來講,能夠指定指針自己爲const,也能夠指定指針所指的數據爲 const,或兩者同時指
定爲const;
(3)在一個函數聲明中,const能夠修飾形參,代表它是一個輸入參數,在函數內部不能改變其值;
(4)對於類的成員函數,若指定其爲const 類型,則代表其是一個常函數,不能修改類的成員變量;
(5)對於類的成員函數,有時候必須指定其返回值爲const 類型,以使得其返回值不爲「左值」。
關鍵字volatile有什麼含義?並給出三個不一樣例子?
一個定義爲volatile的變量是說這變量可能會被意想不到地改變,這樣,編譯器就不會去假設這個變量的值了。精確地說就是,優化器在用到
這個變量時必須每次都當心地從新讀取這個變量的值,而不是使用保存在寄存器裏的備份。下面是volatile變量的幾個例子:
• 並行設備的硬件寄存器(如:狀態寄存器)
• 一箇中斷服務子程序中會訪問到的非自動變量(Non-automatic variables)
• 多線程應用中被幾個任務共享的變量
• 一個參數既能夠是const還能夠是volatile嗎?解釋爲何。
• 一個指針能夠是volatile 嗎?解釋爲何。
下面是答案:
• 是的。一個例子是隻讀的狀態寄存器。它是volatile由於它可能被意想不到地改變。它是const由於程序不該該試圖去修改它。
• 是的。儘管這並不很常見。一個例子是當一箇中服務子程序修該一個指向一個buffer的指針時。
static做用?
函數體內 static 變量的做用範圍爲該函數體,不一樣於 auto 變量,該變量的內存只被分配一次,
所以其值在下次調用時仍維持上次的值;
(2)在模塊內的 static 全局變量能夠被模塊內所用函數訪問,但不能被模塊外其它函數訪問;
(3)在模塊內的 static 函數只可被這一模塊內的其它函數調用,這個函數的使用範圍被限制在聲明
它的模塊內;
(4)在類中的 static 成員變量屬於整個類所擁有,對類的全部對象只有一份拷貝;
(5)在類中的 static 成員函數屬於整個類所擁有,這個函數不接收 this 指針,於是只能訪問類的static 成員變量。
__block
id和void *並不是徹底同樣。在上面的代碼中,id是指向struct objc_object的一個指針,這個意思基本上是說,id是一個指向任何一個繼承了Object(或者NSObject)類的對象。須要注意的是id是一個指針,因此你在使用id的時候不須要加星號。好比id foo=nil定義了一個nil指針,這個指針指向NSObject的一個任意子類。而id *foo=nil則定義了一個指針,這個指針指向另外一個指針,被指向的這個指針指向NSObject的一個子類。
id表明任意對象類型 凡是繼承於NSObject的都是對象#import是將一個文件所有內容拷到當前文件;id是一個指針,指向任何一個繼承了Object(或者NSObject)類的對象,因此在OC中,任意一個object對象均可以是一個id,id不是類型嗎instancetype也是指針,void至關於空指針;
isKindOfClass、isMemberOfClass、selector做用分別是什麼 isKindOfClass,做用是,某個對象屬於某個類型,包括繼承的類型--- isMemberOfClass:某個對象確切屬於某個類型,是否是具體的實例 selector:經過方法名,獲取在內存中的函數的入口地址
請分別寫出SEL、id、@的意思? SEL是「selector」的一個類型,表示一個方法的名字-------就是一個方法的入口地址 id是一個指向任何一個繼承了Object(或者NSObject)類的對象。須要注意的是id是一個指針,因此在使用id 的時候不須要加*。
nil和C語言的NULL相同,在objc/objc.h中定義。nil表示一個Objctive-C對象,這個對象的指針指向空(沒有東西就是空)
if (a.b. c)
上面的代碼當a、b、c中任何一個爲nil時,值爲false,這樣寫徹底ok,因此不必寫成下面那樣:
if a
if b
if c
===============================================================
3.Object-C的內存管理
線程和進程的區別
進程和線程都是由操做系統所體會的程序運行的基本單元,系統利用該基本單元實現系統對應用的併發性。
進程和線程的主要差異在於它們是不一樣的操做系統資源管理方式。進程有獨立的地址空間,一個進程崩潰後,在保護模式下不會對其它進程產生影響,而線程只是一個進程中的不一樣執行路徑。線程有本身的堆棧和局部變量,但線程之間沒有單獨的地址空間,一個線程死掉就等於整個進程死掉,因此多進程的程序要比多線程的程序健壯,但在進程切換時,耗費資源較大,效率要差一些。但對於一些要求同時進行而且又要共享某些變量的併發操做,只能用線程,不能用進程。
(1)管理方式:對於棧來說,是由編譯器自動管理,無需咱們手工控制;對於堆來講,釋放工做由程序員控制,容易產生 memory leak。
(2)申請大小:能從棧得到的空間較小,堆是向高地址擴展的數據結構,是不連續的內存區域。堆的大小受限於計算機系統中 有效的虛擬內存。因而可知,堆得到的空間比較靈活,也比較大。
(3)碎片問題:對於堆來說,頻繁的new/delete勢必會形成內存空間的不連續,從而形成大量的碎片,使程序效率下降。 對於棧來說,則不會存在這個問題,由於棧是先進後出的隊列,他們是如此的一一對應,以致於永遠都不可能有一個內存塊 從棧中間彈出
(4)分配方式:堆都是動態分配的,沒有靜態分配的堆。棧有2種分配方式:靜態分配和動態分配。靜態分配是編譯器完成 的,好比局部變量的分配。動態分配由 alloca函數進行分配,可是棧的動態分配和堆是不一樣的,他的動態分配是由編譯器 進行釋放,無需咱們手工實現。
(5)分配效率:棧是機器系統提供的數據結構,計算機會在底層對棧提供支持:分配專門的寄存器存放棧的地址,壓棧出棧 都有專門的指令執行,這就決定了棧的效率比較高。堆則是C/C++函數庫提供的,它的機制是很複雜的。
OC使用了一種叫作引用計數的機制來管理對象,若是對一個對象使用了alloc、 [Mutable]copy,retain,那麼你必須使用相應的realease或者autorelease。也能夠理解爲 本身生成的對象,本身持有。非本身生成的對象,本身也能持有。不在須要本身持有的 對象時釋放。非本身持有的對象沒法釋放。生成並持有對象<alloc,new,copy,mutableCopy 等>,持有對象<retain>,釋放對象<release>,廢棄對象<dealloc>
1.當你使用new,alloc和copy方法建立一個對象時,該對象的保留計數器值爲1.當你再也不使用該對象時,你要負責向該對象發送一條release或autorelease消息.這樣,該對象將在使用壽命結束時被銷燬.
2.當你經過任何其餘方法得到一個對象時,則假設該對象的保留計數器值爲1,並且已經被設置爲自動釋放,你不須要執行任何操做來確保該對象被清理.若是你打算在一段時間內擁有該對象,則須要保留它並確保在操做完成時釋放它.
3.若是你保留了某個對象,你須要(最終)釋放或自動釋放該對象.必須保持retain方法和release方法的使用次數相等.
爲何不少內置的類,如TableViewController的delegate的屬性是assign不是retain?
循環引用
全部的引用計數系統,都存在循環應用的問題。例以下面的引用關係:
• 對象a建立並引用到了對象b.
• 對象b建立並引用到了對象c.
• 對象c建立並引用到了對象b.
這時候b和c的引用計數分別是2和1。當a再也不使用b,調用release釋放對b的全部權,由於c還引用了b,因此b的引用計數爲1,b不會被釋放。b不釋放,c的引用計數就是1,c也不會被釋放。今後,b和c永遠留在內存中。
這種狀況,必須打斷循環引用,經過其餘規則來維護引用關係。好比,咱們常見的delegate每每是assign方式的屬性而不是retain方式 的屬性,賦值不會增長引用計數,就是爲了防止delegation兩端產生沒必要要的循環引用。若是一個UITableViewController對象a經過retain獲取了UITableView對象b的全部權,這個UITableView對象b的delegate又是a, 若是這個delegate是retain方式的,那基本上就沒有機會釋放這兩個對象了。本身在設計使用delegate模式時,也要注意這點。
委託的interface:聲明一個某協議的屬性delagate用assgin屬性,緣由是,爲了不循環引用。
定義屬性時,什麼狀況使用copy、assign、retain?
assign用於簡單數據類型,如NSInteger,double,bool,
retain和copy用於對象,
copy用於當a指向一個對象,b也想指向一樣的對象的時候,若是用assign,a若是釋放,再調用b會crash,若是用copy 的方式,a和b各自有本身的內存,就能夠解決這個問題。
retain 會使計數器加一,也能夠解決assign的問題。
另外:atomic和nonatomic用來決定編譯器生成的getter和setter是否爲原子操做。在多線程環境下,原子操做是必要的,不然有可能引發錯誤的結果。
加了atomic,setter函數會變成下面這樣:
if (property != newValue) {
[property release];
property = [newValue retain];
}
對象是何時被release的?
引用計數爲0時。
autorelease實際上只是把對release的調用延遲了,對於每個Autorelease,系統只是把該Object放入了當前的Autoreleasepool中,當該pool被釋放時,該pool中的全部Object會被調用Release。對於每個Runloop, 系統會隱式建立一個Autoreleasepool,這樣全部的release pool會構成一個象CallStack同樣的一個棧式結構,在每個Runloop結束時,當前棧頂的Autoreleasepool會被銷燬,這樣這個pool裏的每一個Object(就是autorelease的對象)會被release。那什麼是一個Runloop呢? 一個UI事件,Timercall, delegate call, 都會是一個新的Runloop
readwrite,readonly,assign,retain,copy,nonatomic 、strong、weak屬性的做用?並區別
strong(強引用)、 weak(弱引用)?什麼狀況使用copy,assign,和retain?
readwrite 是可讀可寫特性;須要生成getter方法和setter方法時
readonly 是隻讀特性 只會生成getter方法 不會生成setter方法 ;不但願屬性在類外改變
assign 是賦值特性,setter方法將傳入參數賦值給實例變量;僅設置變量時;
retain 表示持有特性,setter方法將傳入參數先保留,再賦值,傳入參數的retaincount會+1;
copy 表示賦值特性,setter方法將傳入對象複製一份;須要徹底一份新的變量時。
nonatomic 非原子操做,決定編譯器生成的setter getter是不是原子操做,atomic表示多線程安全,通常使用 nonatomic
assign用於簡單數據類型,如NSInteger,double,bool。
retain 和copy用戶對象,copy用於當 a指向一個對象,b也想指向一樣的對象的時候,若是用assign,a若是釋放,再 調用b會crash,若是用copy 的方式,a和b各自有本身的內存,就能夠解決這個問題。retain 會使計數器加1,也能夠解 決assign的問題。另外:atomic和nonatomic用來決定編譯器生成的getter和setter是否爲原子操做。 在多線程環境 下,原子操做是必要的,不然有可能引發錯誤的結果。
爲何不少內置類如UITableViewController的delegate屬性都是assign而不是retain的? 會引發循環引用----如果retain,在alloc一次以後,若release一次,會致使內訓泄漏,若release兩次會致使兩個 對象的dealloc嵌套執行,結果就是都沒有執行成功,最後崩潰! 全部的引用計數系統,都存在循環應用的問題。例以下面的引用關係:
* 對象a建立並引用到了對象b. * 對象b建立並引用到了對象c. * 對象c建立並引用到了對象b.
這時候b和c的引用計數分別是2和1。 當a再也不使用b,調用release釋放對b的全部權,由於c還引用了b,因此b的引用計數爲1,b不會被釋放。 b不釋放,c的引用計數就是1,c也不會被釋放。今後,b和c永遠留在內存中。 這種狀況,必須打斷循環引用,經過其餘規則來維護引用關係。咱們常見的delegate每每是assign方式的屬性而不是 retain方式 的屬性,賦值不會增長引用計數,就是爲了防止delegation兩端產生沒必要要的循環引用。 若是一個UITableViewController 對象a經過retain獲取了UITableView對象b的全部權,這個UITableView對象b的 delegate又是a,若是這個delegate是retain方式的,那基本上就沒有機會釋放這兩個對象了。本身在設計使用 delegate模式時,也要注意這點。
ObjC中,與retain配對使用的方法是dealloc仍是release,爲何?須要與alloc配對使用的方法是dealloc還
是release,爲何?
與retain配對使用的方法是release,由於retain使retainCount計數加1,release使retainCount計數減1;與 retain語義相反的是release。 與alloc配對使用的是release,由於:alloc是使retainCount計數加1,,使retainCount計數減1。與alloc語義相反
的是dealloc,由於:alloc是建立一個對象,,dealloc是銷燬一個對象。
1六、重寫一個NSStrng類型的,retain方式聲明name屬性的setter和getter方法 { NSString * _name; } @property NSString *name;----加上這些是爲了不錯誤
setter方法:
-(void)setName:(NSString*)name
{
{[_name release];
_name=[name retain];
}
}
分別描述內存管理要點、autorelease、release、NSAutoreleasePool?並說明autorelease是什 麼時候被release的?簡述何時由你負責釋放對象,何時不禁你釋放?
[NSAutoreleasePool release]
和[NSAutoreleasePool drain]有什麼區別?
內存管理要點:
Objective-C 使用引用計數機制(retainCount)來管理內存。內存每被引用一次,該內存的引用計數+1,每被釋放一次引 用計數-1。當引用計數 = 0 的時候,調用該對象的 dealloc 方法,來完全從內存中刪除該對象。 alloc,allocWithZone,new(帶初始化)時:該對象引用計數 +1;
retain:手動爲該對象引用計數 +1;
copy:對象引用計數 +1;
mutableCopy:生成一個新對象,新對象引用計數爲 1;
release:手動爲該對象引用計數 -1; autorelease:把該對象放入自動釋放池,當自動釋放池釋放時,其內的對象引用計數 -1。
NSAutoreleasePool: NSAutoreleasePool是經過接收對象向它發送的autorelease消息,記錄該對象的release消息,當自動釋放池被銷燬 時,會自動向池中的對象發送release消息。
autorelease 是在自動釋放池被銷燬,向池中的對象發送release
只能釋放本身擁有的對象, 區別是:在引用計數環境下(在不使用ARC狀況下),二者基本同樣,在GC環境下,release 是一個no-op(無效操 做),因此不管是否是gc都使用drain
內存管理 Autorelease、retain、copy、assign的set方法和含義?
1,你初始化(alloc/init)的對象,你須要釋放(release)它。例如:
NSMutableArray aArray = [[NSArray alloc] init];
後,須要
[aArray release];
2,你retain或copy的,你須要釋放它。例如:
[aArray retain]
後,須要
[aArray release];
3,被傳遞(assign)的對象,你須要斟酌的retain和release。例如:
obj2 = [[obj1 someMethod] autorelease];
對象2接收對象1的一個自動釋放的值,或傳遞一個基本數據類型(NSInteger,NSString)時: 你或但願將對象2進行retain,以防止它在被使用以前就被自動釋放掉。可是在retain後,必定要在適當的時候進行釋放。
關於索引計數(Reference Counting)的問題
retain值 = 索引計數(ReferenceCounting)
NSArray對象會retain(retain值加一)任何數組中的對象。當NSArray被卸載(dealloc)的時候,全部數組中的對象會被執行一次釋放(retain值減一)。不只僅是NSArray,任何收集類(CollectionClasses)都執行相似操做。例如NSDictionary,甚至UINavigationController。
Alloc/init創建的對象,索引計數爲1。無需將其再次retain。
[NSArray array]和[NSDate date]等「方法」創建一個索引計數爲1的對象,可是也是一個自動釋放對象。因此是本地臨時對象,那麼無所謂了。若是是打算在全Class中使用的變量(iVar),則必須retain它。
缺省的類方法返回值都被執行了「自動釋放」方法。(*如上中的NSArray)
在類中的卸載方法「dealloc」中,release全部未被平衡的NS對象。(*全部未被autorelease,而retain值爲1的)
===============================================================
4.RunLoop
RunLoop就是一個消息傳送機制,用於異步的或線程內部的通訊,它提供了一套機制來處理系統的輸入源(像 socekts,ports,files,keyboard,mouse,定時器等),你能夠把它想象成是一個郵局,在等待信件和傳遞信件給收件人,每一個 NSThread都有屬於它本身的RunLoop。
RunLoop能夠用來區分交互式App和命令行。命令行經過一些參數運行後,執行完他們的程序,這個程序結束了,就像「Hello world」, 只要打印出「Hello world」,這個程序也就結束了。可是交互式的App就會一直等待用戶輸入,而後作出反應,而後再等待用戶輸入,知道某一個觸發條件發生,纔會退出程 序。
因此從上面的講述就能夠知道,RunLoop的工做實際上就是在等待觸發事件的發生。這些觸發事件能夠是外部的事件,好比用戶的一些行爲或者像網絡 請求,又或者像App內部的信息,好比線程內部的通知,異步代碼的執行,定時器等等,一旦一個觸發事件發生,而且RunLoop接受到這個信息,它就會去 尋找相關的收信人,並把信息發送給這個收信人。
RunLoop的時間線
-
通知觀察者RunLoop已經被啓動
-
通知觀察者一些定時器已經準備開始
-
通知觀察者一些不是基於端口的輸入源準備開始
-
啓動那些已經準備好的不是基於端口的輸入源
-
若是一個基於端口的輸入源已經準備好,正等待被啓動,那麼就會立刻啓動這個輸入源。進入第9步。
-
通知觀察者這個線程準備休眠。
-
把這個線程變成休眠狀態直到下面一個事件發生:
-
-
一個事件到達了一個基於端口的源
-
一個定時器啓動
-
RunLoop設置的時間已經到時
-
該RunLoop被喚醒
-
通知觀察者該線程被喚醒
-
處理等待事件
-
-
通知觀察者RunLoop退出。
===============================================================
Foundation 框架 中的傳遞機制 delegation,block 和 target-action
Foundation 框架外的 (KVO 和 通知) KVC KVO
KVO(Key-Value-Observing)鍵-值-監看
KVC(Key-Value-Coding)鍵-值-編碼
Key-Value Coding(KVC)實現分析
KVC運用了一個isa-swizzling技術。isa-swizzling就是類型混合指針機制。KVC主要經過isa-swizzling,來實現其內部查找定位的。isa指針,如其名稱所指,(就是is a kind of的意思),指向維護分發表的對象的類。該分發表實際上包含了指向實現類中的方法的指針,和其它數據。
好比說以下的一行KVC的代碼:
- [site setValue:@"sitename" forKey:@"name"];
就會被編譯器處理成:
- SEL sel = sel_get_uid ("setValue:forKey:");
- IMP method = objc_msg_lookup (site-]] > isa,sel);
- method(site, sel, @"sitename", @"name");
首先介紹兩個基本概念:
(1)SEL數據類型:它是編譯器運行Objective-C裏的方法的環境參數。
(2)IMP數據類型:他其實就是一個 編譯器內部實現時候的函數指針。當Objective-C編譯器去處理實現一個方法的時候,就會指向一個IMP對象,這個對象是C語言表述的類型(事實上,在Objective-C的編譯器處理的時候,基本上都是C語言的)。
這下KVC內部的實現就很清楚的清楚了:一個對象在調用setValue的時候,(1)首先根據方法名找到運行方法的時候所須要的環境參數。(2)他會從本身isa指針結合環境參數,找到具體的方法實現的接口。(3)再直接查找得來的具體的方法實現。
Key-Value Observing(KVO)實現
在上面所介紹的KVC機制上加上KVO的自動觀察消息通知機制就水到渠成了。
當觀察者爲一個對象的屬性進行了註冊,被觀察對象的isa指針被修改的時候,isa指針就會指向一箇中間類,而不是真實的類。因此isa指針其實不須要指向實例對象真實的類。因此咱們的程序最好不要依賴於isa指針。在調用類的方法的時候,最好要明確對象實例的類名。
熟悉
KVO的朋友都知道,只有當咱們調用
KVC去訪問key值的時候
KVO纔會起做用。因此確定肯定的是,KVO是基於KVC實現的。其實看了上面咱們的分析之後,關係KVO的架構的構思也就水到渠成了。
由於
KVC的實現機制,能夠很容易看到某個
KVC操做的Key,然後也很容易的跟觀察者註冊表中的Key進行匹對。假如訪問的Key是被觀察的Key,那麼咱們在內部就能夠很容易的到觀察者註冊表中去找到觀察者對象,然後給他發送消息。
KVC和KVO的使用原則:
1、KVC(key-value-coding)
一、只針對類屬性,設置鍵值對
二、設置setValue: forKey:,即forKey只能爲類屬性
三、取值valueForKey
2、KVO(key-value-observing)被觀察者的屬性發生改變時,通知觀察者
一、利用KVC對類屬性進行設置
二、註冊observing對象addObserver:forKeyPath:options:context:
三、觀察者類必須重寫方法 observeValueForKeyPath:ofObject:change:context:
四、應用,MVC模型中,數據庫(dataModal)發生變化時,引發view改變,用這種方式實現很是方便
KVC:
優勢:
1.設置值,會查找屬性的getter(valueForKey)和setter(setValueForKey)方法,而後查找其名字的實例變量。
2.支持指定鍵路徑,就像文件系統的路徑同樣。好比:car.emgine.horespower
3.總體操做,好比:給一個汽車的輪子發送kvc消息,因爲汽車有4個輪子,調用代碼(valueForKeyPath:@「tires.pressure」)後會返回一個數組(包含4個數據)。
4.快速運算:能夠引用一些運算符來進行一些計算。如:cars.@count,cars.@avg.mileage (cars.用來獲取cars的屬性,@count中@符號表明着後面將進行一些運算,@count用於通知kvc機制計算鍵路徑左側值的對象總數。同理,後者是計算全部對象的平均行駛距離)。 可是你還不能添加本身的運算符。
5.批處理:有兩個調用能夠爲對象進行批量處理。第一個調用爲:dictionaryWithValueForKey。第二個調用爲:setValueForKeyWithDictinary
6.nil仍然可用:重寫setNilValueForKey方法
7.處理未定義的鍵:重寫valueForUndefinedKey方法
缺點:
KVC須要解析字符串來計算你須要作的事情,速度會比較慢,此外,編譯器還沒法對字符串進行錯誤檢查,沒法判斷錯誤的鍵路徑。所以,當你使用時,可能會出現運行時錯誤。
當經過KVC調用對象時,好比:[self valueForKey:@」someKey」]時,程序會自動試圖經過幾種不一樣的方式解析這個調用。首先查找對象是否帶有 someKey 這個方法,
若是沒找到,會繼續查找對象是否帶有someKey這個實例變量(iVar),
若是尚未找到,程序會繼續試圖調用 -(id)valueForUndefinedKey:這個方法。
若是這個方法仍是沒有被實現的話,程序會拋出一個NSUndefinedKeyException異常錯誤。
字典類其實裝載的是鍵值關係,obc裏面,NSObject有個function叫作hash,字典類在裝載的時候,都會對每一個對象取key值
(Key-Value Coding查找方法的時候,不只僅會查找someKey這個方法,還會查找getsomeKey這個方法,前面加一個get,或者_someKey以及_getsomeKey這幾種形式。同時,查找實例變量的時候也會不只僅查找someKey這個變量,也會查找_someKey這個變量是否存在。)
設計valueForUndefinedKey:方法的主要目的是當你使用-(id)valueForKey方法從對象中請求值時,對象可以在錯誤發生前,有最後的機會響應這個請求。
簡述NotificationCenter、KVC、KVO、Delegate?並說明它們之間的區別?
Notification 是觀察者模式的實現,KVO是觀察者模式的OB-C底層實現。
NOtification 經過 Notifydcation addobserver 和 remove observer 工做。
KVO是鍵值監聽,鍵值觀察機制,提供了觀察某一屬性變化的方法 KVC是鍵值編碼,是一種間接訪問對象的屬性,使用字符串來標示屬性(例如:setValue:forKey:) Delegate:把某個對象要作的事情委託給別的對象去作。那麼別的對象就是這個對象的代理,代替它來打理要作的 事。反映到程序中,首先要明確一個對象的委託方是哪一個對象,委託所作的內容是什麼。
區別:
委託代理(delegate),顧名思義,把某個對象要作的事情委託給別的對象去作。那麼別的對象就是這個對象的代理,代替它來打理要作的事。反映到程序中,首先要明確一個對象的委託方是哪一個對象,委託所作的內容是什麼。
委託機制是一種設計模式,在不少語言中都用到的,這只是個通用的思想,網上會有不少關於這方面的介紹。
那麼在蘋果開發過程當中,用到委託的程序實現思想以下,我主要拿如何在視圖之間傳輸信息作個例子。
譬如:在兩個頁面(UIIview視圖對象)實現傳值,用委託(delegate)能夠很好作到!
方法:
類A
@interface A:UIView
id transparendValueDelegate;
@property(nomatic, retain) idtransparendValueDelegate;
@end
@implemtion A
@synthesize transparendValueDelegate
-(void)Function
{
NSString* value = @"hello";
//讓代理對象執行transparendValue動做
[transparendValueDelegate transparendValue:value];
}
@end
類B
@interface B:UIView
NSString* value;
@end
@implemtion B
-(void)transparendValue:(NSString*)fromValue
{
value = fromValue;
NSLog(@"the value is %@",value);
}
@end
//下面的設置A代理委託對象爲B
//在定義A和B類對象處:
A* a = [[A alloc] init];
B* b = [[B alloc] init];
a. transparendValueDelegate = b;//設置對象a代理爲對象b
這樣在視圖A和B之間能夠經過委託來傳值!
下面這個例子委託有兩類:
一、一個視圖類對象的代理對象爲父視圖,子視圖用代理實現讓父視圖顯示別的子視圖
二、同一父視圖下的一個子視圖爲另外一個子視圖的代理對象,讓另外一個子視圖改變自身背景色爲給定的顏色
===============================================
規範格式以下:
@protocol TransparendValueDelegate;
@interface A:UIView
id< TransparendValueDelegate > m_dTransparendValueDelegate;
@property(nomatic, retain) id m_dTransparendValueDelegate;
@end
//代理協議的聲明
@protocol TransparendValueDelegat<NSObject>
{
-(void)transparendValue:(NSString*)fromValue;
}
block的運用和傳值
Block的內存:
block的三種類型:NSGlobalBlock,NSStackBlock,NSMallocBlock
GlobalBlock呢?由於它不須要運行時(Runtime)任何的狀態來改變行爲,不須要放在堆上或者棧上,直接編譯後在代碼段中便可,就像個c函數同樣。這種類型的Block在ARC和non-ARC狀況下沒有差異。
StackBlock呢?這個Block訪問了做用域外的變量d,在實現上就是這個block會多一個成員變量對應這個d,在賦值block時會將方法exmpale中的d變量值複製到成員變量中,從而實現訪問。因爲局部變量d和這個block的實現不在同一做用域,僅僅在調用過程當中用到了值傳遞,因此不能直接修改。而須要加一個標識符__block;,這樣對Block外的變量訪問從值傳遞轉變爲引用,從而有了修改內容的能力。那麼block就能夠實現對這個局部變量的修改了。
MallocBlock呢?正常咱們使用Block是在棧上生成的,離開了棧做用域便釋放了,若是copy一個Block,那麼會將這個Block copy到堆上分配,這樣就再也不受棧的限制,能夠隨意使用啦。
Block一開始是在棧上分配的,屬於NSStackBlock,若是是non-ARC狀況下return這個NSStackBlock,那麼其實已經被銷燬了,在函數中使用時就會crash。若是是ARC狀況下,getBlock返回的block會自動copy到堆上,那麼block的類型就是NSMallocBlock,能夠在example()中繼續使用。因此,若是是non-ARC時,要寫明[block copy]。
Block的循環引用問題:
retain cycle問題的根源在於Block和obj可能會互相強引用,互相retain對方,這樣就致使了retain cycle,最後這個Block和obj就變成了孤島,誰也釋放不了誰。
在Block中雖然沒直接使用self,但使用了成員變量。在Block中使用成員變量,retain的不是這個變量,而會retain self。
注意:MRC中
__block
是不會引發retain;但在ARC中
__block
則會引發retain。ARC中應該使用
__weak
或
__unsafe_unretained
弱引用。
__weak
只能在iOS5之後使用。
開發者必需要注意對象和Block的生命週期。
__block不要亂用:
將Block做爲參數傳給dispatch_async時,系統會將Block拷貝到堆上,若是Block中使用了實例變量,還將retain self,由於dispatch_async並不知道self會在何時被釋放,爲了確保系統調度執行Block中的任務時self沒有被意外釋放掉,dispatch_async必須本身retain一次self,任務完成後再release self。但這裏使用__block,使dispatch_async沒有增長self的引用計數,這使得在系統在調度執行Block以前,self可能已被銷燬,但系統並不知道這個狀況,致使Block被調度執行時self已經被釋放致使crash
BLOCK用處:
1.回調傳值 2.做爲方法的參數,在block裏面定義任意的代碼段。
5.單例,代理,觀察者模式,mvc框架
單例:單例模式的意思就是隻有一個實例。單例模式確保某一個類只有一個實例,並且自行實例化並向整個系統提供這個實例。這個類稱爲單例類。
1.單例模式的要點:
單例設計模式確保這個類僅僅擁有一個實例,而且爲這個實例提供一個全局的訪問點。
apple用了不少的單例:[NSUserDefaults
standardUserDefaults],[UIApplication sharedApplication], [UIScreen mainScreen], [NSFileManager defaultManager]他們都是返回一個單例對象。
2.單例模式的優勢:
1.實例控制:Singleton 會阻止其餘對象實例化其本身的 Singleton 對象的副本,從而確保全部對象都訪問惟一實例。
2.靈活性:由於類控制了實例化過程,因此類能夠更加靈活修改實例化過程
IOS中的單例模式
在objective-c中要實現一個單例類,至少須要作如下3個步驟:
1:定義一個靜態變量來保存你類的實例確保在你的類裏面保持全局。
2:定義一個靜態的dispatch_once_t變量來確保這個初始化存在一次。
3:用GCD來執行block初始化libraryAPI實例。這是單例設計模式的本質。這個初始化不在被調用這個類已經被初始化。而且是線程安全的。
代理:是一個表明或者協調另外一個對象的行爲機制。在Objective-c實現代理的設計模式。一個類能夠定義可選或者必須的方法經過協議。這是一個重要的設計模式。蘋果在UIKit類裏面用到了不少。 UITableView,UITextView, UITextField, UIWebView, UIAlert, UIActionSheet, UICollectionView, UIPickerView,UIGestureRecognizer, UIScrollView.
完整的單例:
一、爲單例對象實現一個靜態實例,並初始化,而後設置成nil,
二、實現一個實例構造方法檢查上面聲明的靜態實例是否爲nil,若是是則新建並返回一個本類的實例,
三、重寫allocWithZone方法,用來保證其餘人直接使用alloc和init試圖得到一個新實力的時候不產生一個新實例,
四、適當實現allocWitheZone,copyWithZone,release和autorelease。
觀察者:在觀察者設計模式裏面,一個對象通知其餘的對象一些狀態的改變。涉及這些對象不須要知道另外一個對象---所以鼓勵解耦設計模式。這個設計模式常常被用來通知感興趣的對象當一個屬性被改變時候。
一般實現須要一個觀察者註冊另外一個對象感興趣的狀態。當狀態改變,全部的觀察者對象被通知改變了。蘋果的遠程通知服務就是一個全球性的例子。
cocoa實現觀察者有兩個類似的方法:通知和鍵值觀察:
通知:不要被本地通知和遠程通知迷惑,通知是根據訂閱和通知的模式容許一個對象(通知者)發送消息給另外一些對象(訂閱者也就是監聽者)。這個通知者不須要知道訂閱者的任何信息。
蘋果公司大量的使用通知,例如當鍵盤隱藏時候系統發送一個UIKeyboardWillShowNotification/UIKeyboardWillHideNotification通知。當你的應用進入後臺系統發送一個UIApplicationDidEnterBackgroundNotification 通知。
鍵值觀察模式:在KVO,一個對象能夠要求被通知當他的某個特殊的屬性被改變了。
注意:記得要刪除你的觀察者們當他們收回,不然你的應用程序會崩潰當系統試圖將消息發送到這些不存在的觀察者!
Block的做用:
同 代理-協議、通知同樣用於對象和對象之間的通訊。
block的定義:匿名函數
是一個匿名的函數代碼塊,此代碼塊能夠做爲參數傳遞給其餘對象。
block的應用場景:
對象與對象之間的通訊。
block的語法:
block的聲明:
第一種:
int (^ myBlock)(int);
第二種:
typedef int (^MyBlock) (int);
block的建立:
MyBlock myblock = ^(int a){
NSLog(@"參數:%d",a);
return 10;
};
block的調用:
int ret = myblock(20);
第三種:
block做爲參數:
Myblock myblock = ^(int a)
{
NSLog(@"這是block代碼塊,a = %d",a);
return 10;
};
[self testBlock:myblock];
-(void) testBlock:(Myblock) myblock
{
//可能有一些邏輯判斷
//block回調
myblock(50);
}
block引用局部變量:將局部變量當作是常量:
__block int number = 10;
Myblock myblock2 = ^(int a)
{
number = 20;
number++;
NSLog(@"%d",number);
return 10;
};
block的內存管理:
在block裏面引用一個局部的objective-c對象的時候,該對象會被retain
若是局部變量使用_block修飾,則不會被retain。
//引用計數爲1
NSObject *obj = [[NSObject alloc] init];
Myblock *myBlcok = ^(int a)
{
//引用計數爲2
NSLog(@"引用計數:%d",obj.retainCount);
return 10;
};
//引用計數1,block引用該變量時不會將它retain
__block NSObject *obj = [[NSObject alloc] init];
在block裏面引用一個實例變量時,該實例對象會被retain
NSLog(@"計數:%d",self.retainCount);
[self objectMethod:^{
//引用了全局變量number
int value = number;
//引用計數2
NSLog(@"計數:%d",self.retainCount);
return a +b;
}];
block自己能夠就像對象同樣copy和release。
block建立後內存是分配在棧上,調用copy方法,會將block從棧移到堆上。
當block聲明爲全局變量時,咱們應該調用block的copy方法。
兩種copy方式:
[_block copy];
Block_copy(_block);
兩種release方式:
[_block release];
iphoneSDK中哪裏用到的block? (UIView動畫)
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations ;
===============================================================
6.iOS程序生命週期
iOS程序運行流程{
1. 系統調用app的main函數
2. main函數調用UIApplicationMain.
3. UIApplicationMain建立sharedapplication instance, UIApplication默認的instance.
4. UIApplicationMain讀取Info.plist找到主nib文件, 加載nib,把shared applicationinstance 設爲nib的owner.
5. 經過nib文件,建立app的獨立UIWindows object.
6. 經過nib,實例化了程序的AppDelegate object.
7. app內部啓動結束,application:didFinishLaunchingWith-Options: 被設定成 wAppDelegate instance.
8. AppDelegate向UIWindowinstance發makeKeyAndVisible消息, app界面展現給用戶. app準備好接收用戶的操做指令.
}
程序究竟是怎麼執行的?
1. 當你單擊iPhone上的應用程序圖標的時候,系統就會查找這個程序的入口地址,即main函數在內存代碼區的首地址.而後執行main函數裏的程序,這裏main函數裏僅有一句: return UIApplicationMain(argc, argv, nil, NSStringFromClass([ZHAppDelegate class]));
2. 上面的這句話有什麼做用呢?他會建立一個單例的application對象,這在系統中表徵這個程序的存在,生命週期;創建事件循環,如不斷的檢查程序的運行狀態,是否被觸摸,搖晃等;還有就是創建一個application的delegate,delegate的做用是根據應用程序的狀態並做出相應的處理,好比程序啓動了,進入活躍狀態,進入非活躍狀態,將要進入前臺,將要進入後臺,程序將要結束等消息.這是主程序傳遞過來的又delegate代理完成的,由於主程序的側重點不在此(猜想),
3. 那麼這個代理的具體都作些什麼事呢?在程序加載的時候,主程序會根據不一樣的狀況給代理髮送相應的消息,處理上面代理完成的輔助功能外,最總要的時delegate會建立一個window,這個window像個畫板同樣把視圖呈現給用戶.若是咱們使用mvc模式設計程序,那麼咱們會把window的管理權交給一個叫作controler(管理器),也就是mvc中的c來管理.controler的做用是用來往window上加載視圖,這裏咱們能夠猜想,controller原則上能夠有不少個,哪一個controler擁有了對window的管理權,誰就能夠往window上加載視圖.
4. Controller的都有什麼做用或者controler是怎麼工做的呢?加載視圖的時候首先會初始化函數(或者實例化)一個controler類的對象,由它來調用本身所在類的方法管理視圖,controller所在的類都有哪些方法呢?最典型的方法有loadView方法(加載視圖). viewDidLoad(視圖加載完成), didReceiveMemoryWarning(內存警告)等.
===========================================================
7.
MVC設計模式MVVM
怎麼理解MVC,在Cocoa中MVC是怎麼實現的?
MVC設計模式考慮三種對象:模型對象、視圖對象、和控制器對象。模型對象表明特別的知識和專業技能,它們負責保有應用程序的數據和定義操做數據的邏輯。視圖對象知道如何顯示應用程序的模型數據,並且可能容許用戶對其進行編輯。控制器對象是應用程序的視圖對象和模型對象之間的協調者。
談談你對MVC的理解?爲何要用MVC?在Cocoa中MVC是怎麼實現的?你還熟悉其餘的OC設計模式或別 的設計模式嗎?
MVC就是Model-View-Controller的縮寫,M指的是業務模型,V指的是用戶頁面,C指的是控制器。MVC是架構模式,是講M和 V的代碼分離,從而使同那個一個程序能夠使用不一樣的表現形式。 M:表示數據和業務規則,V是用戶看到的並與之交互的頁面,C是接受用戶的輸入並調用M和V取完成用戶需求的 單例,代理,觀察者,工廠模式等 單例模式:說白了就是一個類不經過alloc方式建立對象,而是用一個靜態方法返回這個類的對象。系統只須要擁有一個的 全局對象,這樣有利於咱們協調系統總體的行爲; 代理模式:代理模式給某一個對象提供一個代理對象,並由代理對象控制對源對象的引用.好比一個工廠生產了產品,並不想 直接賣給用戶,而是搞了不少代理商,用戶能夠直接找代理商買東西,代理商從工廠進貨.常見的如QQ的自動回覆就屬於代 理攔截,代理模式在iphone中獲得普遍應用.
觀察者模式: 當一個物體發生變化時,會通知全部觀察這個物體的觀察者讓其作出反應。實現起來無非就是把全部觀察者的 對象給這個物體,當這個物體的發生改變,就會調用遍歷全部觀察者的對象調用觀察者的方法從而達到通知觀察者的目的
MVC(Model-View-Controller)應用程序結構被用來分析分佈式應用程序的特徵。這種抽象結構能有助於將應用程序分割成若干邏輯部件,使程序設計變得更加容易。
MVC結構提供了一種按功能對各類對象進行分割的方法(這些對象是用來維護和表現數據的),其目的是爲了將各對象間的耦合程度減至最小。MVC結構原本是爲了將傳統的輸入(input)、處理(processing)、輸出(output)任務運用到圖形化用戶交互模型中而設計的。可是,將這些概念運用於基於Web的企業級多層應用領域也是很適合的。
在MVC結構中,模型(Model)表明應用程序的數據(data)和用於控制訪問和修改這些數據的業務規則(business rule)。一般模型被用來做爲對現實世界中一個處理過程的軟件近似,當定義一個模型時,能夠採用通常的簡單的建模技術。
當模型發生改變時,它會通知視(View),而且爲視提供查詢模型相關狀態的能力。同時,它也爲控制器(Controller)提供訪問封裝在模型內部的應用程序功能的能力。
一個視(View)用來組織模型的內容。它從模型那裏得到數據並指定這些數據如何表現。當模型變化時,視負責維持數據表現的一致性。視同時將用戶要求告知控制器(Controller)。
控制器(Controller)定義了應用程序的行爲;它負責對來自視的用戶要求進行解釋,並把這些要求映射成相應的行爲,這些行爲由模型負責實現。在獨立運行的GUI客戶端,用戶要求多是一些鼠標單擊或是菜單選擇操做。在一個Web應用程序中,它們的表現形式多是一些來自客戶端的GET或POST的HTTP請求。模型所實現的行爲包括處理業務和修改模型的狀態。根據用戶要求和模型行爲的結果,控制器選擇一個視做爲對用戶請求的應答。一般一組相關功能集對應一個控制器。下圖描述了一個MVC應用程序中模型、視、控制器三部分的關係
===========================================================
8.UIView CALayer frame與bounds
frame 和 bounds 的區別 ,bound的大小改變frame 改變嗎?
frame:該view在父view座標系統中的位置和大小。(參照點是,父親的座標系統)
bounds:該view在本地座標系統中的位置和大小。(參照點是,本地座標系統)
簡述你對UIView、UIWindow和CALayer的理解 UIView:屬於UIkit.framework框架,負責渲染矩形區域的內容,爲矩形區域添加動畫,響應區域的觸摸事件,佈局和管 理一個或多個子視圖 UIWindow:屬於UIKit.framework框架,是一種特殊的UIView,一般在一個程序中只會有一個UIWindow,但能夠手 動建立多個UIWindow,同時加到程序裏面。UIWindow在程序中主要起到三個做用:
一、做爲容器,包含app所要顯示的全部視圖
二、傳遞觸摸消息到程序中view和其餘對象
三、與UIViewController協同工做,方便完成設備方向旋轉的支持
CAlayer:屬於QuartzCore.framework,是用來繪製內容的,對內容進行動畫處理依賴與UIView來進行顯示,不能處 理用戶事件。UIView和CALayer是相互依賴的,UIView依賴CALayer提供內容,CALayer依賴UIView一共容器顯示 繪製內容。
延伸: UIViewController:管理視圖的幾成熟,每一個視圖控制器都有一個自帶的視圖,而且負責這個視圖相關的一切事務。方便 管理視圖中的子視圖,負責model與view的通訊;檢測設備旋轉以及內存警告;是全部視圖控制類的積累,定義了控制器 的基本功能。
drawRect和layoutSubviews的區別
兩個方法都是異步執行的,layoutSubviews方便數據計算,drawRect方便視圖重繪。 layoutSubviews對subviews從新佈局
layoutSubviews方法調用先於drawRect
在 UIView 中有一個autoresizingMask的屬性,它對應的是一個枚舉的值(以下),屬性的意思就是自動調整子控件與父控件中間的位置,寬高。
enum {
UIViewAutoresizingNone = 0,
UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
UIViewAutoresizingFlexibleWidth = 1 << 1,
UIViewAutoresizingFlexibleRightMargin = 1 << 2,
UIViewAutoresizingFlexibleTopMargin = 1 << 3,
UIViewAutoresizingFlexibleHeight = 1 << 4,
UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};
UIViewAutoresizingNone就是不自動調整。
UIViewAutoresizingFlexibleLeftMargin 自動調整與superView左邊的距離,保證與superView右邊的距離不變。
UIViewAutoresizingFlexibleRightMargin 自動調整與superView的右邊距離,保證與superView左邊的距離不變。
UIViewAutoresizingFlexibleTopMargin 自動調整與superView頂部的距離,保證與superView底部的距離不變。
UIViewAutoresizingFlexibleBottomMargin 自動調整與superView底部的距離,也就是說,與superView頂部的距離不變。
UIViewAutoresizingFlexibleWidth 自動調整本身的寬度,保證與superView左邊和右邊的距離不變。
UIViewAutoresizingFlexibleHeight 自動調整本身的高度,保證與superView頂部和底部的距離不變。
UIViewAutoresizingFlexibleLeftMargin |UIViewAutoresizingFlexibleRightMargin 自動調整與superView左邊的距離,保證與左邊的距離和右邊的距離和原來距左邊和右邊的距離的比例不變。好比原來距離爲20,30,調整後的距離應爲68,102,即68/20=102/30。
===========================================================
9.根類 NSObject
NSObject是大部分Objective-C類的根類,它沒有父類。其它類繼承NSObject,訪問Objective-C運行時系統的基本接口,這樣其餘類的實例能夠得到運行時的能力。
根類和根類協議
NSObject不可是個類名,NSObject也是個協議的名稱,
參考NSObject協議 , NSObject協議指定了根類必須實現的接口。
根類的主要方法:
• 分配、初始化、和複製:
alloc和allocWithZone:方法用於從某內存區域中分配一個對象內存,並使對象指向其運行時的類定義。
init方法是對象初始化。
new是一個將簡單的內存分配和初始化結合起來的方法。
copy和copyWithZone:
•對象的保持和清理:
retain方法增長對象的保持次數。
release方法減小對象的保持次數。
autorelease方法也是減小對象的保持次數,可是以推遲的方式。
retainCount方法返回對當前的保持次數。
dealloc方法由須要釋放對象的實例變量以及釋放動態分配的內存的類實現。
轉碼和解碼
下面的方法和對象的編解碼(做爲歸檔過程的一部分)有關:
encodeWithCoder:和initWithCoder:是NSCoding協議僅有的方法。前者使對象能夠對其實例變量進行編碼,後者則使對象能夠根據解碼過的實例變量對自身進行初始化。
NSObject類中聲明瞭一些於對象編碼有關的方法:classForCoder:、replacementObjectForCoder:、和awakeAfterUsingCoder:。
•消息的轉發
forwardInvocation:容許一個對象將消息轉發給另外一個對象。
•消息的派發
在performSelector開頭的一些方法容許你延遲後派髮指定消息,並且能夠將消息(同步或異步的消息)從輔助線程派發到主線程。
。
===========================================================
10.多線程:
l 線程(線程)用於指代獨立執行的代碼段。
l 進程(process)用於指代一個正在運行的可執行程序,它能夠包含多個線程。
l 任務(task)用於指代抽象的概念,表示須要執行工做。
咱們能夠這樣理解:咱們能夠在電腦上開啓多個QQ應用。每一個QQ應用能夠同時打開不少聊天的窗口。這裏每一個QQ是一個進程,而後每一個QQ聊天窗口是一個QQ應用的一個線程。
這三種編程方式從上到下,抽象度層次是從低到高,抽象程度越高的使用越簡單,也是Apple最推薦使用的。
三種方式的優缺點:
NSThread
優勢:NSThread比其餘兩個輕量級
缺點:須要本身管理線程的生命週期,線程同步。線程同步對數據的加鎖會有必定的系統開銷。
Cocoa operation
優勢:不須要關心線程管理,數據同步的事情,能夠把精力放在本身須要執行的操做上。Cocoa operation相關的類是NSOperation , NSOperationQueue。 NSOperation是個抽象類,使用它必須使用它的子類,能夠實現它或者使用它定義好的兩個子類:NSInvocationOperation 和 NSBlockOperation 。 建立NSoperation子類的對象,把對象添加到NSoperationQueue隊列裏執行。
GCD
這是Apple開發的一個多核編程的解決辦法。GCD是一個替代諸如NSThread , NSOperationQueue , NSinvocationOperation等技術的很高效和強大的技術。
1:首先多線程技術是耗費資源的好比系統的內存和cpu的佔用
2:多線程可能形成兩個線程同時訪問一塊內存來形成資源爭奪
3:剛纔也說了多線程耗費寶貴的資源,你應該保證你多線程裏面執行的操做運行時間長並且有效率。咱們還得中斷那些空閒的線程。這樣有能下降咱們應用的內存佔用,來騰出內存給其餘的應用。
4:線程之間的通訊,有時候輔助線程可能要通知主線程作的工做怎麼樣了。這時候就用到了線程通訊的技術。
5:線程的退出,一個線程執行完他的任務天然退出是最好的選擇。可是有時候你強制退出一個線程會形成一些內存泄漏等潛在的安全問題。
6:若是你的一個線程忽然由於什麼緣由拋出一個異常。你須要捕獲異常,由於同一個進程的線程不能捕獲其餘線程的異常信息。咱們必須把異常狀況一個NSException對象從一個線程傳遞到另外一個線程,向這個線程報告發生了什麼,這樣引起異常的線程能夠繼續執行(若是有可能),或者乾脆退出。
7:線程浪費寶貴的資源咱們須要提升他的效率須要專門一個run loop來處理要執行的事件,有時候還須要一個自動釋放池來管理建立的一些臨時對象。
Grand Central Dispatch(
GCD):
系統管理線程,你不須要編寫線程代碼。只需定義想要執行的任務,而後添加到適當的 dispatch queue。 GCD會負責建立線程和調度你的任務。系統直接提供線程管理,比應用實現更加高效。
GCD,全稱
Grand Central Dispath,是蘋果開發的一種支持並行操做的機制。它的主要部件是一個FIFO隊列和一個線程池,前者用來添加任務,後者用來執行任務。
GCD中的FIFO隊列稱爲dispatch queue,它能夠保證先進來的任務先獲得執行(但不保證必定先執行結束)。
經過與線程池的配合,dispatch queue分爲下面兩種:
• Serial Dispatch Queue -- 線程池只提供一個線程用來執行任務,因此後一個任務必須等到前一個任務執行結束才能開始。
• Concurrent Dispatch Queue -- 線程池提供多個線程來執行任務,因此能夠按序啓動多個任務併發執行。
1. Basic Management
咱們能夠經過dispatch_queue_cretae來建立隊列,而後用dispatch_release釋放。好比下面兩段代碼分別建立串行隊列和並行隊列:
dispatch_queue_t serialQ = dispatch_queue_create("eg.gcd.SerialQueue", DISPATCH_QUEUE_SERIAL);
1.dispatch_async(serialQ, ^{
2. // Code here
3.});
4.dispatch_release(serialQ);
dispatch_queue_t concurrentQ = dispatch_queue_create("eg.gcd.ConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
1.dispatch_async(concurrentQ, ^{
2. // Code here
3.});
4.dispatch_release(concurrentQ);
而系統默認就有一個串行隊列main_queue和並行隊列global_queue:
1.dispatch_queue_t globalQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
2.dispatch_queue_t mainQ = dispatch_get_main_queue();
2. Normal Control
•dispatch_once
它能夠保證整個應用程序生命週期中某段代碼只被執行一次!
•dispatch_after
有時候咱們須要等個幾秒鐘而後作個動畫或者給個提示,這時候能夠用
dispatch_after這個函數
•dispatch_set_target_queue
經過
dispatch_set_target_queue函數能夠設置一個dispatch queue的優先級,或者指定一個dispatch source相應的事件處理提交到哪一個queue上。
dispatch_set_target_queue(serialQ, globalQ);
•dispatch_apply
執行某個代碼片斷若干次。
dispatch_apply(10, globalQ, ^(
size_t index) {}
•dispatch group
Dispatch Group機制容許咱們監聽一組任務是否完成。
•dispatch_barrier_async
經過
dispatch_barrier_async函數提交的任務會等它前面的任務執行結束纔開始,而後它後面的任務必須等它執行完畢才能開始。
Operation Queue:
Objective-C對象,相似於 dispatch queue。你定義想要執行的任務,並添加任務到 operation queue,後者負責調度和執行這些任務。和 GCD同樣, Operation Queue也管理了線程,更加高效
asynchronous functions:
系統的一些API接口給你提供了異步的功能自動支持併發功能。這些API也許用系統的機制和進程來建立自定義的線程來執行他們的任務而且返回結果給他們。一旦你設計應用,尋找提供異步功能的API接口來代替用同步放在自定義線程。
timers:
你能夠用timers在你的應用主線程來執行週期性的任務這時須要一個線程是太微不足道了。可是仍然要提供每隔一段時間的服務。
線程是進程的基本單位 進程和線程都是由操做系統所體會的程序運行的基本單元,系統利用該基本單元實現系統對應用的併發性。 進程和線程的主要差異在於它們是不一樣的操做系統資源管理方式。進程有獨立的地址空間,一個進程崩潰後,在保護模式下 不會對其它進程產生影響,而線程只是一個進程中的不一樣執行路徑。線程有本身的堆棧和局部變量,但線程之間沒有單獨的 地址空間,一個線程死掉就等於整個進程死掉,因此多進程的程序要比多線程的程序健壯,但在進程切換時,耗費資源較 大,效率要差一些。但對於一些要求同時進行而且又要共享某些變量的併發操做,只能用線程,不能用進程。
簡述多線程的做用以及什麼地方會用到多線程?OC實現多線程的方法有哪些?
談談多線程安全問題的幾種解決方案?何爲線程同步,如何實現的?分線程回調主線程方法是什麼,有什麼做用?
(1)、多線程的做用:能夠解決負載均衡問題,充分利用cpu資源 。爲了提升CPU的使用率,採用多線程的方式去同時完 成幾件事情而互不干擾,
(2)、大多狀況下,要用到多線程的主要是須要處理大量的IO操做時或處理的狀況須要花大量的時間等等,好比:讀寫文 件、視頻圖像的採集、處理、顯示、保存等。 (3)、ios有三種主要方法:一、NSThread。二、NSOperation。三、GCD。 (4)解決方案:使用鎖:鎖是線程編程同步工具的基礎。鎖可讓你很容易保護代碼中一大塊區域以便你能夠確保代碼的正 確性。使用POSIX互斥鎖;使用NSLock類;使用@synchronized指令等。
(5)回到主線程的方法: dispatch_async(dispatch_get_main_queue(), ^{ });
做用:主線程是顯示UI界面,子線程多數是進行數據處理
==========================================================
11.數據持久化
OC中有哪些數據存儲方式,各有什麼區別?
四種存儲方式: 1.NSUserDefaults,用於存儲配置信息2.SQLite,用於存儲查詢需求較多的數據3.CoreData,用於規劃應用中的對象4.使用基本對象類型定製的個性化緩存方案.
NSUserDefaults:對象中儲存了系統中用戶的配置信息,開發者能夠經過這個實例對象對這些已有的信息進行修改,也 能夠按照本身的需求建立新的配置項。
SQLite擅長處理的數據類型其實與NSUserDefaults差很少,也是基礎類型的小數據,只是從組織形式上不一樣。開發者可 以以關係型數據庫的方式組織數據,使用SQL DML來管理數據。通常來講應用中的格式化的文本類數據能夠存放在數據庫 中,尤爲是相似聊天記錄、Timeline等這些具備條件查詢和排序需求的數據。
CoreData是一個管理方案,它的持久化能夠經過SQLite、XML或二進制文件儲存。它能夠把整個應用中的對象建模並進 行自動化的管理。從歸檔文件還原模型時CoreData並非一次性把整個模型中的全部數據都載入內存,而是根據運行時狀 態,把被調用到的對象實例載入內存。框架會自動控制這個過程,從而達到控制內存消耗,避免浪費。 不管從設計原理仍是使用方法上看,CoreData都比較複雜。所以,若是僅僅是考慮緩存數據這個需求,CoreData絕對不 是一個優選方案。CoreData的使用場景在於:整個應用使用CoreData規劃,把應用內的數據經過CoreData建模,徹底 基於CoreData架構應用。 使用基本對象類型定製的個性化緩存方案:從需求出發分析緩存數據有哪些要求:按Key查找,快速讀取,寫入不影響正常 操做,不浪費內存,支持歸檔。這些都是基本需求,那麼再進一步或許還須要固定緩存項數量,支持隊列緩存,緩存過時 等。
coredata和sqlite有無必然聯繫?coredata是一個關係型數據庫嗎? iOS中能夠有四種持久化數據的方式: 屬性列表、對象歸檔、SQLite3和Core Data coredata能夠使你以圖形界面的方式快速的定義app的數據模型,同時在你的代碼中容易獲取到它。coredata提供了基礎結構去處 理經常使用的功能,例如保存,恢復,撤銷和重作,容許你在app中繼續建立新的任務。在使用coredata的時候,你不用安裝額外的數據 庫系統,由於coredata使用內置的sqlite數據庫。coredata將你app的模型層放入到一組定義在內存中的數據對象。coredata會 追蹤這些對象的改變,同時能夠根據須要作相應的改變,例如用戶執行撤銷命令。當coredata在對你app數據的改變進行保存的時 候,core data會把這些數據歸檔,並永久性保存。
mac os x中sqlite庫,它是一個輕量級功能強大的關係數據引擎,也很容易嵌入到應用程序。能夠在多個平臺使用,sqlite是一個輕 量級的嵌入式sql數據庫編程。與coredata框架不一樣的是,sqlite是使用程序式的,sql的主要的API來直接操做數據表。
Core Data不是一個關係型數據庫,也不是關係型數據庫管理系統(RDBMS)。雖然Core Dta支持SQLite做爲一種存儲類型, 但它不能使用任意的SQLite數據庫。Core Data在使用的過程種本身建立這個數據庫。Core Data支持對1、對多的關係。
第一種:實用屬性列表,第二種:對象歸檔,第三種:嵌入式數據庫(SQLite3)
第四種:文件寫入磁盤
首先是屬性列表。nsuserdefaults位於屬性列表的最頂層,沙盒寫入技術,以及對象的歸檔,以及第三方的SQLlite,還有就是coredata。
首先讓咱們說說這個屬性列表:它是存儲相對較小數據量的簡潔方式,而且耗費很是低的工做量。可是這個屬性列表也是有限制的像剛纔我說的存儲數據量小的數據,還有就是一些自定義的對象是不能存儲的,只能是支持序列化的對象,想字典,數據,字符串,對了 仍是不能添加UI界面的東西。他們能夠以兩種不一樣的方式存儲一個是XML,這個XML是一種可讀性很是強的格式,另外一個是內部專門有的2進制的格式,這種方式速度更快寫,數據量更小些。正如咱們所說的NSuerdefaults位於屬性列表的頂層,所以咱們能夠輕鬆的讀取而且存儲數據。
NSUserDefaults類的使用和NSKeyedArchiver有不少相似之處,可是查看NSUserDefaults的定義能夠看出,NSUserDefaults直接繼承自NSObject而NSKeyedArchiver 繼承自NSCoder。這意味着NSKeyedArchiver其實是個歸檔持久化的類,也就能夠使用NSCoder類的[encodeObject: (id)objv forKey:(NSString *)key]方法來對數據進行持久化存儲
蘋果的一個專門記憶模式的實現是歸檔。把一個對象轉化爲流便於保存和恢復不用向外部類暴露私有的屬性。
聲明專輯經過符合NSCoding協議是能夠歸檔的。在專輯頭文件寫上符合NSCoding協議:@interface Album :
NSObject <NSCoding>
在實現文件裏面,添加兩個方法:
(void)encodeWithCoder:(
NSCoder *)aCoder
(id)initWithCoder:(
NSCoder *)aDecoder
Core Data是一種 穩定,功能全面的持久化工具,和以前的一些持久化 工具相比,他不須要對實體進行歸檔,也就是序列化,而是在數據 模型編輯器中建立一些實體
在代碼中,你再也不使用存取方法和修改方法,而是使用鍵值對編碼來設置屬性或者減縮他們的值
那麼這些託管對象的活動區域在哪 ? 他們位於所謂的持久庫中,默認狀況下,Core Data應用程序將持久庫實現爲存儲在應用程序文檔目錄的sqlite數據庫。
雖然數據是經過sqlite存儲的,但框架中的類將完成加載和保存數據的相關工做。不準要編寫任何sql語句。
sqlite3:
數據庫對象:sqlite3 數據庫指針:sqlite3_stmt
sqlite3_open():打開數據庫
sqlite3_prepare():負責編譯sql語句
sqlite3_step():只能獲得第一行的內容,繼續調用該函數直到全部結果都記錄完畢
sqlite3_column():將sqlite3_step運行的結果讀出來
sqlite3_finalize():在sqlite3_stmt使用完畢後銷燬sqlite3_stmt
sqlite3_close():關閉數據庫
sqlite3_exec():執行sql語句
sqlite3_bind_xxx():根據但願使用的數據類型,選擇不一樣的綁定語句。
什麼是沙盒?沙盒包含哪些文件,描述每一個文件的使用場景。如何獲取這些文件的路徑?如何獲取應用程序包 中文件的路徑?
沙盒是某個iphone工程進行文件操做有此工程對應的指定的位置,不能逾越。 包括:四個文件夾:documents,tmp,app,Library。 手動保存的文件在documents文件裏。 Nsuserdefaults保存的文件在tmp文件夾裏。訪問沙箱以外的磁盤文件是私有API,APPStore 沒法經過
Documents 目錄:您應該將全部de應用程序數據文件寫入到這個目錄下。這個目錄用於存儲用戶數據或其它應該按期備 份的信息。AppName.app 目錄:這是應用程序的程序包目錄,包含應用程序的自己。因爲應用程序必須通過簽名,因此 您在運行時不能對這個目錄中的內容進行修改,不然可能會使應用程序沒法啓動。Library 目錄:這個目錄下有兩個子目 錄:Caches 和 PreferencesPreferences 目錄包含應用程序的偏好設置文件。您不該該直接建立偏好設置文件,而是 應該使用NSUserDefaults類來取得和設置應用程序的偏好.Caches 目錄用於存放應用程序專用的支持文件,保存應用程 序再次啓動過程當中須要的信息。tmp 目錄:這個目錄用於存放臨時文件,保存應用程序再次啓動過程當中不須要的信息。
獲取這些目錄路徑的方法:
1,獲取家目錄路徑的函數:
NSString *homeDir = NSHomeDirectory();
2,獲取Documents目錄路徑的方法:
NSArray *paths =NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docDir = [paths objectAtIndex:0];
3,獲取Caches目錄路徑的方法:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cachesDir = [paths objectAtIndex:0];
4,獲取tmp目錄路徑的方法:
NSString *tmpDir = NSTemporaryDirectory();
5,獲取應用程序程序包中資源文件路徑的方法:
例如獲取程序包中一個圖片資源(apple.png)路徑的方法:
NSString *imagePath = [[NSBundle mainBundle] pathForResource:@」apple」 ofType:@」png」]; UIImage *appleImage = [[UIImage alloc] initWithContentsOfFile:imagePath]; 代碼中的mainBundle類方法用於返回一個表明應用程序包的對象。
===========================================================
分析json、xml的區別?json、xml解析方式的底層是如何處理的?
對於服務器的接口仍是用JSON的比較多。XML的比較少的仍是。
◆可讀性
JSON(Java Object Notation) 和XML的可讀性可謂不相上下,一邊是建議的語法,一邊是規範的標籤形式,很難分出勝負。
◆可擴展性
都具備很好的擴展性
◆編碼難度
XML有豐富的編碼工具,好比Dom4j、JDom等,JSON也有json.org提供的工具,可是JSON的編碼明顯比XML容易許多,即便不借助工具也能寫出JSON的代碼,但是要寫好XML就不太容易了。相對而言:JSON的編碼比較容易
◆解碼難度
XML的解析得考慮子節點父節點,而JSON的解析難度幾乎爲0
◆實例比較
XML和JSON都使用結構化方法來標記數據
與XML同樣,JSON也是基於文本的,且它們都使用Unicode編碼,一樣具備可讀性。XML比較適合於標記文檔,而JSON卻更適合於時行數據交換處理。
數據體積方面:json相對於xml來說,數據體積小,傳遞的速度跟快些
數據交互方面:json與JavaScript的交互更加方面,更容易解析處理,更好的數據交互
數據描述方面:xml對數據描述性比較好
傳輸速度方面:json的速度遠遠快於xml
JSON底層原理:遍歷字符串中的字符,最終根據格式規定的特殊字符,好比{}號,[]號, : 號 等進行區分,{}號是一個字典 的開始,[]號是一個數組的開始, : 號是字典的鍵和值的分水嶺,最終乃是將json數據轉化爲字典,字典中值多是字典,數 組,或字符串而已。
XML底層原理: XML解析經常使用的解析方法有兩種:DOM解析和SAX解析。DOM 採用創建樹形結構的方式訪問 XML 文檔,而 SAX 採用的事件模型。 。DOM 解析把 XML 文檔轉化爲一個包含其內容的樹,並能夠對樹進行遍歷。使用 DOM 解析器的時候需 要處理整個 XML 文檔,因此對性能和內存的要求比較高。SAX在解析 XML 文檔的時候能夠觸發一系列的事件,當發現給定的tag 的時候,它能夠激活一個回調方法,告訴該方法制定的標籤已經找到。SAX 對內存的要求一般會比較低,由於它讓開發人員本身來決 定所要處理的tag。特別是當開發人員只須要處理文檔中所包含的部分數據時,SAX 這種擴展能力獲得了更好的體現。 延伸:SAX與DOM的區別: 一、SAX處理的優勢很是相似於流媒體的優勢。分析可以當即開始,而不是等待全部的數據被處理。並且因爲應用程序只是 在讀取數據時檢查數據,所以不須要將數據存儲在內存中。這對於大型文檔來講是個巨大的優勢。事實上,應用程序甚至不 必解析整個文檔;它能夠在某個條件獲得 知足時中止解析。通常來講,SAX 還比它的替代者 DOM 快許多。另外一方面,由 於應用程序沒有以任何方式存儲數據,使用 SAX 來更改數據或在數據流中日後移是不可能的。
二、DOM 以及廣義的基於樹的處理具備幾個優勢。首先,因爲樹在內存中是持久的,所以能夠修改它以便應用程序能對數 據和結構做出更改。它還能夠在任什麼時候候在樹中上下 導航,而不是像 SAX 那樣是一次性的處理。DOM 使用起來也要簡單 得多。另外一方面,在內存中構造這樣的樹涉及大量的開銷。大型文件徹底佔用系統內存容量的狀況並不鮮見。此外,建立一 棵 DOM 樹多是一個緩慢的過程。
三、選擇 DOM 仍是選擇 SAX,這取決於下面幾個因素:
應用程序的目的:若是打算對數據做出更改並將它輸出爲 XML,那麼在大多數狀況下,DOM 是適當的選擇。並非說使 用 SAX 就不能更改數據,可是該過程要複雜得多,由於您必須對數據的一份拷貝而不是對數據自己做出更改。
數據容量: 對於大型文件,SAX 是更好的選擇。數據將如何使用:若是隻有數據中的少許部分會被使用,那麼使用 SAX 來將該部分數據提取到應用程序中可能更好。 另外一方面,若是您知道本身之後會回頭引用已處理過的大量信息,那麼 SAX 也許不是恰當的選擇。
對速度的須要:SAX 實現一般要比 DOM 實現更快。
SAX 和 DOM 不是相互排斥的,記住這點很重要。您能夠使用 DOM 來建立 SAX 事件流,也能夠使用 SAX 來建立 DOM 樹。事實上,用於建立 DOM 樹的大多數解析器實際上都使用 SAX 來完成這個任務!
解析 XML 一般有兩種方式,DOM 和 SAX:
•DOM解析XML時,讀入整個XML文檔並構建一個駐留內存的樹結構(節點樹),經過遍歷樹結構能夠檢索任意XML節點,讀取它的屬性和值。並且一般狀況下,能夠藉助XPath,直接查詢XML節點。
•SAX解析XML,是基於事件通知的模式,一邊讀取XML文檔一邊處理,沒必要等整個文檔加載完以後才採起操做,當在讀取解析過程當中遇到須要處理的對象,會發出通知對其進行處理。
通常在iOS平臺下,比較經常使用的XML解析類庫有以下幾種:
•NSXMLParser,這是一個SAX方式解析XML的類庫,默認包含在iOS SDK中,使用也比較簡單。
•libxml2,是一套默認包含在iOS SDK中的開源類庫,它是基於C語言的API,因此使用起來可能不如NSXML方便。這套類庫同時支持DOM和SAX解析,libxml2的SAX解析方式仍是很是酷的,由於它能夠邊讀取邊解析,尤爲是在從網上下載一個很大的XML文件,就能夠一邊下載一邊對已經下載好的內容進行解析,極大的提升解析效率。
GDataXML,這是一套Google開發的DOM方式XML解析類庫,支持讀取和修改XML文檔,支持XPath方式查詢。
•若是是讀取很小的XML文檔,性能基本上沒有什麼差異,不過從調用的方便性來講,建議使用TouchXML、KissXML或GDataXML
•若是是須要讀取和修改XML文檔,建議使用KissXML或GDataXML
•若是須要讀取很是大的XML文檔,則建議使用libxml2或TBXML
•若是你不想去調用第三方類庫,那麼使用NSXML也能夠
===========================================================
13.網絡編程 HTTP TCP/IP Socket ASI AFNetwork
從上往下1-7層
計算機網絡中五層協議分別是(從下向上):
1) 物理層
2)數據鏈路層
3)網絡層
4)傳輸層
5)應用層
其功能分別是:
1)物理層主要負責在物理線路上傳輸原始的二進制數據;
2)數據鏈路層主要負責在通訊的實體間創建數據鏈路鏈接;
3)網絡層主要負責建立邏輯鏈路,以及實現數據包的分片和重組,實現擁塞控制、網絡互連等功能;
4)傳輸曾負責向用戶提供端到端的通訊服務,實現流量控制以及差錯控制;
5)應用層爲應用程序提供了網絡服務。
通常來講,物理層和數據鏈路層是由計算機硬件(如網卡)實現的,網絡層和傳輸層由操做系統軟件實現,而應用層由應用程序或用戶建立實現。
TCP (Transmission Control Protocol)和UDP(User Datagram Protocol)協議屬於傳輸層協議。其中TCP提供IP環境下的數據可靠傳輸,它提供的服務包括數據流傳送、可靠性、有效流控、全雙工操做和多路復 用。經過面向鏈接、端到端和可靠的數據包發送。通俗說,它是事先爲所發送的數據開闢出鏈接好的通道,而後再進行數據發送;而UDP則不爲IP提供可靠性、 流控或差錯恢復功能。通常來講,TCP對應的是可靠性要求高的應用,而UDP對應的則是可靠性要求低、傳輸經濟的應用。TCP支持的應用協議主要 有:Telnet、FTP、SMTP等;UDP支持的應用層協議主要有:NFS(網絡文件系統)、SNMP(簡單網絡管理協議)、DNS(主域名稱系 統)、TFTP(通用文件傳輸協議)等.
TCP/IP協議與低層的數據鏈路層和物理層無關,這也是TCP/IP的重要特色
HTTP協議詳解
HTTP是一個屬於應用層的面向對象的協議,因爲其簡捷、快速的方式,適用於分佈式超媒體信息系統。目前在WWW中使用的是HTTP/1.0的第六版,HTTP/1.1的規範化工做正在進行之中。
http(超文本傳輸協議)是一個基於請求與響應模式的、無狀態的、應用層的協議,常基於TCP的鏈接方式,HTTP1.1版本中給出一種持續鏈接的機制,絕大多數的Web開發,都是構建在HTTP協議之上的Web應用。
HTTP協議的主要特色可歸納以下:
1.支持客戶/服務器模式。
2.簡單快速:客戶向服務器請求服務時,只需傳送請求方法和路徑。請求方法經常使用的有GET、HEAD、POST。每種方法規定了客戶與服務器聯繫的類型不一樣。因爲HTTP協議簡單,使得HTTP服務器的程序規模小,於是通訊速度很快。
3.靈活:HTTP容許傳輸任意類型的數據對象。正在傳輸的類型由Content-Type加以標記。
4.無鏈接:無鏈接的含義是限制每次鏈接只處理一個請求。服務器處理完客戶的請求,並收到客戶的應答後,即斷開鏈接。採用這種方式能夠節省傳輸時間。
5.無狀態:HTTP協議是無狀態協議。無狀態是指協議對於事務處理沒有記憶能力。缺乏狀態意味着若是後續處理須要前面的信息,則它必須重傳,這樣可能致使每次鏈接傳送的數據量增大。另外一方面,在服務器不須要先前信息時它的應答就較快。
URL
HTTP URL (URL統一資源定位符是一種特殊類型的URI是他的子類,包含了用於查找某個資源的足夠的信息)的格式以下:
錯誤!超連接引用無效。]
http表示要經過HTTP協議來定位網絡資源;host表示合法的Internet主機域名或者IP地址;port指定一個端口號,爲空則使用缺省端口80;abs_path指定請求資源的URI;若是URL中沒有給出abs_path,那麼當它做爲請求URI時,必須以「/」的形式給出,一般這個工做瀏覽器自動幫咱們完成。
#########################################################
TCP/UDP區別聯繫
TCP---傳輸控制協議,提供的是面向鏈接、可靠的字節流服務。當客戶和服務器彼此交換數據前,必須先在雙方之間創建一個TCP鏈接,以後才能傳輸數據。TCP提供超時重發,丟棄重複數據,檢驗數據,流量控制等功能,保證數據能從一端傳到另外一端。
UDP---用戶數據報協議,是一個簡單的面向數據報的運輸層協議。UDP不提供可靠性,它只是把應用程序傳給IP層的數據報發送出去,可是並不能保證它們能到達目的地。因爲UDP在傳輸數據報前不用在客戶和服務器之間創建一個鏈接,且沒有超時重發等機制,故而傳輸速度很快
TCP(Transmission Control Protocol,傳輸控制協議)是基於鏈接的協議,也就是說,在正式收發數據前,必須和對方創建可靠的鏈接。一個TCP鏈接必需要通過三次「對話」才能創建起來,咱們來看看這三次對話的簡單過程:1.主機A向主機B發出鏈接請求數據包;2.主機B向主機A發送贊成鏈接和要求同步(同步就是兩臺主機一個在發送,一個在接收,協調工做)的數據包;3.主機A再發出一個數據包確認主機B的要求同步:「我如今就發,你接着吧!」,這是第三次對話。三次「對話」的目的是使數據包的發送和接收同步,通過三次「對話」以後,主機A才向主機B正式發送數據。
UDP(User Data Protocol,用戶數據報協議)是與TCP相對應的協議。它是面向非鏈接的協議,它不與對方創建鏈接,而是直接就把數據包發送過去! UDP適用於一次只傳送少許數據、對可靠性要求不高的應用環境。
socket鏈接和http鏈接的區別
簡單說,你瀏覽的網頁(網址以http://開頭)都是http協議傳輸到你的瀏覽器的, 而http是基於socket之上的。socket是一套完成tcp,udp協議的接口。
HTTP協議:簡單對象訪問協議,對應於應用層 ,HTTP協議是基於TCP鏈接的
tcp協議: 對應於傳輸層
ip協議: 對應於網絡層
TCP/IP是傳輸層協議,主要解決數據如何在網絡中傳輸;而HTTP是應用層協議,主要解決如何包裝數據。
Socket是對TCP/IP協議的封裝,Socket自己並非協議,而是一個調用接口(API),經過Socket,咱們才能使用TCP/IP協議。
http鏈接:http鏈接就是所謂的短鏈接,即客戶端向服務器端發送一次請求,服務器端響應後鏈接即會斷掉;
socket鏈接:socket鏈接就是所謂的長鏈接,理論上客戶端和服務器端一旦創建起鏈接將不會主動斷掉;可是因爲各類環境因素可能會是鏈接斷開,好比說:服務器端或客戶端主機down了,網絡故障,或者二者之間長時間沒有數據傳輸,網絡防火牆可能會斷開該鏈接以釋放網絡資源。因此當一個socket鏈接中沒有數據的傳輸,那麼爲了維持鏈接須要發送心跳消息~~具體心跳消息格式是開發者本身定義的
咱們已經知道網絡中的進程是經過socket來通訊的,那什麼是socket呢?socket起源於Unix,而Unix/Linux基本哲學之一就是「一切皆文件」,均可以用「打開open –>讀寫write/read –> 關閉close」模式來操做。個人理解就是Socket就是該模式的一個實現,socket便是一種特殊的文件,一些socket函數就是對其進行的操做(讀/寫IO、打開、關閉),這些函數咱們在後面進行介紹。
Socket鏈接與HTTP鏈接
咱們在傳輸數據時,能夠只使用(傳輸層)TCP/IP協議,可是那樣的話,若是沒有應用層,便沒法識別數據內容,若是想要使傳輸的數據有意義,則必須使用到應用層協議,應用層協議有不少,好比HTTP、FTP、TELNET等,也能夠本身定義應用層協議。WEB使用HTTP協議做應用層協議,以封裝HTTP文本信息,而後使用TCP/IP作傳輸層協議將它發到網絡上。
1)Socket是一個針對TCP和UDP編程的接口,你能夠藉助它創建TCP鏈接等等。而TCP和UDP協議屬於傳輸層 。
而http是個應用層的協議,它實際上也創建在TCP協議之上。
(HTTP是轎車,提供了封裝或者顯示數據的具體形式;Socket是發動機,提供了網絡通訊的能力。)
2)Socket是對TCP/IP協議的封裝,Socket自己並非協議,而是一個調用接口(API),經過Socket,咱們才能使用TCP/IP協議。Socket的出現只是使得程序員更方便地使用TCP/IP協議棧而已,是對TCP/IP協議的抽象,從而造成了咱們知道的一些最基本的函數接口。
下面是一些的重要的概念,特在此作摘抄和總結。
一。什麼是TCP鏈接的三次握手
第一次握手:客戶端發送syn包(syn=j)到服務器,並進入SYN_SEND狀態,等待服務器確認;
第二次握手:服務器收到syn包,必須確認客戶的SYN(ack=j+1),同時本身也發送一個SYN包(syn=k),即SYN+ACK包,此時服務器進入SYN_RECV狀態;
第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=k+1),此包發送完畢,客戶端和服務器進入ESTABLISHED狀態,完成三次握手。
握手過程當中傳送的包裏不包含數據,三次握手完畢後,客戶端與服務器才正式開始傳送數據。理想狀態下,TCP鏈接一旦創建,在通訊雙方中的任何一方主動關閉鏈接以前,TCP 鏈接都將被一直保持下去。斷開鏈接時服務器和客戶端都可以主動發起斷開TCP鏈接的請求,斷開過程須要通過「四次握手」(過程就不細寫了,就是服務器和客戶端交互,最終肯定斷開)
二。利用Socket創建網絡鏈接的步驟
創建Socket鏈接至少須要一對套接字,其中一個運行於客戶端,稱爲ClientSocket ,另外一個運行於服務器端,稱爲ServerSocket 。
套接字之間的鏈接過程分爲三個步驟:服務器監聽,客戶端請求,鏈接確認。
1。服務器監聽:服務器端套接字並不定位具體的客戶端套接字,而是處於等待鏈接的狀態,實時監控網絡狀態,等待客戶端的鏈接請求。
2。客戶端請求:指客戶端的套接字提出鏈接請求,要鏈接的目標是服務器端的套接字。爲此,客戶端的套接字必須首先描述它要鏈接的服務器的套接字,指出服務器端套接字的地址和端口號,而後就向服務器端套接字提出鏈接請求。
3。鏈接確認:當服務器端套接字監聽到或者說接收到客戶端套接字的鏈接請求時,就響應客戶端套接字的請求,創建一個新的線程,把服務器端套接字的描述發給客戶端,一旦客戶端確認了此描述,雙方就正式創建鏈接。而服務器端套接字繼續處於監聽狀態,繼續接收其餘客戶端套接字的鏈接請求。
三。HTTP連接的特色
HTTP協議即超文本傳送協議(Hypertext Transfer Protocol ),是Web聯網的基礎,也是手機聯網經常使用的協議之一,HTTP協議是創建在TCP協議之上的一種應用。
HTTP鏈接最顯著的特色是客戶端發送的每次請求都須要服務器回送響應,在請求結束後,會主動釋放鏈接。從創建鏈接到關閉鏈接的過程稱爲「一次鏈接」。
四。TCP和UDP的區別
1。TCP是面向連接的,雖說網絡的不安全不穩定特性決定了多少次握手都不能保證鏈接的可靠性,但TCP的三次握手在最低限度上(實際上也很大程度上保證了)保證了鏈接的可靠性;而UDP不是面向鏈接的,UDP傳送數據前並不與對方創建鏈接,對接收到的數據也不發送確認信號,發送端不知道數據是否會正確接收,固然也不用重發,因此說UDP是無鏈接的、不可靠的一種數據傳輸協議。
2。也正因爲1所說的特色,使得UDP的開銷更小數據傳輸速率更高,由於沒必要進行收發數據的確認,因此UDP的實時性更好。
tcp協議和udp協議的差異
是否鏈接面向鏈接面向非鏈接
傳輸可靠性可靠不可靠
應用場合傳輸大量數據少許數據
速度慢快
http和scoket通訊的區別?socket鏈接相關庫,TCP,UDP的鏈接方法,HTTP的幾種經常使用方式? http和scoket通訊的區別: http是客戶端用http協議進行請求,發送請求時候須要封裝http請求頭,並綁定請求的數據,服務器通常有web服務器配 合(固然也非絕對)。 http請求方式爲客戶端主動發起請求,服務器才能給響應,一次請求完畢後則斷開鏈接,以節省資 源。服務器不能主動給客戶端響應(除非採起http長鏈接技術)。iphone主要使用類是NSUrlConnection。 scoket是客戶端跟服務器直接使用socket「套接字」進行鏈接,並無規定鏈接後斷開,因此客戶端和服務器能夠保持連 接通道,雙方均可以主動發送數據。通常在遊戲開發或股票開發這種要求即時性很強而且保持發送數據量比較大的場合使 用。主要使用類是CFSocketRef。 UDP:是用戶數據報協議:主要用在實時性要求高以及對質量相對較弱的地方,但面對如今高質量的線路不是容易丟包除非 是一些擁塞條件下 ,如流媒體 TCP:是傳輸控制協議:是面鏈接的,那麼運行環境必然要求其可靠性不可丟包有良好的擁塞控制機制如http ftp telnet 等
你鏈接服務器用的是什麼方法,若是請求過程當中,網絡出了問題這麼辦?
NSUrlConnection 鏈接後,有一系列委託方法來接受來自服務器的響應和數據,其中接受相應的方法回獲得服務器要傳 回的數據有多大,接受數據的方法會反覆調用來不斷接受服務器數據,若是網絡出了問題了,會調用一個方法讓你來作相關 處理。
===========================================================
14.AppStore上傳 及遠程推送
AppStrore 審覈不經過
app的設置界面、按鈕使用了相似iphone的操做方式以及icon的圓角設計 -> 從新設計⋯
app的年齡設置過低 -> 改了年齡⋯
app裏有實物獎勵 -> 免責聲明,和蘋果無關⋯
app描述裏提了後續版本的功能的字樣 -> 刪除⋯
app有打分的功能 -> 有reject的,也有經過的⋯
app須要使用location,沒有提示用戶 -> 加了提示,容許用戶拒絕⋯
app沒提供測試帳號 -> 提供⋯
app不支持64位
app裏有私有api -> 修改⋯ 應用內含有有某公司LOGO的圖片,沒有該公司受權文件,被拒-> 修改⋯
第三方靜態庫包含私有api的調用(聯繫第三方技術支持,更新靜態庫); 包含潛在的色情,暴力等內容(調整應用年齡限制等級,並加入舉報功能) 作瀏覽器的,分級必須選17+
什麼是push?遠程推送?
第一步:UIApplication向APNS註冊push notification服務
一、應用程序 要支持 推送服務(在網頁裏配置) (1)https://developer.
apple.com/devcenter/ios/index.action
(2)登陸 蘋果開發者帳號(注意是收費帳號,$99或$299) 3)下載push證書(主要是給程序簽名,push服務只有收費開發者才具有。因此須要簽名驗證),若是沒有 push證書,建立一個push證書(App ID->鑰匙串程序生成request->push證書)注意事項:App ID的 Bundle ID必須和程序plist文件裏的Bundle identifier一致。App ID一旦生成,將不可修改。 (4)把證書安裝到鑰匙串裏(雙擊證書文件)
(5)生成 編譯程序 用的描述文件(網頁裏進行) 二、向APNS註冊push服務(UIApplication的registerForRemoteNotificationTypes:方法)
第二步 獲取APNS分配的DeviceToken(64位16進制串)
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
第三步 把DeviceToken發送給本身的後臺服務器,服務器記錄每臺設備的DeviceToken以便往後推送信息給客 戶端。(須要有一個網絡接口,讓客戶端發送DeviceToken)
第四步 服務器推送信息給客戶端 一、服務器除了須要有客戶端的DeviceToken以外,還要有push證書,對push的內容進行簽名。(蘋果爲了防 止 惡意向客戶端(好比DeviceToken泄露了)發送消息,每次推送消息,都須要證書進行簽名,從而避免黑客惡 意攻擊用戶手機。) 二、若是你的服務器是java寫的,能夠直接使用鑰匙串導出的p12文件(證書和密鑰一塊兒導出)。若是你的服務器 是php寫的,由於php語言不支持p12文件類型,須要轉換爲pem文件。
三、將p12轉換爲pem文件:終端 先找到你p12所在的目錄 openssl pkcs12 -in CertificateName.p12 - outCertificateName.pem -nodes
四、服務器發送信息給APNS,APNS自動將信息推送給客戶端
第五步 客戶端處理收到的信息
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
一、測試版的push證書僅僅用於開發期間測試,發佈版的程序須要生成一個發佈版的push證書。 二、測試版APNS的ssl地址和發佈版的ssl地址不一樣 三、測試版的DeviceToken和發佈版的DeviceToken也不一樣
四、php文件要喝pem文件放在同一目錄。
五、除了alert sound和badge以外,json串裏還能夠包含自定義信息。
六、推送的信息最大255字節 七、推送的信息受網絡影響較大,有可能形成延遲甚至丟失,重要信息的傳遞不該該使用push通知,應該有專門的 後臺接口。
八、藉助push推送,兩個客戶端能夠實現即時通訊,工程裏面存放咱們的p12文件,客戶端本身組織json串,發 送請求到APNS。
===========================================================
15.XMPP簡介
XMPP(Extensible Messaging and Presence Protocol,前稱)是一種以XML爲基礎的開放式實時通訊協議,是 經由互聯網工程工做小組(IETF)經過的互聯網標準。簡單的說,XMPP就是一種協議,一種規定。就是說,在網絡上傳 東西,要創建鏈接,TCP/IP鏈接,創建後再傳東西,而XMPP就是規定你傳的東西的格式。XMPP是基於XML的協議。 優勢
開放:
XMPP協議是自由、開放、公開的,而且易於瞭解。 並且在客戶端 、 服務器 、 組件 、 源碼庫等方面,都已經各自有多種實現。 標準:
分散式:
XMPP網絡的架構和電子郵件十分相像;XMPP核心協議通訊方式是先建立一個stream,XMPP以TCP傳遞XML數據流,沒有 中央主服務器。 任何人均可以運行本身的XMPP服務器,使我的及組織可以掌控他們的實時傳訊體驗。
安全:
任何XMPP協議的服務器能夠獨立於公衆XMPP網絡(例如在企業內部網絡中),而使用SASL及TLS等技術的可靠安全性,已自 帶於核心XMPP技術規格中。
可擴展:
XML 命名空間的威力可以使任何人在覈心協議的基礎上建造定製化的功能;爲了維持通透性,常見的擴展由XMPP標準基金會 。 彈性佳:
XMPP除了可用在實時通訊的應用程序,還能用在網絡管理、內容供稿、協同工具、文件共享、遊戲、遠程系統監控等。 多樣性:
用XMPP協議來建造及佈署實時應用程序及服務的公司及開放源代碼計劃分佈在各類領域;用XMPP技術開發軟件,資源及支持的 來源是多樣的,使得使你不會陷於被「綁架」的困境。
缺點
數據負載過重:
隨着一般超過70%的XMPP協議的服務器的數據流量的存在和近60%的被重複轉發,XMPP協議目前擁有一個大型架空中存在的 數據提供給多個收件人。 新的議定書正在研究,以減輕這一問題。
沒有二進制數據:
XMPP協議的方式被編碼爲一個單一的長的XML文件,所以沒法提供修改二進制數據。 所以, 文件傳輸協議同樣使用外部的 HTTP。 若是不可避免,XMPP協議還提供了帶編碼的文件傳輸的全部數據使用的Base64 。 至於其餘二進制數據加密會話 (encrypted conversations)或圖形圖標(graphic icons)以嵌入式使用相同的方法。
===========================================================
函數 經過函數名調用;
block 匿名函數 C++中的閉包 蘭木達
int (int x,int y); 沒有名稱額函數
a =(10,8)—>a=8, 逗號表達式取逗號後面的
匿名函數
函數指針
int (*)(int,int)=sum;
Typedef int (* NewType)(int x,int y)NewType;
int (^block)(int,int)=^int (int x,int y){
return x+y;
};
//類型 int(^)(int ,int);
//變量名block1
//初始值^int (int x,int y){ return x+y; } 函數的定義(實現),去掉函數名
局部變量
block是語法//block 購買
返回值(^變量名)(參數列表)=^返回值(參數列表){ //變量名更名 參數列表以,隔開 ,返回值可省;
實現代碼;
};
申明
//—————————————————————————————————————————————
//給block賦值的特色,類型一致,以^託字符 插入箭頭開頭 函數的定義(實現),去掉函數名;後面是賦的值;沒法調用;
// int x= block1(10,20); //接收一下
int y= block2(10,20);
NSLog(@"%d",y);
block2 =^int(int x,int y)//返回值int 能夠刪除,
{
return x-y;
};
//(BOOL)(^)(NSString *,NSString *) 類型
int(^blockSum)(NSString *str1,NSString *str2)//定義(^)加上函數實現
=^int (NSString *str1,NSString *str2){ //int 能夠刪除,但必須保證return後面的類型一致;
return [str1 intValue]+[str2 intValue];//intValue把字符串變成字符
};
int result= blockSum(@"100",@"150");
NSLog(@"%d",result);
int(^aa)()=^(){
return 10;
};
// ^後面的()能夠刪除,當block後面沒有參數的時候,能夠吧()也省略掉;
NSLog(@"%d",aa());
void(^bb)()=^{NSLog(@"hello lanou");}; //分號不能少;
bb();//調用^後面的
// NSString *str1=@"adsj";
// NSString *str2=@"sada";字符串比較
NSComparisonResult (^aaa)(NSString *str1,NSString*str2 )=^(NSString *str1,NSString *str2){
return [str1 compare:str2];//字符串比較函數campare 等號後面^後面()不能漏
};
NSLog(@"%ld",aaa(@"hao",@"jian"));
//block能夠使用外部變量,可是不能修改;若是想改值用__block,兩個下劃線
__block int m=100;//static修飾符
int (^block)(int,int)=^(int x,int y){
a=100;//能夠修改外部變量;
return x+y+(++m)-a;
};
NSLog(@"%d",block(20,30));
//__block至關於static,靜態區,一直存在,直到程序結束.
1,block 在實現時就會對它引用到的它所在方法中定義的棧變量進行一次只讀拷貝,而後在 block 塊內使用該只讀拷貝。
以下代碼:
- - (void)testAccessVariable
- {
- NSInteger outsideVariable = 10;
- //__block NSInteger outsideVariable = 10;
- NSMutableArray * outsideArray = [[NSMutableArray alloc] init];
-
- void (^blockObject)(void) = ^(void){
- NSInteger insideVariable = 20;
- KSLog(@" > member variable = %d", self.memberVariable);
- KSLog(@" > outside variable = %d", outsideVariable);
- KSLog(@" > inside variable = %d", insideVariable);
-
- [outsideArray addObject:@"AddedInsideBlock"];
- };
-
- outsideVariable = 30;
- self.memberVariable = 30;
-
- blockObject();
-
- KSLog(@" > %d items in outsideArray", [outsideArray count]);
- }
輸出結果爲:
- > member variable = 30
- > outside variable = 10
- > inside variable = 20
- > 1 items in outsideArray
注意到沒?outside 變量的輸出值爲10,雖然outside變量在定義 block 以後在定義 block 所在的方法 testAccessVariable 中被修改成 20 了。這裏的規則就是:blockObject 在實現時會對 outside 變量進行只讀拷貝,在 block 塊內使用該只讀拷貝。所以這裏輸出的是拷貝時的變量值 10。若是,咱們想要讓 blockObject 修改或同步使用 outside 變量就須要用 __block 來修飾 outside 變量。
__block NSInteger outsideVariable = 10;
注意:
a),在上面的 block 中,咱們往 outsideArray 數組中添加了值,但並未修改 outsideArray 自身,這是容許的,由於拷貝的是 outsideArray 自身。
b),對於 static 變量,全局變量,在 block 中是有讀寫權限的,由於在 block 的內部實現中,拷貝的是指向這些變量的指針。
c), __block 變量的內部實現要複雜許多,__block 變量實際上是一個結構體對象,拷貝的是指向該結構體對象的指針。
2,非內聯(inline) block 不能直接訪問 self,只能經過將 self 看成參數傳遞到 block 中才能使用,而且此時的 self 只能經過 setter 或 getter 方法訪問其屬性,不能使用句點式方法。但內聯 block 不受此限制。
- typedef NSString* (^IntToStringConverter)(id self, NSInteger paramInteger);
- - (NSString *) convertIntToString:(NSInteger)paramInteger
- usingBlockObject:(IntToStringConverter)paramBlockObject
- {
- return paramBlockObject(self, paramInteger);
- }
-
- typedef NSString* (^IntToStringInlineConverter)(NSInteger paramInteger);
- - (NSString *) convertIntToStringInline:(NSInteger)paramInteger
- usingBlockObject:(IntToStringInlineConverter)paramBlockObject
- {
- return paramBlockObject(paramInteger);
- }
-
- IntToStringConverter independentBlockObject = ^(id self, NSInteger paramInteger) {
- KSLog(@" >> self %@, memberVariable %d", self, [self memberVariable]);
-
- NSString *result = [NSString stringWithFormat:@"%d", paramInteger];
- KSLog(@" >> independentBlockObject %@", result);
- return result;
- };
-
- - (void)testAccessSelf
- {
- // Independent
- //
- [self convertIntToString:20 usingBlockObject:independentBlockObject];
-
- // Inline
- //
- IntToStringInlineConverter inlineBlockObject = ^(NSInteger paramInteger) {
- KSLog(@" >> self %@, memberVariable %d", self, self.memberVariable);
-
- NSString *result = [NSString stringWithFormat:@"%d", paramInteger];
- KSLog(@" >> inlineBlockObject %@", result);
- return result;
- };
- [self convertIntToStringInline:20 usingBlockObject:inlineBlockObject];
- }
3,使用 weak–strong dance 技術來避免循環引用
在第二條中,我提到內聯 block 能夠直接引用 self,可是要很是當心地在 block 中引用 self。由於在一些內聯 block 引用 self,可能會致使循環引用。以下例所示:
- @interface KSViewController ()
- {
- id _observer;
- }
-
- @end
-
- @implementation KSViewController
-
- - (void)viewDidLoad
- {
- [super viewDidLoad];
- // Do any additional setup after loading the view, typically from a nib.
-
- KSTester * tester = [[KSTester alloc] init];
- [tester run];
-
- _observer = [[NSNotificationCenter defaultCenter]
- addObserverForName:@"TestNotificationKey"
- object:nil queue:nil usingBlock:^(NSNotification *n) {
- NSLog(@"%@", self);
- }];
- }
-
- - (void)dealloc
- {
- if (_observer) {
- [[NSNotificationCenter defaultCenter] removeObserver:_observer];
- }
- }
在上面代碼中,咱們添加向通知中心註冊了一個觀察者,而後在 dealloc 時解除該註冊,一切看起來正常。但這裏有兩個問題:
a) 在消息通知 block 中引用到了 self,在這裏 self 對象被 block retain,而 _observer 又 retain 該 block的一份拷貝,通知中心又持有 _observer。所以只要 _observer 對象尚未被解除註冊,block 就會一直被通知中心持有,從而 self 就不會被釋放,其 dealloc 就不會被調用。而咱們卻又指望在 dealloc 中經過 removeObserver 來解除註冊以消除通知中心對 _observer/block 的 retain。
b) 同時,_observer 是在 self 所在類中定義賦值,所以是被 self retain 的,這樣就造成了循環引用。
上面的過程 a) 值得深刻分析一下:
蘋果官方文檔中對 addObserverForName:object:queue:usingBlock: 中的 block 變量說明以下:
The block is copied by the notification center and (the copy) held until the observer registration is removed.
所以,通知中心會拷貝 block 並持有該拷貝直到解除 _observer 的註冊。在 ARC 中,在被拷貝的 block 中不管是直接引用 self 仍是經過引用 self 的成員變量間接引用 self,該 block 都會 retain self。
這兩個問題,能夠用 weak–strong dance 技術來解決。該技術在 WWDC 中介紹過:2011 WWDC Session #322 (Objective-C Advancements in Depth)
- __weak KSViewController * wself = self;
- _observer = [[NSNotificationCenter defaultCenter]
- addObserverForName:@"TestNotificationKey"
- object:nil queue:nil usingBlock:^(NSNotification *n) {
- KSViewController * sself = wself;
- if (sself) {
- NSLog(@"%@", sself);
- }
- else {
- NSLog(@"<self> dealloc before we could run this code.");
- }
- }];
下面來分析爲何該手法可以起做用。
首先,在 block 以前定義對 self 的一個弱引用 wself,由於是弱引用,因此當 self 被釋放時 wself 會變爲 nil;而後在 block 中引用該弱應用,考慮到多線程狀況,經過使用強引用 sself 來引用該弱引用,這時若是 self 不爲 nil 就會 retain self,以防止在後面的使用過程當中 self 被釋放;而後在以後的 block 塊中使用該強引用 sself,注意在使用前要對 sself 進行了 nil 檢測,由於多線程環境下在用弱引用 wself 對強引用 sself 賦值時,弱引用 wself 可能已經爲 nil 了。
經過這種手法,block 就不會持有 self 的引用,從而打破了循環引用。
擴展:其餘還須要注意避免循環引用的地方
與此相似的狀況還有 NSTimer。蘋果官方文檔中提到"Note in particular that run loops retain their timers, so you can release a timer after you have added it to a run loop.",同時在對接口
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)seconds target:(id)target selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)repeats
的 target 說明文檔中提到:
The object to which to send the message specified by aSelector when the timer fires. The target object is retained by the timer and released when the timer is invalidated.
結合這兩處文檔說明,咱們就知道只要重複性 timer 尚未被 invalidated,target 對象就會被一直持有而不會被釋放。所以當你使用 self 看成 target 時,你就不能指望在 dealloc 中 invalidate timer,由於在 timer 沒有被invalidate 以前,dealloc 毫不會被調用。所以,須要找個合適的時機和地方來 invalidate timer,但毫不是在 dealloc 中。
4,block 內存管理分析
block 其實也是一個 NSObject 對象,而且在大多數狀況下,block 是分配在棧上面的,只有當 block 被定義爲全局變量或 block 塊中沒有引用任何 automatic 變量時,block 才分配在全局數據段上。 __block 變量也是分配在棧上面的。
在 ARC 下,編譯器會自動檢測爲咱們處理了 block 的大部份內存管理,但當將 block 看成方法參數時候,編譯器不會自動檢測,須要咱們手動拷貝該 block 對象。幸運的是,Cocoa 庫中的大部分名稱中包含」usingBlock「的接口以及 GCD 接口在其接口內部已經進行了拷貝操做,不須要咱們再手動處理了。但除此以外的狀況,就須要咱們手動干預了。
- - (id) getBlockArray
- {
- int val = 10;
- return [[NSArray alloc] initWithObjects:
- ^{ KSLog(@" > block 0:%d", val); }, // block on the stack
- ^{ KSLog(@" > block 1:%d", val); }, // block on the stack
- nil];
-
- // return [[NSArray alloc] initWithObjects:
- // [^{ KSLog(@" > block 0:%d", val); } copy], // block copy to heap
- // [^{ KSLog(@" > block 1:%d", val); } copy], // block copy to heap
- // nil];
- }
-
- - (void)testManageBlockMemory
- {
- id obj = [self getBlockArray];
- typedef void (^BlockType)(void);
- BlockType blockObject = (BlockType)[obj objectAtIndex:0];
- blockObject();
- }
執行上面的代碼中,在調用 testManageBlockMemory 時,程序會 crash 掉。由於從 getBlockArray 返回的 block 是分配在 stack 上的,但超出了定義 block 所在的做用域,block 就不在了。正確的作法(被屏蔽的那段代碼)是在將 block 添加到 NSArray 中時先 copy 到 heap 上,這樣就能夠在以後的使用中正常訪問。
在 ARC 下,對 block 變量進行 copy 始終是安全的,不管它是在棧上,仍是全局數據段,仍是已經拷貝到堆上。對棧上的 block 進行 copy 是將它拷貝到堆上;對全局數據段中的 block 進行 copy 不會有任何做用;對堆上的 block 進行 copy 只是增長它的引用記數。
若是棧上的 block 中引用了__block 類型的變量,在將該 block 拷貝到堆上時也會將 __block 變量拷貝到堆上若是該 __block 變量在堆上尚未對應的拷貝的話,不然就增長堆上對應的拷貝的引用記數