iOS面試題

1、什么情況使用 weak 關(guān)鍵字,相比 assign 有什么不同

在ARC中,為了防止出現(xiàn)循環(huán)引用,一方需要使用weak來修飾,比如delegate,而assign用于修飾基本數(shù)據(jù)類型、結(jié)構(gòu)體。

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

#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

當(dāng)運(yùn)行到箭頭指向行時(shí),會出現(xiàn)壞內(nèi)存訪問:


壞內(nèi)存訪問

那這時(shí)候有人又要問了:為什么用assign修飾基本數(shù)據(jù)類型的時(shí)候,不會發(fā)生類似的壞內(nèi)存訪問的錯誤呢?
那是因?yàn)?,對象一般分配?code>堆內(nèi)存,如果之后系統(tǒng)訪問到了這塊內(nèi)存空間,便會出錯,而基本數(shù)據(jù)類型,分配在棧內(nèi)存,系統(tǒng)會自動管理?xiàng)?nèi)存,不會出現(xiàn)野指針錯誤。
被用weak和assign修飾的屬性指向的對象的引用計(jì)數(shù)器不變。

2、copy和mutableCopy

iOS 集合的深復(fù)制與淺復(fù)制 這本篇文章講的非常好,自己總結(jié)如下:
淺拷貝:復(fù)制指向?qū)ο蟮闹羔?,即指針拷貝?br> 深拷貝:拷貝整個(gè)對象的內(nèi)容,即內(nèi)容拷貝。

非集合類對象(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);

結(jié)果如下:


可以看到copyStrstr內(nèi)存地址一樣,mutCopyMutStrstr內(nèi)存地址不一樣

  • 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);

結(jié)果如下:


可以看到,copyStr、mutCopyMutStrmutStr的內(nèi)存地址都不一樣

集合類對象
  • 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);

結(jié)果如下:


可以看到,copyArrayarray的內(nèi)存地址一樣,mutCopyArrayarray的內(nèi)存地址不一樣,

  • 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);

結(jié)果如下:


可以看到,copyMutArray、mutCopyMutArraymutArray的內(nèi)存地址都不一樣

結(jié)論:

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

個(gè)人覺得可以這樣總結(jié):

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

3、@property中NSString、NSArray、NSDictionary等數(shù)據(jù)類型為什么要用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

因?yàn)?code>NSString、NSArray、NSDictionary等類,均有可變的子類:NSMutableString、NSMutableArray、NSMutableDictionary,如果用strong來修飾,則自身和被拷貝對象指向的是同一塊內(nèi)存地址,如果被拷貝的可變對象發(fā)生改變,那自身的值也會發(fā)生變化,使用copy即可避免這種問題,因?yàn)樵谶@種情況下,copy進(jìn)行的是深拷貝,即內(nèi)容拷貝,開辟了新的內(nèi)存空間。

4、如何實(shí)現(xiàn)自定義類的copy方法,如何重寫用copy修飾的對象的setter方法

實(shí)現(xiàn)自定義類的copy方法

自定義QLCar類:

#import <Foundation/Foundation.h>

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

@end

遵守NSCopying, NSMutableCopying協(xié)議

#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

驗(yàn)證:


    QLCar *c = [[QLCar alloc] init];
    c.brand = @"Mercedes";
    
    self.car = [c copy];
    NSLog(@"%@", self.car.brand);
    NSLog(@"c : %p car : %p",c, self.car);
打印結(jié)果
如何重寫用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,則默認(rèn)為atomic
  • 讀寫權(quán)限:readonly(只讀)、readwrite(可讀可寫)
  • 內(nèi)存管理:weakstrongassign、copy、retainunsafe_unretained
  • Access Method:
@property (nonatomic, assign, getter=isHidden) BOOL hidden;
  • Nullability:nonnullnullablenull_resettablenull_unspecified

6、多線程類

  • 有ABC三個(gè)任務(wù),C必須等AB先執(zhí)行完再執(zhí)行,AB兩個(gè)同時(shí)執(zhí)行,ABC必須都在子線程執(zhí)行

方案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的導(dǎo)航控制器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);
    
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 設(shè)計(jì)模式是什么? 你知道哪些設(shè)計(jì)模式,并簡要敘述? 設(shè)計(jì)模式是一種編碼經(jīng)驗(yàn),就是用比較成熟的邏輯去處理某一種類型的...
    卑微的戲子閱讀 641評論 0 1
  • 出題者簡介: 孫源(sunnyxx),目前就職于百度,負(fù)責(zé)百度知道 iOS 客戶端的開發(fā)工作,對技術(shù)喜歡刨根問底和...
    戈多_于勒閱讀 1,815評論 0 5
  • 慘綠青年 你短發(fā)密且軟 誰給你剪 誰給你剪
    壁花少年Min閱讀 459評論 0 0
  • 1.提交建模論文。咨詢情況。 2.
    小散哥閱讀 165評論 0 0
  • 回顧燒寫過程 如果開發(fā)板里已經(jīng)燒過了的話,就不需要再次進(jìn)行燒寫,只需要如下 插入有轉(zhuǎn)接頭的線,在終端中輸入:ls ...
    帥碧閱讀 361評論 1 2