iOS零碎知识总结(一)

1.密码知识
(1)Base64 是一种编码格式,并不是真正意义上的(加密和解密)。他可以将任何二进制的数据都按照一定的编码格式进行编码成base64形式的(字符串)

(2)MD5 到底是不是加密?
严格来说,MD5 是一种对数据的数字签名,实质上,MD5 只是一种哈希算法。
什么是哈希算法,即 hash,又叫散列算法,是一类把任意数据转换为定长(或限制长度)数据的算法统称。例如我叫张三,你叫李四,那么「人 -> 人名」的算法就叫属于一种哈希算法。哈希算法通常用于制作数字指纹,数字指纹的意思就是「你看到这个东西就像看到原数据一样」,例如我们在一些网站下载大文件的时候,网站提供给我们验证文件完整性的 MD5 或者 SHA1 码,就是原文件的哈希值。哈希算法有很多种,MD5 是其中的一种,这就是 MD5。所以,优秀的哈希算法通常需要具有低碰撞概率(即不同数据的哈希值通常也不一样)。

加密是什么?

加密,指的是对数据进行转换以后,数据变成了另一种格式,并且除了拿到解密方法的人,没人能把数据转换回来。因此,加密通常用于网络通信。因为网络上的通信数据,任何人都有可能会拿到,把数据加密后再传送,送达以后由对方解密后再查看,就可以防止网络上的偷窥。例如大家都知道「安全」但很少人知道「为什么安全」的 HTTPS,就是通过加密算法来保障的网络安全性。

加密算法的目的,在于别人无法成功查看加密后的数据,并且在需要的时候还可以对数据进行解密来重新查看数据。而 MD5 算法是一种哈希算法,哈希算法的设计目标本身就决定了,它在大多数时候都是不可逆的,即你经过哈希算法得出的数据,无法再经过任何算法还原回去。所以,既然不能将数据还原,也就不能称之为可以解密;既然不能解密,那么哈希的过程自然也就不能称作是「加密」了。
总结:通常我们在说MD5 加密的时候 都会在后面加上(算法)两个字,也就是说,MD5 只是一个算法而已,是一个特殊的算法,是一个不可逆的算法。所以我们通常在开发的时候,都会采用 (对原数据加盐的方法来进行加密算法)。

2.iOS异常处理

@try
    {
        //可能出错的代码(注意:只能检测出数组越界之类的问题异常)
    }
    @catch (NSException *ext)
    {
        //异常处理代码
    }
    // 可以捕捉 N 个 异常 ...
    @finally
    {
        //回收资源 (可以省了不写)
    }

3.当参数为地址的时候,输出结果,是输出参数。

- (void)viewDidLoad {
    [super viewDidLoad];
   
    [self dizhifhao];
}

-(void)dizhifhao{
    NSString *strone = @"";
    [self demo:&strone];
    NSLog(@"%@",strone);
}

-(void)demo:(NSString **)str{
    *str = @"123456789";
}

打印输出结果是:
2018-10-24 15:56:43.530150+0800 iOS 线程等待[3282:315795] 123456789

4.iPhone 区分新旧设备的工具代码

static inline BOOL isIPhoneXSeries() {
    BOOL iPhoneXSeries = NO;
    // 是iPhone 设备
    if (UIDevice.currentDevice.userInterfaceIdiom != UIUserInterfaceIdiomPhone) {
        return iPhoneXSeries;
    }
    
    if (@available(iOS 11.0, *)) {
        // 有安全距离, 是iPhone X, iPhone XS, iPhone XS Max, iPhone XR
        UIWindow *mainWindow = [[[UIApplication sharedApplication] delegate] window];
        if (mainWindow.safeAreaInsets.bottom > 0.0) {
            iPhoneXSeries = YES;

        }
        NSLog(@"安全距离 == %f",mainWindow.safeAreaInsets.bottom);
    }
    
    return iPhoneXSeries;
}

5.Xcode9之后的折叠方法

相关快捷键:
局部折叠(折叠一个函数) :Command+Option+Left/Right
全局折叠(折叠当前文件下的全部函数):Shift+Command+Option+Left/Right
折叠注释块:(/ /之间的文字) : Ctrl+Shift+Command+Left/Right
当前文件的所有方法 折叠: command+option + shift + 向左按键 展开: command+option + shift + 向右按键

6.Mac 查看本机ip地址的方法,在终端输入以下命令,回车即可看到

ifconfig | grep "inet " | grep -v 127.0.0.1

7.iOS 通知 观察者移除注意点!!!
通知NSNotification在注册者被回收时需要手动移除,是一直以来的使用准则。
原因是在MRC时代,通知中心持有的是注册者的unsafe_unretained指针,在注册者被回收时若不对通知进行手动移除,则指针指向被回收的内存区域,成为野指针。
这时再发送通知,便会造成crash。

而在iOS 9以后,通知中心持有的是注册者的weak指针,这时即使不对通知进行手动移除,指针也会在注册者被回收后自动置空。我们知道,向空指针发送消息是不会有问题的。
***但是有一个例外。如果用

- (id )addObserverForName:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));

这个API来注册通知,可以直接传入block类型参数。使用这个API会导致注册者被系统retain,因此仍然需要像以前一样手动移除通知,同时这个block类型参数也需注意避免循环引用。

转载 halohily
原地址链接:https://www.jianshu.com/p/94405368bb27

从 iOS 9 开始通知中心会对观察者进行弱引用,所以不需要在观察者对象释放之前从通知中心移除。但是,通过-[NSNotificationCenter addObserverForName:object:queue:usingBlock]方法注册的观察者依然需要手动的释放,因为通知中心对它们持有的是强引用。

8.iOS多线程(刷新UI为什么要放在主线程进行)
1、在子线程中是不能进行UI 更新的,而可以立刻更新的原因是:子线程代码执行完毕了,又自动进入到了主线程,这中间的时间非常的短,让我们误以为子线程可以更新UI。如果子线程一直在运行,则无法更新UI,因为无法进入到主线程.
2、程序一开始运行就进入了主线程
3、处理某些数据太过费时,影响用户交互,可以开辟子线程处理,处理完之后,然后通知主线程进行界面更新。
4.iOS中只有主线程 才能立即刷新UI。主线程中用于显示\刷新UI界面,处理UI事件(比如点击事件、滚动事件、拖拽事件等).耗时操作会卡住主线程,严重影响UI的流畅度,给用户一种“卡”的坏体验,所以解决办法是:异步开启一条子线程,让耗时操作在子线程中完成,这样又不会影响主线程的任务。当子线程中的任务完成之后,回到主线程刷新UI,显示UI即可。类比,1个人同时做两件事和2个人分别做一件事哪种效率高呢。
5.如果是通过侦听异步消息,触发回调函数,或者调用异步方法,请求刷新UI,都会产生线程阻塞和延迟的问题。

总结:
(1)当我们在子线程种刷新UI的时候,如果子线程里在刷新UI操作后面还有好多事情要处理,那么,刷新UI的操作要等待后面的所有操作都进行完以后,才能进行。所以会照成UI不能够及时的刷新。
(2)这时我们要及时的通过线程间的通信方法回到主线程去刷新UI。

第一种解决方法
if ([NSThread isMainThread]){// 主线程就执行{}
  [self.downloadMapBtn setImage:[UIImage imageNamed:@"download_map.png"] forState:UIControlStateNormal];
  [self.downloadMapBtn setNeedsDisplay];
  }
  else{// 不是主线程,就调用gcd的函数+主队列,实现回到主线程刷新UI
  dispatch_sync(dispatch_get_main_queue(), ^{
  //Update UI in UI thread here
  [self.downloadMapBtn setImage:[UIImage imageNamed:@"download_map.png"] forState:UIControlStateNormal];
  [self.downloadMapBtn setNeedsDisplay];
  });
  }

第二种解决方法
[self performSelectorOnMainThread:@selector(doming) withObject:nil waitUntilDone:YES];

-(void)doming{
 [self.downloadMapBtn setImage:[UIImage imageNamed:@"download_map.png"] forState:UIControlStateNormal];
  [self.downloadMapBtn setNeedsDisplay];
}
  
这两种方法来进行 消息派送给主线程,进行刷新。

补充一个小的tip
[self performSelectorOnMainThread:@selector(doming) withObject:nil waitUntilDone:YES];
其中waitUntilDone后面的BOOL参数。

当为yes的时候,先让主线程运行doming中的一些操作,之后再进行当前线程中的操作。

当为no的时候,先进行当前线程中的操作,之后让主线程运行doming中的一些操作。

9.当多网络请求任务都执行完毕的时候,在进行UI界面的更新和渲染效果的做法总结?
1>利用网络请求的嵌套方法,进行(链式)的请求:A(完成)----B(成功)----C(成功)----等等--UI(刷新)
好处:简单,方便,逻辑性强!
坏处:代码可读性差,线程阻塞,其中一个请求不成功就会停止,UI刷新不成功。
2>利用异步串行队列执行
好处:代码可读性强
坏处:阻塞线程
3>利用 dispatch_barrier_async和dispatch_barrier_sync
好处:代码可读性强
怀处:代码片段多些

dispatch_queue_t queue = dispatch_queue_create("thread", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        sleep(3);
        NSLog(@"test1");
    });
    dispatch_async(queue, ^{
        NSLog(@"test2");
    });
    dispatch_sync(queue, ^{
        NSLog(@"test3");
    });
    dispatch_barrier_sync(queue, ^{
      sleep(1);
        for (int i = 0; i<50; i++) {
            if (i == 10 ) {
                NSLog(@"point1");
            }else if(i == 20){
                NSLog(@"point2");
            }else if(i == 40){
                NSLog(@"point3");
            }
        }
    });
    NSLog(@"hello");
    dispatch_async(queue, ^{
        NSLog(@"test4");
    });
    NSLog(@"world");
    dispatch_async(queue, ^{
        NSLog(@"test5");
    });
    dispatch_async(queue, ^{
        NSLog(@"test6");
    });

打印结果是:
2018-11-12 09:25:14.045031+0800 多线程的问题[879:44496] test3
2018-11-12 09:25:14.045038+0800 多线程的问题[879:44530] test1
2018-11-12 09:25:14.045040+0800 多线程的问题[879:44531] test2
2018-11-12 09:25:15.046334+0800 多线程的问题[879:44496] point1
2018-11-12 09:25:15.046714+0800 多线程的问题[879:44496] point2
2018-11-12 09:25:15.046934+0800 多线程的问题[879:44496] point3
2018-11-12 09:25:15.047090+0800 多线程的问题[879:44496] hello
2018-11-12 09:25:15.047287+0800 多线程的问题[879:44496] world
2018-11-12 09:25:15.047496+0800 多线程的问题[879:44534] test5
2018-11-12 09:25:15.047511+0800 多线程的问题[879:44530] test6
2018-11-12 09:25:17.047441+0800 多线程的问题[879:44531] test4
当使用 dispatch_barrier_sync(同步栅栏的时候)同步栅栏添加进入队列的时候,当前线程会被锁死,直到同步栅栏之前的任务和同步栅栏任务本身执行完毕时,当前线程才会打开然后继续执行下一句代码。

当把上面的dispatch_barrier_sync 替换成 dispatch_barrier_async 时,打印的是结果是
2018-11-12 09:34:25.020534+0800 多线程的问题[955:58590] test1
2018-11-12 09:34:25.020535+0800 多线程的问题[955:58546] test3
2018-11-12 09:34:25.020536+0800 多线程的问题[955:58588] test2
2018-11-12 09:34:25.020746+0800 多线程的问题[955:58546] hello
2018-11-12 09:34:25.020840+0800 多线程的问题[955:58546] world
2018-11-12 09:34:26.024451+0800 多线程的问题[955:58588] point1
2018-11-12 09:34:26.024876+0800 多线程的问题[955:58588] point2
2018-11-12 09:34:26.025070+0800 多线程的问题[955:58588] point3
2018-11-12 09:34:26.025381+0800 多线程的问题[955:58590] test5
2018-11-12 09:34:26.025392+0800 多线程的问题[955:58592] test6
2018-11-12 09:34:28.026044+0800 多线程的问题[955:58588] test4

总结:
当使用 dispatch_barrier_async(异步栅栏的时候)异步栅栏添加进入队列的时候,当前线程不会被锁死,会把后面的任务继续添加到队列。
共同点:
1、等待在它前面插入队列的任务先执行完
2、等待他们自己的任务执行完再执行后面的任务
不同点:
1、dispatch_barrier_sync将自己的任务插入到队列的时候,需要等待自己的任务结束之后才会继续插入被写在它后面的任务,然后执行它们
2、dispatch_barrier_async将自己的任务插入到队列之后,不会等待自己的任务结束,它会继续把后面的任务插入到队列,然后等待自己的任务结束后才执行后面任务。

dispatch_barrier_sync(queue,void(^block)())会将queue中barrier前面添加的任务block全部执行后,再执行barrier任务的block,再执行barrier后面添加的任务block.

dispatch_barrier_async(queue,void(^block)())会将queue中barrier前面添加的任务block只添加不执行,继续添加barrier的block,再添加barrier后面的block, 很重要的区别------同时不影响主线程(或者操作添加任务的线程)中代码的执行!

总而言之,dispatch_barrier_async是用于任务按序执行的!

4>利用 dispatch_group_t 创建线程组
好处:代码可读性强,示例如下:

-(void)group{
    
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("cn.gcd-group.www", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"正在下载第一张图片");
        NSLog(@"第一张图片下载完毕");
        NSLog(@"111%@",[NSThread currentThread]);
    });
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"正在下载第二张图片");
        NSLog(@"第二张图片下载完毕");
        NSLog(@"222%@",[NSThread currentThread]);
        
    });
    
    dispatch_group_notify(group, queue, ^{
        
        
        dispatch_async(dispatch_get_main_queue(), ^{
            
            NSLog(@"任务完成了");
        });
    });
}

打印结果是:
2018-11-12 13:35:35.319758+0800 多线程的问题[3005:293125] 正在下载第一张图片
2018-11-12 13:35:35.319759+0800 多线程的问题[3005:293121] 正在下载第二张图片
2018-11-12 13:35:35.319897+0800 多线程的问题[3005:293125] 第一张图片下载完毕
2018-11-12 13:35:35.319899+0800 多线程的问题[3005:293121] 第二张图片下载完毕
2018-11-12 13:35:35.320208+0800 多线程的问题[3005:293121] 222{number = 4, name = (null)}
2018-11-12 13:35:35.320208+0800 多线程的问题[3005:293125] 111{number = 3, name = (null)}
2018-11-12 13:35:35.325216+0800 多线程的问题[3005:293087] 任务完成了

5>利用 NSOperation 和 NSOperationQueue 实现(添加线程的相互依赖和等待)

-(void)NSOperationQueue{
    
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    queue.maxConcurrentOperationCount = 4; //最大并发数
    NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
        

        NSLog(@"excute operation2 %@",[NSThread currentThread]);
    }];
    NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"excute operation1 %@",[NSThread currentThread]);
    }];
    NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
        sleep(10);
        NSLog(@"excute operation3 %@",[NSThread currentThread]);
    }];
    [operation3 addDependency:operation1];
    [operation2 addDependency:operation3];
    
    [queue addOperation:operation1];
    [queue addOperation:operation2];
    [queue addOperation:operation3];

}
打印结果是:
2018-11-12 15:57:39.293930+0800 多线程的问题[6329:478093] excute operation1 {number = 3, name = (null)}
2018-11-12 15:57:49.294922+0800 多线程的问题[6329:478092] excute operation3 {number = 4, name = (null)}
2018-11-12 15:57:49.295294+0800 多线程的问题[6329:478092] excute operation2 {number = 4, name = (null)}

总结: [operation(a) addDependency:operation(b)]; 就是operation(a)永远在等待operation(b)执行完了以后,才去执行的,所以在处理顺序上有一定的控制权。

你可能感兴趣的:(iOS零碎知识总结(一))