美文网首页
iOS面试题

iOS面试题

作者: himyfairy | 来源:发表于2017-03-17 15:15 被阅读50次

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

    相关文章

      网友评论

          本文标题:iOS面试题

          本文链接:https://www.haomeiwen.com/subject/emuenttx.html