1.堆和棧的區(qū)別
棧疾宏,是由編譯器自動管理黍特,無需我們手工控制;
堆薪丁,釋放工作由程序員控制遇西,容易產生memory leak(內存泄漏)。
申請大醒鲜取:
棧:在Windows下,棧是向低地址擴展的數(shù)據(jù)結構粱檀,是一塊連續(xù)的內存的區(qū)域薄榛。
堆:堆是向高地址擴展的數(shù)據(jù)結構缤弦,是不連續(xù)的內存區(qū)域。
分配方式:
1. 堆都是動態(tài)分配的闻妓,沒有靜態(tài)分配的堆睦优。
2. 棧有2種分配方式:靜態(tài)分配和動態(tài)分配渗常。
2.死鎖問題
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"11111");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"22222");
});
NSLog(@"33333");
}
//死鎖原因
dispatch_sync
在等待block
語句執(zhí)行完成,而block
語句需要在主線程里執(zhí)行汗盘,所以dispatch_sync
如果在主線程調用就會造成死鎖dispatch_sync
是同步的皱碘,本身就會阻塞當前線程,也即主線程衡未。而又往主線程里塞進去一個block
尸执,所以就會發(fā)生死鎖。
/**正確方法**/
//async 在主線程中 創(chuàng)建了一個異步線程 加入 全局并發(fā)隊列缓醋,async 不會等待block 執(zhí)行完成如失,立即返回
dispatch_async(dispatch_get_global_queue(), ^{
NSLog(@2);//不會造成死鎖;
});
分析這段代碼:view DidLoad
在主線程中送粱,也即dispatch_get_main_queue()
中褪贵,執(zhí)行到sync
時向dispatch_get_main_queue()
插入同步thread
,sync
會等到后面的block
執(zhí)行完成才返回抗俄。
sync
又在主隊列里面脆丁,是個串行隊列,sync
是后面才加入的动雹,前面一個是主線程槽卫,所以sync
想執(zhí)行block
必須等待前一個主線程執(zhí)行完成,而主線程卻在等待sync
返回胰蝠,去執(zhí)行后續(xù)工作歼培,從而造成死鎖震蒋。
注意: dispatch_sync 和 dispatch_async 區(qū)別:
dispatch_async(queue,block) async
異步隊列,dispatch_async
函數(shù)會立即返回, block
會在后臺異步執(zhí)行躲庄。
dispatch_sync(queue,block) sync
同步隊列查剖,dispatch_sync
函數(shù)不會立即返回,即阻塞當前線程,等待 block
同步執(zhí)行完成噪窘。
GCD Queue 分為三種:
1.The main queue:
主隊列笋庄,主線程就是在個隊列中。
2.Global queues:
全局并發(fā)隊列倔监。
3.用戶隊列:是用函數(shù)dispatch_queue_create
創(chuàng)建的自定義隊列
3.UIImage初始化方法的區(qū)別
- 方法一:
UIImage *image = [UIImage imageNamed:@"test.png"];
這個方法創(chuàng)建的圖片是從緩存里面獲取的直砂,先在緩存里查看,看是不是有這個圖片丐枉,沒有的話見圖片添加到緩存再使用哆键。有的話直接使用緩存里面的。在程序中瘦锹,如果這個圖片要在多個地方使用的話籍嘹,建議使用這個方法。缺點是:一旦加入到緩存中就一直占用內存弯院,不能被釋放掉辱士。
- 方法二:
//讀取本地圖片路徑
NSString *imagePath=[NSString stringWithFormat:@"%@/Documents/
%@.jpg",NSHomeDirectory(),@"test"];
[UIImage imageWithContentsOfFile:imagePath];
從手機本地讀取,比較第一種方式听绳,這個是直接加載圖片的颂碘,圖片不需要的時候,可以release掉椅挣。所以建議在使用重復率低的地方使用這種方法头岔。
- 方法三:
// 下面的這種方式會出現(xiàn)卡線程的情況,所以建議在子線程中操作
// imageWithData: data
NSURL *url = [NSURL URLWithString:@“http://e.picphotos.baidu.com/album/abc.jpg"];
UIImage *image2 = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
需要注意的是鼠证,如果imageWithData:是同步網(wǎng)絡請求峡竣,如果在主線程直接使用的話,會卡主線程量九,因此一般不會在主線程中直接使用适掰,而是采用異步網(wǎng)絡請求獲得data的值。
4. iOS中self.和下劃線的區(qū)別
1.首先通過self.xxx 通過訪問的方法的引用:包含了set和get方法荠列。而通過下劃線是獲取自己的實例變量类浪,不包含set和get的方法。(回答面試官這一句就行了)
2.
self.xxx
是對屬性的訪問肌似;而_xxx
是對局部變量的訪問费就。所有被聲明為屬性的成員,再ios5之前需要使用編譯指令@synthesize
來告訴編譯器幫助生成屬性的getter和setter方法川队,之后這個指令可以不用認為的指定了力细,默認情況下編譯器會幫助我們生成垦搬。
編譯器在生成getter,setter方法時是有優(yōu)先級的艳汽,他首先查找當前的類中用戶是否定義屬性的getter,setter方法对雪,如果有河狐,則編譯器會跳過,不會再生成瑟捣,使用用戶定義的方法馋艺。也就是說你在使用self.xxx時是調用一個getter方法。
會使引用計數(shù)加一迈套,而_xxx不會使用引用技術加一的捐祠。
3.所有使用self.xxx是更好的選擇桑李,因為這樣可以兼容懶加載踱蛀,同時也避免了使用下滑線的時候忽略了self這個指針,后者容易在BLock中造成循環(huán)引用贵白。同時率拒,使用 _是獲取不到父類的屬性,因為它只是對局部變量的訪問禁荒。
最后總結:self方法實際上是用了get和set方法間接調用猬膨,下劃線方法是直接對變量操作。
5. 總結UITableViewCell重用機制
/*
UITableView內部定義了兩種數(shù)據(jù)結構:
NSMutableArray: visiableCells
NSMutableDictionary:reuseTableCells
其中 visiableCells 保存屏幕上可見的 cell ,而 reuseTableCells 保存可重用的 cells.
*/
NSString* cellIdentifier = @"cellid"
/*
1.在tableView顯示之初, reuseTableCells為空呛伴。
那么[tableView dequeueReusableCellWithIdentifier:cellIdentifier]返回nil勃痴。
*/
/*
2.開始時的cell都是通過
[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier]
來創(chuàng)建,而且cellForRowAtIndexPath:只是調用最大顯示cell數(shù)的次數(shù)。
比如:有100條數(shù)據(jù)热康,iPhone一屏最多顯示10個cell沛申。
程序最開始顯示TableView的情況是:
創(chuàng)建10次cell,并給cell指定同樣的重用標識(當然褐隆,可以為不同顯示類型的cell指定不同的標識)污它。
并且10個cell全部都加入到visiableCells數(shù)組,reusableTableCells為空庶弃。
*/
[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
//向下拖動tableView衫贬,當cell1完全移出屏幕,并且cell11(它也是alloc出來的歇攻,原因同上)完全顯示出來的時候固惯。
//cell11加入到visiableCells,cell1移出visiableCells缴守,cell1加入到reusableTableCells葬毫≌蚧裕
/*
3.接著向下拖動tableView,因為reusableTableCells中已經(jīng)有值,所以,當需要顯示新的cell,cellForRowAtIndexPath再次被調用的時候,
[tableView dequeueReusableCellWithIdentifier:CellIdentifier]返回cell1。
cell1加入到visiableCells,cell1移出reusableTableCells;
cell2移出visiableCells,cell2加入到reusableTableCells贴捡。
之后再需要顯示的Cell就可以正常重用了忽肛。
*/
所以整個過程并不難理解烂斋,但需要注意正是因為這樣的原因:配置Cell的時候一定要注意屹逛,對取出的重用的cell做重新賦值,不要遺留老數(shù)據(jù)汛骂。
6.iOS多線程的四種技術方案
-
如圖
14902857217308.png
7. 假設有一個字符串aabcad,請寫一段程序,去掉字符串中不相鄰的重復字符串罕模,即上述字符串處理之后的輸出結果為:aabcd
NSMutableString * str1 = [[NSMutableString alloc] initWithFormat:@"aabcad"];
for (int i = 0; i < str1.length - 1; i++) {
for (int j = i + 1; j < str1.length ; j++) {
// 由于字符的特殊性 無法使用 字符串 isEqualToString 進行比較 只能轉化為ASCII 值進行比較 所以 需要加 unsigined 修飾
unsigned char a = [str1 characterAtIndex:i];
unsigned char b = [str1 characterAtIndex:j];
if (a == b) {
if (j - i > 1) {
// NSRange: 截取字符串 {j, 1} j: 第一個字符開始 1: 截取幾個字符
NSRange range = {j, 1};
[str1 deleteCharactersInRange:range];
j = i--;
}
}
}
}
NSLog(@"------ %@-------", str1);
8. iOS中幾種數(shù)據(jù)持久化方案
- plist文件(屬性列表)
- preference(偏好設置)
- NSKeyedArchiver(歸檔)
- SQLite 3
- CoreData
假如你不熟練:面試官問常用哪種,就回答SQLite . 問詳細的話,就回答上GitHub上面找封裝好的工具類來實現(xiàn)存儲...
9.iOS傳參數(shù)的幾種方案
- 1.屬性傳值
UIViewController *B = [UIViewController new];
B.title = @"B的標題";
[A.navigationController pushViewController:B animated:YES];
通常用于正向傳值,適用于A和B相互具有一定關聯(lián)性帘瞭。不能用于隔頁面?zhèn)髦凳缯啤6遥枰獋髦档膶傩圆荒苁撬接袑傩缘睿簿褪钦f在.h中聲明出來的屬性才可以傳值抛腕。
- 2.Block傳值
使用場景:
常用于回調,簡單的說就是B有一個按鈕祸轮,當按鈕被點擊時把點擊事件傳傳給A兽埃,并傳一個字符串"B被點了"。
//首先在B控制器中聲明一個block,參數(shù)是一個字符串
@property (nonatomic,copy) void(^block)(NSString *title);
//傳值
- (void)buttonClick:(UIButton *)sender
{
self.block(@"B被點了");
}
//回調代碼塊
BController *B = [BController new];
B.block = ^(NSString *title) {
//do someThing
A.title = title;
};
[A.navigationController pushViewController:B animated:YES];
同樣的Block在這里作為屬性存在适袜,同屬性傳值一樣柄错,需要兩個控制器間具有一定關聯(lián)性。不能跨頁面?zhèn)髦怠?br> 如果一定要跨苦酱,就要像接力賽一樣售貌,A傳給B,B傳給C這樣
3.代理傳值
代理傳值和block傳值相似疫萤,都是將事件分發(fā)出去颂跨。但是與block的不同在于,代理具有松耦合性扯饶,誰想處理事件成為代理即可恒削。4.通知中心傳值
上面說的代理屬于一對一的關系,就好像一夫一妻制尾序。你有需求只能找你的代理(你老婆)钓丰。而通知中心屬于一對多的,就像村頭的喇叭一喊每币,全村人都能聽到携丁。5.單例傳值
iOS系統(tǒng)中常間的單例模式莫過于UIApplication、 NSNotificationCenter兰怠、 NSUserDefaults(常用)6.數(shù)據(jù)庫傳值
數(shù)據(jù)庫無非在于打開數(shù)據(jù)庫梦鉴、建表以及基于數(shù)據(jù)庫表的增刪改查操作
這里有一個唐巧大大的FMDB的demo自行學習吧demo點這里7.NSFileManager
跟數(shù)據(jù)庫類似李茫,只是將數(shù)據(jù)寫成文件保存在沙盒中。
需要注意的:
( 1 ) 文件路徑是否正確
( 2 ) 不能保存復雜對象
相關篇幅還是有點長度的,這里推薦個鏈接詳細了解點這里8.全局變量傳值
在某個文件的.m文件創(chuàng)建一個全局變量肥橙,其他文件只要引用該變量魄宏,即可對該變量值進行修改和使用。需要注意存筏,變量名要保證全局唯一娜庇。
10.iOS的APP實現(xiàn)相互調起和參數(shù)的傳值
一、首先為要跳轉的App,添加自定義URL協(xié)議的Schemes的id方篮,很多成熟的App都有固定的Schemes的id,下面再說励负,首先添加自定義URL協(xié)議藕溅,添加方法這里介紹兩種:
- 第一種:直接在Info.plist里面添加,如圖:
在這里,URL Schemes
的item
的值是APP跳轉過程中的key
,也就是自定義的url
協(xié)議向iphone注冊的key
,URL identifier
就相當于參數(shù)继榆,你可以跳轉到你的app的某一個具體功能頁面巾表,甚至事件。這里也可以不填寫
- 第二種:可以直接在
info
的URL types
中添加如圖:
二略吨、實現(xiàn)跳轉的代碼集币,在這里使用openURL來實現(xiàn)APP之間的跳轉,隨著xcode的更新翠忠,目前需要添加白名單鞠苟,過程如下:
- 第一:添加白名單,在自己的的應用程序的的
info.plist
中添加LSApplicationQueriesSchemes
屬性,其類型為數(shù)組,然后在數(shù)組的下面添加要跳轉的app
的URL Schemes
的key
秽之。
添加白名單如圖:
14902870096220.png
- 第二:編寫代碼實現(xiàn)跳轉当娱,這里跳轉用的是
openurl
,跳轉的test02
的app
如圖:
14902870208780.jpg
無參數(shù)的打開url schemes
為test02
的app
代碼如下:
- (IBAction)skipOtherApp:(UIButton *)sender {
NSURL* open_URL_A = [NSURL URLWithString:@"test02://"];
//判斷是否是否有can打開應用程序考榨,如果成功就打開
if ([[UIApplication sharedApplication] canOpenURL:open_URL_A]) {
NSLog(@"可以打開");
[[UIApplication sharedApplication] openURL:open_URL_A];
}
}
三:app
在跳轉過程中的參數(shù)傳輸,當跳轉到url Schemes
為test02
時跨细,將指定的數(shù)據(jù)傳送過去,url Schemes
為test02
的程序在對數(shù)據(jù)處理(常用的參數(shù)傳輸為:test02
登錄數(shù)據(jù),test02
跳轉到指定界面等)
(1)參數(shù)傳遞
將上面的代碼:
NSURL* open_URL_A = [NSURL URLWithString:@"test02://"];
修改成為:
NSURL* open_URL_A = [NSURL URLWithString:@"test02://name=test01&password=123456"];
這樣test01
跳轉到test02
時的傳輸數(shù)據(jù)為"name=test01&password=123456"
,而在test02
中處理傳遞數(shù)值的位置為appdelegate.m
,在這里添加方法如下:
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation{
/*
*sour ceAppl i cat i on 從那個app跳轉的
*url 跳轉時,openurl中的數(shù)據(jù)
*str url 為 test02://name=test01&password=123456 然后對字符串處理
*/
NSString* str_url = [NSString stringWithlContentsOfURL:url
encoding:kCFStringEncodingUTF8
error:nil] ;
return YES;
}
test02
接受參數(shù)
(2)從自己的app
跳轉到AppStore
下載指定的app
,具體代碼如下:
NSString* urlString = @"itms://itunes.apple.com/gb/app/id391945719?mt=8";
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlString]];
其中id391945719
可以改為你指定的app
的id
河质,當然你也可以將指定app
的下載地址的https
改為itms
就可以了冀惭。
11.自動釋放池,原理以及如何工作的
- 1.什么是自動釋放池
自動釋放池autorelease pool
是OC
的一種內存自動回收機制.
當你向一個對象發(fā)送一個autorelease
消息的時候,cocoa
就會將對象的一個引用放入
到最新的自動釋放池中(當前線程棧頂位置),它任然是一個正當?shù)膶ο?因此自動釋放池
定義的作用域內的其他對象都可以向他發(fā)送消息. - 2.如何工作
objective-C
是通過一種referring counting(引用計數(shù))
的方式管理內存的
對象在開始分配內存alloc
的時候引用計數(shù)為1
,以后如果有copy
,retain
的時候
都會加1
,每當release
和autorelease
的時候引用計數(shù)就會減1
,如果一個對象的引
用計數(shù)為0
,就會被系統(tǒng)銷毀.
NSAutoreleasePool
就是用來做引用計數(shù)的管理工作的,這個東西一般不用你管的
autorelease
和release
沒什么區(qū)別,只是引用計數(shù)減1
的時機不同而已.autorelease
會在對象的使用真正結束的時候才做引用計數(shù)減1
. - 3.自動釋放池的實現(xiàn)原理
實現(xiàn)原理:自動釋放池以棧的形式實現(xiàn):當你創(chuàng)建一個新的自動釋放池時,它將被添加到
棧頂.當一個對象收到autorelease消息的時候,它被添加到當前線程的處于
棧頂?shù)牡淖詣俞尫懦刂?當自動釋放池被回收時,他們就從棧中被刪除,并且會
給池子里面的所有對象都會做一次release操作.