iOS常见问题总结

1.是否可以把比较耗时的操作放在NSNotificationCenter中处理?为什么?应该如何处理?

参考链接: http://www.imlifengfeng.com/blog/?p=620

2.CAAnimation是什么?写一下它的层次结构(主要写下其各个子类)

参考链接: http://www.imlifengfeng.com/blog/?p=548

3.Cocoa的Foundation对象与Core Foundation对象通过什么关键字进行转换?这些关键字有什么区别?

参考链接: https://blog.csdn.net/yiyaaixuexi/article/details/8553659

4.利用NSOperation与NSOperationQueue处理多线程时,有3个NSOperation分别为A、B、C,要求A、B执行完之后才执行C,如何做?
    // 1.创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    // 2.创建操作
    NSBlockOperation *operationA = [NSBlockOperation   blockOperationWithBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"A---%@", [NSThread currentThread]); // 打印当前线程
        }
    }];
    NSBlockOperation *operationB = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"B---%@", [NSThread currentThread]); // 打印当前线程
        }
    }];
    NSBlockOperation *operationC = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"C---%@", [NSThread currentThread]); // 打印当前线程
        }
    }];
    //添加依赖
    [operationC addDependency:operationA];
    [operationC addDependency:operationB];
   
    [queue addOperation:operationA];
    [queue addOperation:operationB];
    [queue addOperation:operationC];
5. KVO是什么?内部是如何实现的?

参考链接: https://www.jianshu.com/p/e59bb8f59302

6.什么是中间人攻击?如何避免?

参考链接: http://jashoka.com/2018/03/15/iOS-https防止中间人攻击/

iOS实现代码

// 单向认证只需这段代码就行
....
shareManager = [AFHTTPSessionManager manager];
shareManager.securityPolicy = [self customSecurityPolicy];
.....
+ (AFSecurityPolicy*)customSecurityPolicy
{
    // /先导入证书
    NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"server" ofType:@"cer"];//证书的路径
    NSData *certData = [NSData dataWithContentsOfFile:cerPath];

    // AFSSLPinningModeCertificate 使用证书验证模式
    AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];

    // allowInvalidCertificates 是否允许无效证书(也就是自建的证书),默认为NO
    // 如果是需要验证自建证书,需要设置为YES
    securityPolicy.allowInvalidCertificates = YES;

    //validatesDomainName 是否需要验证域名,默认为YES;
    //假如证书的域名与你请求的域名不一致,需把该项设置为NO;如设成NO的话,即服务器使用其他可信任机构颁发的证书,也可以建立连接,这个非常危险,建议打开。
    //置为NO,主要用于这种情况:客户端请求的是子域名,而证书上的是另外一个域名。因为SSL证书上的域名是独立的,假如证书上注册的域名是www.google.com,那么mail.google.com是无法验证通过的;当然,有钱可以注册通配符的域名*.google.com,但这个还是比较贵的。
   //如置为NO,建议自己添加对应域名的校验逻辑。
    securityPolicy.validatesDomainName = NO;

    securityPolicy.pinnedCertificates = @[certData];

    return securityPolicy;
}
// 双向认证需添加这些代码
[shareManager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential *__autoreleasing *_credential) {
    NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
    __autoreleasing NSURLCredential *credential = nil;
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        if ([shareManager.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
            credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
            if(credential) {
                disposition =NSURLSessionAuthChallengeUseCredential;
            } else {
                disposition =NSURLSessionAuthChallengePerformDefaultHandling;
            }
        } else {
            disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
        }
    } else {
        SecIdentityRef identity = NULL;
        SecTrustRef trust = NULL;
        NSString *p12 = [[NSBundle mainBundle] pathForResource:@"client"ofType:@"p12"];
        NSFileManager *fileManager =[NSFileManager defaultManager];
        
        if(![fileManager fileExistsAtPath:p12])
        {
            NSLog(@"client.p12:not exist");
        }
        else
        {
            NSData *PKCS12Data = [NSData dataWithContentsOfFile:p12];
            
            if ([[self class] extractIdentity:&identity andTrust:&trust fromPKCS12Data:PKCS12Data])
            {
                SecCertificateRef certificate = NULL;
                SecIdentityCopyCertificate(identity, &certificate);
                const void*certs[] = {certificate};
                CFArrayRef certArray =CFArrayCreate(kCFAllocatorDefault, certs,1,NULL);
                credential =[NSURLCredential credentialWithIdentity:identity certificates:(__bridge  NSArray*)certArray persistence:NSURLCredentialPersistencePermanent];
                disposition =NSURLSessionAuthChallengeUseCredential;
            }
        }
    }
    *_credential = credential;
    return disposition;
}]

+ (BOOL)extractIdentity:(SecIdentityRef*)outIdentity andTrust:(SecTrustRef *)outTrust fromPKCS12Data:(NSData *)inPKCS12Data {
    OSStatus securityError = errSecSuccess;
    //client certificate password
    NSDictionary*optionsDictionary = [NSDictionary dictionaryWithObject:@"your p12 file pwd"
                                                                 forKey:(__bridge id)kSecImportExportPassphrase];
    
    CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
    securityError = SecPKCS12Import((__bridge CFDataRef)inPKCS12Data,(__bridge CFDictionaryRef)optionsDictionary,&items);
    
    if(securityError == 0) {
        CFDictionaryRef myIdentityAndTrust =CFArrayGetValueAtIndex(items,0);
        const void*tempIdentity =NULL;
        tempIdentity= CFDictionaryGetValue (myIdentityAndTrust,kSecImportItemIdentity);
        *outIdentity = (SecIdentityRef)tempIdentity;
        const void*tempTrust =NULL;
        tempTrust = CFDictionaryGetValue(myIdentityAndTrust,kSecImportItemTrust);
        *outTrust = (SecTrustRef)tempTrust;
    } else {
        NSLog(@"Failedwith error code %d",(int)securityError);
        return NO;
    }
    return YES;
}
7. 反射是什么?可以举出几个应用场景么?

参考链接1: https://cdn2.jianshu.io/p/4fde3afcaf1a
参考链接2: https://www.jianshu.com/p/5bbde2480680

8. __weak修饰符与__unsafe_unretained修饰符有相同点是什么?有什么区别呢?

__weak__unsafe_unretained都不会对对象产生强引用, 区别是__weak修饰的指针一旦它指向的对象被销毁了,__weak修饰的指针会被置为nil,比较安全,_unsafe_unretained修饰的指针指向的对象被销毁后,__unsafe_unretained修饰的指针仍然指向原对象的内存空间,会产生野指针错误。

9. __weak修饰的指针指向的对象会被自动置为nil,其实现原理是什么?

当对象被废弃之后,对象会调用dealloc方法,在delloc内部实现中,通过当前对象,经过哈希算法,在SideTables当中,找到它所属的SideTable,然后根据当前对象指针,访问SideTable中的弱引用表,去查找它所对用的弱引用表,然后遍历弱引用表中的所有弱引用指针,把弱引用指针置为nil。

10. release实现原理是怎样的?

通过当前对象,经过哈希算法,在SideTables当中,找到它所属的SideTable,然后再根据当前对象指针访问SideTable中的引用计数表,去查找它对应的引用计数表,查找到之后把对应的值进行减1操作。

11. retain实现原理是怎样的?

通过当前对象,经过哈希算法,在SideTables当中,找到它所属的SideTable,然后再根据当前对象指针访问SideTable中的引用计数表,去查找它所对应的引用计数表,查找到之后把对应的值进行加1操作。

12. Autoreleasepool的实现原理是怎样的?

Autoreleasepool它的实现是以栈为节点,以双向链表的形式,组合而成的数据结构。

13. Autoreleasepool为何可以嵌套使用?

多层嵌套就是多次插入哨兵对象。

14. 在非ARC中,可以对哪些对象加上autorelease关键字?它的作用是什么?被autorelease修饰的对象什么时候释放?

NSArray、NSDictionary、NSString等对象在初始化init、retain、copy等时可以autorelease。对象执行autorelease方法会将对象添加到自动释放池中,当自动释放池销毁时,自动释放池中所有对象进行release操作,对象执行autorelease方法自身引用计数不会改变,而且返回对象本身。autorelease的作用,只是把release调用延迟了,每次的autorelease把对象放入autoreleasepool中,当pool释放时,该pool所有对象都会进行release操作。

15.一个NSObject对象占用多少内存?

系统分配16个字节给NSObject对象,但NSObject对象内部只使用了8个字节的空间(64bit环境下,可以通过class_getInstanceSize函数获得)

16.说说深拷贝与浅拷贝

浅拷贝:浅拷贝之后原指针和拷贝后的指针是指向同一个对象,不会产生新的对象。
深拷贝:深拷贝之后原指针和拷贝后的指针指向不同的对象,会产生新的对象。
copy: 对不可变对象为浅拷贝,对可变对象为深拷贝。
mutableCopy: 不管是可变对象还是不可变对象都为深拷贝。

17.如何实现对NSArray或者NSMutableArray中的对象实现深拷贝?

使用归档解档即可。

    NSMutableString *str1 = [NSMutableString stringWithFormat:@"非容器可变对象"];
    NSArray *array = [NSArray arrayWithObjects:str1, @"非容器不可变对象", nil];
    
    NSMutableArray *mutableArray = [array mutableCopy];
    
    NSData *data = [NSKeyedArchiver archivedDataWithRootObject:mutableArray];
    NSMutableArray *newMutableArray = [NSKeyedUnarchiver unarchiveObjectWithData:data];
    
    NSLog(@"===原对象===");
    NSLog(@"array[0] : %p, array[1] : %p", array[0], array[1]);
    
    NSLog(@"===新对象===");
    NSLog(@"mutableArray[0] : %p, mutableArray[1] : %p", mutableArray[0], mutableArray[1]);
    
    NSLog(@"===完全拷贝对象===");
    NSLog(@"newMutableArray[0] : %p, newMutableArray[1] : %p", newMutableArray[0], newMutableArray[1]);

打印结果


1.png
18.自定义对象的深拷贝
#import <Foundation/Foundation.h>

@interface MJDog : NSObject

@property (nonatomic,copy)NSString *name;

@end
#import "MJDog.h"

@implementation MJDog

- (id)copyWithZone:(NSZone *)zone{
    MJDog *dog = [MJDog allocWithZone:zone];
    dog.name = [self.name mutableCopy];
    return dog;
}

- (id)mutableCopyWithZone:(NSZone *)zone{
    MJDog *dog = [MJDog allocWithZone:zone];
    dog.name = [self.name mutableCopy];
    return dog;
}
@end

#import <Foundation/Foundation.h>
#import "MJDog.h"
@interface MJPerson : NSObject<NSCopying,NSMutableCopying>
@property(nonatomic,copy)NSString *name;
@property (nonatomic,copy)MJDog *dog;
@end
#import "MJPerson.h"
@implementation MJPerson
- (id)copyWithZone:(NSZone *)zone{
    MJPerson *person = [MJPerson allocWithZone:zone];
    person.name = [self.name mutableCopy];
    person.dog = self.dog;
    return person;
}
- (id)mutableCopyWithZone:(NSZone *)zone{
    MJPerson *person = [MJPerson allocWithZone:zone];
    person.name = self.name;
    person.dog = self.dog;
    return person;
}
@end

#import "ViewController.h"
#import "MJPerson.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    MJPerson *person = [[MJPerson alloc] init];
    person.name = @"日天";
    MJDog *dog = [[MJDog alloc] init];
    person.dog = dog;
    MJPerson *person1 = [person copy];
    NSLog(@"person.name = %p,person1.name=%p",person.name,person1.name);
}
19.使用NSTimer应该注意哪些问题?
  1. 子线程启动定时器问题
    NSTimer是依赖runloop的,在主线程中因为默认启动了runloop,可是子线程没有默认的runloop,因此在子线程启动定时器是不生效的。
    解决防方案,在子线程启动runloop
  dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self s    elector:@selector(timered:) userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
        [[NSRunLoop currentRunLoop] run];
    });
  1. UIScrollView滑动过程中定时器失效
    解决方案就是把timer添加到runloop的NSRunLoopCommonModes。UITrackingRunLoopMode和kCFRunLoopDefaultMode都被标记为common模式,所以只需要将timer的模式设置为NSRunLoopCommonModes,就可以在默认模式和追踪模式都能运行。
  2. 循环引用问题
    NSTimer会对target产生强引用,如果target又对timer产生强引用,这样就会引发循环引用。
    解决方案一:
#import <Foundation/Foundation.h>
@interface NSTimer (BlockTimer)
+ (NSTimer*)scheduledTimerWithTimerInterval:(NSTimeInterval)interval repeats:(BOOL)repeats blockTimer:(void (^)(NSTimer *))block;
@end


#import "NSTimer+BlockTimer.h"
@implementation NSTimer (BlockTimer)
+ (NSTimer*)scheduledTimerWithTimerInterval:(NSTimeInterval)interval
                                    repeats:(BOOL)repeats
                                 blockTimer:(void (^)(NSTimer *))block{
    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:interval target:self selector:@selector(timered:) userInfo:[block copy] repeats:repeats];
    return timer;
}

+ (void)timered:(NSTimer *)timer{
    void (^block)(NSTimer *timer) = timer.userInfo;
    block(timer);
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容