本人參考GitHub《招聘一個(gè)靠譜的iOS》面試題參考答案(上)
1. 風(fēng)格糾錯(cuò)題

2. 什么情況使用 weak 關(guān)鍵字,相比 assign 有什么不同?
3. 怎么用 copy 關(guān)鍵字?
4. 這個(gè)寫法會(huì)出什么問題: @property (copy) NSMutableArray *array;
5. 如何讓自己的類用 copy 修飾符?如何重寫帶 copy 關(guān)鍵字的 setter?
1. 風(fēng)格糾錯(cuò)題

修改完的代碼:
typedef NS_ENUM(NSInteger, YZZYSex) {
YZZYMan,
YZZYSexWoman
};
@interface YZZYUser : NSObject<NSCopying>
@property (nonatomic, readonly, copy) NSString *name;
@property (nonatomic, readonly, assign) NSUInteger age;
@property (nonatomic, readonly, assign) YZZYSex sex;
- (instance)initWithName:(NSString *)name age:(NSUInteger)age sex:(YZZYSex)sex;
+ (instance)userWithName:(NSString *)name age:(NSUInteger)age sex:(YZZYSex)sex;
@end
2. 什么情況下使用weak關(guān)鍵字,相比assign有什么不同?
(1)什么情況下使用weak關(guān)鍵字?
1.在ARC中,在可能出現(xiàn)循環(huán)引用的時(shí)候,要通過讓一端使用weak來解決,比如:delegate代理屬性;
2.自身已經(jīng)對(duì)它經(jīng)行了一次強(qiáng)引用,沒有必要再強(qiáng)引用一次,此時(shí)也會(huì)使用weak,自定義IBOutlet控件屬性一般也使用weak;(ViewController強(qiáng)引用view,view強(qiáng)引用了其他UI控件)
(2)不同點(diǎn):
1.weak特質(zhì)表明該屬性定義了一種“非擁有關(guān)系”。為這種屬性設(shè)置新值時(shí),設(shè)置方法既不保留新值,也不釋放舊值,此特質(zhì)與assign類似,然而在屬性所指的對(duì)象遭到摧毀時(shí),屬性值也會(huì)清空(nil out)。而assign的“設(shè)置方法”只會(huì)執(zhí)行對(duì)“純量類型”(如CGFloat或NSInteger等)的簡(jiǎn)單賦值操作,assign修飾對(duì)象類型時(shí),當(dāng)對(duì)象銷毀時(shí)其屬性值不會(huì)清空。
2.assign可以用于修飾非OC對(duì)象,而weak必須用于OC對(duì)象。
3. 怎么用copy關(guān)鍵字?
用途:
1.NSString、NSArray、NSDictionary等經(jīng)常使用copy關(guān)鍵字,是因?yàn)樗麄冇袑?duì)應(yīng)的可變類型:NSMutableString、NSMutableArray、NSMutableDictionary;
2.block也經(jīng)常使用copy關(guān)鍵字。block使用copy是從MRC遺留下來的“傳統(tǒng)”,在MRC中,方法內(nèi)部的block是在棧區(qū)的,使用copy可以把它放到堆區(qū)。在ARC中寫不寫都行:對(duì)于block使用copy還是strong效果是一樣的。寫上copy可以時(shí)刻提醒我們:編譯器自動(dòng)對(duì)block進(jìn)行了copy操作。
4. @propety (copy) NSMutableArray *array;這個(gè)寫法會(huì)出什么問題?
兩個(gè)問題:
1.添加、刪除、修改數(shù)組內(nèi)的元素的時(shí)候,程序會(huì)因?yàn)檎也坏綄?duì)應(yīng)的方法而崩潰。因?yàn)閏opy就是復(fù)制一個(gè)不可變NSArray的對(duì)象;
2.使用了atomic屬性會(huì)嚴(yán)重影響性能。
第一條的相關(guān)原因在下文《15.用@property聲明的NSString(或NSArray,NSDictionary)經(jīng)常使用 copy 關(guān)鍵字,為什么?如果改用strong關(guān)鍵字,可能造成什么問題?》有論述。
比如下面的代碼就會(huì)發(fā)生崩潰:
//.h文件
// 下面的代碼會(huì)發(fā)生崩潰
@property (nonatomic, copy) NSMutableArray *mutableArray;
//.m文件
// 下面的代碼會(huì)發(fā)生崩潰
NSMutableArray *array = [NSMutableArray arrayWithObjects:@1,@2,nil];
self.mutableArray = array;
[self.mutableArray removeObjectAtIndex:0];
接下來就會(huì)崩潰
- [__NSArrayI removeObjectAtIndex:]:unrecognized selector sent to instance 0x7fcd1bc30460
原因是copy生成的是不可變數(shù)組,無法調(diào)用子類可變數(shù)組的對(duì)象方法
第二條原因,如下:
該屬性使用了同步鎖,會(huì)在創(chuàng)建時(shí)生成一些額外的代碼用于幫助編寫多線程程序,這會(huì)帶來性能問題,通過聲明nonatomic可以節(jié)省這些雖然很小但是不必要的額外開銷。
在默認(rèn)情況下,由編譯器所合成的方法會(huì)通過鎖定機(jī)制確保其原子性。如果屬性具備nonatomic特質(zhì),則不使用同步鎖。請(qǐng)注意,該項(xiàng)特質(zhì)默認(rèn)為atomic。
在iOS開發(fā)中,你會(huì)發(fā)現(xiàn),幾乎所有屬性都聲明為nonatomic。
一般情況下并不要求屬性必須是“原子的”,因?yàn)檫@并不能保證“線程安全”(thread safety),若要實(shí)現(xiàn)“線程安全”的操作,還需采用更為深層的鎖定機(jī)制才行。例如,一個(gè)線程在連續(xù)多次讀取某屬性值的過程中有別的線程在同時(shí)改寫該值,那么即便將屬性聲明為atomic,也還是會(huì)讀到不同的屬性值。
因此,開發(fā)iOS程序時(shí)一般都會(huì)使用nonatomic屬性。但是在開發(fā)Mac OS X程序時(shí),使用atomic屬性通常不會(huì)有性能瓶頸。
線程安全:允許被多個(gè)線程同時(shí)執(zhí)行且結(jié)果不會(huì)出錯(cuò)的代碼是線程安全的代碼,線程安全的代碼不包含競(jìng)態(tài)條件。當(dāng)多個(gè)線程同時(shí)更新共享資源時(shí)會(huì)引發(fā)競(jìng)態(tài)條件。
為了保證iOS線程安全,常用的方式有:
(1)@synchronized
(2)NSLock
(3)dispatch_semaphore_t
(4)OSSpinLock
其他方法有:
(5)NSRecursiveLock遞歸鎖
(6)NSConditionLock條件鎖
(7)NSCondition
(8)pthread_mutex
(9)pthread_mutex(recursive)
5. 如何讓自己的類用copy修飾符?如何重寫帶copy關(guān)鍵字的setter
若想令自己所寫的對(duì)象具有拷貝功能,則需要實(shí)現(xiàn)NSCopying協(xié)議。如果自定義的對(duì)象分為可變版本與不可變版本,那么就要同時(shí)實(shí)現(xiàn)NSCopying與NSMutableCopying協(xié)議。
具體步驟:
1.需聲明該類遵從NSCopying協(xié)議
2.實(shí)現(xiàn)NSCopying協(xié)議,該協(xié)議只有一個(gè)方法:
- (id)copyWithZone:(NSZone *)zone;
注意:一提到讓自己的類用copy修飾符,我們總是想復(fù)寫copy方法,其實(shí)真正需要實(shí)現(xiàn)的卻是“copyWithZone”方法。
以第一題的代碼為例:
typedef NS_ENUM(NSInteger, YZZYSex) {
YZZYMan,
YZZYSexWoman
};
@interface YZZYUser : NSObject<NSCopying>
@property (nonatomic, readonly, copy) NSString *name;
@property (nonatomic, readonly, assign) NSUInteger age;
@property (nonatomic, readonly, assign) YZZYSex sex;
- (instance)initWithName:(NSString *)name age:(NSUInteger)age sex:(YZZYSex)sex;
+ (instance)userWithName:(NSString *)name age:(NSUInteger)age sex:(YZZYSex)sex;
@end
然后實(shí)現(xiàn)協(xié)議中規(guī)定的方法:
- (id)copyWithZone:(NSZone *)zone
{
YZZYUser *copy = [[[self class] allocWithZone:zone] initWithName:_name age:_age sex:_sex];
return copy;
}
關(guān)于NSZone可以參考這篇iOS NSZone