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

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

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