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阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容