iOS面試題

1、什么情況使用 weak 關鍵字,相比 assign 有什么不同

在ARC中,為了防止出現循環引用,一方需要使用weak來修飾,比如delegate,而assign用于修飾基本數據類型、結構體。

區別:用weak修飾的屬性指向的內容被清空時(引用計數器為0),系統會自動將屬性賦值為nil,而assign不會,所以如果用assign修飾了對象并且當對象指向內容被清空時,再次訪問對象會發生野指針錯誤,下面上代碼:

#import "ViewController.h"

@interface ViewController ()
@property(nonatomic, strong) id strongPointer;
@property(nonatomic, weak) id weakPointer;
@property(nonatomic, assign) id assignPointer;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.strongPointer = [NSDate date];
    self.weakPointer = self.strongPointer;
    self.assignPointer = self.strongPointer;
    
    NSLog(@"%@ %@ %@", self.strongPointer, self.weakPointer, self.assignPointer);
    
    self.strongPointer = nil;
    NSLog(@"%@", self.strongPointer);
    NSLog(@"%@", self.weakPointer);
    NSLog(@"%@", self.assignPointer); ///<---
    
}

@end

當運行到箭頭指向行時,會出現壞內存訪問:


壞內存訪問

那這時候有人又要問了:為什么用assign修飾基本數據類型的時候,不會發生類似的壞內存訪問的錯誤呢?
那是因為,對象一般分配在堆內存,如果之后系統訪問到了這塊內存空間,便會出錯,而基本數據類型,分配在棧內存,系統會自動管理棧內存,不會出現野指針錯誤。
被用weak和assign修飾的屬性指向的對象的引用計數器不變。

2、copy和mutableCopy

iOS 集合的深復制與淺復制 這本篇文章講的非常好,自己總結如下:
淺拷貝:復制指向對象的指針,即指針拷貝。
深拷貝:拷貝整個對象的內容,即內容拷貝。

非集合類對象(NSString、NSNumber等)
  • immutable對象
    NSString *str = @"Hello";
    NSLog(@"str : %p", str);
    
    NSString *copyStr = [str copy];
    NSLog(@"copyStr : %p", copyStr);
    
    NSMutableString *mutCopyMutStr = [str mutableCopy];
    NSLog(@"mutCopyMutStr : %p", mutCopyMutStr);

結果如下:


可以看到copyStrstr內存地址一樣,mutCopyMutStrstr內存地址不一樣

  • mutable對象
    NSMutableString *mutStr = [NSMutableString stringWithString:@"Hello"];
    NSLog(@"mutStr : %p", mutStr);
    
    NSString *copyStr = [mutStr copy];
    NSLog(@"copyStr : %p", copyStr);
    
    NSMutableString* mutCopyMutStr = [mutStr mutableCopy];
    NSLog(@"mutCopyMutStr : %p", mutCopyMutStr);

結果如下:


可以看到,copyStrmutCopyMutStrmutStr的內存地址都不一樣

集合類對象
  • immutable對象

    NSArray *array = @[@"1", @"2", @"3"];
    NSLog(@"array : %p", array);
    
    NSArray *copyArray = [array copy];
    NSLog(@"copyArray : %p", copyArray);
    
    NSMutableArray *mutCopyArray = [array mutableCopy];
    NSLog(@"mutCopyArray : %p", mutCopyArray);

結果如下:


可以看到,copyArrayarray的內存地址一樣,mutCopyArrayarray的內存地址不一樣,

  • mutable對象
    NSMutableArray *mutArray = [NSMutableArray arrayWithObjects:@"1", @"2", @"3", nil];
    NSLog(@"mutArray : %p", mutArray);
    
    NSArray *copyMutArray = [mutArray copy];
    NSLog(@"copyMutArray : %p", copyMutArray);
    
    NSMutableArray *mutCopyMutArray = [mutArray mutableCopy];
    NSLog(@"mutCopyMutArray : %p", mutCopyMutArray);

結果如下:


可以看到,copyMutArraymutCopyMutArraymutArray的內存地址都不一樣

結論:

可以用一張圖來說明,原鏈接在這里

個人覺得可以這樣總結:

  • 首先判斷是copy還是mutableCopy,如果是mutableCopy,那不管被拷貝對象是mutableObject還是immutableObject,都屬于深拷貝,即內容拷貝,產生新對象,并且返回的是mutableObject,要用可變的數據類型來接收。
  • 如果是copy,就得判斷被拷貝對象,如果被拷貝對象是mutableObject,那也是深拷貝,即內容拷貝;如果被拷貝對象是immutableObject,那是淺拷貝,即指針拷貝。但不管是那種,返回的都是immutableObject
  • 注意點就是,淺拷貝對應的拷貝方法只可能是copy;但深拷貝不僅僅與拷貝方法有關,還與被拷貝對象的類型有關。

3、@property中NSString、NSArray、NSDictionary等數據類型為什么要用copy來修飾,用strong會有什么問題

NSString為例來說明問題:

#import "ViewController.h"

@interface ViewController ()
@property (nonatomic, strong) NSString *str_strong;
@property (nonatomic, copy) NSString *str_copy;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSMutableString *mutStr = [[NSMutableString alloc] initWithString:@"Hello"];
    
    self.str_strong = mutStr;
    self.str_copy = mutStr;
    NSLog(@"str_strong : %@ str_copy : %@", self.str_strong, self.str_copy);
    
    [mutStr appendString:@" World"];
    NSLog(@"str_strong : %@ str_copy : %@", self.str_strong, self.str_copy);
    NSLog(@"mutStr : %p str_strong : %p str_copy : %p", mutStr, self.str_strong, self.str_copy);
}

@end

因為NSString、NSArray、NSDictionary等類,均有可變的子類:NSMutableString、NSMutableArray、NSMutableDictionary,如果用strong來修飾,則自身和被拷貝對象指向的是同一塊內存地址,如果被拷貝的可變對象發生改變,那自身的值也會發生變化,使用copy即可避免這種問題,因為在這種情況下,copy進行的是深拷貝,即內容拷貝,開辟了新的內存空間。

4、如何實現自定義類的copy方法,如何重寫用copy修飾的對象的setter方法

實現自定義類的copy方法

自定義QLCar類:

#import <Foundation/Foundation.h>

@interface QLCar : NSObject <NSCopying, NSMutableCopying>
@property (nonatomic, copy) NSString *brand;

@end

遵守NSCopying, NSMutableCopying協議

#import "QLCar.h"

@implementation QLCar
- (id)copyWithZone:(NSZone *)zone {
    QLCar *car = [[[self class] allocWithZone:zone] init];
    car.brand = _brand;
    return car;
}

- (id)mutableCopyWithZone:(NSZone *)zone {
    QLCar *car = [[[self class] allocWithZone:zone] init];
    car.brand = _brand;
    return car;
}
@end

驗證:


    QLCar *c = [[QLCar alloc] init];
    c.brand = @"Mercedes";
    
    self.car = [c copy];
    NSLog(@"%@", self.car.brand);
    NSLog(@"c : %p car : %p",c, self.car);
打印結果
如何重寫用copy修飾的對象的setter方法
#import "ViewController.h"
#import "QLCar.h"

@interface ViewController ()
@property (nonatomic, copy) QLCar *sedan;
@end

@implementation ViewController
- (void)setSedan:(QLCar *)sedan {
    _sedan = [sedan copy];
}
@end

5、@property 后面可以有哪些修飾符

  • 原子性/非原子性:atomic/nonatomic,如果不指定nonatomic,則默認為atomic
  • 讀寫權限:readonly(只讀)readwrite(可讀可寫)
  • 內存管理:weakstrongassigncopyretainunsafe_unretained
  • Access Method:
@property (nonatomic, assign, getter=isHidden) BOOL hidden;
  • Nullability:nonnullnullablenull_resettablenull_unspecified

6、多線程類

  • 有ABC三個任務,C必須等AB先執行完再執行,AB兩個同時執行,ABC必須都在子線程執行

方案A(waitUntilFinished)

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        [self printNumber:1];
    }];
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        [self printNumber:2];
    }];
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        [self printNumber:3];
    }];
    
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperations:@[op1, op2] waitUntilFinished:YES];
    [queue addOperation:op3];
}

- (void)printNumber:(NSInteger)number {
    for (NSInteger i = 0; i < 1000; i++) {
        NSLog(@"%ld %@", number, [NSThread currentThread]);
    }
}

方案B(addDependency)

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        [self printNumber:1];
    }];
    
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        [self printNumber:2];
    }];
    
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        [self printNumber:3];
    }];
    
    [op3 addDependency:op1];
    [op3 addDependency:op2];
    
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperation:op1];
    [queue addOperation:op2];
    [queue addOperation:op3];
}

- (void)printNumber:(NSInteger)number {
    for (NSInteger i = 0; i < 1000; i++) {
        NSLog(@"%ld %@", number, [NSThread currentThread]);
    }
}

7、Push、Present中的控制器生命周期

  • Push(A的導航控制器push出B)
  • Present(A控制器present出B)


8、runtime

  • Method Swizzling(方法交換)
//自定義Car類.h文件
@interface Car : NSObject
- (void)mercedes;
- (void)bmw;
+ (void)audi;
+ (void)toyota;
@end


//自定義Car類.m文件
@interface Car()
{
    CGFloat _price;
}
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) CGFloat speed;
@end

@implementation Car
- (void)mercedes {
    NSLog(@"mercedes");
}

- (void)bmw {
    NSLog(@"bmw");
}

+ (void)audi {
    NSLog(@"audi");
}

+ (void)toyota {
    NSLog(@"toyota");
}

- (void)privateMethod1 {
    
}

- (void)privateMethod2 {
    
}

@end


#import "ViewController.h"
#import "Car.h"
#import <objc/runtime.h>

@interface ViewController ()
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];

    Car *myCar = [[Car alloc] init];
    Method m1 = class_getInstanceMethod([Car class], @selector(mercedes));
    Method m2 = class_getInstanceMethod([Car class], @selector(bmw));
    method_exchangeImplementations(m1, m2);
    
    [myCar mercedes];
    [myCar bmw];

    Method m3 = class_getClassMethod([Car class], @selector(audi));
    Method m4 = class_getClassMethod([Car class], @selector(toyota));
    method_exchangeImplementations(m3, m4);
    
    [Car audi];
    [Car toyota];
}
@end
  • get ivarList(獲取成員變量列表)
#import "ViewController.h"
#import "Car.h"
#import <objc/runtime.h>

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    unsigned int num = 0;
    Ivar *ivarList = class_copyIvarList([Car class], &num);
    
    for (int i = 0; i<num; i++) {
        Ivar ivar = ivarList[i];
        const char *name = ivar_getName(ivar);
        NSLog(@"%s", name);
    }
    
    free(ivarList);
    
}

  • get methodList(獲取方法列表)
#import "ViewController.h"
#import "Car.h"
#import <objc/runtime.h>

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    unsigned int num = 0;
    Method *methodList = class_copyMethodList([Car class], &num);
    
    for (int i = 0; i<num; i++) {
        Method method = methodList[i];
        SEL methodSel = method_getName(method);
        NSString *name = NSStringFromSelector(methodSel);
        NSLog(@"%@", name);
    }
    
    free(methodList);
    
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,983評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,772評論 3 422
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,947評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,201評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,960評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,350評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,406評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,549評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,104評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,914評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,089評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,647評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,340評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,753評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,007評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,834評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,106評論 2 375

推薦閱讀更多精彩內容