#### 講一下atomic的實(shí)現(xiàn)機(jī)制盈简;為什么不能保證絕對的線程安全(最好可以結(jié)合場景來說)?
? runtime里的源碼:
? getter方法
? ```
? id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
? ? if (offset == 0) {
? ? ? ? return object_getClass(self);
? ? }
? ? // Retain release world
? ? id *slot = (id*) ((char*)self + offset);
? ? if (!atomic) return *slot;
? ? // Atomic retain release world
? ? spinlock_t& slotlock = PropertyLocks[slot];
? ? slotlock.lock();
? ? id value = objc_retain(*slot);
? ? slotlock.unlock();
? ? // for performance, we (safely) issue the autorelease OUTSIDE of the spinlock.
? ? return objc_autoreleaseReturnValue(value);
}
? ```
? setter方法:
? ```
? static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
{
? ? if (offset == 0) {
? ? ? ? object_setClass(self, newValue);
? ? ? ? return;
? ? }
? ? id oldValue;
? ? id *slot = (id*) ((char*)self + offset);
? ? if (copy) {
? ? ? ? newValue = [newValue copyWithZone:nil];
? ? } else if (mutableCopy) {
? ? ? ? newValue = [newValue mutableCopyWithZone:nil];
? ? } else {
? ? ? ? if (*slot == newValue) return;
? ? ? ? newValue = objc_retain(newValue);
? ? }
? ? if (!atomic) {
? ? ? ? oldValue = *slot;
? ? ? ? *slot = newValue;
? ? } else {
? ? ? ? spinlock_t& slotlock = PropertyLocks[slot];
? ? ? ? slotlock.lock();
? ? ? ? oldValue = *slot;
? ? ? ? *slot = newValue;? ? ? ?
? ? ? ? slotlock.unlock();
? ? }
? ? objc_release(oldValue);
}
? ```
? 從上面可以看出彪腔,使用atomic之后,在setter和getter方法里會使用自旋鎖spinlock_t來保證setter方法和getter方法的線程的安全进栽,可以看做getter方法獲取到返回值之前不會執(zhí)行setter方法里的賦值代碼德挣。但是出了getter方法和setter方法后就不能保證線程安全了,舉個(gè)例子:
? ```
@property (atomic, assign)? ? int? ? ? intA;//初始值是0
//thread A
? ? self.intA = self.intA + 2 ;
//thread B
? ? self.intA = self.intA + 3;
? ```
? 上面的例子快毛,雖然settr和getter方法是線程安全的格嗅,但是 self.intA = self.intA + 2 ;這個(gè)語句不是線程安全的,因?yàn)樗梢苑譃槿齻€(gè)指令:
? - 從內(nèi)存中取出intA的值放到寄存器中唠帝;
? - 把寄存器中值加上某一個(gè)數(shù)屯掖;
? - 再把寄存器中得值放回內(nèi)存中;
來觀察一下AB兩個(gè)線程交錯(cuò)執(zhí)行會發(fā)生什么:
1. A線程:讀取intA的放到一個(gè)寄存器A(0);
2. B線程:讀取intA的放到一個(gè)寄存器B(0);
3. A線程:將寄存器A的值加2(2);
4. B線程:將寄存器B的值加3(3);
5. A線程:將寄存器A的值加協(xié)會到內(nèi)存襟衰,此時(shí)intA的值是2;
6. B線程:將寄存器B的寫回到內(nèi)存贴铜,此時(shí)intA的值是3;
我們預(yù)期的結(jié)果是5,但實(shí)際返回的結(jié)果可能是最后寫入內(nèi)存的那個(gè)值瀑晒。
再舉個(gè)栗子:
```
@property (atomic, strong) NSArray*? ? ? ? ? ? ? ? arr;
//thread A
for (int i = 0; i < 100000; i ++) {
? ? if (i % 2 == 0) {
? ? ? ? self.arr = @[@"1", @"2", @"3"];
? ? }
? ? else {
? ? ? ? self.arr = @[@"1"];
? ? }
? ? NSLog(@"Thread A: %@\n", self.arr);
}
//thread B
for (int i = 0; i < 100000; i ++) {
? ? if (self.arr.count >= 2) {
? ? ? ? NSString* str = [self.arr objectAtIndex:1];
? ? }
? ? NSLog(@"Thread B: %@\n", self.arr);
}
```
上面的例子線程B里面可能會因?yàn)閿?shù)組越界而引起crash绍坝,因?yàn)榧尤朐贐線程里判斷self.arr.count >= 2的時(shí)候數(shù)組是self.arr = @[@"1", @"2", @"3"];但是當(dāng)調(diào)用[self.arr objectAtIndex:1]可能self.arr的值已經(jīng)在線程A里被更改為了@[@"1"],此時(shí)數(shù)組越界了苔悦。因此轩褐,雖然self.arr是atomic的,還是會出現(xiàn)線程安全問題玖详。
1.什么情況下使用weak關(guān)鍵詞把介,weak和assign有什么不同?
在ARC里竹宋,為了避免出現(xiàn)循環(huán)引用時(shí)劳澄,要使用weak關(guān)鍵詞,比如delegate和block蜈七;
在xib中的IBOutlet屬性秒拔,由于已經(jīng)有了強(qiáng)引用,不需要再用stong關(guān)鍵詞飒硅;
weak修飾的屬性砂缩,在釋放時(shí)會置成nil,assign只是簡單地進(jìn)行賦值操作三娩;
weak只能用于OC對象庵芭,assign可用于非OC對象;
2.為什么NSString雀监、NSArray和NSDictionary使用copy修飾而不用strong双吆;
NSString眨唬、NSArray和NSDictionary有對應(yīng)的可變類型NSMutableString、NSMUtableArray和NSMutableDictionary好乐;當(dāng)我們給設(shè)置方法賦新知識匾竿,比如NSArray類型的對象A,如果傳遞的是其子類NSMutableArray類型的B蔚万,使用strong修飾的A話只是對B進(jìn)行了強(qiáng)引用岭妖,它們指向的是同一個(gè)對象,當(dāng)B發(fā)生變化時(shí)反璃,A其實(shí)也發(fā)生了變化昵慌;使用copy則會copy一份新的對象,B的改變不會影響到A淮蜈。
3.block為什么要用copy修飾斋攀?
在MRC里,block里面的對象是在棧區(qū)的礁芦,使用copy修飾不把他們copy到堆區(qū)仔拟;在ARC里咳胃,編譯器會自動進(jìn)行copy工作隐圾。
4.下面的代碼會有什么問題掂林?
// .h文件
@property (nonatomic, copy) NSMutableArray * mutableArray;
// .m文件
NSMutableArray*array = [NSMutableArray arrayWithObjects:@1,@2,nil];
self.mutableArray = array;
[self.mutableArray removeObjectAtIndex:0];
因?yàn)殛P(guān)鍵詞是copy絮缅,所以復(fù)制后self.mutableArray是不可變對象堡妒,在調(diào)用NSMutableArray的方法時(shí)會造成崩潰晶框。
5.如何使自定義的類支持copy
自定義的類需要遵守NSCopying協(xié)議蜕径,如果類分為可變版本和不可變版本析桥,需要遵守NSMutableCopying協(xié)議司草;
6.@property的本質(zhì)是什么?
@property = ivar(實(shí)例變量) + setter + getter
可以把屬性當(dāng)做一種關(guān)鍵字泡仗,編譯器會自動生成存取方法埋虹,并且向類中自動添加適當(dāng)?shù)膶?shí)例變量,并且在屬性名前添加下劃線娩怎。
7. @protocol 和 category 中如何使用 @property
在 protocol 中使用 property 只會生成 setter 和 getter 方法聲明,我們使用屬性的目的,是希望遵守我協(xié)議的對象能實(shí)現(xiàn)該屬性
category 使用 @property 也是只會生成 setter 和 getter 方法的聲明,如果我們真的需要給 category 增加屬性的實(shí)現(xiàn),需要借助于運(yùn)行時(shí)的兩個(gè)函數(shù):
objc_setAssociatedObject
objc_getAssociatedObject
8.@property中有那些關(guān)鍵字
原子性--nonatomic搔课,atomic;
讀寫權(quán)限--readonly(只讀)readwrite(讀寫)
內(nèi)存管理--assign截亦,weak爬泥,strong,copy unsafe_unretained
方法名--setter崩瓤,getter
9. ARC下袍啡,不顯式指定任何屬性關(guān)鍵字時(shí),默認(rèn)的關(guān)鍵字都有哪些却桶?
對應(yīng)基本數(shù)據(jù)類型默認(rèn)關(guān)鍵字是
atomic,readwrite,assign
對于普通的 Objective-C 對象
atomic,readwrite,strong
10.ASI和AFN的對比
http://www.infoq.com/cn/articles/afn_vs_asi
ASI是基于CFNetwork的境输, 而AFN是基于NSURL的,從性能上來說ASI要好一些。
AFN的推薦用是用一個(gè)公用的HTTPClient嗅剖,使用公用的URL辩越,把網(wǎng)絡(luò)請求就的參數(shù)傳遞到HTTPClient的靜態(tài)方法里,最后通過block回調(diào)把網(wǎng)絡(luò)請求的數(shù)據(jù)回調(diào)出來窗悯。
ASI的用法更傳統(tǒng)区匣,使用時(shí)要初始化一個(gè)ASIHTTPRequest實(shí)例,通過這個(gè)實(shí)例來配置網(wǎng)絡(luò)請求的參數(shù)蒋院,用代理或block進(jìn)行數(shù)據(jù)回調(diào)亏钩。
ASI的直接操作對象ASIHTTPRequest是NSOPeration的子類,在異步請求處理的時(shí)候欺旧,在調(diào)用startAsynchronous方法后會把對象放入到共享的操作隊(duì)列姑丑,所有的操作都是在這個(gè)對象所處的子線程中完成的。
11.網(wǎng)絡(luò)優(yōu)化:
1)減小數(shù)據(jù)請求大小辞友,對于post請求栅哀,Body可以做gzip壓縮;使用專門的算法對于音視頻称龙,圖片進(jìn)行壓縮留拾;
2)精簡數(shù)據(jù)格式,使用json替代xml鲫尊;
3)根據(jù)不同的設(shè)備返回不同分辨率的圖片痴柔;
4)緩存數(shù)據(jù),在一定有效的時(shí)間內(nèi)再次請求時(shí)直接取緩存疫向;
5)對較大的文件咳蔚,可以考慮多連接;
#####HTTP冪等性
HTTP方法的冪等性是指一次和多次請求某一個(gè)資源應(yīng)該具有同樣的副作用