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]);
打印结果
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应该注意哪些问题?
- 子线程启动定时器问题
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];
});
- UIScrollView滑动过程中定时器失效
解决方案就是把timer添加到runloop的NSRunLoopCommonModes。UITrackingRunLoopMode和kCFRunLoopDefaultMode都被标记为common模式,所以只需要将timer的模式设置为NSRunLoopCommonModes,就可以在默认模式和追踪模式都能运行。 - 循环引用问题
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);
}