知识点总结

1、项目中最能体现你技术的地方,项目的结构

2、 KVO内部实现原理?KVC KVO是为了解决什么问题

链接 1、深入理解 KVC\KVO 实现机制 — KVO  2、 KVC本质

实现原理

KVO的实现使用了isa-swizzling技术以及观察者模式。
isa指针指向了对象的类对象,这个类对象维护着一个分发表,分发表保存了类方法、成员方法实现的指针。

当对一个对象的属性第一次进行监听器注册后,编译器会默认生成一个名称为NSKVONotifying_原有类名称的派生中间类,该类继承原有类,然后修改原有类对象的isa指针,使其指向新生成的中间类,接着,会在派生类中修改监听属性的setter和getter方法,执行willChangeValueForKey:和didChangeValueForKey:方法和父类的setter方法,并通知所有监听的对象,监听属性被修改了。

因此,对于使用KVO监听的类来说,isa指针的指向并不一定指向对象的实际类。你不应该依赖isa指针取决定类的成员关系,而应该使用class方法去正确的获取对象的实际类。

1.首先调用willChangeValueForKey:方法。
2.然后调用setAge:方法真正的改变属性的值。
3.开始调用didChangeValueForKey:这个方法,调用[super didChangeValueForKey:key]时会通知监听者属性值已经改变,
然后监听者执行自己的- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void *)context这个方法。

KVO是基于KVC实现的

KVO其实也是“观察者”设计模式的一种实现。我的看法是,这种模式有利于两个类间的解耦合,尤其是对于业务逻辑与视图控制器这两个功能的解耦合

手动触发KVO

有时我们可能有一些需求,在属性值满足要求下才去触发KVO,有的人可能会说直接在回调函数中进行判断就好啦,但是当我们开发一些供他人使用的框架时我们不能保证其他用户能够按照要求进行条件判断,此时就需要手动触发KVO。
触发监听器回调函数时需要满足一个类方法:

//balance属性实现该方法
+ (BOOL)automaticallyNotifiesObserversOfBalance

//其他属性按照以下格式实现类方法
+ (BOOL)automaticallyNotifiesObserversOfXXXX

通过函数名就可以判断,该函数是用来判断是否自行进行监听器通知,默认返回true,因此默认情况下都是自动触发KVO的回调函数,如果要手动触发则需要返回false并在需要触发KVO回调函数的地方执行以下方法:

//对需要触发回调函数的属性名称调用如下方法
[self willChangeValueForKey:@"balance"];
//为其赋新值
_balance = balance;
[self didChangeValueForKey:@"balance"];

举例如下:

#import <Foundation/Foundation.h>

@interface Account: NSObject

@property (nonatomic, copy) NSString *accountNumber;
@property (nonatomic, assign) double balance;

@end

@implementation Account

@synthesize accountNumber = _accountNumber;
@synthesize balance = _balance;

- (void)setBalance:(double)balance
{
    //如果新值小于0不触发KVO
    if (balance < 0)
    {
        _balance = balance;
    }
    else
    {
        //新值大于0才触发KVO回调函数
        [self willChangeValueForKey:@"balance"];
        _balance = balance;
        [self didChangeValueForKey:@"balance"];
    }
}

+ (BOOL)automaticallyNotifiesObserversOfBalance
{
    return NO;
}


@end

@interface Person : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSUInteger age;
@property (nonatomic, strong) Account *account;

- (void)setObserver;

@end

@implementation Person

@synthesize name = _name;
@synthesize age = _age;

- (void)setObserver
{
    [self.account addObserver:self forKeyPath:@"balance" options:NSKeyValueObservingOptionNew context:nil];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
    if (object == self.account && [keyPath isEqualToString:@"balance"])
    {
        NSLog(@"NewBalance: %lf", self.account.balance);
    }
}

- (void)dealloc
{
    [self.account removeObserver:self forKeyPath:@"balance" context:nil];
}

@end


int main(int argc, const char * argv[]) {
    @autoreleasepool {

        Person *p = [[Person alloc] init];

        p.account = [[Account alloc] init];
        p.account.balance = 100.0;
        [p setObserver];
        //执行下面的代码不会触发KVO回调函数
        p.account.balance = -1000;
        //执行下面这行代码会输出 NewBalance: 220.000000
        p.account.balance = 220.0;
    }
    return 0;
}
View Code

 

3、http与https有什么不同,HTTPS原理?

4、TCP  UDP socket的传输的最小单位

5、iOS内存管理

6、缓存出异常了,怎么处理?

7、UIWebView 与WKWebView的区别

UIWebView属于UIKit,WKWebView属于WebKit.framework
UIWebView特点:
1.加载速度慢;
2.内存占用多,内存优化困难;
3.如果内存占用过多,还可能因为占用过多被系统kill掉。
WKWebView特点:
运行速度更快,占用内存更少
WKWebView:网页的渲染与展示,通过WKWebViewConfiguration可以进行自定义配置。
WKWebViewConfiguration:这个类专门用来配置WKWebView。
WKPreference:这个类用来进行相关webView设置。
WKProcessPool:这个类用来配置进程池,与网页视图的资源共享有关(session级别的cookie等),单例来共享登录状态
WKUserContentController:这个类主要用来做native与JavaScript的交互管理。
WKUserScript:用于进行JavaScript注入。
WKScriptMessageHandler:这个类专门用来处理JavaScript调用native的方法。
WKNavigationDelegate:网页跳转间的导航管理协议,这个协议可以监听网页的活动。
WKNavigationAction:网页某个活动的示例化对象。
WKUIDelegate:用于交互处理JavaScript中的一些弹出框。
WKBackForwardList:堆栈管理的网页列表。
WKBackForwardListItem:每个网页节点对象。

8、保持界面流畅的技巧(UITableView的优化方法)?

iOS 保持界面流畅的技巧

9、多线程方面(GCD,NSOperation,NSThread)

IOS 多线程-NSThread
iOS 多线程-NSOperation/NSOperationQueue

iOS 多线程-GCD

10、事件传递以及响应者链

史上最详细的iOS之事件的传递和响应机制-原理篇

11、线程同步

链接:iOS中的八大锁

GCD 线程同步:

组队列(dispatch_group):

使用Dispatch Group追加block到Global Group Queue,这些block如果全部执行完毕,就会执行通过dispatch_group_notify添加到主队列中的block

dispatch_queue_t queue = dispatch_get_global_queue( 0, 0 );
dispatch_group_t group = dispatch_group_create();

dispatch_group_enter(group);
dispatch_async( queue, ^{
    NSLog( @"task 1 --- %@", [NSThread currentThread] );
    dispatch_group_leave(group);
} );
dispatch_group_enter(group);
dispatch_async( queue, ^{
    NSLog( @"task 2 --- %@", [NSThread currentThread] );
    dispatch_group_leave(group);
} );
dispatch_group_notify( group, queue, ^{
    NSLog( @"all task done %@", [NSThread currentThread] );
} );

阻塞任务(dispatch_barrier):

通过dispatch_barrier_async添加的操作会暂时阻塞当前队列,即等待前面的并发操作都完成后执行该阻塞操作,待其完成后后面的并发操作才可继续。可以将其比喻为一根霸道的独木桥,是并发队列中的一个并发障碍点,或者说中间瓶颈,临时阻塞并独占。注意dispatch_barrier_async只有在并发队列中才能起作用,在串行队列中队列本身就是独木桥,将失去其意义。

可见使用dispatch_barrier_async可以实现类似dispatch_group_t组调度的效果,同时主要的作用是避免数据竞争,高效访问数据。

dispatch_queue_t queue = dispatch_queue_create("test.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        NSLog(@"task 1 on %@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"task 2 on %@",[NSThread currentThread]);
    });
    dispatch_barrier_async(queue, ^{
        NSLog(@"barrier ==========");
    });
    dispatch_async(queue, ^{
        NSLog(@"task 3 on %@",[NSThread currentThread]);
    });
//上面的代码,task 1和task 2会并发执行,然后执行barrier,最后是task 3

信号量机制(dispatch_semaphore):

dispatch_semaphore_create用来创建一个semaphore信号量并设置初始信号量的值;
dispatch_semaphore_signal发送一个信号让信号量增加1(对应PV操作的V操作);
dispatch_semaphore_wait等待信号使信号量减1(对应PV操作的P操作);

信号量创建的时候, 可以给他指定一个值.dispatch_semaphore_signal(sem)对信号进行+1操作.dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER)对信号进行-1操作.当进行-1时,如果发现信号结果会小于0,那么线程进入阻塞状态.只有当信号>=0才能通过.
项目中用到的例子:
{//从首页直接进扫码
        dispatch_group_t group = dispatch_group_create();//dispatch_group 线程组,同步机制
        dispatch_semaphore_t sema = dispatch_semaphore_create(1);//创建一个信号量(semaphore),允许同时并发的操作最多只有1次
        for (id ScanObject in self.thirdScanObj) {
            NSLog(@"ScanObject=%@",ScanObject);
            if ([ScanObject respondsToSelector:sel])
            {
                
                dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
                    
                    dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);//等待,直到信号量大于0时,即可操作,同时将信号量-1
                    hasResult=hasResult==2?2:0;
                    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{//倒计时5.0s,超时抛弃返回结果
                        if (hasResult==0&&![PassScanObject containsObject:ScanObject]) {//该第三方扫描完成
                            NSLog(@"unlock %@",NSStringFromClass([ScanObject class]));
                            [PassScanObject addObject:ScanObject];

                            dispatch_semaphore_signal(sema);//信号通知,即让信号量+1
                            dispatch_group_leave(group);
                        }
                    });
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
                    dispatch_group_enter(group);
                    [ScanObject performSelector:sel withObject:strResult withObject:^(BOOL success,id dict){
                        NSLog(@"第三方%@返回结果success=%d,dict=%@",NSStringFromClass([ScanObject class]),success,dict);
                        if ([PassScanObject containsObject:ScanObject]) {//扫描超时,不再处理超时的结果
                            return ;
                        }
                        [PassScanObject addObject:ScanObject];
                        if (hasResult==2) return;
                        NSLog(@"继续");
                        if (success) {
                            hasResult=2;
                            //NSLog(@"不包含======%@-----%@--------%lu",strResult,laststr,(unsigned long)strResult.length);
                            NSString *resultData=nil;
                            if ([dict isKindOfClass:[NSDictionary class]]) {
                                resultData=dict[@"data"];
                            }
                            if(resultData&&([resultData.lowercaseString hasPrefix:@"http"]||[resultData.lowercaseString hasPrefix:@"https"]))
                            {//是个 url网址
                                
                                [selfWeak GotoWeb:resultData];
                                
                            }else if ([NSStringFromClass([ScanObject class]) isEqualToString:@"TYKYScanResult"]) {
                                //二维码扫描后查询办事进度
                                [selfWeak.navigationController pushViewController:[TJOnlineWorkHome itemProgressViewController:dict] animated:YES];
                            }else if ([NSStringFromClass([ScanObject class]) isEqualToString:@"AddGroupByScan"]) {
                                //扫码加群
                                AddGroupProfileViewController *vc = [[AddGroupProfileViewController alloc] initWith:dict];
                                [selfWeak.navigationController pushViewController:vc animated:YES];
                            }else{
                                
                                dispatch_semaphore_signal(sema);
                                dispatch_group_leave(group);
                                return ;
                            }
                            
                            dispatch_semaphore_signal(sema);
                            dispatch_group_leave(group);
                            
                            
                            return;
                        }else{
                            hasResult=1;
                            
                            dispatch_semaphore_signal(sema);
                            dispatch_group_leave(group);
                        }
                    }];
    #pragma clang diagnostic pop
                    
                });
                
            }
        }
        dispatch_group_notify(group, dispatch_get_main_queue(), ^{
            if (hasResult!=2) {//都处理失败,直接显示扫码结果
                NSLog(@"跳转显示结果");
                ScanResultViewController *scanResult=[[ScanResultViewController alloc] init];
                scanResult.Result=strResult;
                [selfWeak.navigationController pushViewController:scanResult animated:YES];
            }
        });
    }
View Code

 

posted on 2018-07-30 17:00  二狗你变了  阅读(243)  评论(0)    收藏  举报

导航