diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml deleted file mode 100644 index 9d31ed0..0000000 --- a/.github/workflows/deploy.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: Hexo Deploy - -on: - push: - branches: [ hexo ] # 源代码分支 - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 18 - - - name: Install dependencies - run: npm install - - - name: Build Hexo - run: npx hexo generate - - - name: Deploy - uses: peaceiris/actions-gh-pages@v4 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_branch: gh-pages # 部署分支 - publish_dir: ./public diff --git a/.nojekyll b/.nojekyll deleted file mode 100644 index 8b13789..0000000 --- a/.nojekyll +++ /dev/null @@ -1 +0,0 @@ - diff --git "a/2016/10/13/SQlite\344\275\277\347\224\250\345\255\246\344\271\240/index.html" "b/2016/10/13/SQlite\344\275\277\347\224\250\345\255\246\344\271\240/index.html" new file mode 100644 index 0000000..4eec08c --- /dev/null +++ "b/2016/10/13/SQlite\344\275\277\347\224\250\345\255\246\344\271\240/index.html" @@ -0,0 +1,331 @@ + + + + + + + + SQL 使用 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ SQL 使用 +

+ + +
+ +
+ +

常用SQL 语句

创建一张表

+
create table if not exists t_product(productId integer, productName text, productPrice real);
+

删除一张表

+
drop table if exists t_product;
+

插入一条记录

+

如果不写某个字段和对应值就会为NUll

+
insert into t_product(productId, productName, productPrice) values (0001, 'iPhone7', 6100.8);
+

修改记录

+
update t_product set productId = 20002 where productName = 'iPhone5s';
+

删除

+
delete from t_product where productName = 'iphone5s';
+

注意:条件语句不能单独存在,只能在修改/删除/查询之后

+

简单查询

准备数据生成sql语句文件

+
NSMutableString *mutableString = [NSMutableString string];
+int productIdValue = arc4random_uniform(1000);
+NSString *productNameValue = [NSString stringWithFormat:@"iPhone_%d",1 + arc4random_uniform(8)];
+CGFloat productPriceValue = 3000 + arc4random_uniform(1000);
+
+for (NSInteger i = 0; i < 1000; i++) {
+    [mutableString appendFormat:@"insert into t_product(productId, productName, productPrice) values (%d, %@, %f); \r\n", productIdValue, productNameValue, productPriceValue];
+}
+
+// 写入文件
+[mutableString writeToFile:@"/Users/Apeng/work/Demo/Sqlite/products.sql" atomically:YES encoding:NSUTF8StringEncoding error:NULL];
+

生成的sql语句文件

+

+

SQL 查询语句

+
select *(表示字段)from t_product(表名)where(条件语句) productId > 100 (字段满足的条件) ;
+
+select * from t_product where productPrice < 5000; 
+


or: 或

+

+

and: 且

+

+

分页查询

+

limit 0,5 ( limit 索引,每页取得数据条数 )

+

第一页 limit 0,5;

+

第二页 limit 5,5;

+

第三页 limit 10,5;

+

第四页 limit 15,5;

+

第N页 limit (N - 1)* 5

+
select * from t_product where productName = 'iPhone_8' and productPrice < 3300 order by productPrice limit 5,5;
+

+

排序查询

+

order by 默认是从小到大,一般是在某一个结果之后

+

默认升序

+
// 默认升序
+select * from t_product where productName = 'iPhone_8' and productPrice < 3300 order by productPrice;
+
+// 降序
+select * from t_product where productName = 'iPhone_8' and productPrice < 3300 order by productPrice desc;
+

+

模糊查询

+

关键字搜索 ,% 是指通配符

+

SQL 顺序

+
select * from 表名 条件语句(模糊匹配)排序语句 分页语句
+
+select * from t_product where productName like '%_4' order by productPrice limit 0,5;
+

+

主键

+

数据库的约定俗称,自增长,建议创建表时增加主键,也可以之后再设计表

+
CREATE TABLE IF NOT EXISTS t_class (id integer PRIMARY KEY, className text, classNO integer); 
+

外键约束

+

当两张表有关联时,需要使用外键约束,一张表中的字段名所对应的值只能来自于另一张表中

+

+

添加外键约束之后的变化

+

+

多表查询

+

t_department(部门表)和 t_employee(员工表)两张表

+

需求:在 t_employee 表中查出部门研发部的人员

+

1 嵌套查询:

+

先查根据部门名称查出部ID,

+

+

然后再用部门ID查出员工表中的员工

+

以上两步的代码可合并

+

根据部门名称查出部门ID

+
SELECT id from t_department WHERE departmentName = '研发部';
+

根据部门id查出这个部门的人员

+
SELECT * from t_employee WHERE departmentID = 1;
+

合并后为

+
select * from t_employee where departmentID = (SELECT id from t_department WHERE departmentName = '研发部'); 
+

结果为:

+

+
    +
  1. 链接查询
  2. +
+

给表起别名,给字段起别名

+
select employee.*, depart.departmentName deptN from t_department depart, t_employee employee;
+

/*多表查询之链接查询/

+
select t_employee.*, t_department.departmentName FROM t_department, t_employee;
+

+

消除笛卡尔积

+
select employee.*, depart.departmentName deptN from t_department depart, t_employee employee where employee.departmentID = depart.id and employee.departmentID = 1;
+

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2016/10/20/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" "b/2016/10/20/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" new file mode 100644 index 0000000..4a83a99 --- /dev/null +++ "b/2016/10/20/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" @@ -0,0 +1,230 @@ + + + + + + + + 本地缓存 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ 本地缓存 +

+ + +
+ +
+ +

保存图片到沙盒

1
2
3
4
5
6
- (void)saveImage:(UIImage *)image InSanBoxWithIndexPath:(NSInteger)indexPath {

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *filePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:@"pic_%ld.png", indexPath]]; // 保存文件的名称
[UIImagePNGRepresentation(image)writeToFile:filePath atomically:YES];
}
+

获取沙盒中的图片

1
2
3
4
5
6
7
- (UIImage *)fetchImageFromSanBoxWithIndexPath:(NSInteger)indexPath
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *filePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:@"pic_%ld.png", indexPath]]; // 保存文件的名称
UIImage *img = [UIImage imageWithContentsOfFile:filePath];
return img;
}
+

删除沙盒中的图片

1
2
3
4
5
6
7
8
9
10
11
12
- (BOOL)deleteImageAtIndexPath:(NSInteger)index;
{

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *filePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:@"pic_%ld.png", index]];

if([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
return [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
}

return NO;
}
+

修改图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
- (UIImage *)fixOrientation:(UIImage *)aImage {

// No-op if the orientation is already correct
if (aImage.imageOrientation == UIImageOrientationUp)
return aImage;

// We need to calculate the proper transformation to make the image upright.
// We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.
CGAffineTransform transform = CGAffineTransformIdentity;

switch (aImage.imageOrientation) {
case UIImageOrientationDown:
case UIImageOrientationDownMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.width, aImage.size.height);
transform = CGAffineTransformRotate(transform, M_PI);
break;

case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.width, 0);
transform = CGAffineTransformRotate(transform, M_PI_2);
break;

case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
transform = CGAffineTransformTranslate(transform, 0, aImage.size.height);
transform = CGAffineTransformRotate(transform, -M_PI_2);
break;
default:
break;
}

switch (aImage.imageOrientation) {
case UIImageOrientationUpMirrored:
case UIImageOrientationDownMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.width, 0);
transform = CGAffineTransformScale(transform, -1, 1);
break;

case UIImageOrientationLeftMirrored:
case UIImageOrientationRightMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.height, 0);
transform = CGAffineTransformScale(transform, -1, 1);
break;
default:
break;
}

// Now we draw the underlying CGImage into a new context, applying the transform
// calculated above.
CGContextRef ctx = CGBitmapContextCreate(NULL, aImage.size.width, aImage.size.height,
CGImageGetBitsPerComponent(aImage.CGImage), 0,
CGImageGetColorSpace(aImage.CGImage),
CGImageGetBitmapInfo(aImage.CGImage));
CGContextConcatCTM(ctx, transform);
switch (aImage.imageOrientation) {
case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
// Grr...
CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.height,aImage.size.width), aImage.CGImage);
break;

default:
CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.width,aImage.size.height), aImage.CGImage);
break;
}
CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
UIImage *img = [UIImage imageWithCGImage:cgimg];
CGContextRelease(ctx);
CGImageRelease(cgimg);
return img;
}
+ +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2016/10/27/iOS\345\274\271\346\241\206\345\274\217\345\233\276/index.html" "b/2016/10/27/iOS\345\274\271\346\241\206\345\274\217\345\233\276/index.html" new file mode 100644 index 0000000..627ce39 --- /dev/null +++ "b/2016/10/27/iOS\345\274\271\346\241\206\345\274\217\345\233\276/index.html" @@ -0,0 +1,248 @@ + + + + + + + + iOS 弹框视图 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ iOS 弹框视图 +

+ + +
+ +
+ +

UIActionSheet UIAlertView UIAlertViewController 的区别

+

UIActionSheet

    +
  • iOS 8.3 之后过期
  • +
  • 模态弹出,显示在试图的底部 类似菜单提供选择
  • +
+
1
2
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"title" delegate:self cancelButtonTitle:@"取消" destructiveButtonTitle:nil otherButtonTitles:@"按钮1", @"按钮2", nil];
[actionSheet showInView:self.view];
+

UIAlertView

    +
  • iOS 9.0 之后过期
  • +
  • 模态弹出,显示在试图的中间位置 类似一个对话框提供选择
  • +
+
1
2
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"考勤方式" message:nil delegate:self cancelButtonTitle:@"取消" otherButtonTitles:faceBtn, fingerPrintBtn, nil];
[alertView show];
+

UIAlertViewController

    +
  • iOS 8.0 起可以使用
  • +
  • style : 默认样式, 类似 UIActionSheet 列表形式
  • +
  • style 为:UIAlertControllerStyleAlert 对话框形式
  • +
  • 设置按钮的样式
  • +
+

如果修改 UIAlertAction 中stye 为:

+

UIAlertActionStyleCancel
取消按钮会和其他按钮分开

+

如果修改 UIAlertAction 中stye 为:

+

UIAlertActionStyleDestructive
取消按钮会以红色警告的方式显示, 但是按钮之间不会分开

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"考勤方式" message:nil preferredStyle:UIAlertControllerStyleActionSheet];
UIAlertAction *faceBtn = [UIAlertAction actionWithTitle:@"人脸识别" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { }];

UIAlertAction *fingerPrintBtn = [UIAlertAction actionWithTitle:@"指纹认证" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {

}];

UIAlertAction *cancelBtn = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {

}];

[alert addAction:faceBtn];
[alert addAction:fingerPrintBtn];
[alert addAction:cancelBtn];
[self presentViewController:alert animated:YES completion:nil];
+ +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2016/11/05/Hexo\344\270\252\344\272\272\345\215\232\345\256\242\346\220\255\345\273\272/index.html" "b/2016/11/05/Hexo\344\270\252\344\272\272\345\215\232\345\256\242\346\220\255\345\273\272/index.html" new file mode 100644 index 0000000..6f1cba4 --- /dev/null +++ "b/2016/11/05/Hexo\344\270\252\344\272\272\345\215\232\345\256\242\346\220\255\345\273\272/index.html" @@ -0,0 +1,306 @@ + + + + + + + + Hexo个人博客搭建 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ Hexo个人博客搭建 +

+ + +
+ +
+ +

安装Homebrew

+
1
/usr/bin/ruby -e " (curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
+

安装git

1
sudo brew install git
+

安装node.js

+

安装hexo

    +
  • 需要输入密码: sudo 管理员权限 -g 全局安装
  • +
+
1
sudo npm install -g hexo
+

初始化 执行 hexo init 命令

    +
  • 指定安装文件 blog
  • +
+
1
hexo init blog
+
    +
  • cd 到 文件夹 blog 下,安装 npm
  • +
+
1
npm install
+
    +
  • 开启服务
  • +
+
1
hexo s
+ +

关联 github

    +
  • 添加 ssh key 到github

    +
  • +
  • 设置 Mac 显示隐藏文件

    +
      +
    • 在终端执行一下命令后,重启Finder

      +
      1
      defaults write com.apple.finder AppleShowAllFiles -bool true
      +
    • +
    +
  • +
  • 检查 SSH keys 是否存在 github

    +
      +
    • 打开 Finder 前往(command + shit + G)

      +
      1
      ~/.ssh/id_rsa.pub
      +
    • +
    +
  • +
  • 如果有文件说明存在 SSH key, 打开 id_rsa.pub 文件,复制里面的信息

    +
  • +
  • 进入 Github –> Settings –> SSH keys –> add SSH key
    任意添加一个title 粘贴内容到key 里,点击Add key 绿色按钮即可。
  • +
+

创建名字为 用户名.github.io 的仓库

自行百度。。。。

+

配置 hexo

    +
  • vim 打开 _config.yml修改 deploy 模块
  • +
+
1
vim _config.yml
+
1
2
3
4
deploy:
type: git
repository: https://github.com/用户名/用户.github.io.git
branch: master
+

冒号后需要加空格

+

生成 hexo 的静态页面

1
hexo generate 后者 hexo g
+
    +
  • 报以下错
  • +
+
1
2
ERROR Local hexo not found in ~/blog
ERROR Try runing: 'npm install hexo --save'
+
    +
  • 执行下面代码
  • +
+
1
npm install hexo --save
+

部署 hexo

1
hexo deploy 或者 hexo d
+
    +
  • 报以下错误
  • +
+
1
ERROR Deployer not found: git
+

解决方法:

+
1
2
npm install hexo-deployer-git --save
hexo d
+
    +
  • 配置 Deloyment 前,需要配置自己的身份信息
  • +
+
1
2
git config --global user.name "yourname"
git config --global user.email "youremail"
+

如果执行完以上命令还是不行,删除blog文件下的 .deploy_git 文件重新 hexo deploy

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2016/11/05/TableView\344\275\277\347\224\250\346\200\273\347\273\223/index.html" "b/2016/11/05/TableView\344\275\277\347\224\250\346\200\273\347\273\223/index.html" new file mode 100644 index 0000000..d0fba67 --- /dev/null +++ "b/2016/11/05/TableView\344\275\277\347\224\250\346\200\273\347\273\223/index.html" @@ -0,0 +1,293 @@ + + + + + + + + TableView 使用 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ TableView 使用 +

+ + +
+ +
+ +

tableView 使用总结

    +
  • 字典的 key enumerator 的使用

    +
  • +
  • tableViewCell 的 selected 方法可以监听cell 的选中, 使用selected 参数

    +
  • +
  • 网络慢时点击左边cell右边数据不能及时刷新, 用户看到的还是上一个cell显示的数据

    +
      +
    • 解决办法: 在网络加载之前就reload数据,让显示空白
    • +
    +
  • +
  • 点击左边的cell时 上拉加载更多控件的显示和隐藏逻辑

    +
      +
    • 解决办法: 在每次刷新右边数据时,控制footer的显示
    • +
    +
  • +
+
    +
  • 在numberOfRowsInSection方法中控制

    +
  • +
  • 每次点击cell之前要结束上一个没有刷新完的,防止奔溃

    +
  • +
  • 加载更多时

    +

    在加载第一页时设置currentPage为1,在加载更多的逻辑中 ++currentPage
    点击cell加载时上一个加载更多的网络数据还没有回来,在当前请求之前结束掉所有的网络请求
    停止之后的刷新控件状态会有错误,没有更多还是还可以加载状态不确定
    在点击左边cell后刷新右边的之前需要检查右边tableView 的footer的状态
    下拉刷新

    +
  • +
  • 第一次下拉刷新之后,第二次下拉刷新,将数据添加到数组中会导致两次数据重复

    +
      +
    • 解决办法:清除以前的所有数据
    • +
    +
  • +
  • 下拉刷新完毕后结束刷新后需要检查footer的状态

    +
  • +
  • 网络慢时连续点击cell,连发多个请求成功回调时数据显示错乱,或者是数据已经回来了但是还是显示上一个请求正在请求。。。

    +
      +
    • 解决办法:点击左边某个类别时,结束所有的刷新(上拉和下拉)
    • +
    +
  • +
  • 连续点击发送请求

    +
      +
    • 解决办法:保存请求参数, 比较上一次参数和当前参数, 如果 != 直接return,不让进入成功回调解析数据, 失败的时候也是同样 return,不提示用户
    • +
    +
  • +
  • 控制器正在请求数据时控制器销毁, 导致奔溃

    +
      +
    • 解决办法:在dealloc 中停止所有的操作
    • +
    +
  • +
+

cell 的frame 设置

    +
  • cell 之间的间隙以及cell的边距 重写frame 在super 之前设置

    +
      +
    • 设置frame 的x ,width, height 等属性
    • +
    +
  • +
  • 分页的2种做法

    +
      +
    • 下拉刷新

      +

      page 加载最新的 加载第一页 浪费用户的流量
      加载比当前更新的数据, 需要服务器支持,将当前最新的数据id发送给服务器,服务器加载比当前id还大的数据插入到最前面

      +
    • +
    • 上拉加载

      +

      将页码发给服务器

      +

      将最后面的数据的id发给服务器,

      +

      加载比当前id更小的数据

      +
    • +
    +
  • +
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2016/11/20/git\345\255\246\344\271\240\346\200\273\347\273\223/index.html" "b/2016/11/20/git\345\255\246\344\271\240\346\200\273\347\273\223/index.html" new file mode 100644 index 0000000..2dad8fe --- /dev/null +++ "b/2016/11/20/git\345\255\246\344\271\240\346\200\273\347\273\223/index.html" @@ -0,0 +1,290 @@ + + + + + + + + git 使用 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ git 使用 +

+ + +
+ +
+ +

使用 git 提交代码

    +
  • 初始化工作目录

    +
    1
    git init
    +
  • +
  • 配置用户名和邮箱(当前仓库)

    +
    1
    2
    git config user.name "aTreey"
    git config user.email "480814177@qq.com"
    +
  • +
+
    +
  • 配置全局的用户名和密码

    +
    1
    2
    git config --global user.name "aTreey"
    git config --global user.email "480814177@qq.com"
    +
  • +
  • 创建文件

    +
    1
    touch main.m
    +
  • +
  • 添加文件到暂缓区

    +
    1
    git add main.m
    +
  • +
  • 查看文件状态

    +
    1
    git status
    +
  • +
  • 新添加的文件或者新修改的文件在工作区中,没有添加到暂缓区中

    +
  • +
+
1
Untracked files: (红色)
+
    +
  • 需要添加到暂缓区中
  • +
+
1
git add main.m
+
    +
  • 出现一下提示方可提交
  • +
+
1
Changes to be committed: (绿色)
+
    +
  • 给git起别名

    +
    1
    2
    git config alias.st "status"
    git config alias.ci "commit -m"
    +
  • +
  • 查看所有版本库日志(只能查看当前版本的以前日志)

    +
    1
    git log
    +
  • +
  • 查看指定文件的版本库日志

    +
    1
    git log 文件名
    +
  • +
  • 查看所有的操作的日志

    +
    1
    git reflog
    +
  • +
+

版本会退

    +
  • 强制回退到当前版本

    +
    1
    git reset --hard HEAD
    +
  • +
  • 强制回退到当前版本的上一个版本

    +
    1
    git reset --hard HEAD^
    +
  • +
  • 强制回退到当前版本的上上个版本

    +
    1
    git reset --hard HEAD^^
    +
  • +
  • 强制回退到当前版本的前100个版本

    +
    1
    git reset --hard HEAD~100
    +
  • +
  • 强制回退指定版本

    +
    1
    git reset --hard HEAD 版本号前七位
  • +
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2016/11/22/OC RunTime\345\255\246\344\271\240/index.html" "b/2016/11/22/OC RunTime\345\255\246\344\271\240/index.html" new file mode 100644 index 0000000..9748dda --- /dev/null +++ "b/2016/11/22/OC RunTime\345\255\246\344\271\240/index.html" @@ -0,0 +1,241 @@ + + + + + + + + OC中运行时 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ OC中运行时 +

+ + +
+ +
+ +

RunTime
对于C语言,函数的调用在编译的时候会决定调用哪个函数,而对于面向对象的语言对于[调用某个对象的方法或函数]就叫「消息传递」。所以在 Java,C++ 这些语言里[调用某个对象的方法或函数]和 Objective-C 里[向某个对象发送消息]在概念上就是一样的事情,

+

RunTime 简介

    +
  • RunTime 简称运行时,OC中通常叫运行时机制,主要的是消息机制
  • +
  • 消息机制:在OC中,属于动态调用,在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用
  • +
+

RunTime 作用

    +
  • 发送消息:让对象发送消息
  • +
  • 交换方法
  • +
  • 动态添加方法
  • +
  • 动态获取属性和方法
  • +
  • 字典转模型
  • +
  • 给分类添加属性
  • +
+

原理:给分类添加一个属性,重写了setter、getter方法,但是没有生成 _成员变量,需要通过运行时关联到分类中, 其本质上就是给这个类添加关联,存值和取值
code

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
self.oldurlPath = urlPath;

- (void)setOldurlPath:(NSString *)oldurlPath {

// RunTime 关联
// 第一个参数:给哪个对象添加关联
// 第二个参数:关联的key,通过这个key获取
// 第三个参数:关联的value
// 第四个参数:关联的策略
objc_setAssociatedObject(self, key, oldurlPath, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (NSString *)oldurlPath {
return objc_getAssociatedObject(self, key);
}
+ +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2016/11/22/Quartz2D\347\273\230\345\210\266\347\272\277\346\235\241/index.html" "b/2016/11/22/Quartz2D\347\273\230\345\210\266\347\272\277\346\235\241/index.html" new file mode 100644 index 0000000..2f30c60 --- /dev/null +++ "b/2016/11/22/Quartz2D\347\273\230\345\210\266\347\272\277\346\235\241/index.html" @@ -0,0 +1,278 @@ + + + + + + + + WKWebiView 使用 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ WKWebiView 使用 +

+ + +
+ +
+ +

WKWebiView iOS8.0 出现,性能要远远好于 iOS 2.0时出现的 UIWebView,主要记录WKWebView 的创建和代理方法

创建WKWebView

WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
+self.webView = webView;
+[self.view addSubview:webView];
+webView.UIDelegate = self;
+webView.navigationDelegate = self;
+[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.baidu.com"]]];
+

遵守协议 WKNavgationDelegate WKUIDelegate

+

代理方法

    +
  • WKNavgationDelegate 方法

    +
      +
    • 发送请求之前决定是否跳转

      +
      1
      2
      3
      4
      5
      6
      7
      - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {

      decisionHandler(WKNavigationActionPolicyAllow);
      // 不允许跳转
      decisionHandler(WKNavigationActionPolicyCancel);
      NSLog(@"decidePolicyForNavigationAction");
      }
      +
    • +
    • 收到相应之后决定是否跳转

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {

      decisionHandler(WKNavigationResponsePolicyAllow);

      decisionHandler(WKNavigationResponsePolicyCancel);

      NSLog(@"decidePolicyForNavigationResponse");

      }
      +
    • +
    • 开始加载时调用

      +
      1
      2
      3
      4
      - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation {

      NSLog(@"didStartProvisionalNavigation");
      }
      +
    • +
    • 接受到服务器的跳转请求时调用

      +
      1
      2
      3
      - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
      NSLog(@"didReceiveServerRedirectForProvisionalNavigation");
      }
      +
    • +
    +
  • +
+
- 页面加载失败时调用 **存在缓存问题**
+
+
1
2
3
4
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

NSLog(@"error= %@", error);
}
+ + +- 内容开始返回时调用 + +
1
2
3
4
- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation {

NSLog(@"didCommitNavigation");
}
+
    +
  • 内容返回成功后调用

    +
    1
    2
    3
    4
    - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {

    NSLog(@"didFinishNavigation");
    }
    +
  • +
  • 开始返回错误时调用

    +
    1
    2
    3
    4
    - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

    NSLog(@"didFailNavigation");
    }
    +
  • +
+
    +
  • 目前不知道什么时候调用

    +
    1
    2
    3
    - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler {
    NSLog(@"didReceiveAuthenticationChallenge");
    }
    +
      +
    • 目前不知道什么时候调用

      +
      1
      2
      3
      4
      - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView API_AVAILABLE(macosx(10.11), ios(9.0)) {

      NSLog(@"webViewWebContentProcessDidTerminate");
      }
      +
    • +
    +
  • +
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2016/11/22/Swift3.0\345\255\246\344\271\240(\344\272\224)/index.html" "b/2016/11/22/Swift3.0\345\255\246\344\271\240(\344\272\224)/index.html" new file mode 100644 index 0000000..a042a7b --- /dev/null +++ "b/2016/11/22/Swift3.0\345\255\246\344\271\240(\344\272\224)/index.html" @@ -0,0 +1,278 @@ + + + + + + + + WKWebiView 使用 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ WKWebiView 使用 +

+ + +
+ +
+ +

WKWebiView iOS8.0 出现,性能要远远好于 iOS 2.0时出现的 UIWebView,主要记录WKWebView 的创建和代理方法

创建WKWebView

WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
+self.webView = webView;
+[self.view addSubview:webView];
+webView.UIDelegate = self;
+webView.navigationDelegate = self;
+[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.baidu.com"]]];
+

遵守协议 WKNavgationDelegate WKUIDelegate

+

代理方法

    +
  • WKNavgationDelegate 方法

    +
      +
    • 发送请求之前决定是否跳转

      +
      1
      2
      3
      4
      5
      6
      7
      - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {

      decisionHandler(WKNavigationActionPolicyAllow);
      // 不允许跳转
      decisionHandler(WKNavigationActionPolicyCancel);
      NSLog(@"decidePolicyForNavigationAction");
      }
      +
    • +
    • 收到相应之后决定是否跳转

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {

      decisionHandler(WKNavigationResponsePolicyAllow);

      decisionHandler(WKNavigationResponsePolicyCancel);

      NSLog(@"decidePolicyForNavigationResponse");

      }
      +
    • +
    • 开始加载时调用

      +
      1
      2
      3
      4
      - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation {

      NSLog(@"didStartProvisionalNavigation");
      }
      +
    • +
    • 接受到服务器的跳转请求时调用

      +
      1
      2
      3
      - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
      NSLog(@"didReceiveServerRedirectForProvisionalNavigation");
      }
      +
    • +
    +
  • +
+
- 页面加载失败时调用 **存在缓存问题**
+
+
1
2
3
4
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

NSLog(@"error= %@", error);
}
+ + +- 内容开始返回时调用 + +
1
2
3
4
- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation {

NSLog(@"didCommitNavigation");
}
+
    +
  • 内容返回成功后调用

    +
    1
    2
    3
    4
    - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {

    NSLog(@"didFinishNavigation");
    }
    +
  • +
  • 开始返回错误时调用

    +
    1
    2
    3
    4
    - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

    NSLog(@"didFailNavigation");
    }
    +
  • +
+
    +
  • 目前不知道什么时候调用

    +
    1
    2
    3
    - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler {
    NSLog(@"didReceiveAuthenticationChallenge");
    }
    +
      +
    • 目前不知道什么时候调用

      +
      1
      2
      3
      4
      - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView API_AVAILABLE(macosx(10.11), ios(9.0)) {

      NSLog(@"webViewWebContentProcessDidTerminate");
      }
      +
    • +
    +
  • +
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2016/11/22/Swift3.0\345\255\246\344\271\240(\345\233\233)/index.html" "b/2016/11/22/Swift3.0\345\255\246\344\271\240(\345\233\233)/index.html" new file mode 100644 index 0000000..2ac8ac7 --- /dev/null +++ "b/2016/11/22/Swift3.0\345\255\246\344\271\240(\345\233\233)/index.html" @@ -0,0 +1,278 @@ + + + + + + + + WKWebiView 使用 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ WKWebiView 使用 +

+ + +
+ +
+ +

WKWebiView iOS8.0 出现,性能要远远好于 iOS 2.0时出现的 UIWebView,主要记录WKWebView 的创建和代理方法

创建WKWebView

WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
+self.webView = webView;
+[self.view addSubview:webView];
+webView.UIDelegate = self;
+webView.navigationDelegate = self;
+[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.baidu.com"]]];
+

遵守协议 WKNavgationDelegate WKUIDelegate

+

代理方法

    +
  • WKNavgationDelegate 方法

    +
      +
    • 发送请求之前决定是否跳转

      +
      1
      2
      3
      4
      5
      6
      7
      - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {

      decisionHandler(WKNavigationActionPolicyAllow);
      // 不允许跳转
      decisionHandler(WKNavigationActionPolicyCancel);
      NSLog(@"decidePolicyForNavigationAction");
      }
      +
    • +
    • 收到相应之后决定是否跳转

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {

      decisionHandler(WKNavigationResponsePolicyAllow);

      decisionHandler(WKNavigationResponsePolicyCancel);

      NSLog(@"decidePolicyForNavigationResponse");

      }
      +
    • +
    • 开始加载时调用

      +
      1
      2
      3
      4
      - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation {

      NSLog(@"didStartProvisionalNavigation");
      }
      +
    • +
    • 接受到服务器的跳转请求时调用

      +
      1
      2
      3
      - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
      NSLog(@"didReceiveServerRedirectForProvisionalNavigation");
      }
      +
    • +
    +
  • +
+
- 页面加载失败时调用 **存在缓存问题**
+
+
1
2
3
4
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

NSLog(@"error= %@", error);
}
+ + +- 内容开始返回时调用 + +
1
2
3
4
- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation {

NSLog(@"didCommitNavigation");
}
+
    +
  • 内容返回成功后调用

    +
    1
    2
    3
    4
    - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {

    NSLog(@"didFinishNavigation");
    }
    +
  • +
  • 开始返回错误时调用

    +
    1
    2
    3
    4
    - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

    NSLog(@"didFailNavigation");
    }
    +
  • +
+
    +
  • 目前不知道什么时候调用

    +
    1
    2
    3
    - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler {
    NSLog(@"didReceiveAuthenticationChallenge");
    }
    +
      +
    • 目前不知道什么时候调用

      +
      1
      2
      3
      4
      - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView API_AVAILABLE(macosx(10.11), ios(9.0)) {

      NSLog(@"webViewWebContentProcessDidTerminate");
      }
      +
    • +
    +
  • +
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2016/11/22/WKWebiView\344\275\277\347\224\250/index.html" "b/2016/11/22/WKWebiView\344\275\277\347\224\250/index.html" new file mode 100644 index 0000000..12804e1 --- /dev/null +++ "b/2016/11/22/WKWebiView\344\275\277\347\224\250/index.html" @@ -0,0 +1,278 @@ + + + + + + + + WKWebiView 使用 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ WKWebiView 使用 +

+ + +
+ +
+ +

WKWebiView iOS8.0 出现,性能要远远好于 iOS 2.0时出现的 UIWebView,主要记录WKWebView 的创建和代理方法

创建WKWebView

WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
+self.webView = webView;
+[self.view addSubview:webView];
+webView.UIDelegate = self;
+webView.navigationDelegate = self;
+[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.baidu.com"]]];
+

遵守协议 WKNavgationDelegate WKUIDelegate

+

代理方法

    +
  • WKNavgationDelegate 方法

    +
      +
    • 发送请求之前决定是否跳转

      +
      1
      2
      3
      4
      5
      6
      7
      - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {

      decisionHandler(WKNavigationActionPolicyAllow);
      // 不允许跳转
      decisionHandler(WKNavigationActionPolicyCancel);
      NSLog(@"decidePolicyForNavigationAction");
      }
      +
    • +
    • 收到相应之后决定是否跳转

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {

      decisionHandler(WKNavigationResponsePolicyAllow);

      decisionHandler(WKNavigationResponsePolicyCancel);

      NSLog(@"decidePolicyForNavigationResponse");

      }
      +
    • +
    • 开始加载时调用

      +
      1
      2
      3
      4
      - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation {

      NSLog(@"didStartProvisionalNavigation");
      }
      +
    • +
    • 接受到服务器的跳转请求时调用

      +
      1
      2
      3
      - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
      NSLog(@"didReceiveServerRedirectForProvisionalNavigation");
      }
      +
    • +
    +
  • +
+
- 页面加载失败时调用 **存在缓存问题**
+
+
1
2
3
4
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

NSLog(@"error= %@", error);
}
+ + +- 内容开始返回时调用 + +
1
2
3
4
- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation {

NSLog(@"didCommitNavigation");
}
+
    +
  • 内容返回成功后调用

    +
    1
    2
    3
    4
    - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {

    NSLog(@"didFinishNavigation");
    }
    +
  • +
  • 开始返回错误时调用

    +
    1
    2
    3
    4
    - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

    NSLog(@"didFailNavigation");
    }
    +
  • +
+
    +
  • 目前不知道什么时候调用

    +
    1
    2
    3
    - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler {
    NSLog(@"didReceiveAuthenticationChallenge");
    }
    +
      +
    • 目前不知道什么时候调用

      +
      1
      2
      3
      4
      - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView API_AVAILABLE(macosx(10.11), ios(9.0)) {

      NSLog(@"webViewWebContentProcessDidTerminate");
      }
      +
    • +
    +
  • +
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2016/12/04/Quartz2D\345\210\235\350\257\206/index.html" "b/2016/12/04/Quartz2D\345\210\235\350\257\206/index.html" new file mode 100644 index 0000000..708368f --- /dev/null +++ "b/2016/12/04/Quartz2D\345\210\235\350\257\206/index.html" @@ -0,0 +1,355 @@ + + + + + + + + Quartz2D 初识 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ Quartz2D 初识 +

+ + +
+ +
+ +

Quartz2D 二维绘图引擎,支持iOS和Mac系统么,可以实现的功能有

+
    +
  • 绘制图形: 线条,三角形,矩形,圆,弧等
  • +
  • 绘制文字
  • +
  • 绘制图像或者图片
  • +
  • 读取生成PDF
  • +
  • 截图/裁剪图片
  • +
  • 自定义UI控件
  • +
  • 手势解锁
  • +
  • 图形上下文
  • +
+

保存绘图信息和状态

确定输出目标(PDF,Bitmap或者显示器)

+
Bitmap Graphics Context
+PDF Granhics Context
+Window Graphics Context
+Layer Graphics Context
+

绘制线段

+
- (void)drawLine {
+    // 获取图形上下文
+    // 所用的是UIGraphics上下文
+    CGContextRef ctx = UIGraphicsGetCurrentContext();
+
+    // 创建描述路径
+    CGMutablePathRef path = CGPathCreateMutable();
+    // 设置起点
+    // path : 表示给那个路径设置起点
+    CGPathMoveToPoint(path, NULL, 10, 10);
+    CGPathAddLineToPoint(path, NULL, 80, 80);
+
+    // 添加路径到上下文
+    CGContextAddPath(ctx, path);
+
+    // 渲染
+    CGContextStrokePath(ctx);
+}
+

系统底层自动将路径添加到图形上下文

+
- (void)drawLine2 {
+
+    CGContextRef ctx = UIGraphicsGetCurrentContext();
+
+    // 描述路径
+    // 此方法底层会自动将路径添加到图形上下文
+    CGContextMoveToPoint(ctx, 50, 50);
+    CGContextAddLineToPoint(ctx, 100, 100);
+
+    CGContextSetLineWidth(ctx, 5);
+
+    [[UIColor redColor] set];
+    // 渲染
+    CGContextStrokePath(ctx);
+}
+

使用C语言函数的封装

+
- (void)drawLine3 {
+    // 使用UIKit 已经封装的功能,面向对象
+    // 贝塞尔路径
+
+    UIBezierPath *path = [UIBezierPath bezierPath];
+    [path moveToPoint:CGPointMake(10, 10)];
+    [path addLineToPoint:CGPointMake(50, 50)];
+
+    // 绘制路径
+    [path stroke];
+}
+

绘制多条线段

+
    +
  • 绘制多条线,状态不好管理
  • +
  • 默认下一条线的起点是上一条线的终点
  • +
  • 绘制多天不连接的线时需要重新设置起点
  • +
  • 为了方便管理,应该让每一条线对应一条路径
  • +
  • 使用UIBeizerPath
  • +
+

code:

+
- (void)drawMoreLine {
+    CGContextRef ctx = UIGraphicsGetCurrentContext();
+
+    // 描述路径
+    CGContextMoveToPoint(ctx, 20, 20);
+    CGContextAddLineToPoint(ctx, 100, 100);
+
+    // 设置第二条线的起点
+    CGContextMoveToPoint(ctx, 20, 30);
+    CGContextAddLineToPoint(ctx, 150, 100);
+
+    // 设置绘图状态要在渲染之前,都是给上下文设置
+    // 描边颜色
+    [[UIColor greenColor] setStroke];
+    // 线宽
+    CGContextSetLineWidth(ctx, 10);
+    // 连接样式
+    // kCGLineJoinMiter,    斜接
+    // kCGLineJoinRound,    圆角
+    // kCGLineJoinBevel     平切
+    CGContextSetLineJoin(ctx, kCGLineJoinBevel);
+    // 顶角样式,线的顶端效果
+    CGContextSetLineCap(ctx, kCGLineCapRound);
+
+    // 渲染
+    CGContextStrokePath(ctx);
+}
+

使用UIBezierPath 绘制多条直线并设置不同状态

+
- (void)drawUIBezierPahtState {
+
+    // 第一条线
+    UIBezierPath *path = [UIBezierPath bezierPath];
+    [path moveToPoint:CGPointMake(10, 10)];
+    [path addLineToPoint:CGPointMake(80, 80)];
+
+    path.lineWidth = 20;
+    path.lineJoinStyle = kCGLineJoinRound;
+    path.lineCapStyle = kCGLineCapRound;
+    [[UIColor redColor] setStroke];
+
+    [path stroke];
+
+    // 第二条线
+    {
+        UIBezierPath *path = [UIBezierPath bezierPath];
+        [path moveToPoint:CGPointMake(40, 70)];
+        [path addLineToPoint:CGPointMake(150, 40)];
+
+        path.lineWidth = 20;
+        path.lineJoinStyle = kCGLineJoinRound;
+        path.lineCapStyle = kCGLineCapRound;
+        [[UIColor greenColor] setStroke];
+
+        [path stroke];
+    }
+}
+
+ +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2016/12/04/Quartz2D\347\273\230\345\210\266\346\233\262\347\272\277/index.html" "b/2016/12/04/Quartz2D\347\273\230\345\210\266\346\233\262\347\272\277/index.html" new file mode 100644 index 0000000..5b46b8c --- /dev/null +++ "b/2016/12/04/Quartz2D\347\273\230\345\210\266\346\233\262\347\272\277/index.html" @@ -0,0 +1,317 @@ + + + + + + + + Quartz2D绘制曲线 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ Quartz2D绘制曲线 +

+ + +
+ +
+ +

使用函数绘制曲线

+

Quartz 2D提供了CGContextAddCurveToPoint() 函数和CGContextAddQuadCurveToPoint()两个函数来向当前上下文添加曲线,前者用于添加贝塞尔曲线,后者用于添加二次曲线。
确定一条贝塞尔曲线需要4个点:开始点、第一个控制点、第二个控制点和结束点。

+

确定一条二次曲线需要三个点:开始点、控制点和结束点。

+

添加贝塞尔曲线

+
CGContextAddCurveToPoint()
+
+CGContextRef ctx = UIGraphicsGetCurrentContext();
+
+// 添加曲线路径
+// 设置起点
+CGContextMoveToPoint(ctx, 0, 0);
+
    +
  • 参数1: 上下文对象
  • +
  • 参数2: 控制点1X坐标
  • +
  • 参数3: 控制点1Y坐标
  • +
  • 参数4: 控制点2X坐标
  • +
  • 参数5: 控制点2Y坐标
  • +
  • 参数6: 终点x坐标
  • +
  • 参数7: 终点Y坐标
  • +
+

code:

+
CGContextAddCurveToPoint(ctx, 30, 200, 300, 20, 300, 300);
+
+// 设置颜色
+[[UIColor magentaColor] setStroke];
+
+// 渲染
+CGContextStrokePath(ctx);
+
+// 关闭图形上下文
+CGContextClosePath(ctx);
+添加二次曲线
+CGContextAddQuadCurveToPoint()
+
+// 获取图形上下文
+CGContextRef ctx = UIGraphicsGetCurrentContext();
+
+// 起始点
+CGContextMoveToPoint(ctx, 20, 10);
+
    +
  • 参数: 图形上下文
  • +
  • 参数: 控制点坐标
  • +
  • 参数: 结束点坐标
  • +
+

添加二次曲线

+
CGContextAddQuadCurveToPoint(ctx, 30, 200, 200, 40);
+
+[[UIColor cyanColor] setStroke];
+CGContextStrokePath(ctx);
+// 关闭图形上下文
+CGContextClosePath(ctx);
+

绘制形状

+

使用绘制二次曲线的函数绘制花瓣

+

程序中CGContextAddFlower代码分别添加5瓣花朵路径、6瓣花朵路径、7瓣花朵路径,然后使用不同的颜色来填充这些路径。注意到上面的程序并未在每次添加花朵路径后立即关闭,这也是允许的,而且每次填充路径时并不会再次填充前一次已经填充过的路径。这是因为只用程序绘制了CGContextRef当前所包含的路径,系统会自动清除已经绘制的路径。

+

注意:每次绘制完成后,CGContextRef会自动清除已经绘制完成的路径

+

该方法负责绘制花朵。
n:该参数控制花朵的花瓣数;dx、dy:控制花朵的位置;size:控制花朵的大小;
length:控制花瓣的长度

+
void CGContextAddFlower(CGContextRef c , NSInteger n
+                        , CGFloat dx , CGFloat dy , CGFloat size , CGFloat length)
+{
+    CGContextMoveToPoint(c , dx , dy + size);  // 移动到指定点
+    CGFloat dig = 2 * M_PI / n;
+    // 采用循环添加n段二次曲线路径
+    for(int i = 1; i < n + 1 ; i++)
+    {
+        // 计算控制点坐标
+        CGFloat ctrlX = sin((i - 0.5) * dig) * length + dx;
+        CGFloat ctrlY= cos((i - 0.5 ) * dig) * length + dy;
+        // 计算结束点的坐标
+        CGFloat x = sin(i * dig) * size + dx;
+        CGFloat y =cos(i * dig) * size + dy;
+        // 添加二次曲线路径
+        CGContextAddQuadCurveToPoint(c, ctrlX , ctrlY , x , y);
+    }
+}
+

绘制花瓣

+
- (void)drawStar
+{
+    CGContextRef ctx = UIGraphicsGetCurrentContext();  // 获取绘图的CGContextRef
+    CGContextBeginPath(ctx);  // 开始添加路径
+    CGContextAddFlower(ctx , 5 , 50 , 100 , 30 , 80);  // 添加5瓣花朵的路径
+    CGContextSetRGBFillColor(ctx, 1, 0, 0, 1);  // 设置填充颜色
+    CGContextFillPath(ctx);
+    CGContextAddFlower(ctx , 6 , 160 , 100 , 30 , 80);  // 添加6瓣花朵的路径
+    CGContextSetRGBFillColor(ctx, 1, 1, 0, 1);  // 设置填充颜色
+    CGContextFillPath(ctx);
+    CGContextAddFlower(ctx , 7 , 270 , 100 , 30 , 80);  // 添加7瓣花朵的路径
+    CGContextSetRGBFillColor(ctx, 1, 0, 1, 1);  // 设置填充颜色
+    CGContextFillPath(ctx);
+    CGContextClosePath(ctx);  // 关闭路径
+}
+
+ +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2016/12/05/GCD\345\256\236\347\216\260\346\214\211\351\222\256\345\256\232\346\227\266\344\272\256\350\265\267/index.html" "b/2016/12/05/GCD\345\256\236\347\216\260\346\214\211\351\222\256\345\256\232\346\227\266\344\272\256\350\265\267/index.html" new file mode 100644 index 0000000..30a8873 --- /dev/null +++ "b/2016/12/05/GCD\345\256\236\347\216\260\346\214\211\351\222\256\345\256\232\346\227\266\344\272\256\350\265\267/index.html" @@ -0,0 +1,274 @@ + + + + + + + + GCD实现按钮定时亮起 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ GCD实现按钮定时亮起 +

+ + +
+ +
+ +

某些时候需要按钮不能连续点击,需要在一定的时间之后才可以允许交互,或者是实现类似发送验证码的按钮效果,具体做法是采用定时器

+

使用GCD定时器

创建定时器对象

__block NSInteger time = 30; // 需要强应用
+

创建队列

dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
+

创建dispatch源(定时器)

(dispatach_source…timer..)
+
    +
  • 01参数:要创建的source 是什么类型的,

    +

    (DISPATCH_SOURCE_TYPE_TIMER)定时器

    +
  • +
  • 04参数:队列 —-线程 决定block 在哪个线程中调用

    +
  • +
+

代码

+
dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
+

设置定时器

    +
  • 01参数:定时器对象

    +
  • +
  • 02参数:开始时间 (DISPATCH_TIME_NOW) 什么时候开始执行第一次任务

    +
  • +
  • 03参数:间隔时间 GCD时间单位:纳秒

    +
  • +
  • 04参数:leewayInSeconds精准度:允许的误差: 0 表示绝对精准

    +
  • +
+

code:

+
dispatch_source_set_timer(_timer, dispatch_walltime(NULL, 0), 1.0 * NSEC_PER_SEC, 0);
+

定时器每隔一段时间就要执行任务(block回调)

dispatch_source_set_event_handler(_timer, ^{
+    if (time <= 0) {
+        dispatch_source_cancel(_timer);
+        dispatch_async(dispatch_get_main_queue(), ^{
+
+            // 设置按钮的样式
+            [self.button setTitle:@"重新获取验证码" forState:UIControlStateNormal];
+            [self.button setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
+            [self.button setUserInteractionEnabled:YES];
+        });
+    } else {
+
+        NSInteger seconds = time;
+        dispatch_async(dispatch_get_main_queue(), ^{
+            [self.button setTitle:[NSString stringWithFormat:@"重新发送(%.2ld)", seconds] forState:UIControlStateNormal];
+            [self.button setTitleColor:[UIColor lightGrayColor] forState:UIControlStateNormal];
+            [self.button setUserInteractionEnabled:NO];
+        });
+        time--;
+    }
+});
+

启动定时器(默认是停止的)

dispatch_resume(timer);
+
+ +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2016/12/10/Quartz2D\347\273\230\345\210\266\345\275\242\347\212\266/index.html" "b/2016/12/10/Quartz2D\347\273\230\345\210\266\345\275\242\347\212\266/index.html" new file mode 100644 index 0000000..840e5b9 --- /dev/null +++ "b/2016/12/10/Quartz2D\347\273\230\345\210\266\345\275\242\347\212\266/index.html" @@ -0,0 +1,239 @@ + + + + + + + + Quartz 2D绘制形状(二) | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ Quartz 2D绘制形状(二) +

+ + +
+ +
+ +

使用Quartz 2D绘制圆弧,扇形,下载进度以及绘制的图形的填充和描边属性的设置.
将贝塞尔路径添加到图形上下文中
图形上下文中的状态保存和恢复
对图形上下文进行矩阵操作(平移,缩放,选转),实现一些特殊效果

+

使用贝塞尔曲线绘制矩形

1
2
3
4
5
6
7
{
UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, 20, 30)];
[[UIColor magentaColor] setStroke];
[[UIColor brownColor] setFill];
[path fill]; // 填充
[path stroke]; // 描边
}
+

圆角矩形

1
2
3
4
5
6
7
8
// 绘制圆角矩形
{
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(35, 10, 50, 70) cornerRadius:5.0];
[[UIColor magentaColor] setStroke];
[[UIColor cyanColor] setFill];
[path fill];
[path stroke];
}
+

绘制圆

1
2
3
4
5
6
7
8
// 画圆
{
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(90, 5, 40, 40) cornerRadius:20.0];
[[UIColor purpleColor] setStroke];
[[UIColor magentaColor] setFill];
[path fill];
[path stroke];
}
+

绘制扇形

1
2
3
4
5
6
CGPoint center = CGPointMake(200, 50);
UIBezierPath *sectorPath = [UIBezierPath bezierPathWithArcCenter:center radius:100 startAngle:0 endAngle:M_PI_4 clockwise:YES];
[sectorPath addLineToPoint:center];
[[UIColor brownColor] setStroke];
[sectorPath fill];
[sectorPath stroke];
+

绘制圆弧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 绘制圆弧
* 参数1: 圆心坐标
* 参数2: 半径
* 参数3: 开始角度
* 参数4: 结束角度 使用弧度制表示
* 参数5: clockwis顺时针方法的
*/

UIBezierPath *circuLarArcPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(100, 60) radius:50.0 startAngle:0 endAngle:M_PI_2 clockwise:YES];

[circuLarArcPath moveToPoint:CGPointMake(100, 60)];

[[UIColor magentaColor] setStroke];
[[UIColor brownColor] setFill];
[circuLarArcPath stroke];
[circuLarArcPath fill];
+

绘制文字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
* 绘制文字
* drawAtPoint 不能换行,当文字过多过长时不会有效果
* 使用 drawInRect 可以实现换行
*/


NSString *text = @"我是绘制文字价格就看过几个框架刚开始大家高考的感觉是快乐的关键时刻大家赶快来时的结果看了风水格局来看就哭了感觉树大根深来对抗肌肤抵抗力";

NSMutableDictionary *attributeDict = [NSMutableDictionary dictionary];
attributeDict[NSFontAttributeName] = [UIFont systemFontOfSize:18];

// 字体颜色
attributeDict[NSForegroundColorAttributeName] = [UIColor greenColor];
// 描边颜色
attributeDict[NSStrokeColorAttributeName] = [UIColor yellowColor];
// 描边宽度
attributeDict[NSStrokeWidthAttributeName] = @1;
// 阴影
NSShadow *shadow = [[NSShadow alloc] init];
shadow.shadowColor = [UIColor redColor];
shadow.shadowOffset = CGSizeMake(1.0, 1.0);

attributeDict[NSShadowAttributeName] = shadow;

// 可以换行
[text drawInRect:self.bounds withAttributes:attributeDict];

// 不换行
[text drawAtPoint:CGPointZero withAttributes:attributeDict];
+

绘制图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 绘制图片
* drawAtPoint 默认绘制的内容尺寸和图片的尺寸一样大
*
*/

- (void)drawImage {

UIImage *image = [UIImage imageNamed:@"wc_dk"];
UIImage *image1 = [UIImage imageNamed:@"wc_hq"];

[image drawAtPoint:CGPointMake(10, 90)];
[image1 drawAtPoint:CGPointMake(70, 80)];

// 绘制在控件内部,和控件大小一致
[image1 drawInRect:self.bounds];

// 以平铺的方式绘制在控件的内部
[image1 drawAsPatternInRect:self.bounds];
}
+

圆形下载进度的绘制

drawRect 方法不能手动调用,因为图形上下文不能手动创建
需要调用setNeedsDisplay方法来重绘,系统会创建图形上下文
绘制时用到的定时器,一般不使用NSTimer定时器,因为调度优先级比较低,不会被准时带,界面会出现卡顿的现象
CADisplayLink 每次刷新屏幕的时候就会调用,1s刷新 60 次
setNeedsDisplay调用此方法时并不是立即调用drawRect方法,只是给给当前控件添加刷新标记,直到下一次屏幕刷新的时候才调用drawRect 方法,正好与CADisplayLink 方法一致,所以界面流畅不会卡顿
图形上下文状态栈

+

UIBezierPath图形上下文和CGContextRef的图形上下文不是同一个图形上下文

+

可以将贝塞尔路径添加到图形上下文中

+

也可以保存当前的图形上下文,CGContextSaveGState(ctx);
在下一次使用时可以恢复,CGContextRestoreGState(ctx);
画两种不同状态的线条

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
/**
* 图形上下文栈的理解
* 画两条状态不同的线段
*/

// 获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();

// 第一条
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(10, 60)];
[path addLineToPoint:CGPointMake(240, 60)];

// 把路径添加到上下文
CGContextAddPath(ctx, path.CGPath);

// 保存上下的状态
CGContextSaveGState(ctx);

// 设置当前状态
[[UIColor greenColor] setStroke];
//此时设置path 的状态无效,需要设置当前上下文的状态
path.lineWidth = 5.0;
CGContextSetLineWidth(ctx, 5.0);

// 渲染上下文
CGContextStrokePath(ctx);


// 第二根

// 可使用第一根的路径,因为每次绘制完成之后都会清除
path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(60, 10)];
[path addLineToPoint:CGPointMake(60, 240)];

// 添加路径到上下文 需要转换为CGPath
CGContextAddPath(ctx, path.CGPath);

// 还原之前保存的上下文状态
CGContextRestoreGState(ctx);
CGContextStrokePath(ctx);
图形上下文矩阵

// 使用矩阵

// 获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();

// 使用贝塞尔路径
UIBezierPath *ovalPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(-150, -100, 300, 200)];
[[UIColor cyanColor] setFill];



// 以上绘制的图形有一部分看不到,通过对上下文矩阵操作来实现,平移上下文
// 操作矩阵必须在添加路径之前

// 平移
CGContextTranslateCTM(ctx, 150, 100);

// 缩放
CGContextScaleCTM(ctx, 0.5, 0.5);

// 旋转
// 可以实现点无法计算时的效果
CGContextRotateCTM(ctx, M_PI_4);



// 添加路径到上下文
CGContextAddPath(ctx, ovalPath.CGPath);

// 操作矩阵放在添加路径之后无效果
// CGContextTranslateCTM(ctx, 150, 100);

CGContextFillPath(ctx);

// 正常的绘制椭圆
// UIBezierPath *ovalPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(50, 20, 200, 100)];
// ovalPath.lineWidth = 4.0;
// [[UIColor magentaColor] setFill];
// [[UIColor yellowColor] setStroke];
// [ovalPath stroke];
// [ovalPath fill];
+ +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2017/01/18/\345\274\202\346\255\245\344\270\213\350\275\275\345\233\276\347\211\207/index.html" "b/2017/01/18/\345\274\202\346\255\245\344\270\213\350\275\275\345\233\276\347\211\207/index.html" new file mode 100644 index 0000000..917dadb --- /dev/null +++ "b/2017/01/18/\345\274\202\346\255\245\344\270\213\350\275\275\345\233\276\347\211\207/index.html" @@ -0,0 +1,275 @@ + + + + + + + + 异步下载图片 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ 异步下载图片 +

+ + +
+ +
+ +

异步下载

同步发送网络请求会卡UI线程,采用异步来下载图片。

+

异步下载存在的问题

+
    +
  • 错行问题

    +
      +
    • 解决:使用MVC模式,使数据一对一可解决,
    • +
    • 在MVC中增加image属性后,会导致单个模式的体积增大
    • +
    • 内存占用
    • +
    • 当接收到内存警告时清空图片时需要遍历模型中将image置为nil
    • +
    • 将图片用字典缓存起来达到一对一, 清空内存中的图片时只需要remove字典就ok
    • +
    • 创建下载任务的字典,在开始下载时将正在下载的URL写入到字典中下载完成后将字典中对应的值移除,写入防止来回滑动时重复发送网络请求,下载完后移除是为了防止如果接受到内存警告后下载的图片都已清除,但是下载任务的字典的值为移除就不会再一次去下载图片
    • +
    • 缓存下载的图片到沙盒中

      +
    • +
    • 获取图片下载路径最后一个分隔符后面的字符串作为图片名保存

      +
    • +
    +
  • +
+

stringByAppendingPathComponentstringByAppendingString 两个方法有区别

+

stringByAppendingString 方法是获取caches文件名,在文件夹名后面拼接字符串,在caches上一级目录下创建文件,和caches文件夹属于同一个级别

+
// 获取caches文件路径
+NSString *cachesPath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
+
+NSString *URL = @"http://wwww.baidu.com/image/123456.png";
+
+NSString *imageName = [URL lastPathComponent];
+
+// 拼接路径
+// 此方法生成的路径在caches文件下目录下
+NSString *filePath = [cachesPath stringByAppendingPathComponent];
+
+
+// 读取图片
+if (内存中没有) {
+    // 沙盒读取
+    UIImage *sandBoxImage = [UIImage imageWithContetsOfFile:filePath];
+
+    **加入到内存中,方便下次加载**
+} 
+
+if (sandBoxImage == nil) {
+    // 下载图片
+}
+
+// 写入到沙盒
+if (imageData != nil) {
+    [imageData writeToFile: filePath atomically:NO];
+}
+
+ +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2017/02/02/iOS\351\235\231\346\200\201\345\272\223\345\212\250\346\200\201\345\272\223/index.html" "b/2017/02/02/iOS\351\235\231\346\200\201\345\272\223\345\212\250\346\200\201\345\272\223/index.html" new file mode 100644 index 0000000..8550c89 --- /dev/null +++ "b/2017/02/02/iOS\351\235\231\346\200\201\345\272\223\345\212\250\346\200\201\345\272\223/index.html" @@ -0,0 +1,266 @@ + + + + + + + + WKWebiView 使用 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ WKWebiView 使用 +

+ + +
+ +
+ +

iOS中静态库动态库介绍及编译

    +
  • 静态库
  • +
+

iOS中根据源码的公开情况可分为2种

+
    +
  • 开源库
    源代码公开,能够看到具体的代码实现
  • +
+

比如 SDWebImage AFNetworking。。。

+
    +
  • 闭源库
    不公开源代码, 是经过编译后的二进制文件, 看不到具体实现
    主要分为:静态库、动态库

    +
  • +
  • 存在形式

    +
  • +
+

静态库: .a 和 .framework
动态库:.tbd 和 .framework (iOS 9 之前是 .dylib, iOS 9 之后取消了 .dylib 而使用 .tbd 来代替 .dylib )

+

.framework 的库可以是静态库也可以是静态库, 凡是系统的 .framework 都是动态库,第三方的基本上全是静态中

+
    +
  • 两者区别
  • +
+

静态库:链接时会被完整的复制到可执行文件中,每次被使用都会被复制
动态库:链接时不复制,程序运行时由系统动态加载到内存中,供程序调用,系统只加载一次,多个程序可以公用,节省内存

+

静态库编译

编译时如果使用的是模拟器,生成的 .a 只能模拟器用,真机不可用,生成的 .a 文件在 Debug-iphonesimulator 文件夹下

+

编译时如果使用 generic iOS Device,生成的 .a 只能真机而模拟器不可用,生成的 .a 文件在 Debug-iphoneos 文件夹下

+

编译静态库时,自己手动创建的类的头文件不会被导出,只是默认导出创建工程时类的头文件

+

导出手动添加类的头文件,点击工程名称 –> build Phases –> Copy Files 添加头文件,然后编译

+
    +
  • 架构种类
  • +
+

模拟器:i386 32位架构 (4s~5) / x86_64 64位架构 (5s以后机型)
真机:armv7 32位架构 (4~4s) / armv7s 特殊架构 (5~5C) / arm64 64位架构 (5s 以后机型)

+
    +
  • 架构查看
    lipo -info xxxx.a
    默认生成模拟器只生成一种架构 i386 x86_64;真机默认导出两种架构 armv7 / arm64

    +
  • +
  • 合并多个架构

    +
  • +
+

将模拟器的多个架构合并,项目名称–>Build Settings –> Achitectures –> Build Active Architecture Only –> Debug设置为NO
合并真机多个架构 按照以上方法运行真机即可
合并真机和模拟器架构 cd products lipo -create Debug-iphoneos/xxx.a Debug-iphonessimulator/xxx.a -output xx.a
合成 5 个架构 在Build Setting –> Architectures 中手动添加三种架构,再编译合并

+
    +
  • 静态库报错
  • +
+

Undefined symbols for architecture (arm64 / armv7 / armv7s / i386 / x86_64),架构包导入出错,静态库有真机与模拟器

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2017/02/08/SDWebImage\345\255\246\344\271\240/index.html" "b/2017/02/08/SDWebImage\345\255\246\344\271\240/index.html" new file mode 100644 index 0000000..38cdba3 --- /dev/null +++ "b/2017/02/08/SDWebImage\345\255\246\344\271\240/index.html" @@ -0,0 +1,300 @@ + + + + + + + + SDWebImage的用法及原理 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ SDWebImage的用法及原理 +

+ + +
+ +
+ +

SDWebImage 介绍

SDWebImage 是用于网络中下载且缓存图片,并设置图片到对应的控件或上

+

Demo: https://github.com/aTreey/DownImage.git

+
    +
  • 提供了UIImageView的category用来加载网络图片并且下载的图片的缓存进行管理
  • +
  • 采用异步方式来下载,确保不会阻塞主线程,memory+disk来缓存网络图片,自动管理缓存
  • +
  • 支持GIF动画,[self.imageView setImageWithURL:[[NSBundle mainBundle] URLForResource:@”xx.gif” withExtension:nil];
  • +
  • 支持WebP格式
  • +
  • 同一个URL的网络图片不会被重复下载
  • +
  • 失效的URL不会被无限重试
  • +
+

SDWebImage 的使用

克隆

+

git clone https://github.com/rs/SDWebImage.git
使用以上命令克隆会报错,框架中Vendors文件夹中的文件未能全部下载导致报错

+

解决办法:https://github.com/rs/SDWebImage/blob/master/Docs/ManualInstallation.md

+

git clone –recursive https://github.com/rs/SDWebImage.git

+

文件全部下载

+

自定义operation 加入NSOperationQueue中

+
    +
  • 自定义NSOperation,
    重写main方法
  • +
+
1
2
3
4
5
6
7
8
9
10
11
- (void)main {
@autoreleasepool {
NSURL *url = [NSURL URLWithString:_urlStr];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];

if (_finishBlock) {
_finishBlock(image);
}
}
}
+
    +
  • 一个NSOperation对象可以通过调用start方法来执行任务,默认是同步执行的。也可以将NSOperation添加到一个NSOperationQueue(操作队列)中去执行,而且是异步执行的
    创建队列
  • +
+
1
2
3
4
- (NSOperationQueue *)downLoadQueue {
if (!_downLoadQueue) _downLoadQueue = [[NSOperationQueue alloc] init];
return _downLoadQueue;
}
+
    +
  • 添加一个任务到队列
  • +
+
1
2
3
4
5
6
7
8
9
[_downLoadQueue addOperation:operation];
添加一组operation, 是否阻塞当前线程

[_downLoadQueue addOperations:@[operation] waitUntilFinished:NO];
添加一个block 形式的operation

[_downLoadQueue addOperationWithBlock:^{
NSLog(@"执行一个新的线程");
}];
+

注意:

+
    +
  1. NSOperation 添加到 queue之后,通常短时间内就会执行,但是如果存在依赖,或者整个queue被暂停等原因,也可能会需要等待

    +
  2. +
  3. NSOperation添加到queue之后,绝不要修改NSOperation对象的状态,因为NSOperation对象可能会在任何时候运行,因此改变NSOperation对象的依赖或者数据会产生不利的影响,只能查看NSOperation 对象的状态,比如是否正在运行、等待运行、已经完成等

    +
  4. +
+
    +
  • NSOperation 添加依赖,
    依赖关系不局限于相同的queue 中的NSOperation对象,可以夸队列进行依赖,但是不能循环依赖,
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// NSOperation 添加依赖
{
NSOperationQueue *testQueue = [[NSOperationQueue alloc] init];
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"operation1 NSThread = %@", [NSThread currentThread]);
}];

NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"operation2 NSThead = %@", [NSThread currentThread]);
}];

// 添加依赖,1 依赖于 2,只有2 执行完之后才执行1
[operation1 addDependency:operation2];
// 加入到队列
[testQueue addOperation:operation1];
[testQueue addOperation:operation2];
}
+
    +
  • 修改Operation的执行顺序
    对于添加到queue中的operation执行顺序有一下2点决定
    operation 是否已经准备好,是否添加了依赖
    根据多有operations的相对优先性来决定,优先等级是operation对象本身的一个属性,默认都是“普通”优先级,可以通过setQueuePriority方法提高和降低优先级,优先级只能用于相同 queue 中的 operation,多个queue中operation的优先级相互独立,因此不同queue中的低优先级的operation可能比高优先级的operation更早执行
    注意优先级和依赖不能相互替代,优先级只是对已经准备好的operation确定执行顺序,先满足依赖,然后再看已经准备好的operation中的优先级

    +
  • +
  • 设置 queue 的最大并发数,队列中最多同时运行几条线程
    虽然NSOperationQueue类设计用于并发执行Operations,你也可以强制单个queue一次只能执行一个Operation。setMaxConcurrentOperationCount:方法可以配置queue的最大并发操作数量。设为1就表示queue每次只能执行一个操作。不过operation执行的顺序仍然依赖于其它因素,比如operation是否准备好和operation的优先级等。因此串行化的operation queue并不等同于GCD中的串行dispatch queue

    +
  • +
+
1
2
3
4
// 每次只能执行一个操作  
queue.maxConcurrentOperationCount = 1;
// 或者这样写
[queue setMaxConcurrentOperationCount:1];
+
    +
  • 取消 operations
    一旦添加到operation queue,queue就拥有了这个Operation对象并且不能被删除,只能取消。调用Operation对象的cancel方法取消单个操作,也可以调用operation queue的cancelAllOperations方法取消当前queue中的所有操作, 使用cancel属性来判断是否已经取消了
  • +
+

[operation cacel] 只是打了一个死亡标记, 并没有正真意义上的取消,称为 “自杀“,需要在被取消的任务中时时判断是否取消(在程序的关键处), 如果取消,结束任务, 具体是指在自定义的 operation 的main方法中结束

+
1
2
3
4
5
6
[queue cancelAllOperations] 由队列来取消,称为 “他杀”

// 取消单个操作
[operation cancel];
// 取消queue中所有的操作
[queue cancelAllOperations];
+
    +
  • 等待operation 完成
  • +
+

为了最佳的性能,你应该设计你的应用尽可能地异步操作,让应用在Operation正在执行时可以去处理其它事情。如果需要在当前线程中处理operation完成后的结果,可以使用NSOperation的waitUntilFinished方法阻塞当前线程,等待operation完成。通常我们应该避免编写这样的代码,阻塞当前线程可能是一种简便的解决方案,但是它引入了更多的串行代码,限制了整个应用的并发性,同时也降低了用户体验。绝对不要在应用主线程中等待一个Operation,只能在第二或次要线程中等待。阻塞主线程将导致应用无法响应用户事件,应用也将表现为无响应。

1
2
// 会阻塞当前线程,等到某个operation执行完毕  
[operation waitUntilFinished];

+

除了等待单个Operation完成,你也可以同时等待一个queue中的所有操作,使用NSOperationQueue的waitUntilAllOperationsAreFinished方法。注意:在等待一个 queue时,应用的其它线程仍然可以往queue中添加Operation,因此可能会加长线程的等待时间。

+

// 阻塞当前线程,等待queue的所有操作执行完毕
[queue waitUntilAllOperationsAreFinished];
暂停和继续operation
如果你想临时暂停Operations的执行,可以使用queue的setSuspended:方法暂停queue。不过暂停一个queue不会导致正在执行的operation在任务中途暂停,只是简单地阻止调度新Operation执行。你可以在响应用户请求时,暂停一个queue来暂停等待中的任务。稍后根据用户的请求,可以再次调用setSuspended:方法继续queue中operation的执行

1
2
3
4
5
// 暂停queue  
[queue setSuspended:YES];

// 继续queue
[queue setSuspended:NO];

+

增加Manager管理类,

作用:

+
    +
  • 下载队列
  • +
  • 图像缓存
  • +
  • 操作缓存(内存缓存)
  • +
  • 沙盒缓存
  • +
  • 防止错乱,取消老的未开始下载的操作
  • +
  • 图片显示逻辑:内存缓存 – 沙盒缓存 – 网络下载
  • +
  • 图片缓存逻辑:下载完成 – 缓存沙盒 – 加载到内存
  • +
+

注意:缓存到本地时只能保存property列表里的对象(NSData, NSDate, NSNumber, NSString, NSArray, NSDictory), 图片需要转化为二进制数据

+

SDWebImage

    +
  • SDWebImageDownloader 下完图片后都需要手动设置给UIImageView
  • +
  • SDWebImageManager 下完图片后都需要手动设置给UIImageView
  • +
  • 下载后要显示可以使用 sd_setImageWithURL 方法
  • +
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2017/02/20/OC\344\270\216js\344\272\244\344\272\222\345\255\246\344\271\240/index.html" "b/2017/02/20/OC\344\270\216js\344\272\244\344\272\222\345\255\246\344\271\240/index.html" new file mode 100644 index 0000000..ed9c3c0 --- /dev/null +++ "b/2017/02/20/OC\344\270\216js\344\272\244\344\272\222\345\255\246\344\271\240/index.html" @@ -0,0 +1,238 @@ + + + + + + + + OC和JS交互 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ OC和JS交互 +

+ + +
+ +
+ +

WebView相关属性

autoResizingMask 是UIView的属性,作用是实现子控件相对于父控件的自动布局

+
1
2
3
4
5
6
7
8
9
autoResizingMask 是UIViewAutoresizing 默认是 UIViewAutoresizingNone

UIViewAutoresizingNone = 0,
UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
UIViewAutoresizingFlexibleWidth = 1 << 1,
UIViewAutoresizingFlexibleRightMargin = 1 << 2,
UIViewAutoresizingFlexibleTopMargin = 1 << 3,
UIViewAutoresizingFlexibleHeight = 1 << 4,
UIViewAutoresizingFlexibleBottomMargin = 1 << 5
+

各属性解释:

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
UIViewAutoresizingNone
不会随父视图的改变而改变

UIViewAutoresizingFlexibleLeftMargin

自动调整view与父视图左边距,以保证右边距不变

UIViewAutoresizingFlexibleWidth
自动调整view的宽度,保证左边距和右边距不变

UIViewAutoresizingFlexibleRightMargin
自动调整view与父视图右边距,以保证左边距不变

UIViewAutoresizingFlexibleTopMargin
自动调整view与父视图上边距,以保证下边距不变

UIViewAutoresizingFlexibleHeight
自动调整view的高度,以保证上边距和下边距不变

UIViewAutoresizingFlexibleBottomMargin
自动调整view与父视图的下边距,以保证上边距不变
+

OC 调用js

webViewDidFinishLoad:(UIWebView *)webView方法中

+
    +
  • 删除增加结点
  • +
  • 添加点击事件
  • +
+

js调用OC

webView:(UIWebview )webView shouldStartLoadWithRequest:(NSURLRequest )request方法中

+

调用js页面点击按钮后保存图片到相册,必须调用系统提供的方法,不能自己自定义方法

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2017/03/01/Swift3.0\345\255\246\344\271\240(\344\270\200)/index.html" "b/2017/03/01/Swift3.0\345\255\246\344\271\240(\344\270\200)/index.html" new file mode 100644 index 0000000..424d37d --- /dev/null +++ "b/2017/03/01/Swift3.0\345\255\246\344\271\240(\344\270\200)/index.html" @@ -0,0 +1,296 @@ + + + + + + + + Swift 3.0 学习 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ Swift 3.0 学习 +

+ + +
+ +
+ +

和OC区别

    +
  • 没有了.h 和 .m 文件,没有main.m 文件
  • +
  • 程序入口发生变化 在appdelegate中的 @UIApplicationMain
  • +
  • 每行代码可以不写 ‘;’
  • +
  • self可以不写,建议不写self,因为闭包中必须要写
  • +
  • 真假表示只有 true/ false,没有非0即真的概念
  • +
  • 类型自动推到
  • +
+

类型自动推到

提前指定类型

1
2
let float :Double = 9.6 
print(float)
+

常量和变量

    +
  • let 声明常量,有且只有一次赋值机会
  • +
  • var 声明变量
  • +
  • 尽量选择使用let如需修改值再使用var 系统会报警告
  • +
+

可选项

    +
  • Optional: 表示一个常量或者是变量可能有值可能为nil,在打印的时候会带有Optional字样
  • +
  • ? 表示是一个可选项
  • +
  • 可选项不能参与运算
  • +
  • ! 表示可选项中一定要有值,可以强制解包,解包时如果没有值会boom

    +
  • +
  • ?? 合并空选项 快速判断可选项是否为nil, 如果为nil取后面的默认值,在基本数据类型和字符串使用较多

    +
  • +
+

条件分支

    +
  • if let
  • +
+

快速赋值,判断赋值对象是否为nil,如果不为nil,就赋值进入分支
生成request是url是一个可选项,需要强制解包或者合并空选项

+
1
2
3
4
let urlString = "http://www.baidu.com"
let url = NSURL(string: urlString)
let request = NSURLRequest(URL: url!)
print(request)
+
    +
  • 以上代码使用if let实现
  • +
+
1
2
3
4
if let u = NSURL(string: urlString) {
let request = NSURLRequest(URL: u)
print(request)
}
+
    +
  • where条件语句配合多重判断
  • +
+
1
2
3
4
5
6
if url != nil  {
if url?.host == "www.baidu.com" {
let request = NSURLRequest(URL: url!)
print(request)
}
}
+
    +
  • 使用if let
  • +
+
1
2
3
4
if let u = url where u.host == "www.baidu.com" {
let request = NSURLRequest(URL: u)
print(request)
}
+
    +
  • 多重判断
  • +
+
1
2
3
4
if let u = url, s = string {
let request = NSURLRequest(URL: u)
print(request)
}
+
    +
  • guard let 和 if let 相反如果为nil,可以直接返回
  • +
+
1
2
3
4
5
6
7
let urlString  = "http://www.baidi.com"
let url = NSURL(string: urlString)
guard let u = url else { return }

//程序走到这个地方就表示 u 一定有值
let request = NSURLRequest(URL: u)
print(request)
+
    +
  • swith语句

    +
      +
    • swift语句中可以case任意类型,
    • +
    • 在OC中只能case整数
    • +
    • 不需要写break,会自动跳出,
    • +
    • 如果需要实现case穿透,需要加入fallthrough语句
    • +
    • case语句中临时变量不需要写‘{ }’
    • +
    • 可同时case多个值
    • +
    • 每句case语句中至少要有一行可执行的代码
    • +
    +
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let string = "string1"
switch string {
case "string", "string1":
let str = "stringOne"
print(str)

case "string2":
let str = "stringTWO"
print(str)

case "string3":
print(string)

case "string4":
print(string)
default:
print("empty")
}
+
    +
  • switch 中同样能够赋值和使用 where 子句
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
let point = CGPoint(x: 10, y: 10)
switch point {
case let p where p.x == 0 && p.y == 0:
print("中心点")
case let p where p.x == 0:
print("Y轴")
case let p where p.y == 0:
print("X轴")
case let p where abs(p.x) == abs(p.y):
print("对角线")
default:
print("其他")
}
+
    +
  • 循环语句
  • +
+
1
2
3
4
5
6
7
for i in 0 ..< 10 { // 不包含10  循环范围中两边的值格式必须一直(空格问题)
print(i)
}

for _ in 0...5 {// 包含5 _ 表示忽略,不关心,只占位
print("循环语句")
}
+ +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2017/03/05/Swift3.0\345\255\246\344\271\240(\344\272\214)/index.html" "b/2017/03/05/Swift3.0\345\255\246\344\271\240(\344\272\214)/index.html" new file mode 100644 index 0000000..bef608b --- /dev/null +++ "b/2017/03/05/Swift3.0\345\255\246\344\271\240(\344\272\214)/index.html" @@ -0,0 +1,352 @@ + + + + + + + + Swift3.0学习(二) | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ Swift3.0学习(二) +

+ + +
+ +
+ +

字符串

+

字符串遍历
字符串字节长度
字符串长度

+

let string = “我是switf,语言”

+

// 字符串遍历
for s in string.characters {
print(s)
}

+

// 字符串的长度
let length = string.characters.count;
print(length)

+

// 字符串字节长度 汉字对应3个字节,英文是1个字节
let characterLength = string.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
print(characterLength)

+

字符串拼接

+
let str1 = "你好"
+   let str2 = "不好"
+   let a = 20
+
+   // 第一种拼接
+   print("\(str1)\(str2)\(a)")
+
+   // 第二种拼接
+   let str = str1 + str2
+   let strA = str1 + String(a)
+   print(str)
+   print(strA)
+

截取子串 as 的使用

+
let subString = str1.substringToIndex("小程生生世世".endIndex);
+   print(subString)
+
+   let subStr = str1.substringFromIndex("听说".startIndex)
+   print(subStr)
+
+   let range = str1.rangeOfString("小程序")
+   let subStr1 = str1.substringWithRange(range!)
+
+   print(subStr1)
+
+   // 截取到某一个位置
+   let subStr2 = str1.substringFromIndex(str1.endIndex.advancedBy(-2))
+   print(subStr2)
+
+   // 限制截取几个字符
+   let subStr3 = str1.substringToIndex(str1.startIndex.advancedBy(5, limit: str1.characters.endIndex))
+   print(subStr3)
+String 和 NSString 之间的 使用 as 无缝转换
+let helloString = "我们一起飞"
+(helloString as NSString).substringWithRange(NSMakeRange(2, 3))
+数组
+

可以放不同类型的元素
let不可变数组,var可变数组

+
// 初始化一个空数组 [String]声明是一个装有字符串的数组类型
+   var emptyArray: [String] = [String]()
+
+// 可变数组
+   var array = ["哈哈","呵呵","嘿嘿"]
+   array.append("咯咯")
+   print("array = \(array)")
+
+   // 不可变数组
+   let array2 = ["可可","噗噗"]
+
+   // 数组拼接
+   let arr = array + array2
+   print("arr = \(arr)")
+
+   array += array2
+   print("array = \(array)")
+

数组遍历

+
// 1. 
+   for i in 0 ..< tempArray.count {
+       print(tempArray[i])
+   }
+
+
+   // 2. 
+   for obj in tempArray {
+       print("第二种方法:\(obj)")
+   }
+
+
+   // 3. 快速便利
+   for (index,value) in tempArray.enumerate() {
+       print("index = \(index), value = \(value)")
+   }
+

字典和OC类似

+

函数调用

+

必须在函数声明的下面调用内部函数

+

可以定义函数的外部参数和内部参数

+

可以在参数前面加上 ‘# ’ 表明函数的外部参数和内部参数名称一样 swift3.0 以后废弃

+

无参无返回值
三种写法

+
func demo9() -> Void {
+       print("无参无返回值第一种写法")
+   }
+
+
+   func demo_9() -> () {
+       print("无参无返回值第二种写法")
+   }
+
+   func demo_99() {
+       print("无参无返回值第三种写法")
+   }
+

有参有返回值

+
func demo10(width a: Int, height b: Int) -> Int {
+   return a * b
+

}

+

闭包

+

和OC中block类似,
和在函数中调用一个内部函数原理相同
可当作参数传递
在需要时执行闭包实现回调

+
// 定义闭包
+let closure = {() -> () in
+       print("闭包实现")
+   }
+
+   // 执行闭包
+   closure()
+

注意循环引用

+

只有两个对象相互强引用或者三个对象相互强引用形成闭环时才会形成循环引用

+

weak 和 unsafe_unretained的区别

+

__weak iOS5.0 推出,当对象被系统回收时,对象的地址会自动指向nil

+

____unsafe_unretained iOS4.0 推出,当对象被系统回收时,对象的地址不会自动指向nil,会造成野指针访问
解决办法:(官方推荐)weak - strong -dance 解决循环引用,AFNetworking中

+

尾随闭包

+

函数的最后一个参数时闭包的时候,函数的参数 ‘()’可以提前关闭,闭包写在 ‘()’后面,当作尾随闭包来使用

+

swift 和 OC 区别

+

swift 和 OC语法的快速的对比

+

XXX.init(xxx) ==> XXX.(xxx)
selector 类型 ==> ‘函数名’
对象方法的调用 是 ‘.’ self是可以省略的
枚举 枚举名 + 枚举值的名 => .枚举值的名
常量 和变量

+

let 声明常量 var 声明变量
变量/常量的类型是自动推到的
不同类型之间不能够直接运算 需要手动转换数据类型
可选项
‘?’ 表示表示可选项 可能有值 可能为 nil
可选项会自动带上 Optional 字样
可选项不能够直接参与运算 需要强制解包
‘!’ 表示强制解包 获取可选项中具体的值
‘??’ 快速判断可选项是否为nil 如果为 nil 就去取 ‘??’后面的默认值
控制流
if 没有非零即真的概念 必须制定明确的条件
if let 快速赋值 并且判断赋值对象是否为 nil 如果不为nil 就进入分之执行相关逻辑代码
guard let 作用和 if let 相反 好处: 可以减少一层分支嵌套
where 多重判断 注意 和 && || 不一样的
循环

+

for in
0..<10
0…10
字符串

+

更加轻量级 更加高效 是 结构体
支持快速遍历
长度
拼接
截取 as
集合 let 声明不可变的集合 var 声明可变的集合

+

声明 []
声明一个 空的集合
增删改查
合并
遍历
函数

+

func 函数名称(外部参数1 内部参数1: 类型, 外部参数2 内部参数2: 类型) -> Int {执行的代码}
函数没有返回值的三种方式
函数内部函数
闭包 () -> () 没有参数没有返回值的闭包类型

+

提前准备好的一段可以执行代码块
可以当做参数传递
在需要的时候执行闭包产生回调的效果
在闭包中使用self 需要考虑循环引用
函数是一种特殊的闭包

+

block 的循环引用的解除
weak 和 weak关键字作用类似 当属性对象被回收是 会自动指向nil
_
unsafeunretained 和 assgin关键字作用类似 当属性对象被回收是 不会自动指向nil, 会造成野指针访问 weak -strong - dance

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2017/03/08/Swift3.0\345\255\246\344\271\240(\344\270\211)/index.html" "b/2017/03/08/Swift3.0\345\255\246\344\271\240(\344\270\211)/index.html" new file mode 100644 index 0000000..4abed13 --- /dev/null +++ "b/2017/03/08/Swift3.0\345\255\246\344\271\240(\344\270\211)/index.html" @@ -0,0 +1,248 @@ + + + + + + + + Swift3.0学习(三) | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ Swift3.0学习(三) +

+ + +
+ +
+ +

面向对象–必选属性

+

构造函数时是分段构造,先构造子类,给’必选属性’设置初始值,然后再构造父类,
定义全局变量(属性)时,需要在构造函数中设置初始化变量值
super.init()是一个类构造结束
重写: override 函数重写(覆盖),如果在重写的方法中 没有super, 父类相关的一些操作就不会被执行
重载:函数名相同, 参数的类型 或者参数的个数不同 就形成了函数的重载
注意:重载构造函数,并且父类默认的构造函数 init()不重写, 父类默认的构造函数就不能够被访问,不能够确保必选属性设置初始值,只能调用重载的构造函数
面向对象–可选属性

+

使用KVC给对象设置值
注意: 在swift中使用KVC 基本数据类型不能声明为可选项,必须设置为必选项给定初始值

+

KVC实现的流程
遍历字典的键值 给对象发送 setValue: forKey消息
如果key 对象的属性不存在就将消息转发给 setVale: forUndefinedKey:
如果存在就直接设置值
注意:setVale: forUndefinedKey: 默认抛出异常 不能够super,如果super 相当于没有实现此方法

+

面向对象–便利构造函数

+

作用:方便快捷的创建对象
场景:
可检查参数是否正确来实例化
实例化控件
特点
必须以self来调用构造函数
指定的构造函数不能被重写,也不能被super调用
便利构造函数可被子类继承
可以构造失败,返回nil
面向对象 – 描述信息

+

重写 description 方法,返回值是string类型
使用kvc讲对象属性转换为字典
将字典转化为string类型

+
override var description: String {
+        let keys = ["name","age","number","sex"]
+
+        // 对象转为字典
+        let dict = self.dictionaryWithValuesForKeys(keys)
+
+        // 每个对象都有描述属性
+        return dict.description
+    }
+

面向对象 – 存储属性

+

面向对象 – 计算属性(readOnly)

+

只有getter,没有setter
只能取值不能赋值
每次调用都会被执行,消耗cpu
不占内存空间,依赖其他的属性来计算
面向对象 – didset 属性设置监察器

+

能获取 oldValue 和 newValue 的值
通常用来重写setter方法,实现视图绑定模型
在一个属性的didset中计算其他属性

+

缺点:消耗内存,被计算的属性占内存
优点:减少了cpu消耗
懒加载

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2017/03/25/\347\237\245\350\257\206\347\202\271\346\200\273\347\273\223/index.html" "b/2017/03/25/\347\237\245\350\257\206\347\202\271\346\200\273\347\273\223/index.html" new file mode 100644 index 0000000..973b32c --- /dev/null +++ "b/2017/03/25/\347\237\245\350\257\206\347\202\271\346\200\273\347\273\223/index.html" @@ -0,0 +1,234 @@ + + + + + + + + 知识点总结 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ 知识点总结 +

+ + +
+ +
+ +

开发中的知识点总结

+

textField

+

使用textField自定义搜索框,textField及leftView等属性
使用leftView 时需要设置model为始终显示
可以设置leftView的frame 调整图片大小
IBInsepectable IBDesignable 的使用

+

给属性检查器面板中添加属性设置选项
IBDesignalbe 实时显示UI控件,需要讲对应的xib和对应类绑定

+

增加分类

+

新建一个swift 文件,

+

使用extension,重写 get set方法
使用newValue 时需要判断,注意返回值的类型是否匹配
试图切换

+

一个控制器中加载不同视图,可以在loadView 方法中自定义根视图

+

loadView 使用注意事项
如果实现loadView 未调用 super loadView ,sb/xib实效
loadView调用时,如果根视图是nil(本质是未调用super loadView),会自动调用loadView 方法初始化根视图,形成递归调用
判断当前登陆标识的状态确定是调用 super loadView 还是 把创建自定义的根视图赋值给 view
VFL 的使用

+

使用之前需要先 关闭frame 的布局才能手动的添加约束 translatesAutoresizingMaskIntoConstraints = false
基本动画使用

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2017/04/10/Alamofire\346\272\220\347\240\201\345\255\246\344\271\240/index.html" "b/2017/04/10/Alamofire\346\272\220\347\240\201\345\255\246\344\271\240/index.html" new file mode 100644 index 0000000..03e27e1 --- /dev/null +++ "b/2017/04/10/Alamofire\346\272\220\347\240\201\345\255\246\344\271\240/index.html" @@ -0,0 +1,245 @@ + + + + + + + + Alamofire源码学习总结 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ Alamofire源码学习总结 +

+ + +
+ +
+ +

网络请求时Path和Query 之间是用 ‘?’ 号隔开,后边是传给服务器的参数,GET请求Query是放在URL之后,POST请求是放在Body中

    +
  • 如果参数是一个 key-value 形式,Query 格式为:key=value

    +
  • +
  • 如果参数是一个数组 key = [value1, value2, value3 ….],

    +
  • +
  • Query 格式为 key[]=value1&key[]=value2&key[]=value3

    +
  • +
  • 如果参数是一个字典

    +
  • +
+
1
key = [“subKey1”:”value1”, “subKey2”:”value2”, “subKey3”:”value3”….],
+
    +
  • Query 的格式为
  • +
+
1
key[subKey1]=value1&key[subKey2]=value2&key[subKey3]=value3
+
    +
  • Alamfire中的编码
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
private func query(_ parameters: [String: Any]) -> String {
var components: [(String, String)] = []
for key in parameters.keys.sorted(by: <) {
let value = parameters[key]!
components += queryComponents(fromKey: key, value: value)
}
return components.map { "\($0)=\($1)" }.joined(separator: "&")
}
public func queryComponents(fromKey key: String, value: Any) -> [(String, String)] {
var components: [(String, String)] = [] // 元祖数组
if let dictionary = value as? [String: Any] { // value 为字典,key[subKey]=value 形式
for (nestedKey, value) in dictionary {
components += queryComponents(fromKey: "\(key)[\(nestedKey)]", value: value)
}
} else if let array = value as? [Any] { // value为数组, key[]=value 形式
for value in array {
components += queryComponents(fromKey: "\(key)[]", value: value)
}
} else if let value = value as? NSNumber { // value为 NSNumber
if value.isBool {
components.append((escape(key), escape((value.boolValue ? "1" : "0"))))
} else {
components.append((escape(key), escape("\(value)")))
}
} else if let bool = value as? Bool { // value 为 Bool
components.append((escape(key), escape((bool ? "1" : "0"))))
} else { // value 为字符串时 直接转义
components.append((escape(key), escape("\(value)")))
}
return components
}
+ +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2017/11/29/CocoaPods\345\210\266\344\275\234\347\247\201\346\234\211pod/index.html" "b/2017/11/29/CocoaPods\345\210\266\344\275\234\347\247\201\346\234\211pod/index.html" new file mode 100644 index 0000000..ef6e7dd --- /dev/null +++ "b/2017/11/29/CocoaPods\345\210\266\344\275\234\347\247\201\346\234\211pod/index.html" @@ -0,0 +1,269 @@ + + + + + + + + CocoaPods制作私有pod | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ CocoaPods制作私有pod +

+ + +
+ +
+ +

CocoaPods原理

本文主要记录制作自己的pod库过程

+

CocoaPods 是自动化工具,主要用来管理一些开源的第三方框架,或者是制作自己的私有库,开源库,也可以使用它讲自己的项目组件化。

+

具体可以参考:

+
    +
  1. 细聊 Cocoapods 与 Xcode 工程配置
  2. +
  3. 你真的会用 CocoaPods 吗?
  4. +
+

CocoaPods 安装

安装教程

+

创建私有索引仓库

每次使用第三方开源库的时候只需要在 Podfile 文件中指定所用的库然后 Pod install ,这是因为安装pod的时候 pod setup远程仓库(索引库) clone到了本地,此仓库中存放了所有支持pod框架的描述信息(包括每个库的各个版本)

+

文件目录:

+
~/.cocoapods/repos/master
+
    +
  • 码云 或者 coding 上创建一个私有仓库

    +
  • +
  • 添加私有库到CocoaPods

    +
      +
    • 格式 pod repo add [repoName] (URL)

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      	pod repo add PrivateSpec https://gitee.com/aapenga/PrivateSpec.git 
      ```


      ## 创建本地私有库

      - `cd` 到任意一个文件夹下, 执行 `pod lib create [项目名]`

      ```
      pod lib create PPExcelView
      +
    • +
    +
  • +
+
    +
  • 将文件放入到生成的 Demo 文件夹 的 Classes 文件目录下
  • +
  • cdExample 下(含有 Podfile 文件) 执行

    +
    1
    pod install
    +

    或者

    +
    1
    pod update
    +
  • +
+

此时添加的文件出现在 Dome Pod 的 Development Pods 目录中

+
    +
  • 从本地验证 pod 是否通过

    +
    1
    pod lib lint
    +

    如果有警告,可以使用 --private 或者 --allow-warnings 忽略

    +
    1
    2
    pod lib lint --allow-warnings
    pod lib lint --private
    +

    出现 PPExcelView passed validation. 为通过

    +
  • +
  • 从远程仓库验证 pod

    +
    1
    pod spec lint
    +

    出现 PPExcelView.podspec passed validation 为通过

    +
  • +
+

推送 .podspec 文件到私有仓库

cd.podspec 所在的文件下

+
pod repo push MyGitSpec PPExcelView.podspec
+
+ +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2017/12/02/\346\234\254\345\234\260\345\267\262\346\234\211\351\241\271\347\233\256\346\267\273\345\212\240Pod/index.html" "b/2017/12/02/\346\234\254\345\234\260\345\267\262\346\234\211\351\241\271\347\233\256\346\267\273\345\212\240Pod/index.html" new file mode 100644 index 0000000..b7712e9 --- /dev/null +++ "b/2017/12/02/\346\234\254\345\234\260\345\267\262\346\234\211\351\241\271\347\233\256\346\267\273\345\212\240Pod/index.html" @@ -0,0 +1,240 @@ + + + + + + + + 本地已有项目添加Pod | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ 本地已有项目添加Pod +

+ + +
+ +
+ +

上一篇中主要记录了如何使用 pod 工具自动创建 pod 库并且生成一个测试Demo,这边主要记录给已有的项目添加 pod

+

创建 .podsepc 文件

    +
  • cd 到已有项目的根目录执行命令: pod spec create [ProjectName].podspec

    +
    1
    pod spec create EmotionKeyboard.podspec
    +
  • +
+

修改编辑 .podsepc 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
	s.name         = "EmotionKeyboard"
s.version = "0.0.1"
s.summary = "A Emotional Keyboard."

# 此处的格式不能修改
s.description = <<-DESC

Swift Emotional Keyboard
DESC

s.homepage = "https://github.com/aTreey/EmotionKeyboard"

# 两种写法,使用下面第二种
# s.license = "MIT (example)"
s.license = { :type => "MIT", :file => "LICENSE" }

s.author = { "aTree" => "480814177@qq.com" }

s.platform = :ios, "8.0"

s.source = { :git => "https://github.com/aTreey/EmotionKeyboard.git", :tag => "#{s.version}" }


# 从 .podspec 同级别的目录下起匹配
s.source_files = "EmotionKeyboard", "EmotionKeyboard/Classes/**/*.{h,m}"

# 设置自己项目中依赖的系统库
s.framework = "UIKit"

```

## 调整文件的目录结构

- 在项目根目录下新建一个放组件代码的文件夹
- 如果项目中包含 `bundle` 文件, 需要在组件文件夹下新建一个 `Assets` 文件夹,用来存放组件中的资源

最终结果如下:

![](https://ws1.sinaimg.cn/large/a1641e1bly1fm2jl7ykslj21gc0o80xt.jpg))

- 创建并添加必要文件

- `LICENSE` 文件: 开源许可文件, 如果创建项目的时候选择了开源许可就可以自动生成忽略此步骤,如果没有直接拷贝一份其他的工程中的即可
- `README.md` 文件: 主要用来说明你开源库的信息及使用方法


## 验证本地 `.podspec` 文件

- 错误1: `.podspec `中的文件路径设置错误

![](https://ws1.sinaimg.cn/large/a1641e1bly1fm2jlboq82j20v807ejug.jpg)

- 错误2: bundle 文件资源路径错误

![](https://ws1.sinaimg.cn/large/a1641e1bly1fm2jl4shnnj20zq0cwgqf.jpg)


- 本地验证结果

- **EmotionKeyboard passed validation.** 本地验证验证通过

## 网络验证 `.podspec` 文件

- 给 `master` 分支打上 `.podspec` 文件中对应的 `tag` 标签, 提交推送,
- 执行命令
+ +pod spec lint +
1
- 网络验证结果
+ +EmotionKeyboard.podspec passed validation. +``` +

本文参考

+

给 Pod 添加资源文件

+

基于 Swift 创建 CocoaPods 完全指南

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2017/12/14/Mac-\345\256\211\350\243\205MySQL/index.html" "b/2017/12/14/Mac-\345\256\211\350\243\205MySQL/index.html" new file mode 100644 index 0000000..9eaccf2 --- /dev/null +++ "b/2017/12/14/Mac-\345\256\211\350\243\205MySQL/index.html" @@ -0,0 +1,250 @@ + + + + + + + + Mac 安装MySQL | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ Mac 安装MySQL +

+ + +
+ +
+ +

搭建 XMPP 即使通讯

+

安装MySQL数据库

    +
  • 安装MySQL 数据库

    +
      +
    • 官网 下载安装包

      +

      特别强调:不要一直下一步最后的时候会有个弹窗提示你生成root的临时密码,记录一下

      +
    • +
    +
  • +
  • 安装数据库管理工具 MySQLWorkbench

    +

    官网安装包下载安装

    +
  • +
  • 修改数据库 root 密码,

    +

    打开MySQLWorkbench 随便打开一个SQL 输入安装时记录的密码,确认后提示你修改密码,

    +
  • +
  • 如果为记住安装时密码,参考下面链接修改

    +

    +
  • +
+

安装 openfire

    +
  • 官网 下载安装包安装

    +
  • +
  • 安装后需重启系统偏好设置

    +
  • +
  • 如果出现右边的管理员配置按钮不能点击,请使用 brew 安装 或者更新 Java 环境,openfire 需要 Java 环境
  • +
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2018/01/08/Swift\345\212\240\345\257\206\347\233\270\345\205\263/index.html" "b/2018/01/08/Swift\345\212\240\345\257\206\347\233\270\345\205\263/index.html" new file mode 100644 index 0000000..3b1c9b4 --- /dev/null +++ "b/2018/01/08/Swift\345\212\240\345\257\206\347\233\270\345\205\263/index.html" @@ -0,0 +1,450 @@ + + + + + + + + Swift加密相关 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ Swift加密相关 +

+ + +
+ +
+ +

最近公司项目开发中引用了 SwiftOC 混编, 涉及到了加密相关, 使用了第三方 CryptoSwift, 以下是使用过程中的简单记录

+

CryptoSwift简介

+

数组转换

    +
  • data 转化为字节数组

    +
    1
    2
    let bytes = data.bytes
    print(bytes)
    +
  • +
  • 字节数组转化为 data

    +
    1
    2
    let data = Data(bytes: [0x01, 0x02, 0x03])
    print(data)
    +
  • +
  • 十六进制创建字节

    +
    let bytes_Hex = Array<UInt8>(hex: "0x010203")
    +print(bytes_Hex)
    +
  • +
  • 字节转十六进制数

    +
    let hex = bytes_Hex.toHexString()
    +print(hex)       
    +
  • +
+
    +
  • 字符串生成字节数组

    +
    let string2bytes = "string".bytes
    +print(string2bytes)
    +
  • +
+
    +
  • 字节数组 base64

    +
    let bytes1: [UInt8] = [1, 2, 3]
    +let base64Str = bytes1.toBase64()
    +print(base64Str!)
    +
  • +
  • 字符串base64

    +
    let str0 = "test"
    +let base64Str1 = str0.bytes.toBase64()
    +print(base64Str1!)
    +
  • +
+

MD5加密

    +
  • MD5(RFC1321)诞生于 1991 年,全称是“Message-Digest Algorithm(信息摘要算法)5”,由 MIT 的计算机安全实验室和 RSA 安全公司共同提出。
  • +
  • 之前已经有 MD2、MD3 和 MD4 几种算法。MD5 克服了 MD4 的缺陷,生成 128bit 的摘要信息串,出现之后迅速成为主流算法

    +
  • +
  • 字节数组MD5

    +
    let str = "测试test"
    +let bytes = str.bytes
    +
    +// 写法一
    +let digest = bytes.md5().toHexString()
    +print(digest)
    +// 04f47b69e3573867a5b3c1e5edb00789
    +
    +// 写法二
    +let digest1 = Digest.md5(bytes).toHexString()
    +print(digest1)
    +// 04f47b69e3573867a5b3c1e5edb00789
    +
  • +
  • Data的MD5值

    +
    /// data MD5
    +let data = Data(str.bytes)
    +let digest3 = data.md5().toHexString()
    +print(digest3)
    +// 04f47b69e3573867a5b3c1e5edb00789
    +
  • +
  • 字符串MD5

    +
    ///写法 一
    +let digest4 = str.md5()
    +print(digest4)
    +// 04f47b69e3573867a5b3c1e5edb00789
    +
    +// 写法二
    +do {
    +    var digest4_2 = MD5()
    +
    +    let _ = try digest4_2.update(withBytes: "测试".bytes)
    +    let _ = try digest4_2.update(withBytes: "test".bytes)
    +    let result = try digest4_2.finish()
    +    print(result.toHexString())
    +    //04f47b69e3573867a5b3c1e5edb00789
    +} catch {
    +
    +}    
    +
  • +
+

哈希算法

    +
  • 字节数组SHA值

    +
    let str = "测试test"
    +let bytes = str.bytes
    +
    +//方式一
    +let digest1 = bytes.sha1().toHexString()
    +let digest2 = bytes.sha224().toHexString()
    +let digest3 = bytes.sha256().toHexString()
    +let digest4 = bytes.sha384().toHexString()
    +let digest5 = bytes.sha512().toHexString()
    +print(digest1, digest2, digest3, digest4 ,digest5, separator: "\n")
    +
    +//方式二
    +let digest6 = Digest.sha1(bytes).toHexString()
    +let digest7 = Digest.sha224(bytes).toHexString()
    +let digest8 = Digest.sha256(bytes).toHexString()
    +let digest9 = Digest.sha384(bytes).toHexString()
    +let digest10 = Digest.sha512(bytes).toHexString()
    +print(digest6, digest7, digest8, digest9 ,digest10, separator: "\n")
    +
  • +
+
    +
  • Data的SHA值

    +
    let data = Data(bytes: str.bytes)
    +let digest11 = data.sha1().toHexString()
    +let digest12 = data.sha224().toHexString()
    +let digest13 = data.sha256().toHexString()
    +let digest14 = data.sha384().toHexString()
    +let digest15 = data.sha512().toHexString()
    +print(digest11, digest12, digest13, digest14 ,digest15, separator: "\n")
    +
  • +
+
    +
  • 字符串的SHA值

    +
    let digest16 = str.sha1()
    +let digest17 = str.sha224()
    +let digest18 = str.sha256()
    +let digest19 = str.sha384()
    +let digest20 = str.sha512()
    +print(digest16, digest17, digest18, digest19 ,digest20, separator: "\n")
    +
  • +
+
// 方式一
+let digest_str = str.sha1()
+print(digest_str)
+
+// 方式二
+do {
+    var digest_str = SHA1()
+    let _ = try digest_str.update(withBytes: Array("测试".bytes))
+    let _ = try digest_str.update(withBytes: Array("test".bytes))
+    let result = try digest_str.finish()
+    print(result.toHexString())
+} catch {
+
+}
+

AES 加密

    +
  • key:公共密钥

    +
  • +
  • IV: 密钥偏移量

    +
  • +
  • padding:key 的补码方式

    +
    key 长度不够16 字节时,需要手动填充, zeroPadding 将其补齐至 blockSize 的整数倍
    +zeroPadding 补齐规则:
    +- 将长度补齐至 blockSize 参数的整数倍。比如我们将 blockSize 设置为 AES.blockSize(16)
    +- 如果长度小于 16 字节:则尾部补 0,直到满足 16 字节。
    +- 如果长度大于等于 16 字节,小于 32 字节:则尾部补 0,直到满足 32 字节。
    +- 如果长度大于等于 32 字节,小于 48 字节:则尾部补 0,直到满足 48 字节。 以此类推......
    +
  • +
  • 补码方式padding, noPadding, zeroPadding, pkcs7, pkcs5, 默认使用 pkcs7,两者写法结果相同

    +
  • +
+
let iv_paddding = "1234567890123456" // 默认是
+let aes_padding1 = try AES(key: key.bytes, blockMode: .CBC(iv: iv_paddding.bytes))
+let aes_padding2 = try AES(key: key.bytes, blockMode: .CBC(iv: iv_paddding.bytes), padding: .pkcs7)            
+
    +
  • ECB 模式

    +
    let key = "hangge.com123456"
    +let str = "E蜂通信"
    +print("加密前 \(str)")
    +
    +//MARK: 使用ECB 模式, , 使用aes128 加密
    +let aes = try AES(key: key.bytes, blockMode: .ECB)
    +let encrpyted = try aes.encrypt(str.bytes)
    +print("加密后 \(encrpyted)", separator: "-----")
    +
    + /// 解密
    + let decrypted = try aes.decrypt(encrpyted)
    + print("解密后 \(decrypted)")
    + let decryptedString = String(data: Data(decrypted), encoding: .utf8)!
    + print("解密后字符串 \(decryptedString)")
    +
  • +
+
    +
  • CBC 模式

    +
    //MARK: 使用CBC 模式, 需要提供一个额外的密钥偏移量 iv
    +
    +    /// 写法 1 --  便捷写法
    +    // 偏移量
    +    let iv = "1234567890123456" // 默认是
    +
    +    // TODO: 随机密钥偏移量
    +    let data = Data(bytes: AES.randomIV(AES.blockSize))
    +    let random_iv = String(data: data, encoding: .utf8)
    +
  • +
+
/// 写法 2 --
+// 创建密码器
+let cbc_aes = try AES(key: key.bytes, blockMode: .CBC(iv: iv.bytes))
+
+let cbc_aes2 = try AES(key: key, iv: iv)
+
+
+let cbc_aes3 = try AES(key: key, iv: iv)
+
+let cbc_encrypted = try cbc_aes.encrypt(str.bytes)
+
+let cbc_encrypted2 = try cbc_aes2.encrypt(str.bytes)
+
+let cbc_encrypted3 = try cbc_aes3.encrypt(str.bytes)
+
+
+// 便捷写法解密
+let cbc_decrypted = try cbc_aes.decrypt(cbc_encrypted)
+let cbc_decryptedData = Data(cbc_decrypted)
+let cbc_decryptedStr = String(data: cbc_decryptedData, encoding: .utf8)
+print(cbc_decryptedStr)
+
+// 正常写法2 解密
+let cbc_decrypted2 = try cbc_aes2.decrypt(cbc_encrypted2)
+let cbc_decryptedStr2 = String(data: Data(cbc_decrypted2), encoding: .utf8)
+print(cbc_decryptedStr2)
+
+
+// 随机密钥偏移量解密
+let cbc_decrypted3 = try cbc_aes3.decrypt(cbc_encrypted3)
+let cbc_decryptedStr3 = String(data: Data(cbc_decrypted3), encoding: .utf8)
+print(cbc_decryptedStr3)
+
+ +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2018/01/10/\347\275\221\347\273\234\344\270\203\345\261\202\345\215\217\350\256\256/index.html" "b/2018/01/10/\347\275\221\347\273\234\344\270\203\345\261\202\345\215\217\350\256\256/index.html" new file mode 100644 index 0000000..f9eb604 --- /dev/null +++ "b/2018/01/10/\347\275\221\347\273\234\344\270\203\345\261\202\345\215\217\350\256\256/index.html" @@ -0,0 +1,262 @@ + + + + + + + + iOS 即使通讯网络知识篇 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ iOS 即使通讯网络知识篇 +

+ + +
+ +
+ +

网络七层协议

读《图解TCP/IP》第5版以及看过的关于网络文章之后自己的理解

+

OSI (Open System Interconnec) 译为 开发式系统互联 七层模型从下到上一次是:物理层 -> 数据链路层 —> 网络层 -> 传输层 -> 会话层 -> 表示层 -》 应用层

+

数据每次经过一层之后就会在数据头部增加一个首部信息

+
物理层:网卡,网线,集线器,中继器,调制解调器
    +
  • 定义设备标准,如网线,光纤接口类型,将0和1转化为电信号的强和弱,达到传输比特流目的,这层数据叫比特
  • +
+
数据链路层:网桥,交换机
    +
  • 传输的地址帧,并且有检测错误功能,保证数据可靠传输, 这层数据叫帧
  • +
+
网络层:路由器
    +
  • 两个计算机通信时可能会经过多个数据链路或者通信子网,网络层将数据链路层的帧组成数据包,包中有封装好的包头,其中含有逻辑地址信息(源站点和目的站点),此层为数据包选择合适的路由和交换结点,IP协议产生,这层数据叫着数据包
  • +
+
传输层:
    +
  • 解决数据如何在网络中传输,这个层负责获取全部信息,因此,它必须跟踪数据单元碎片、乱序到达的 数据包和其它在传输过程中可能发生的危险。第4层为上层提供端到端(最终用户到最终用户)的透明的、可靠的数据传输服务。所为透明的传输是指在通信过程中 传输层对上层屏蔽了通信传输系统的具体细节。传输层协议的代表包括:TCP、UDP、SPX等
  • +
+
会话层:
    +
  • 这一层也可以称为会晤层或对话层,在会话层及以上的高层次中,数据传送的单位不再另外命名,而是统称为报文。会话层不参与具体的传输,它提供包括访问验证和会话管理在内的建立和维护应用之间通信的机制。如服务器验证用户登录便是由会话层完成的
  • +
+
表示层:
    +
  • 这一层主要解决拥护信息的语法表示问题。它将欲交换的数据从适合于某一用户的抽象语法,转换为适合于OSI系统内部使用的传送语法。即提供格式化的表示和转换数据服务。数据的压缩和解压缩, 加密和解密等工作都由表示层负责。
  • +
+
应用层:HTTP 协议
    +
  • 如何包装数据
  • +
+

通常从上到下会分成四层

+

TCP/IP 通常是指TCP/IP协议族,是一组不同协议组合在一起构成的协议族

+
    +
  • 应用层:FTP 应用层协议
  • +
  • 传输层:TCP,UDP 传输层协议,TCP 提供了一个可靠的服务
  • +
  • 网络层:IP,ICMP 网络层协议,传输时不可靠的
  • +
  • 链路层:以太网协议
  • +
+

Socket

为了解决不同计算机上进程间通信问题
服务器在指定的端口上监听,然后生成一对儿新的socket 进行通讯,一个线程对应一个socket

+

英文翻译为“插座”通常称为”套接字”, 本质是对于TCP/IP的封装,有客户端 socket 和 服务端socket

+
    +
  • 流式 Socket (STREAM): 是一种面向连接的socket,针对于面向连接的TCP服务应用,因为安全所以效率低
  • +
  • 数据报式Socket (DATAGRAM): 是一种无连接的SOcket,正对于无连接的UDP服务应用,无序,不安全,需要在接收端分析重排或者要求重发,所以效率高
  • +
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2018/01/12/iOS\345\215\263\346\227\266\351\200\232\350\256\257\345\256\236\347\216\260\344\272\214/index.html" "b/2018/01/12/iOS\345\215\263\346\227\266\351\200\232\350\256\257\345\256\236\347\216\260\344\272\214/index.html" new file mode 100644 index 0000000..b28d145 --- /dev/null +++ "b/2018/01/12/iOS\345\215\263\346\227\266\351\200\232\350\256\257\345\256\236\347\216\260\344\272\214/index.html" @@ -0,0 +1,366 @@ + + + + + + + + iOS即时通讯实现二 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ iOS即时通讯实现二 +

+ + +
+ +
+ +

实现IM通信所用协议

几个协议理解

+

xmpp 是基于xml协议主要是易于扩展,早起用于PC时代的产品使用,流量大,耗电大交互复杂其实并不适合移动互联网时代的IM产品

+

MQTT 协议 适配多平台,需自己扩展好友,群组等功能

+

protobut 协议

+

封装socket,自己实现

所要考虑的问题

+
    +
  • 传输协议选择

    +
      +
    • TCP ? 一般公司都选用(技术不是很成熟)
    • +
    • UDP ?QQ 腾讯增加自己的私有协议,保证数据传递的可靠性,解决了丢包,乱序的问题
    • +
    +
  • +
  • 聊天协议选择及代表框架

    +
      +
    • Socket ? CocoaAsyncSocket
    • +
    • WebSockt ?是传输通讯协议,基于socket封装的一个协议 SocketRocket
    • +
    • MQTT ?MQTTKit 聊天协议为上层协议
    • +
    • protobut 协议 ?
    • +
    • XMPP ?XMPPFramework 聊天协议为上层协议
    • +
    • 自定义
    • +
    +
  • +
  • 传输数据格式

    +
      +
    • Json?
    • +
    • XML?
    • +
    • ProtocolBuffer?
    • +
    +
  • +
  • 细节相关

    +
      +
    • TCP 长连接如何保持,
    • +
    • 心跳机制
    • +
    • 重连机制
    • +
    • 数据安全机制
    • +
    +
  • +
+

CocoaAsyncSocket

    +
  • CocoaAsyncSocket 的 delegate 用来设置代理处理各个回调
  • +
  • delegateQueue
  • +
  • socketQueue 串行队列,贯穿全类并没有任何加锁,确保了socket 操作中每一步都是线程安全的
  • +
  • 创建了两个读写队列(本质是数组)

    +
      +
    • NSMutableArray *readQueue;
    • +
    • NSMutableArray *writeQueue;
    • +
    +
  • +
  • 全局数据缓冲: 当前socket未获取完的数据大小

    +
      +
    • GCDAsyncSocketPreBuffer *preBuffer;
    • +
    +
  • +
  • 交替延时变量: 用于进行另一服务端地址请求的延时

    +
      +
    • alternateAddressDelay
    • +
    +
  • +
  • connect

    +
  • +
  • disconnect

    +
  • +
+

数据粘包,断包

    +
  • 粘包: 如果客户端同一时间发送几条数据,而服务器只收到一大条数据

    +
      +
    • 原因:
    • +
    • 由于传输的是数据流,经过TCP传输后,TCP使用了优化算法将多次间隔较小且数据量小的数据合并成一个大的数据块,因此三条数据合并成了一条

      +

      TCP,UDP都可能造成粘包的原因

      +
    • +
    • 发送端需要等缓冲区满了才发送出去,做成粘包

      +
    • +
    • 接收方不及时接收缓冲区的包,造成多个包接收
    • +
    +
  • +
  • 断包: 因为一次发送很大的数据包,缓冲区有限,会分段发送或读取数据

    +
  • +
+

CocoaAsyncSocket的封包,拆包处理

    +
  • 读取数据方法:

    +
    // 有数据时调用,读取当前消息队列中的未读消息
    +- (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag
    +
  • +
+
**每次读取数据,每次都必须手动调用上述 readData 方法超时设置为不超时才能触发消息回调的代理**
+
+**因此在第一连接时调用,再在接到消息时调用,上述方法就可以达到每次收到消息都会触发读取消息的代理**
+
+以上做法存在的问题就是没有考虑数据的拆包会有粘包情况,这时候需要用下面两个`read`方法,1. 读取指定长度、2.读取指定边界
+
+    // 读取特定长度数据时调用
+    - (void)readDataToLength:(NSUInteger)length withTimeout:(NSTimeInterval)timeout tag:(long)tag
+
+    // 读到特定的 data 边界时调用
+    - (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag
+
+
+**解决办法及具体思路**
+
+- 封包:给每个数据包增加一个长度或者加一个开始结束标记,表明数据的长度和类型(根据自己的项目:文本、图片、语音、红包、视频、话题、提问等等)
+    - 可以在数据包之后加一个结束标识符,解决了传输过程中丢包,丢失头部信息的错误包读取,读到这样的就丢弃直接读下一个数据包
+
+- 拆包:获取每个包的标记,根据标记的数据长度获取数据,最后根据类型处理数据,最后读取数据包头部的边界
+
    +
  • 利用缓冲区对数据进行读取

    +
      +
    • 创建读取数据包
    • +
    • 添加到读取的队列中
    • +
    • 从队列中取出读取任务包
    • +
    • 使用偏移量 maxLength 读取数据

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      		- (void)readDataWithTimeout:(NSTimeInterval)timeout
      buffer:(NSMutableData *)buffer
      bufferOffset:(NSUInteger)offset
      maxLength:(NSUInteger)length
      tag:(long)tag
      {
      if (offset > [buffer length]) {
      LogWarn(@"Cannot read: offset > [buffer length]");
      return;
      }

      // 1. 创建读取数据包
      GCDAsyncReadPacket *packet = [[GCDAsyncReadPacket alloc] initWithData:buffer
      startOffset:offset
      maxLength:length
      timeout:timeout
      readLength:0
      terminator:nil
      tag:tag];

      dispatch_async(socketQueue, ^{ @autoreleasepool {

      LogTrace();

      if ((flags & kSocketStarted) && !(flags & kForbidReadsWrites))
      {
      // 2. 向读的队列添加任务包
      [readQueue addObject:packet];
      // 3. 从队列中取出读取任务包
      [self maybeDequeueRead];
      }
      }});

      // Do not rely on the block being run in order to release the packet,
      // as the queue might get released without the block completing.
      }
      +
    • +
    +
  • +
+

doReadData 方法时读取数据的核心方法

+
    +
  • 如果读取对列中没有数据,就去判是否是上次已经读取了数据,但是因为没有目前还没有读取到数据的标记,这是正好设置了 read 方法中的超时时间,所以要断开socket 连接
  • +
+

+
    +
  • 接收到数据后在 socket系统进程的数据缓冲区中
      +
    • (基于TLS的分为两种)他们在各自管道中流动,完成数据解密后又流向了全局数据缓冲区
        +
      • CFStreem
      • +
      • SSLPrebuffer
      • +
      +
    • +
    +
  • +
+

处理数据逻辑: 如果当前读取包长度给明了,则直接流向currentRead,如果数据长度不清楚,那么则去判断这一次读取的长度,和currentRead可用空间长度去对比,如果长度比currentRead可用空间小,则流向currentRead,否则先用prebuffer来缓冲。

+

心跳机制

    +
  • TCP 自带的keep-alive 使用空闲时间来发并且默认超时时间太长是2 小时,
  • +
  • 如果应用使用了socks ,socks proxy会让tcp keep-alive失效,因为socks 协议只管转发TCP层具体的数据包,并不会转发TCP协议内的实现具体细节数据包
  • +
  • TCP 的长连接理论上是一直保持连接的,可以设置 TCP keep-alive的时间 但是实际情况下中,可能出现故障或者是因为防火墙的原因会自动把一定时间段内没有数据交互的连接给断开,心跳机制就可以维持长连接,保持活跃状态

    +
  • +
  • iOS 中使用 NSTimer scheduledTimerWithTimeInterval 需要在调用之前的关键位置设置 fireDate 为未来的某个时间, 然后再需要的时候开启

    +
  • +
  • 或者直接使用 timerWithTimeInterval 创建,然后添加到runloop
  • +
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2018/01/17/\351\235\242\350\257\225\344\270\255\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223/index.html" "b/2018/01/17/\351\235\242\350\257\225\344\270\255\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223/index.html" new file mode 100644 index 0000000..e797061 --- /dev/null +++ "b/2018/01/17/\351\235\242\350\257\225\344\270\255\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223/index.html" @@ -0,0 +1,298 @@ + + + + + + + + 面试中常见问题总结 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ 面试中常见问题总结 +

+ + +
+ +
+ +

OC 的动态性

OC是基于C语言的,C语言在编译阶段就会确定了调用具体的那个函数,OC它的底层是通过runtime 运行时机制来调用函数,在OC中成为消息发送,具体来说就是在编译阶段OC可以调用任何函数,即时这个函数是没有实现的,只有在运行的时候才会根据isa指针和已经注册的方法列表中找到 IMP(函数指针)真正要调用的那个函数,我们平时写的OC代码,在运行的时候也都是转换成了runtime 的方式进行的,不管是什么方法本质都是发送一个消息

+

runtime

    +
  • 消息机制原理: 根据方法编号列表 SEL 去映射表中查找对应方法的实现
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Person *p = [Person allo] init];

// 底层的写法
// alloc
Person *p = objc_msgSend(objc_getClass("Person"), sel_registerName("alloc"),);

// init
p = objc_msgSend(p, sel-registerName("init"));

// 调用对象方法本质就是:让对象发消息
objc_msgSend(p, @selector(eat));

// 调用类方法本质: 让类发消息
objc_msgSend([Person class], @selector(run:), 20);

// alloc init 也可以理解为:

id objc = objc_msgSend([NSObject class], @selector(alloc));

objc = objc_msgSend(objc, @selector(alloc));
+
    +
  • runtime 消息机制调用流程

    +

    每个对象内部都有一个isa指针,指向他自己真实的类来确定调用的是那个类的方法; 每个方法sel都有一个IMP指针(指向现实函数的指针),不管是对象方法还是类方法,都会有一个方法列表

    +
      +
    1. 发消息时根据对象内部的isa 指针去该类所对应的方法类表中查找方法,如果没有去父类中查找
    2. +
    3. 注册每个方法编号,为了以后快速查找调用
    4. +
    5. 根据方法编号查找对应方法
    6. +
    7. 通过isa指针和IMP指针确定最终调用的函数
    8. +
    +
  • +
  • runtime 常见使用

    +
      +
    1. 运行是动态添加方法
    2. +
    3. 给分类添加属性,(关联属性)
    4. +
    5. 字典转模型中从先遍历模型属性再从字典中查找相关值实现模型属性赋值
    6. +
    7. NSCoding 自动归档解档时对象属性过多时 encodeWithCoder:initWithCoder 两个方法实现
    8. +
    +
  • +
+

Runloop

    +
  • 概念

    +

    NSRunloop 是封装 CFRunloopRef 一套纯C的函数,

    +
  • +
  • 作用

    +
      +
    1. 保持程序持续运行
    2. +
    3. 处理App中各种事件,具体的有:toches事件, NSTimer 事件, Selector 事件, PerformSelector 事件,source 源等等
    4. +
    5. 有事件触发时就运行,没有就休眠
    6. +
    7. 渲染app 中唯一一个主线程上所有UI界面的更新
    8. +
    +
  • +
  • 使用和要点

    +
      +
    1. Runloop 在程序入口 main 函数中就会开启,并且开启的是主线程不会销毁除非退出程序,保证了程序一直运行并能响应事件和交互
    2. +
    3. 每一条线程都是唯一对应一个Runloop
    4. +
    5. 主线程不需要创建,因为在程序启动的时候就已经创建了,而子线程的需要手动创建 直接获取currentRunloop 方式(本质是使用了懒加载)
    6. +
    7. Runloop 对象是利用字典进行存储,因为一一对应,所有key 和value 就是线程和 他所对应的runloop

      +
    8. +
    9. Runloop 中 NSDefaultRunLoopMode 一般情况下主线程运行的 Mode

      +
    10. +
    11. UIInitializationRunLoopMode 初始化mode,通常不用
    12. +
    13. UITrackingRunLoopMode 界面跟踪, 用于scrollView 追踪触摸滑动,保证界面不受其他mode影响
    14. +
    15. GSEventReceiveRunLoopMode 用于接收系统事件内部 mode, 通常不用

      +
    16. +
    17. NSRunLoopCommonModes, 并不是某种具体的 mode ,开发中使用最多,他是一个组合 mode,组合了 NSDefaultRunLoopMode和 NSRunLoopCommonModes

      +
    18. +
    +
  • +
  • Runloop 相关类

    +
      +
    1. Source 事件源/输入源可以理解为 激发源
    2. +
    3. Timer 基于事件触发,CADisplayLink,NSTimer 都是加到了 Runloop,受 Mode 的影响,GCD 定时器不受Runloop 影响
    4. +
    5. Observer 相当于runloop 循环中的监听器,时刻监听当前Runloop的运行状态
    6. +
    7. 休眠没事情做的时候可以停下来,处于休眠状态
    8. +
    +
  • +
  • 使用场景

    +
      +
    1. 创建NSTimer
    2. +
    3. PersforSelector方法
    4. +
    5. 常驻线程:(创建后处于等待状态,有事件时响应事件)
        +
      • 实现后台收集用户停留时间或者是某个按钮的点击次数,如果使用主线程会不方便,使用runloop解决
      • +
      +
    6. +
    7. AutoreleasePool 自动释放池
    8. +
    9. UI更新
    10. +
    +
  • +
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2018/01/18/\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223-\344\272\214/index.html" "b/2018/01/18/\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223-\344\272\214/index.html" new file mode 100644 index 0000000..fe2ba6b --- /dev/null +++ "b/2018/01/18/\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223-\344\272\214/index.html" @@ -0,0 +1,264 @@ + + + + + + + + 面试中常见问题总结(二) | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ 面试中常见问题总结(二) +

+ + +
+ +
+ +

非正式协议和正式协议

非正式协议:凡是给NSObject或者其子类添加的类别都是非正式协议
正式协议:使用 @protocol 方式声明的方法列表,有 @require 和 @optional 两种类型

+

KVC 键值编码

之所以可以使用就是因为给 NObject 增加了 NSKeyValueCoding 非正式协议,NSObject 实现了协议,OC中几乎所有的对象都支持KVC,获取值的方法是不通过属性的setter、getter 方法,而是通过属性名称的key间接访问了实例变量,可以访问私有变量

+
1
2
3
4
 // 官方注释
/* Send -setObject:forKey: to the receiver, unless the value is nil, in which case send -removeObjectForKey:.
*/
- (void)setValue:(nullable ObjectType)value forKey:(NSString *)key;
+

如果为nil, 就会自动过滤掉, 此方法常用在字典中过滤空值

+

// forKeyPath: 用于符合属性,能够通过层层访问内部属性,相当于调用属性点语法, 拥有forKey 的所有功能

+
1
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;
+
    +
  • 实现原理
      +
    • 利用 setValue: forKeyPath: 赋值
    • +
    +
      +
    1. 底层实现还是使用 runtime 给对象发消息,先去查找属性是否有setter方法,存在setter 就会通过 setter 方法复制,
    2. +
    3. 如果没有setter方法,接着查找下是否划线的成员变量,如果有直接给赋值
    4. +
    5. 如果没有下划线的成员变量,查找是否存在和key值相同的属性,如果有就直接给属性赋值
    6. +
    7. 如果没有和key值相同的属性,就会执行 setValue: forUndefinedKey: 抛出一个异常程序奔溃,这也是字典转模型中需要实现这个方法的原因
    8. +
    +
  • +
+

KVO 键值监听

利用一个key值来找到某个属性并监听某个属性的值的变化发送给观察者,可以理解为简单的观察者模式

+
    +
  • KVO 使用

    +
      +
    1. 为目标对象的属性添加观察者

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      - (void)testKVO {
      Person *p = [[Person alloc] init];
      self.person = p;
      p.name = @"哈哈";

      [p addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];

      p.name = @"我不是原来的我!你能监听到我 ???";
      p.name = @"看我七十二变";
      }
      +
    2. +
    +
  • +
+
2. 实现监听的方法
+
+
1
2
3
4
5
#pragma mark - kvo 回调方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {

NSLog(@"KVO监听到的对象是 = %@, 值 = %@ , change = %@", [object class], keyPath, change);
}
+ +3. 移除观察者 + +
1
2
3
4
5
- (void)dealloc {

[self removeObserver:self forKeyPath:@"name"];
NSLog(@"-----控制器销毁-----");
}
+
    +
  • KVO原理

    +

    类的属性被添加了观察者后,OC底层就会通过Runtime 动态的创建一个这个类的派生类,当前类内部的isa 指针指向了这个派生类,在派生类中重写基类被观察属性的 setter 方法,调用前后会调用 willChangeValueForKey, setValueForKey, didChangeValue 方法, 从而达到监听属性值的方法

    +
  • +
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2018/01/19/Block\345\255\246\344\271\240\346\216\242\347\251\266/index.html" "b/2018/01/19/Block\345\255\246\344\271\240\346\216\242\347\251\266/index.html" new file mode 100644 index 0000000..be2b5dd --- /dev/null +++ "b/2018/01/19/Block\345\255\246\344\271\240\346\216\242\347\251\266/index.html" @@ -0,0 +1,244 @@ + + + + + + + + Block学习探究 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ Block学习探究 +

+ + +
+ +
+ +

代码及结果

    +
  • 普通局部变量

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    - (void)block1 {

    NSInteger num = 100;

    // 定义一个block
    dispatch_block_t block = ^ {
    NSLog(@"block1 -- numer = %zd", num);
    };

    num = 200;

    block();
    }
    +
  • +
  • 使用__block修饰变量

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    - (void)block2 {

    __block NSInteger num = 100;

    // 定义一个block
    dispatch_block_t block = ^ {
    NSLog(@"block2 -- numer = %zd", num);
    };

    num = 200;

    block();
    }
    +
  • +
  • 全局变量

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    - (void)block3 {


    // 定义一个block
    dispatch_block_t block = ^ {
    NSLog(@"block3 -- numer = %zd", blockNum);
    };

    blockNum = 200;

    block();
    }
    +
  • +
  • 静态常量

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    - (void)block4 {

    static NSInteger num = 100;

    // 定义一个block
    dispatch_block_t block = ^ {
    NSLog(@"block4 -- numer = %zd", num);
    };

    num = 200;

    block();
    }
    +
  • +
+

运行结果

2018-01-19 00:04:13.759416+0800 CycleRetain[91251:2382380] block1 -- numer = 100
+2018-01-19 00:04:13.760206+0800 CycleRetain[91251:2382380] block2 -- numer = 200
+2018-01-19 00:04:13.760473+0800 CycleRetain[91251:2382380] block3 -- numer = 200
+2018-01-19 00:04:13.760603+0800 CycleRetain[91251:2382380] block4 -- numer = 200
+

原理及本质

block 根据创建位置不同,共有三种:栈block,堆block,全局block

+

使用block本质就是为了保证栈上和堆上block内访问和修改的是同一个变量,具体的实现是将block 修饰的变动自动封装成一个结构体,让他在堆上创建
基于OC 底层的runtime 机制
block 在内部会有一个指向结构体的指针,当调用block的时候其实就是让block找出对应的指针所指的函数地址进行调用。并传入了block自己本身

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2018/02/03/Mac\345\256\211\350\243\205VirtualBox\350\277\236\346\216\245U\347\233\230/index.html" "b/2018/02/03/Mac\345\256\211\350\243\205VirtualBox\350\277\236\346\216\245U\347\233\230/index.html" new file mode 100644 index 0000000..834d76b --- /dev/null +++ "b/2018/02/03/Mac\345\256\211\350\243\205VirtualBox\350\277\236\346\216\245U\347\233\230/index.html" @@ -0,0 +1,228 @@ + + + + + + + + Mac安装VirtualBox连接U盘 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ Mac安装VirtualBox连接U盘 +

+ + +
+ +
+ +

最近因为某些原因需要再Mac上安装虚拟机,所以选择了VirtualBox安装了
win10系统,但是需要再虚拟机的win10中访问U盘数据,遇到的问题是不能连接并且读取到U盘,错误提示不能分配USB内存。。。

+

网上找了好多办法都胡扯,根本没有解决…

+

解决方法:
1.升级VirtualBox到最新版
2.关闭当前的虚拟机中的操作系统
3.安装增强工具
4.重启VirtualBox,回到Mac桌面推出U盘即可

+

最重要的就是在第四步

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/Charles \346\212\223\345\214\205/index.html" "b/2019/06/18/Charles \346\212\223\345\214\205/index.html" new file mode 100644 index 0000000..9878ae7 --- /dev/null +++ "b/2019/06/18/Charles \346\212\223\345\214\205/index.html" @@ -0,0 +1,313 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

Charles 抓包

+

原理

简单理解如下:

+
    +
  • 正常请求

    +

    客户端 ——> 服务器

    +
  • +
+
    +
  • Charles 抓包

    +

    客户端 ——> 代理 (Charles)——> 服务器

    +
  • +
+

安装

Charles官网

+

破解版本请自行百度

+

重要说明安装之前请暂时关闭电脑的VPN

+

重要说明安装之前请暂时关闭电脑的VPN

+

重要说明安装之前请暂时关闭电脑的VPN

+

启动Charles

安装Charles证书

+
如果安装失败,请检查以前是否已经安装过,如果已经安装过并且已经失效,请删除失效证书后再安装
+

打开钥匙串–> 选择Charles CA -> 双击证书 –> 信任证书

+

设置 https

- Proxy -> SSL Proxying Settings -> SSL Proxying -> Add
+- Host: * 为需要过滤的域名地址,
+- *: 表示不过滤Port, 固定为443 `*`表示任意端口
+

+

Mac 抓包

    +
  • Proxy -> macOS Proxy (☑️)

    +

    +
  • +
+

真机抓包

此时可以将 macOS Proxy 选项中的 ✅ 去掉,

+

Mac与iPhone连接必须同一网络

设置代理端口号

+

手机安装证书

    +
  • 安装手机证书

    +

    +
  • +
  • 安装完成后弹窗

    +

    +
  • +
+
    +
  • 查看mac 地址,为手机设置代理

    +
      +
    1. 根据安装手机证书后的弹窗显示IP配置

      +
    2. +
    3. Charles –> Help –> Loca IP Address

      +

      +
    4. +
    5. 打开电脑网络设置查看

      +

      +
    6. +
    +
  • +
+
    +
  • 设置代理

    +

    第一步

    +

    第二步

    +

    第三步

    +
  • +
  • 根据弹窗提示,在手机浏览器下载并安装证书

    +

    +
  • +
+
    +
  • 手机信任证书

    +
      +
    • 设置 –> 通用 –> 关于本机 –> 证书信任设置

      +

      +
    • +
    +
  • +
+

模拟器抓包

    +
  • 安装模拟器证书

    +

    +
  • +
  • 勾选 macOS Proxy 选项

    +

    +
  • +
+

如果失败,请先关闭模拟器,重新启动Charles 再打开模拟器

+

使用浏览器打开网页提示不是私密链接

解决办法

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/ES6\350\257\255\346\263\225/index.html" "b/2019/06/18/ES6\350\257\255\346\263\225/index.html" new file mode 100644 index 0000000..0bdfd6a --- /dev/null +++ "b/2019/06/18/ES6\350\257\255\346\263\225/index.html" @@ -0,0 +1,314 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

JS ES6 语法

1. let var 变量,作用域

var a = 10;
+
+if (a > 10) {
+    let b = 5; // let 只能在代码块内被访问
+
+    console.log(b);
+}
+
+// 访问 b 报错
+console.log(b);
+console.log(a);
+

2. const 只能分配一次值

{
const const_a = 1;
console.log(const_a)

+
// 报错
+const_a = 2;
+
+// 声明一个常量数组
+const const_array = [];
+const_array.push(1);
+const_array.push(2);
+// 再次赋值时会报错
+const_array = [3];
+

}

+

3. 解构语法

{
// 1.解构数组
function getArray() {
return [10, 20, 30];
}

+
let [a,b,c] = getArray()
+console.log(a, b, c)
+
+
+// 函数返回一个对象
+function getObj() {
+    return {dessert: '面包', drink: '水', play: 'Game'};
+}
+
+// 2.解构对象
+let {des: dessert,  dri: drink, pla: play} = getObj()
+console.log(des, dri, pla);
+

}

+

4. 模版字符串

{
let name = ‘姓名:’, password = ‘密码:’
let str_4 = name + ‘李老师’ + ‘—-‘ + password + ‘北京太热。。。’
console.log(str_4)

+
// 使用 `${}` 包裹会生成字符串
+let str_41 = `${name}李老师----${password}北京太热`
+console.log(str_41)
+
+// 多行显示时直接回车换行
+let str_42 = `${name}李老师
+----
+${password}北京太热`
+console.log(str_42)
+

}

+

5. 带标签的模版字符串

{
let name = ‘姓名:’, password = ‘密码:’
// 多行显示时直接回车换行
let str_5 = strTest${name}李老师 ---- ${password}北京太热
console.log(str_5)

+
// strings 字符中包含的被插值分割的子字符串已数组形式展现
+// values 字符串中的插值数组
+function strTest(strings, ...values) {
+    console.log(strings)
+    console.log(values)
+}
+
+function strTest1(strings, ...values) {
+    let result = '';
+    for (let i = 0; i < values.length; i++) {
+        result += strings[i]
+        result += values[i]
+    }
+    return result
+}
+

}

+

6. 判断字符串是否包含其他字符

{
let name = ‘姓名:’, password = ‘密码:’
let str_6 = ${name}李老师---${password}北京真热
console.log(str_6.startsWith(${name}))
console.log(str_6.endsWith(${name}))
console.log(str_6.includes(‘李老师’))
}

+

7. 函数默认参数

{
function funcDefaultParame(param1 = ‘a’, param2 = ‘b’, param3 = ‘c’) {
return ${param1}--${param2}--${param3}
}
console.log(funcDefaultParame(1, 2,3))
}

+

8. “…”展开操作符

{
let a = [1,2,3];
console.log(a);
console.log(…a)

+
let b = [...a, 4, 5];
+console.log(a);
+console.log(b)
+

}

+

9. ‘… ‘剩余操作符, 一般用在函数参数, 可以看作是一个可以展开的参数

//  ... param3表示函数可以接收除了param1, param2,两个参数外,多个参数,都会放在param3中
+function operator(param1, param2, ...param3) {
+    console.log(param1, param2, param3)
+}
+operator('a','b','c','d','e','f','g')
+

10. 解构对象解构函数

// 当函数参数是对象是可以按照对象解构
+function getObjcValue(param1, param2, {value1, value2} = {}) {
+    console.log(param1, param2, value1, value2)
+}
+
+getObjcValue(1,2,{value1: -3,value2: -4})
+

11. 获取函数名称

function getObjcValue(param1, param2, {value1, value2} = {}) {
+    console.log(param1, param2, value1, value2)
+}
+console.log(getObjcValue.name)
+
+let funName = function getfunctionName() {  }
+console.log(funName.name)
+

12. 箭头函数

// 原来普通写法
+var funType = function arrowfunction(param) {
+    return param
+}
+
+// 函数参数 => 函数返回值
+// '=>' 的左边表示箭头函数的参数,多个时表示为 (param1, param2, param3)
+// '=>' 的右边表示箭头函数的返回值,如果返回值是一个对象时用 { } 表示
+let arrowFunction = (param1, param2) => {
+    return `${param1}${param2}`
+

}

+

13

+ +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/Flutterr\345\205\245\351\227\250/index.html" "b/2019/06/18/Flutterr\345\205\245\351\227\250/index.html" new file mode 100644 index 0000000..97ec784 --- /dev/null +++ "b/2019/06/18/Flutterr\345\205\245\351\227\250/index.html" @@ -0,0 +1,231 @@ + + + + + + + + Flutter 入门.md | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ Flutter 入门.md +

+ + +
+ +
+ +

flutter安装

+

flutter入门

+

flutter 安装

flutter 项目代码

fluter 中一切皆是组件

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 导入头文件
import 'package:flutter/material.dart';

// 程序的入口,main 函数
void main() => (MyApp());

// 定义类, 继承

class MyApp extends StatelessWidget {
// 重写
@override

Widget build(BuildContext context) {

// 返回一窗口
return MaterialApp(
title:'Flutter',
home: Scaffold(
appBar: AppBar(
title: Text('flutter 初体验')
),

body: Center(
child: Text('flutter 学习之路'),
),
),
);
}
}
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}

class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);

// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.

// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".

final String title;

@override
_MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;

void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}

@override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Invoke "debug painting" (press "p" in the console, choose the
// "Toggle Debug Paint" action from the Flutter Inspector in Android
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
// to see the wireframe for each widget.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'你可以点击按钮来修改文字,我是热重载的结果',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
+ +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/Flutter\344\270\255GridView\347\273\204\344\273\266/index.html" "b/2019/06/18/Flutter\344\270\255GridView\347\273\204\344\273\266/index.html" new file mode 100644 index 0000000..53db2dc --- /dev/null +++ "b/2019/06/18/Flutter\344\270\255GridView\347\273\204\344\273\266/index.html" @@ -0,0 +1,237 @@ + + + + + + + + GridView组件使用.md | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ + +

+ GridView组件使用.md +

+ + +
+ +
+ +

GridView 组件使用

    +
  • padding: const EdgeInsets.all(10.0) 设置边距为10
  • +
  • crossAxisCount:横轴子元素的数量crossAxisCount。
  • +
  • mainAxisSpacing:主轴方向的间距。
  • +
  • crossAxisSpacing:横轴元素的间距。
  • +
  • childAspectRatio:元素的宽高比
  • +
+

简单例子代码

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class MyGridView2 extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return Container(
child: new GridView(
// 设置边距
padding: const EdgeInsets.all(10.0),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(

// 横轴 数量
crossAxisCount: 3,
crossAxisSpacing: 10,
mainAxisSpacing: 10.0,
// 宽高比:默认是1.0
childAspectRatio: 1.5,
),

children: <Widget>[
new Image.network('https://github.com/Jinxiansen/SwiftUI/raw/master/images/example/Text.png', fit: BoxFit.cover),
new Image.network('https://p.ampmake.com/mall/product/9666f00e-f02a-46ce-9b7c-090bf6aba9aa.png', fit: BoxFit.cover),
new Image.network('https://p.ampmake.com/mall/product/d6fdc891-eec3-47ce-b837-df726c73a9b8.png', fit: BoxFit.cover),
new Image.network('https://p.ampmake.com/mall/product/0cccf224-57d1-4a3e-bf06-9df5fed0f4e0.png', fit: BoxFit.cover),
new Image.network('https://p.ampmake.com/mall/product/2bf5c0d7-dc3a-45d2-ab1b-40fe737aa79a.jpg', fit: BoxFit.cover),
new Image.network('https://p.ampmake.com/mall/product/26153760-b964-45d5-b811-7f2bf5844102.jpg', fit: BoxFit.cover),
new Image.network('https://p.ampmake.com/mall/product/a20bb53c-b710-4b39-9607-91bef06ee54e.png', fit: BoxFit.cover),
],
)
);
}
}
+

以前的写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class MyGridView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
child: new GridView.count(
padding: const EdgeInsets.all(15.0),
// 横轴 item 数量
crossAxisCount: 3,
// 横轴 间距
crossAxisSpacing: 10.0,
children: <Widget>[
const Text('我是第一个'),
const Text('我是第二个'),
const Text('我是第三个'),
const Text('我是第四个'),
const Text('我是第五个'),
const Text('我是第六个'),
const Text('我是第七个'),
],
),
);
}
}

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/Flutter\345\270\270\347\224\250\347\273\204\344\273\266/index.html" "b/2019/06/18/Flutter\345\270\270\347\224\250\347\273\204\344\273\266/index.html" new file mode 100644 index 0000000..f34ccab --- /dev/null +++ "b/2019/06/18/Flutter\345\270\270\347\224\250\347\273\204\344\273\266/index.html" @@ -0,0 +1,249 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

flutter 常用组件

+

组件

    +
  • StatefulWidget
      +
    • 具有可变状态的窗口部件,使用应用时可随时变化,例如进度条
    • +
    +
  • +
  • StatelessWidget
      +
    • 不可变窗口部件
    • +
    +
  • +
+

Text 组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//  Text 组件
class MyText extends StatelessWidget{
@override
Widget build(BuildContext context){
return Text('reacctNative、 flutter 学习之路是非常坚信不疑的,作为一个程序员,因该对于技术有着追求,不断的提高自己才是王道,可是道理都懂依然过不好这一生',
maxLines: 2,
overflow: TextOverflow.fade,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20.0,
color: Colors.deepOrange,
decoration: TextDecoration.underline,
decorationColor: Colors.redAccent,
decorationStyle: TextDecorationStyle.dotted
),
);
}
}
+

COntainer 组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

// Container容器组件
class MyContainer extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
child: Text('Container 容器中有文字',
style: TextStyle(
backgroundColor: Colors.cyan,
),
),
alignment: Alignment.center, // 容器中内容对其方式
width: 300.0,
height: 200.0,
padding: const EdgeInsets.all(10), // 容器内边距
margin: const EdgeInsets.fromLTRB(10, 5, 10, 5), // container 和外部控件的边距
decoration: new BoxDecoration(
gradient: const LinearGradient(
colors: [Colors.lightBlue, Colors.greenAccent, Colors.yellow]
),

// 设置 border 的宽度
border: Border.all(width: 5, color: Colors.black)

),
);
}
}
+

Image 组件

fit属性

    +
  • BoxFit.fill:全图显示,图片会被拉伸,并充满父容器。
  • +
  • BoxFit.contain:全图显示,显示原比例,可能会有空隙。
  • +
  • BoxFit.cover:显示可能拉伸,可能裁切,充满(图片要充满整个容器,还不变形)。
  • +
  • BoxFit.fitWidth:宽度充满(横向充满),显示可能拉伸,可能裁切。
  • +
  • BoxFit.fitHeight :高度充满(竖向充满),显示可能拉伸,可能裁切。
  • +
  • BoxFit.scaleDown:效果和contain差不多,但是此属性不允许显示超过源图片大小,可小不可大。
  • +
+

图片的混合模式

    +
  • color:是要混合的颜色,如果你只设置color是没有意义的。
  • +
  • colorBlendMode: 是混合模式。
  • +
+

repeat 图片填充模式

    +
  • ImageRepeat.repeat : 横向和纵向都进行重复,直到铺满整个画布。
  • +
  • ImageRepeat.repeatX: 横向重复,纵向不重复。
  • +
  • ImageRepeat.repeatY:纵向重复,横向不重复
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class MyImage extends StatelessWidget {
@override
Widget build(BuildContext context){
return Container(
child: new Image.network(
// 'https://images.xiaozhuanlan.com/photo/2019/28ed76123023e46483fcf0ae5c2ba11b.png',
'https://p.ampmake.com/mall/product/9666f00e-f02a-46ce-9b7c-090bf6aba9aa.png',

// 1. 设置填充模式
// fit: BoxFit.scaleDown,
// fit: BoxFit.none,
// fit: BoxFit.fill,
// fit: BoxFit.fitHeight,
// fit: BoxFit.fitWidth,

// 保持原图比例
// fit: BoxFit.contain,

// 充满整个容器,图片被裁切
// fit: BoxFit.cover,
// scale: 1.0,

// 2. 图片混合
color: Colors.red,
colorBlendMode: BlendMode.overlay,

// 3. reap
repeat: ImageRepeat.repeat,
),
width: 300.0,
height: 200.0,
color: Colors.lightBlue,
);
}
}
+

ListView 列表组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class MyListView extends StatelessWidget{
@override
Widget build(BuildContext contex){

return ListView(
children: <Widget>[
new ListTile(
leading: new Icon(Icons.camera),
title: Text('相册'),
),

new ListTile(
leading: new Icon(Icons.camera_alt),
title: Text('相机'),
),

new ListTile(
leading: new Icon(Icons.camera_enhance),
title: Text('相机1'),
),

new ListTile(
leading: new Icon(Icons.camera_front),
title: Text('相机2'),
),

new ListTile(
leading: new Icon(Icons.camera_rear),
title: Text('相机3'),
),
],
);
}
}
+

ListView.builder 适合列表项比较多(或者无限)的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
class MyListViewBuilder extends StatelessWidget {

@override
Widget build(BuildContext context){
return ListView.builder(
itemCount: 100, // 数量
itemExtent: 40, // Tile 高度
itemBuilder: (BuildContext context, int index){
return ListTile(title: Text("$index"));
}
);
}
}
+

横向列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class MyHorizontalListView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
height: 100,
child: ListView(
// 设置滚动方式
scrollDirection: Axis.horizontal,
children: <Widget>[
new Container(
width: 180.0,
color: Colors.green,
),
new Container(
width: 180.0,
color: Colors.yellow,
),
new Container(
width: 180.0,
color: Colors.blue,
),
new Container(
width: 180.0,
color: Colors.cyan,
),
new Container(
width: 180.0,
color: Colors.purple,
),
],
),
);
}
}
+

动态列表和构造方法的创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class MyDynamicListView extends StatelessWidget {
// 声明参数
final List<String> items;
// 构造方法
MyDynamicListView({Key key, @required this.items}):super(key:key);
@override
Widget build(BuildContext context) {
return Container(
child: new ListView.builder(
itemCount: items.length,
itemBuilder: (context, index){
return ListTile(
title: new Text('通过构造方法创建的动态列表${items[index]}'),
);
},
),
);
}
}
+

GridView 网格列表的使用

+ +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/GCD \344\277\241\345\217\267\351\207\217/index.html" "b/2019/06/18/GCD \344\277\241\345\217\267\351\207\217/index.html" new file mode 100644 index 0000000..a81c6b5 --- /dev/null +++ "b/2019/06/18/GCD \344\277\241\345\217\267\351\207\217/index.html" @@ -0,0 +1,256 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

GCD信号量用法

+

对于异步网络请求相互依赖问题一般用三种方式解决:

+
    +
  • 网络请求嵌套
  • +
  • 使用 BlockOperation
  • +
  • GCD DispatchGroup
  • +
  • GCD semaphore(信号量)
  • +
+

GCD 信号量

    +
  • semaphore 值 <= 0 阻塞
  • +
  • semaphore semaphore.signal() 使信号量值增加
  • +
  • semaphore.wait() 等待,可设置时长
  • +
+

request – 3 依赖 request – 1 和 request – 2,1 和 2 无需关注顺序

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private func gcdSemaphore() {
let semaphore = DispatchSemaphore(value: 0)
DispatchQueue.global().async {
for i in 0...300 {
print("request -- 1")
}
semaphore.signal()
}

DispatchQueue.global().async {
for i in 0...200 {
print("request -- 2")
}
semaphore.signal()
}


DispatchQueue.global().async {
semaphore.wait()
semaphore.wait()
for i in 0...160 {
print("request -- 3")
}
}
}
+

request – 3 ,request – 2 和 request – 1,依次相互依赖

+
private func gcdSemaphore() {
+        let semaphore = DispatchSemaphore(value: 0)
+        DispatchQueue.global().async {
+            for i in 0...300 {
+                print("request -- 1")
+            }
+            semaphore.signal()
+        }
+
+        DispatchQueue.global().async {
+            semaphore.wait()
+            for i in 0...200 {
+                print("request -- 2")
+            }
+            semaphore.signal()
+        }
+
+
+        DispatchQueue.global().async {
+            semaphore.wait()
+            for i in 0...160 {
+                print("request -- 3")
+            }
+        }
+    }
+
+
+ +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/JavaScript\345\237\272\347\241\200/index.html" "b/2019/06/18/JavaScript\345\237\272\347\241\200/index.html" new file mode 100644 index 0000000..9731d37 --- /dev/null +++ "b/2019/06/18/JavaScript\345\237\272\347\241\200/index.html" @@ -0,0 +1,264 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

js 学习

+

数据类型

1
2
3
4
5
6
7
8
var fullName;
// undefined

fullName
// undefined

fullName + 2
// NaN
+
    +
  • 判断类型名称

    +
    1
    2
    3
    4
    fullName = '张'
    "张"
    typeof(fullName)
    // "string"
    +
  • +
+
1
2
3
4
var weight = 100;
undefined
typeof(weight)
// "number"
+ + + +
1
2
3
4
var firstName = '王', lastName = '张'
undefined
firstName + lastName
// "王张"
+
    +
  • 类型不相同时转换后在拼接

    +
    1
    2
    3
    4
    var weightIncrease = '2.5斤'
    undefined
    weight + weightIncrease
    // "1002.5斤"
    +
  • +
+

数组

var array = [];
+undefined
+array.length
+// 0
+typeof(array)
+// "object"
+
    +
  • 增加元素

    +
    1
    2
    3
    4
    array.push('1')
    // 4
    array
    // (4) ["ab", "cbd", "fcg", "1"]
    +
  • +
  • 数组前面添加

    +
  • +
+
1
2
3
4
array.unshift('0')
// 6
array
// (6) ["0", "ab", "cbd", "fcg", "1", Array(2)]
+
    +
  • 删除最后元素

    +
    1
    2
    3
    4
    array.pop() // 返回删除的元素
    // (2) ["2", "3"]
    array
    // (5) ["0", "ab", "cbd", "fcg", "1"]
    +
  • +
  • 删除第一个元素

    +
    1
    2
    3
    4
    5
    6
    	array.shift()
    // "0"
    array
    // (4) ["ab", "cbd", "fcg", "1"]
    ```
    - 删除指定的值,但数组元素没有被删
    +

    delete array[2]
    // true
    array
    // (4) [“ab”, “cbd”, empty, “1”]

    +
    1
    2
    	
    - 删除指定的元素
    +

    array.splice(1)
    // (3) [“cbd”, empty, “1”]
    array
    // [“ab”]

    +
    1
    2
    3
    4
    	
    ## 函数

    ### 调用调用
    +
  • +
+

alertMessage();

+

function alertMessageWithParamter(message) {
alert(message)
}

+

alertMessageWithParamter(‘有参数的函数’)
alertMessageWithParamter(250)
alertMessageWithParamter(
console.log(‘test’)
)

+
1
### 函数表达式,使用函数声明的方式
+

var alertMessage_expression = function expression_alert (message) {
alert(message)
}

+

alertMessage_expression(‘匿名函数调用’)

+

expression_alert(‘函数表达式’)
`

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/MasonrySnapkit\344\275\277\347\224\250\346\200\273\347\273\223/index.html" "b/2019/06/18/MasonrySnapkit\344\275\277\347\224\250\346\200\273\347\273\223/index.html" new file mode 100644 index 0000000..d83c9f3 --- /dev/null +++ "b/2019/06/18/MasonrySnapkit\344\275\277\347\224\250\346\200\273\347\273\223/index.html" @@ -0,0 +1,369 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

Masonry使用

基本使用官网Demo非常详细,以下总结的是常见问题和技巧

+

安装遇到的问题

    +
  • pod 安装后出现⚠️,项目运行报错

    +
    1
    [UIView mas_makeConstraints:]: unrecognized selector sent to instance
    +

    github

    +

    解决方法

    +
  • +
  • 使用lastBaseline属性crash

    +

    解决办法

    +
  • +
+

使用时注意点

    +
  • edgesForExtendedLayout 方法

    +

    重写此方法的目的是为了确定布局位置从什么位置开始什么位置结束,

    +
    1
    2
    3
    - (UIRectEdge)edgesForExtendedLayout {
    return UIRectEdgeNone;
    }
    +
  • +
+
**重写后**
+![](https://ws1.sinaimg.cn/large/006tNc79ly1fsgcn3fmo0j30s61iyalk.jpg)
+
+**重写前**
+![](https://ws4.sinaimg.cn/large/006tNc79ly1fsgcokvpdwj30s61iy7eq.jpg)
+
    +
  • translucent 设置UINavigationBar是否半透明, 如果设置了导航栏的styleUIBarStyleBlackTranslucent 就为true

    +

    设置为false不透明效果

    +
  • +
+
    +
  • extendedLayoutIncludesOpaqueBars 导航栏不透明条件下是否可以扩展,默认是NO不可以扩展

    +
    1
    2
    self.navigationController?.navigationBar.isTranslucent = false
    self.extendedLayoutIncludesOpaqueBars = true
    +
  • +
+
**设置不透明,设置可以扩展此时从坐标的(0,0)点开始布局**
+![](https://ws2.sinaimg.cn/large/006tNc79ly1fsggem13mjj30ow0j83zy.jpg)    
+
    +
  • inset 的使用,当约束控件的属性相同时不用再考虑正负值的问题,例如要约束一个控件左边距离另一控件的右边位置时还需要使用负值情况

    +
    1
    2
    3
    4
    5
    6
    7
    [yellowdView makeConstraints:^(MASConstraintMaker *make) {
    make.height.equalTo(@150);
    make.top.equalTo(self).inset(220);
    make.leading.equalTo(self).inset(10);
    make.trailing.equalTo(greenView.leading).inset(10);
    make.width.equalTo(greenView);
    }];
    +
  • +
  • edges的使用,约束边距更方便

    +
    1
    2
    3
    [edgeView remakeConstraints:^(MASConstraintMaker *make) {
    make.edges.equalTo(lasView).insets(UIEdgeInsetsMake(10, 5, 5, 5));
    }];
    +
  • +
+
    +
  • Priority 设置约束的优先级

    +
      +
    • priorityHigh
    • +
    • priorityMedium
    • +
    • priorityLow
    • +
    +
  • +
  • makeConstraintsupdateConstraintsremakeConstraints三者区别

    +
      +
    • makeConstraints 添加约束
    • +
    • updateConstraints 更新约束,更新之前会查找一边控件已经存在的约束,没有就添加,有就更新到最新的约束
    • +
    • remakeConstraints删除控件以前的所有约束重新添加约束
    • +
    +
  • +
  • 设置控件或者某一约束的’key’ 方便冲突时⚠️查找

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    	greenView.mas_key = @"greenView";
    [greenView makeConstraints:^(MASConstraintMaker *make) {
    make.height.equalTo(yellowdView).key(@"greenView-height");
    make.top.equalTo(yellowdView).key(@"greenView-top");
    make.width.equalTo(yellowdView).key(@"greenView-width");
    make.trailing.equalTo(self).inset(10).key(@"greenView-trailing");
    // 冲突约束
    // make.leading.equalTo(greenView.trailing).inset(10).key(@"greenView-leading");
    }];
    ```

    - 使用 `MASAttachKeys`批量给`view`设置key
    +

    MASAttachKeys(greenView, yellowdView);

    +
    1
    2
    3
    4
    	
    ![](https://ws3.sinaimg.cn/large/006tNc79ly1fsgalvd4l6j31a20modnp.jpg)

    - `dividedBy`和 `multipliedBy` 约束宽高比
    +

    [topInnerView makeConstraints:^(MASConstraintMaker *make) {

    +
    // 设置自己的宽高比是3:1
    +make.width.equalTo( topInnerView.height).multipliedBy(3); // 乘因数
    +
    +// 设置宽高并且设置他们的优先级最低
    +make.width.height.lessThanOrEqualTo(topView);
    +make.width.height.equalTo(topView).priorityLow();
    +
    +// 设置位置
    +make.top.equalTo(topView);
    +

    }];

    +
    1
    - 多个控件批量约束
    +

    NSValue sizeValue = [NSValue valueWithCGSize:CGSizeMake(100, 100)];
    [@[blueView, redView, yellowView] makeConstraints:^(MASConstraintMaker
    make) {

    +
    make.size.equalTo(sizeValue);
    +

    }];

    +

    [@[blueView, redView, yellowView] mas_makeConstraints:^(MASConstraintMaker *make) {

    +
    make.top.equalTo(self).inset(20);
    +

    }];

    +

    [blueView makeConstraints:^(MASConstraintMaker *make) {

    +
    make.left.equalTo(self).inset(20);
    +

    }];

    +

    [redView makeConstraints:^(MASConstraintMaker *make) {

    +
    make.left.equalTo(blueView.right).inset(10);
    +

    }];

    +

    [yellowView makeConstraints:^(MASConstraintMaker *make) {

    +
    make.left.equalTo(redView.right).inset(10);
    +

    }];

    +
    1
    2
    3
    4

    ![批量约束](https://ws1.sinaimg.cn/large/006tNc79ly1fsgaz670hvj30s61iyn2l.jpg)

    **以上代码也可以简化为**
    +

    [redView makeConstraints:^(MASConstraintMaker *make) {

    +
    make.left.equalTo(blueView.right).inset(10);
    +

    }];

    +

    [yellowView makeConstraints:^(MASConstraintMaker *make) {

    +
    make.left.equalTo(redView.right).inset(10);
    +

    }];

    +
  • +
+
[blueView makeConstraints:^(MASConstraintMaker *make) {
+    make.top.left.equalTo(self);
+    make.size.equalTo(@[redView, yellowView, sizeValue]);
+}];
+
+
1
2
3
4
5
	![](https://ws2.sinaimg.cn/large/006tNc79ly1fsgbhz8lw5j30s61iyte5.jpg)

## Snapkit 使用

- Swift 中`edgesForExtendedLayout:UIRectEdge` 扩展布局的边缘是一个属性,默认是`All`
+ +self.edgesForExtendedLayout = [] +
1
2
3
4

在Swift中闭包参数可以使用$0、$1、$2 一次代表第一个,第二个,第三个参数

- 使用$0 来代替闭包参数
+ +nullDataImageView.snp.makeConstraints { + $0.top.equalTo(view).offset(44) + $0.centerX.equalTo(view) + $0.width.height.equalTo(200) +} +
1
2
3
4
5
6
7
	
- `deactivate` 和 `activate` 使用

- `activate` 激活指定的约束
- `deactivate` 移除指定的约束

添加约束
+ +nullDataImageView.snp.makeConstraints { + nullDataViewTopConstraint = $0.top.equalTo(view).offset(80).constraint + nullDataViewLeftConstraint = $0.left.equalTo(view).inset(10).constraint + $0.width.height.equalTo(200) +} +
1
2

移除对应约束,设置约束再激活约束
+ +nullDataViewLeftConstraint?.deactivate() +nullDataViewLeftConstraint?.update(inset: 100) +nullDataViewLeftConstraint?.activate() +UIView.animate(withDuration: 1.5) { + self.view.layoutIfNeeded() +} +
1
2
3
4

- 更新约束

- 添加约束
+ +nullDataImageView.snp.makeConstraints { + nullDataViewTopConstraint = $0.top.equalTo(view).offset(64).constraint + $0.centerX.equalTo(view) + $0.width.height.equalTo(200) +} +
1
2

- 更新约束
+ +nullDataViewTopConstraint?.update(inset: 84) +UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseOut, animations: { + self.view.layoutIfNeeded() +}) { (finished) in + print("动画执行完毕") +} +
1
2
	
- 添加`debug`模式中的 `key`使用`labeled()`方法
+ +nullDataImageView.snp.makeConstraints { + nullDataViewTopConstraint = $0.top.equalTo(view).offset(80).constraint + nullDataViewLeftConstraint = $0.left.equalTo(view).inset(10).constraint + $0.width.height.equalTo(200).labeled("width和height约束") +} +``` +
    +
  • 其他注意点都和Masnory相同
  • +
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/RN\346\212\245\351\224\231/index.html" "b/2019/06/18/RN\346\212\245\351\224\231/index.html" new file mode 100644 index 0000000..1470eb5 --- /dev/null +++ "b/2019/06/18/RN\346\212\245\351\224\231/index.html" @@ -0,0 +1,278 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

RN学习中遇到的错误总结

+

执行 npm install 时报错

错误1. Unexpected end of JSON input while parsing near '...er":"0.4.0"},"bin":{"'

本地有package.json文件,执行 npm install 报错,或者clone 的项目执行npm install 也报同样的错误, 可能是因为缓存导致,

+

解决办法:

+
    +
  1. 设置镜像源,以前可能大部分都是用的淘宝

    +
    1
    npm set registry https://registry.npmjs.org/
    +
  2. +
  3. 清除缓存

    +
    1
    npm cache clean –-force
    +
  4. +
  5. 如果提示失败,使用sudo

    +
    1
    sudo npm cache clean --force
    +
  6. +
+

错误2. xcrun: error: unable to find utility "simctl", not a developer tool or in PATH

可能是因为 Xcode 版本问题引起,XCode 偏好设置中 Command line Tools 中为选择版本问题导致(我的情况是安装了Xcode 10 beta版导致的)

+

解决办法:

+

+
1
2
3
4
5
6
7
8
9
10
11
12
The following build commands failed:
CompileC /Users/liepin/LiePinWorkspace/RNDemo/GitHubPopular/ios/build/Build/Intermediates.noindex/RCTWebSocket.build/Debug-iphonesimulator/RCTWebSocket.build/Objects-normal/x86_64/RCTSRWebSocket.o RCTSRWebSocket.m normal x86_64 objective-c com.apple.compilers.llvm.clang.1_0.compiler
(1 failure)
Installing build/Build/Products/Debug-iphonesimulator/GitHubPopular.app
An error was encountered processing the command (domain=NSPOSIXErrorDomain, code=2):
Failed to install the requested application
An application bundle was not found at the provided path.
Provide a valid path to the desired application bundle.
Print: Entry, ":CFBundleIdentifier", Does Not Exist

Command failed: /usr/libexec/PlistBuddy -c Print:CFBundleIdentifier build/Build/Products/Debug-iphonesimulator/GitHubPopular.app/Info.plist
Print: Entry, ":CFBundleIdentifier", Does Not Exist
+

错误3. Error: Cannot find module '../lib/utils/unsupported.js'

安装的node 版本不是稳定的版本,需要删除后重新安装

+
1
2
sudo rm -rf /usr/local/lib/node_modules/npm
brew reinstall node
+

错误4. Couldn't find preset "module:metro-react-native-babel-preset" when running jest

解决方法

+

已有项目集成RN

报错1. 文件路径错误,查看你的node_modules 文件夹是在当前目录还是上级目录

+
[!] No podspec found for `React` in `../node_modules/react-native
+
    +
  • ../ 是指父级目录

    +
  • +
  • ./ 是指当前目录

    +
  • +
+

报错 2. yoga Y 大小写为问题

+
[!] The name of the given podspec `yoga` doesn't match the expected one `Yoga`_modules/react-native/ReactCommon/yoga"
+

pod install 之后

报错 1.

+

解决办法: pod ‘React’, :subspecs 中加入 ‘CxxBridge’, ‘DevSupport’

+
pod 'React', :path => './node_modules/react-native', :subspecs => [
+'Core',
+'RCTText',
+'RCTNetwork',
+'RCTWebSocket', # 这个模块是用于调试功能的
+'CxxBridge', # Include this for RN >= 0.47
+'DevSupport', # Include this to enable In-App Devmenu if RN >= 0.43
+

]

+

报错 2.

RCTAnimation/RCTValueAnimatedNode.h' file not found

+

解决方法:

+

+
1
#import <RCTAnimation/RCTValueAnimatedNode.h>
+

替换为

+
1
#import "RCTValueAnimatedNode.h"
+

修改头文件导入方式

+

报错3 版本不匹配 react native version mismatch javascript version 0.54.3 native 0.57.2

    +
  • 关闭所有的 terminal,或者是集成开发环境的命令行窗口

    +
    1
    2
    watchman watch-del-all
    react-native start --reset-cache
    +
  • +
  • 重新打开一个terminal窗口

    +
    1
    react-native run-ios
    +

    问题描述和解决1

    +

    问题描述和解决2

    +
  • +
+

报错4 Unable to resolve module "schedule/tracking"

缺少一些开发依赖的库

+
npm i schedule@0.4.0 --save-dev
+

问题描述和解决

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/React-Native\345\274\200\345\217\221iOS\346\211\223\345\214\205/index.html" "b/2019/06/18/React-Native\345\274\200\345\217\221iOS\346\211\223\345\214\205/index.html" new file mode 100644 index 0000000..d36a2e4 --- /dev/null +++ "b/2019/06/18/React-Native\345\274\200\345\217\221iOS\346\211\223\345\214\205/index.html" @@ -0,0 +1,259 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

React-Native开发iOS打包

打jsbudnle包

创建打包后存放文件目录

    +
  • cd 到 React-Native 项目根目录下 并且 进入 ios 文件目录下
  • +
  • 创建 bundle 文件夹

    +
    1
    mkdir bundle
    +
  • +
+

配置 React-Native 打包命令

    +
  • 打包命令

    +
    1
    react-native bundle
    +
  • +
  • 在React Native项目的根目录下执行命令

    +
    1
    react-native bundle --entry-file index.js --platform ios --dev false --bundle-output ./ios/bundle/index.jsbundle --assets-dest ./ios/bundle
    +
  • +
+
- `--entry-file`: ios或者android入口的js名称,比如 `index.js`
+
+- `--platform`: 平台名称(ios或者android)
+
+- `--dev`: 设置为`false`的时候将会对`JavaScript`代码进行优化处理。
+
+- `--bundle-output`: 生成的jsbundle文件的名称和路径,比如 `./ios/bundle/index.jsbundle`
+
+- `--assets-dest`: 图片以及其他资源存放的目录
+

使用打包命令

    +
  • 将打包命令添加到 packger.json

    +
    1
    2
    3
    4
    5
    "scripts": {
    "start": "node node_modules/react-native/local-cli/cli.js start",
    "test": "jest",
    "bundle-ios": "node node_modules/react-native/local-cli/cli.js bundle --entry-file index.js --platform ios --dev false --bundle-output ./ios/bundle/index.jsbundle --assets-dest ./ios/bundle"
    },
    +
  • +
+
    +
  • 再次打包只需要 React Native项目的根目录下执行命令,不用再次输入类似上面的命令

    +
    1
    npm run bundle-ios
    +
  • +
+

集成到Xcode(iOS原生项目中)

    +
  • 添加已经打好的.jsbundle 离线包

    +

    +
  • +
+
    +
  • iOS 项目代码配置

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    private func loadReactNativeTest() {
    // debug 测试加载本地文件
    let url = URL(string: "http://localhost:8081/index.bundle?platform=ios")!

    // 加载已经加入的离线包
    let url2 = Bundle.main.url(forResource: "index", withExtension: "jsbundle")

    let rootView = RCTRootView(
    bundleURL: url,
    moduleName: "MyApp", //这里的名字要和index.js中相同
    initialProperties: nil,
    launchOptions: nil
    )
    view = rootView
    }
    +
  • +
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/ReactNative\344\270\255ref\344\275\277\347\224\250/index.html" "b/2019/06/18/ReactNative\344\270\255ref\344\275\277\347\224\250/index.html" new file mode 100644 index 0000000..91aefd8 --- /dev/null +++ "b/2019/06/18/ReactNative\344\270\255ref\344\275\277\347\224\250/index.html" @@ -0,0 +1,238 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

Reat Native ref 使用

+

State 状态的使用

在父控件中指定,整个生命周期讲不再改变
需求:一段闪烁的文字, Props 属性显示文字内容创建时初始化,State控制随时间变化是否显示,在父控件中

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Blink extends Component{
// 构造
constructor(props) {
super(props);
// 初始状态
this.state = {showWithText:true};
this.state = {}
setInterval(() => {
this.setState(previousState => {
return {showWithText: !previousState.showWithText}
});
}, 100);
}
render() {
let display= this.state.showWithText ? this.props.text : ' ';
return(
<Text>{display}</Text>
);
}
}
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
export default class App extends Component<Props> {
render() {
return(
<View style={styles.container}>
<Blink
text='I love to blink'
/>

<Blink
text='猜猜猜'
/>

<Blink
text='Blink is great'
/>
</View>
);
}
}
+

Props 属性的使用

1
2
3
4
5
6
7
8
9
// 定义一个组件
class Greeting extends Component {
render() {
return(
<Text> Hello {this.props.name}!
</Text>
);
}
}
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
export default class App extends Component<Props> {
render() {
return(
<View style={styles.container}>
<Image source={{uri:'https://upload.wikimedia.org/wikipedia/commons/d/de/Bananavarieties.jpg'}}
style={{width: 190, height: 110}}
/>
<Text style={styles.instructions}>
使用Props属性
</Text>
<View>
<Greeting name='React-Native'
/>
<Greeting name='iOS'
/>
<Greeting name='Swift'
/>
</View>

</View>
);
}
}
+

ref的使用

    +
  • 可以理解为组件渲染后指向组件的一个应用,可以通过ref获取到真实的组件(类似指针?)
  • +
+

定义

+
1
ref='scrollView'
+

获取

+
1
2
let scrollView = this.refs.scrollView
let scrollView = this.refs['scrollView']
+
    +
  • 可以通过ref访问组件的属性和方法
  • +
+

ES6 定时器

    +
  • 开启定时器
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let scrollView = this.refs.scrollView;
// 2.4timer 定时器的写法
this.timer = setInterval(
()=>{
var tempPage = 0;
// 修改banner索引
if ((this.state.currentPage+1) >= ImageData.data.length) {
tempPage = 0;
} else {
tempPage = this.state.currentPage+1;
}

// 更新状态
this.setState({
currentPage: tempPage
})

let offSet_x = width * tempPage
scrollView.scrollResponderScrollTo({x:offSet_x, y:0, animated: true})
},
this.state.duration
);
+
    +
  • 暂停定时器
  • +
+
1
this.timer && clearInterval(this.timer)
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/Swift \345\215\217\350\256\256\344\275\277\347\224\250/index.html" "b/2019/06/18/Swift \345\215\217\350\256\256\344\275\277\347\224\250/index.html" new file mode 100644 index 0000000..22cf0c5 --- /dev/null +++ "b/2019/06/18/Swift \345\215\217\350\256\256\344\275\277\347\224\250/index.html" @@ -0,0 +1,263 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

Swift 协议使用

对UIView 扩展

按钮/文本框抖动动画

    +
  • 声明协议

    +
      +
    • 协议中定义属性:遵循该协议的类型都具有此属性

      +
        +
      • 必须明确规定该属性是可读的 {get} 或者可写的 {set},或是可读可写的 {get set}
      • +
      • 使用static修饰声明一个类类型属性
      • +
      +
    • +
    • 协议中定义方法

      +
    • +
    +
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
protocol Shakable {}
extension Shakable where Self: UIView {
func shakeAnimation() {
let animation = CABasicAnimation(keyPath: "position")
animation.duration = 0.08
animation.repeatCount = 5
animation.autoreverses = true
animation.fromValue = NSValue(cgPoint: CGPoint(x: self.center.x - 4, y: self.center.y))
animation.toValue = NSValue(cgPoint: CGPoint(x: self.center.x + 4, y: self.center.y))
layer.add(animation, forKey: "position")
}
}
+
    +
  • 自定义UI控件并遵守协议
  • +
+
1
2
3
class MyButton: UIButton, Shakable {  }
class MySwitch: UISwitch, Shakable { }
class MyTextField: UITextField, Shakable { }
+
    +
  • 使用
  • +
+
1
2
3
4
5
6
7
8
9
10
let mySwitch = MySwitch()
mySwitch.isOn = true
self.mySwitch = mySwitch
view.addSubview(mySwitch)
mySwitch.translatesAutoresizingMaskIntoConstraints = false
mySwitch.topAnchor.constraint(equalTo: view.bottomAnchor, constant: 10).isActive = true
mySwitch.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10).isActive = true

// 调用
mySwitch.shakeAnimation()
+

UITableViewCell使用

定义协议

1
2
3
4
5
6
7
8
9
10
11
protocol ReusableView: class {	}

extension ReusableView where Self: UIView {
static var reuseIdentifier: String {
return String(describing: self)
}
}

```

### 对tableView扩展
+

extension UITableView {
/// 注册cell
func register<T: UITableViewCell>(T: T.Type) where T: ReusableView {
print(T.reuseIdentifier)
self.register(T.self, forCellReuseIdentifier: T.reuseIdentifier)
}

+
/// 调用cell
+func dequeueReusableCell<T: UITableViewCell>(_ indexPath: IndexPath) -> T where T: ReusableView {
+    print(T.reuseIdentifier)
+    return self.dequeueReusableCell(withIdentifier: T.reuseIdentifier, for: indexPath) as! T
+}
+

}

1
2
3
4

### 用法

- 创建自定义的cell,需要遵守协议

+

class TableProtocolCell: UITableViewCell, ReusableView {
override var reuseIdentifier: String? {
return “cell”
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.backgroundColor = .cyan
self.textLabel?.text = “通过协议和泛型创建的cell”
self.textLabel?.textColor = .blue
}
required init?(coder aDecoder: NSCoder) {
fatalError(“init(coder:) has not been implemented”)
}
}

+

class TableProtocolCell2: UITableViewCell, ReusableView {
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.backgroundColor = .red
self.textLabel?.text = “cell1 - 通过协议和泛型创建的”
self.textLabel?.textAlignment = .right
}
required init?(coder aDecoder: NSCoder) {
fatalError(“init(coder:) has not been implemented”)
}
}

1
2

- 注册和使用

+

class TableViewProtocolController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(T: TableProtocolCell.self)
tableView.register(T: TableProtocolCell2.self)
}

+
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+    return 10
+}
+
+override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+    var cell = UITableViewCell()
+    if indexPath.row % 2 == 0 {
+        cell = tableView.dequeueReusableCell(indexPath) as TableProtocolCell
+    } else {
+        cell = tableView.dequeueReusableCell(indexPath) as TableProtocolCell2
+    }
+    return cell
+}
+

}

+

`

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/Swift \345\237\272\347\241\200\347\237\245\350\257\206/index.html" "b/2019/06/18/Swift \345\237\272\347\241\200\347\237\245\350\257\206/index.html" new file mode 100644 index 0000000..ccaa0cd --- /dev/null +++ "b/2019/06/18/Swift \345\237\272\347\241\200\347\237\245\350\257\206/index.html" @@ -0,0 +1,310 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

Swift 学习

+

枚举

1. 定义 rawValue 为 Int 类型,初始值为1,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
enum Month: Int {
case January = 1
case February
case March
case April
case May
case June
case July
case August
case September
case October
case November
case December
}
+

2. rawValue 的整型值可以不连续

1
2
3
4
5
6
enum Season: Int {
case Spring = 1
case Summer = 5
case Autumn = 10
case Winter = 40
}
+

3. 枚举可以是字符串,字符串是变量名rawValue 的值相等

1
2
3
4
5
6
7
enum ProgrammerLanguae: String {
case Swift
case OC = "Objective-C"
case C = "语言"
case RN = "React-Native"
case Java
}
+

使用rawValue

+
1
2
3
func residueNewYear(month: Month) -> Int {
return 12 - month.rawValue
}
+

调用枚举的Moth的构造函数生成一个Month

+
1
2
3
4
let month = Month(rawValue: 5)
let swift = ProgrammerLanguae.Swift.rawValue
let oc = ProgrammerLanguae.OC
let RN = ProgrammerLanguae.RN.rawValue
+

4. 枚举关联值 associate value

1
2
3
4
5
6
7
8
9
10
11
12
enum Status {
case success(Int)
case fail(String)
case null // 未关联值
}

func associateValueTest(isSuccess: Bool) -> Status {
if isSuccess {
return .success(200)
}
return .fail("失败")
}
+

5. 枚举associateValue多个值

    +
  • 本质是关联了一个元祖(value0, value1, value2…)
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
enum Shape {
case Square(width: Double)
case Reactangle(width: Double, height: Double)
case Circle(x: Double, y: Double, radius: Double)
case Point
}

let square = Shape.Square(width: 20)
let reactangle = Shape.Reactangle(width: 10, height: 20)
let circle = Shape.Circle(x: 10, y: 10, radius: 20)
let point = Shape.Point

private func area(shape: Shape) -> Double {
switch shape {
case let .Square(width):
return width * width
case let .Reactangle(width, height):
return width * height
case let .Circle(_, _, radius):
return radius * radius * Double.pi
case .Point:
return 0
}
}
+

6. 递归枚举

    +
  • 定义一个递归算术表达式
  • +
  • 使用 indirect 来修饰
  • +
+
1
2
3
4
5
6
7
8
indirect enum ArithmeticalExpression {
case Number(Int)
case Addition(ArithmeticalExpression, ArithmeticalExpression) // + 时两边也是一个表达式
case Multiplication(ArithmeticalExpression, ArithmeticalExpression) // * 时两边也是一个表达式

// indirect case Addition(ArithmeticalExpression, ArithmeticalExpression) // + 时两边也是一个表达式
// indirect case Multiplication(ArithmeticalExpression, ArithmeticalExpression) // * 时两边也是一个表达式
}
+
    +
  • 递归表达式使用 (2+3) * 4
  • +
+
1
2
3
4
let two = ArithmeticalExpression.Number(2)
let one = ArithmeticalExpression.Number(3)
let sum = ArithmeticalExpression.Addition(two, one)
let indirectEnumResult = ArithmeticalExpression.Multiplication(sum, ArithmeticalExpression.Number(4))
+
    +
  • 计算表达式值的函数
  • +
+
1
2
3
4
5
6
7
8
9
10
private func calculate(expression: ArithmeticalExpression) -> Int {
switch expression {
case let .Number(value):
return value
case let .Addition(left, right):
return calculate(expression: left) + calculate(expression: right)
case let .Multiplication(left, right):
return calculate(expression: left) * calculate(expression: right)
}
}
+

结构体

    +
  • 结构体中的属性值没有初始化时必须使用构造函数来初始化,否则报错
  • +
  • +
+
1
2
3
4
5
6
7
struct Location {
var latitude: Double
var longitude: Double
var placeName: String?
}

let location1 = Location(latitude: 37.3230, longitude: -122.0322, placeName: "测试")
+
    +
  • 结构体中属性如果都赋了初始值就可以直接初始化
  • +
+
1
2
3
4
5
6
struct Location2 {
var latitude: Double = 0
var longitude: Double = 0
}

let location2 = Location2()
+
    +
  • 如果未指定初始值,swift 就不会默认初始化为(不初始化)
  • +
  • 当属性值为可选值时此时允许值为nil,那么就允许不通过构造函数来初始化赋值
  • +
  • let 只有一次赋值机会
  • +
  • 不管是类还是结构体都应该提供一个全参数的构造函数 init(latitude: Double, longitude: Double, placeName: String?)
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct Location3 {
let latitude: Double
let longtitude: Double
var placeName: String?

init(coordinateString: String) {
let index = coordinateString.index(of: ",")
let index1 = coordinateString.index(after: index!)
let test_1 = coordinateString.prefix(upTo: index!)
let test_2 = coordinateString.suffix(from: index1)

latitude = Double(test_1)!
longtitude = Double(test_2)!
}

init(latitude: Double, longitude: Double, placeName: String?) {
self.latitude = latitude
self.longtitude = longitude
self.placeName = placeName
}
}

let test3 = Location3(coordinateString: "12,45")
+

属性和方法

计算型属性

类型属性

类型方法

属性管擦器

延迟属性

访问控制

单利模式

继承和构造函数

swift 中继承

多态性

属性和函数重载

子类两段式构造

    +
  • 子类构造函数分为两段,第一段是构造自己,第二段是构造父类

    +
      +
    • 父类中构造函数
    • +
    • 子类要设置一个自己的构造函数
    • +
    • 子类构造函数中需要先初始化自己的属性
    • +
    • 然后在构造函数中调用父类初始化父类构造函数中的相关属性

      +

      父类

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      class Father {
      var name: String
      var sex: Int = 0
      var old: Int = 32
      var desc: String {
      return "我是\(name)"
      }
      init(name: String) {
      self.name = name
      }

      func run() {
      print("Father can Running")
      }
      }
      +
    • +
    +
  • +
+
子类
+
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Son: Father {

var isSwimming = true

var computer: String

override var desc: String {
return "Son is \(name)"
}

init(name: String, computer: String) {
self.computer = computer
super.init(name: name)
}
}
+
    +
  • 子类两段构造都完成后子类的初始化才完成,可以使用self调用属性或者方法

    +
    1
    2
    3
    4
    5
    init(name: String, computer: String) {
    self.computer = computer
    self.getToys() // 此代码报错,子类初始化未完成
    super.init(name: name)
    }
    +
  • +
+

便利构造函数和指定构造函数

    +
  • 便利构造函数只能调用自己的指定构造函数
  • +
  • 指定构造函数通过一系列的调用都会调用super.init()
  • +
  • 便利构造函数无法调用super.init()
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Father {
var name: String
var sex: Int = 0
var old: Int = 32
var desc: String {
return "我是\(name)"
}
init(name: String) {
self.name = name
}

init(name: String, old: Int) {
self.name = name
self.old = old
}

func run() {
print("Father can Running")
}
}

class Son: Father {
var isSwimming = true
var computer: String

override var desc: String {
return "Son is \(name)"
}

// 子类指定构造函数,调用了父类的指定构造函数
init(name: String, computer: String) {
self.computer = computer
super.init(name: name)
}

// 子类便利构造函数,调用了指定构造函数
convenience override init(name: String) {
let computer = "iMac"
self.init(name: name, computer: computer)
}
}
+

构造函数继承

    +
  • 子类有可能会继承父类的构造函数
  • +
  • 子类没有实现父类的任何指定构造函数;则自动继承父类的所有指定构造函数, 因为继承了指定构造函数所以同时便利构造函数也被继承
  • +
+

父类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Father {
var name: String
var sex: Int = 0
var old: Int = 32
var desc: String {
return "我是\(name)"
}

/// Father指定构造函数-1
init(name: String) {
self.name = name
}

/// Father指定构造函数-2
init(name: String, old: Int) {
self.name = name
self.old = old
}

/// Father便利构造函数
convenience init(old: Int) {
self.init(name: "Father", old: old)
}

func run() {
print("Father can Running")
}
}

+

子类

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Son: Father {
var isSwimming = true
var computer: String
var job: String

override var desc: String {
return "Son is \(name)"
}

/// 子类重载的指定构造函数-1
convenience override init(name: String) {
self.init(name: name, computer: "Dell")
}

/// 子类重载的指定构造函数-2
override convenience init(name: String, old: Int) {
self.init(name: name, computer: "acer")
}


/// 子类自己的指定构造函数,调用了父类的指定构造函数
init(name: String, computer: String) {
self.computer = computer
self.job = "C#"
super.init(name: name)
}

// 子类便利构造函数,调用了自己指定构造函数
convenience init(computer: String) {
let name = "小张"
self.init(name: name, computer: computer)
}
}
+

未实现父类任何的指定构造函数

+
1
2
3
class Grandson: Son {
var toy: String = "dog toys"
}
+

子类可以调用的构造函数

+
1
2
3
4
5
let grandSon0 = Grandson(old: 4)
let grandSon1 = Grandson(computer: "Mi")
let grandSon2 = Grandson(name: "小王")
let grandSon3 = Grandson(name: "小虎", computer: "👽")
let grandSon4 = Grandson(name: "小李", old: 8)
+
    +
  • 子类实现了父类所有的指定构造函数,则自动继承父类的所有便利构造函数
  • +
+
1
2
3
4
5
let son0 = Son(old: 30)
let son1 = Son(computer: "Mi")
let son2 = Son(name: "小王")
let son3 = Son(name: "小虎", computer: "👽")
let son4 = Son(name: "小李", old: 8)
+

required 构造函数

    +
  • 父类中有被 required 关键词修饰的构造函数子类必须实现此构造函数
  • +
  • 子类实现的此构造函数不需要再使用 override 关键词修饰,需要 required 修饰
  • +
+

子类

+
1
2
3
4
5
6
7
8
class Father {
var name: String
/// Father指定构造函数-1
// required 修饰的指定构造函数子类中必须实现
required init(name: String) {
self.name = name
}
}
+

父类

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Son: Father {
var isSwimming = true
var computer: String

/// 子类重载的指定构造函数-1
convenience required init(name: String) {
self.init(name: name, computer: "Dell")
}

/// 子类自己的指定构造函数,调用了父类的指定构造函数
init(name: String, computer: String) {
self.computer = computer
self.job = "C#"
super.init(name: name)
}
}
+
    +
  • 子类如果实现了自己的指定构造函数,那么 required 修饰指定构造函数就初始化失败,因为自己实现了指定构造函数所以不能继承父类中的构造函数,此时可以自己实现被required 修饰的便利构造函数
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Son1: Son {
var sport: String

// 指定构造函数,需要调用父类的指定构造函数
init(name: String, sport: String) {
self.sport = sport
Son.init(name: name)
}

// 父类中有被 required 关键词修饰的必须实现的构造函数
convenience required init(name: String) {
// fatalError("init(name:) has not been implemented")
// 调用自己的指定构造函数,
self.init(name: name, sport: "")
}
}
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/SwiftLint\344\275\277\347\224\250/index.html" "b/2019/06/18/SwiftLint\344\275\277\347\224\250/index.html" new file mode 100644 index 0000000..938e8d8 --- /dev/null +++ "b/2019/06/18/SwiftLint\344\275\277\347\224\250/index.html" @@ -0,0 +1,216 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/Swift\344\270\213\346\240\207\345\222\214\350\277\220\347\256\227\347\254\246\351\207\215\350\275\275/index.html" "b/2019/06/18/Swift\344\270\213\346\240\207\345\222\214\350\277\220\347\256\227\347\254\246\351\207\215\350\275\275/index.html" new file mode 100644 index 0000000..c8e55bf --- /dev/null +++ "b/2019/06/18/Swift\344\270\213\346\240\207\345\222\214\350\277\220\347\256\227\347\254\246\351\207\215\350\275\275/index.html" @@ -0,0 +1,266 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

Swift下标和运算符重载

+

字典数组下标

var array = [1,2,3,4,5,6]
+let temp = array[0]     // 通过下标访问
+let temp2 = array[2]    // 通过下标访问
+

结构体下标

    +
  • 对于结构体不是直接使用下标访问,会直接报错
    直接使用下标获取属性值报错: Type 'Vector_3' has no subscript members

    +
  • +
  • 可以通过实现 subscript(index: Int) -> Double?方法让结构体支持下标访问,可以理解为一个特殊的函数,需要传入参数和返回值

    +
  • +
  • 可以增加第二种下标–根据坐标轴获取属性值
  • +
  • 重写 subscript 的 set 方法 可以使用下标修改属性值
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
struct Vector_3 {
var x = 0.0
var y = 0.0
var z = 0.0

// 根据索引获取
subscript(index: Int) -> Double? {

// 重写了get方法 实现通过下标获取属性值
get {
switch index {
case 0: return x
case 1: return y
case 2: return z
default: return nil
}
}

// 重写 set 方法,实现g通过下标设置属性值,使用系统默认的newValue
set {
guard let newValue = newValue else {return}
switch index {
case 0: x = newValue
case 1: y = newValue
case 2: z = newValue
default: return
}
}
}

// 增加第二种下标--根据坐标轴获取属性值
subscript(axis: String) -> Double? {

get {
switch axis {
case "X", "x": return x
case "Y", "y": return y
case "Z", "z": return z
default: return nil
}
}

set {
guard let newValue = newValue else {return}
switch axis {
case "X", "x": x = newValue
case "Y", "y": y = newValue
case "Z", "z": z = newValue
default: return
}
}
}
}

```

### 下标访问

结构体使用下标获取值
+

var v = Vector_3(x: 1, y: 2, z: 3)
print(v[0], v[1], v[2], v[3], v[100]) // Optional(1.0) Optional(2.0) Optional(3.0) nil nil
print(v[“x”], v[“y”], v[“z”], v[“i”]) // Optional(1.0) Optional(2.0) Optional(3.0) nil

+
1
2

结构体使用下标修改值
+

v[0] = 101
v[1] = 202
v[2] = 303
v[3] = 400
v[100] = 51
print(v[0], v[1], v[2], v[3], v[100]) // Optional(101.0) Optional(202.0) Optional(303.0) nil nil

1
 

+

v[“x”] = 100
v[“y”] = 200
v[“z”] = 300
v[“i”] = 50
print(v[“x”], v[“y”], v[“z”], v[“i”]) // Optional(100.0) Optional(200.0) Optional(300.0) nil

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

### 多维下标

定义一个关于矩阵的结构体

struct Matrix {
var data: [[Double]]
let row: Int
let column: Int

init(row: Int, column: Int) {
self.row = row
self.column = column
data = [[Double]]()
for _ in 0..<row {
let aRow = Array(repeating: 1.0, count: column)
data.append(aRow)
}
}

// 通过下标 [row,column] 方式访问
subscript(row: Int, column: Int) -> Double {
get {
assert(row >= 0 && row < self.row && column >= 0 && column < self.column, "下标不合法")
return data[row][column]
}

set {
assert(row >= 0 && row < self.row && column >= 0 && column < self.column, "下标不合法")
data[row][column] = newValue
}
}

// 通过下标 [row][column] 方式访问
subscript(row: Int) -> [Double] {
get {
assert(row >= 0 && row < self.row, "下标不合法")
// 直接返回数组,数组本身有下标
return data[row]
}

set {
assert(newValue.count == column, "下标不合法")
data[row] = newValue
}
}
}


### 运算符重载

- 重载 + 运算符
+

func +(one: Vector_3, other: Vector_3) -> Vector_3 {
return Vector_3(x: one[0]! + other[0]!, y: one[1]! + other[1]!, z: one[2]! + other[2]!)

+

// return Vector_3(x: one.x + other.x, y: one.y + other.y, z: one.z + other.z)
}

+
1
2

- 两个参数时相减
+

func -(one: Vector_3, other: Vector_3) -> Vector_3 {
return Vector_3(x: one.x - other.x, y: one.y - other.y, z: one.z - other.z)
}

+
1
- 一个参数时去反, 需要 prefix 修饰
+

prefix func -(a: Vector_3) -> Vector_3 {
return Vector_3(x: -a.x, y: -a.y, z: -a.z)
}

1
2

- 向量相乘/向量和常量相乘

+

func (one: Vector_3, other: Vector_3) -> Double {
return (one.x
other.x) + (one.y other.y) + (one.z other.z)
}

+
1
2

- 两个参数不能交换,需要重载两次 `*`
+

func (one: Vector_3, a: Double) -> Vector_3 {
return Vector_3(x: a
one.x, y: a one.y, z: a one.z)
}

+

func (a: Double, one: Vector_3) -> Vector_3 {
return one
a

+
// 也可采用下面写法
+

// return Vector_3(x: a one.x, y: a one.y, z: a * one.z)
}

+
1
2

- 修改自身参数,不需要返回值
+

func +=(one: inout Vector_3, other: Vector_3) {
// 已经重载过 + 运算符,可以直接调用
one = one + other
}

+

func ==(one: Vector_3, other: Vector_3) -> Bool {
return one.x == other.x &&
one.y == other.y &&
one.z == other.z
}

+

func !=(one: Vector_3, other: Vector_3) -> Bool {
return !(one == other)

+
// 也可采用下面写法
+return one.x != other.x ||
+        one.y != other.y ||
+        one.z != other.z
+

}

+

func <(one: Vector_3, other: Vector_3) -> Bool {
if one.x != other.x {return one.x < other.x}
if one.y != other.y {return one.y < other.y}
if one.z != other.z {return one.z < other.z}
return false
}

+

func <=(one: Vector_3, other: Vector_3) -> Bool {
return one < other || one == other

+
// 也可采用下面写法
+return one.x > other.x &&
+        one.y > other.x &&
+        one.z > other.z
+

}

+

func >(one: Vector_3, other: Vector_3) -> Bool {
return (one <= other)
}

+
1
2
3
4
5
6

### 自定义操作符

`postfix` 声明前后缀关键词, `operator ` 操作符关键词

- a+++
+

声明后置操作符
postfix operator +++
postfix func +++(vector: inout Vector_3) -> Vector_3 {
vector += Vector_3(x: 1.0, y: 1.0, z: 1.0)
return vector
}

+
1
2

- +++a
+

// 声明前置操作符
prefix operator +++
prefix func +++(vector: inout Vector_3) -> Vector_3 {
let temp = vector
vector += Vector_3(x: 1.0, y: 1.0, z: 1.0)
return temp
}

+

`

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/Swift\345\215\217\350\256\256\345\210\253\345\220\215/index.html" "b/2019/06/18/Swift\345\215\217\350\256\256\345\210\253\345\220\215/index.html" new file mode 100644 index 0000000..8554fc9 --- /dev/null +++ "b/2019/06/18/Swift\345\215\217\350\256\256\345\210\253\345\220\215/index.html" @@ -0,0 +1,350 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

Swift协议和别名

+

协议

某些属性的集合,可以有多个协议组成,具有的特点

+
    +
  1. 可以声明函数,但是不能实现,由实现这协议的具体结构或者类来实现
  2. +
  3. 声明的函数如果有参数也不能设置参数的默认值, 默认参数被认为也是函的一种实现
  4. +
  5. 可以声明属性,必须是计算型属性 只能使用var 修饰,不能是存储型属,必须在 {} 中明确指出属性的读取类型 例如 {get set}
  6. +
  7. 遵守协议的类可选择存储型属性或者计算型属性来实现协议中定义的属性
  8. +
  9. protocol Person: class { } 表示此协议只能被类来遵守,结构体能遵守
  10. +
  11. 协议可以看着是一种类型,如果协议中的属性是只读的,如果是协议类型只能访问get 方法, 但是遵守协议的具体类或者结构可以设置属性的读和写

    +
  12. +
  13. 协议中的构造函数

    +
    - 如果父类中遵守了协议中的构造函数,子类中必须重写父类中的构造函数并且使用require修饰构造函数,表示继承此类的子类也必须实现该构造函数
    +- 如果class 使用了final 修饰,表示不能被继承,此时不需要 require 关键词修饰
    +
  14. +
+

类型别名

    +
  • 定义别名 typealias, 供扩展使用

    +
    1
    2
    3
    4
    5
    6
    7
    8
    typealias Length = Double
    extension Double {
    var km: Length {return self * 1_000.0}
    var m: Length {return self * 100.0}
    var cm: Length {return self / 10.0}
    /// 英尺
    var ft: Length { return self / 3.28084}
    }
    +
  • +
+
使用
+
+
1
let distance: Length = 10.5.km
+
    +
  • 定义别名防止硬编码

    +
    1
    2
    	// 音频采样率
    typealias AudioSample = UInt64
    +
  • +
+

协议中 typealias

协议中定义一个属性,该属性的类型根据不同的情况返回不同的类型(我理解为类似泛型),具体的类型由遵守此协议的类或者结构指定

+
    +
  • 定义协议中别名
  • +
+
1
2
3
4
5
protocol WeightCalculable {
// 属性重量根据不同体积返回的可能是Int,可以可能是double类型,所以使用别名, 协议中使用associatedtype关键词,
associatedtype WeightType
var weight: WeightType { get }
}
+

扩展 Int 中的吨单位

+
1
2
3
4
extension Int {
typealias Weight = Int
var t: Weight {return self * 1_000}
}
+
    +
  • 使用协议中别名
  • +
+
    +
  • 一部手机的时使用该协议WeightCalculable
  • +
+
1
2
3
4
5
6
class iPhone: WeightCalculable {
typealias WeightType = Double
var weight: WeightType {
return 0.5
}
}
+
    +
  • 一辆大卡时使用该协议WeightCalculable
  • +
+
1
2
3
4
5
6
7
8
class Car: WeightCalculable {
typealias WeightType = Int
let weight: WeightType

init(weight: Int) {
self.weight = weight
}
}
+
1
let bigCar = Car(weight: 8_000.t) // 8 吨
+

面向协议编程

    +
  • 定义比赛协议,具有 赢/输/总场次/胜率 等特性
  • +
+
1
2
3
4
5
6
protocol Recordable: CustomStringConvertible {
var wins: Int { get }
var losses: Int { get }

func winningPerent() -> Double
}
+
    +
  • 协议中方法或者属性的默认实现
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
extension Recordable {
// CustomStringConvertible 协议默认实现
var description: String {
return String(format: "WINS: %d---LOSSES: %d", [wins, losses])
}

// 默认实现总场数计算型属性
var totalGames: Int {
return wins + losses
}

// 默认实现的方法
func shoutWins() {
print("come on", wins, "times!")
}
}
+

篮球比赛:

    +
  • Equatable 协议 - 相等比较 需要重载 ==
  • +
  • Comparable 协议 - 是否可比较 ,重载 < 可直接调用sort()函数
  • +
  • CustomStringConvertible 协议 - 打印信息,需重写 description
  • +
  • Recordable协议 - 比赛协议,没有平局
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
struct BasketballRecord:Equatable, Comparable, CustomStringConvertible, Recordable {
var wins: Int
var losses: Int


/// 在具体定义的类型中实现 协议中的属性或者方法就会覆盖协议中的
var totalGames: Int = 200

var description: String {
return "本次比赛胜: " + String(wins) + "负" + "\(losses)"
}

// 胜率
func winningPerent() -> Double {
return (Double(wins) / Double(totalGames))
}

// swift4.1 后,不需要再重写此方法,swift 会自动合成,如果不想让某一个属性参与比较,就需要重写该方法
static func == (lhs: BasketballRecord, rhs: BasketballRecord) -> Bool {
return lhs.wins == rhs.wins && lhs.losses == rhs.losses
}

// Comparable
static func < (lhs: BasketballRecord, rhs: BasketballRecord) -> Bool {
if lhs.wins != rhs.wins {
return lhs.wins < rhs.wins
}
return lhs.losses > rhs.losses
}
}
```

#### 足球比赛:

有平局的情况,原来的协议不能满足,需要增加一个平局的协议

- 定义平局协议
+

protocol Tieable {
/// 平局,可读写
var ties: Int { get set }

+

}

1
2
3
4

- 足球比赛需要遵守的协议 `Recordable` 和 `Tieable`

- 总场数需要加上平局的场数,需要实现totalGames的get方法

+

struct FootableRecord: Recordable, Tieable {
var wins: Int
var losses: Int
// 平局数
var ties: Int

+
// 总场数
+var totalGames: Int {
+    return wins + losses + ties
+}
+
+var description: String {
+    return "本次比赛胜: " + String(wins) + "负" + "\(losses)"
+}
+
+func winningPerent() -> Double {
+    return (Double(wins) / Double(totalGames))
+}
+

}

1
2
3
4
5
6
7
8
9
10
11
12

- 足球比赛中以上写法存在的问题

- 所有有平局的比赛计算总场次的逻辑都是一样,每次都要写一次实现
- 单一修改 Recordable 协议不能解决问题,
- 存在平局和没有平局两种情况 totalGames 的计算逻辑不相同

- 解决办法:
- 扩展遵守了`Recordable`协议的类型,前提条件是:这个类型遵守了 Tieable 同时也遵守了 `Recordable ` 协议,可以理解为如果该类型遵守了 `Tieable `协议之后,`Recordable` 协议中的 totalGames 属性实现是另一种方式了,不在是以前的 `totalGames` 中直接返回 `wins + losses` 的值


定义协议

+

protocol Recordable: CustomStringConvertible {
var wins: Int { get }
var losses: Int { get }

+
func winningPerent() -> Double
+

}

1
2

协议默认实现

+

extension Recordable {
var description: String {
return String(format: “WINS: %d—LOSSES: %d”, [wins, losses])
}

+
// 默认实现总场数计算型属性
+var totalGames: Int {
+    return wins + losses
+}
+
+// 默认实现胜率
+func winningPerent() -> Double {
+    return (Double(wins) / Double(totalGames))
+}
+
+// 默认实现的方法
+func shoutWins() {
+    print("come on", wins, "times!")
+}
+

}

+
1
2

扩展遵守了Tieable 的 类型的 Recordable 协议
+

extension Recordable where Self: Tieable {
var totalGames: Int {
return wins + losses + ties
}
}

+
1
2
3
4
5
6

### 协议聚合

如果对某一类型需要限制他必须遵守某些协议,可以使用协议聚合来定义

比如: 有个奖赏协议
+

protocol Prizable {
func isPrizable() -> Bool
}

+
1
2

篮球比赛遵守此协议
+

// 篮球比赛奖赏
extension BasketballRecord: Prizable {
func isPrizable() -> Bool {
return totalGames > 10 && winningPerent() > 0.5
}
}

+
1
2

足球比赛奖赏遵守此协议
+

extension FootableRecord: Prizable {
func isPrizable() -> Bool {
return wins > 1
}
}

1
2

现在有某一个学生也遵守了此协议

+

struct Student: Prizable {
var name: String
var score: Int

+
func isPrizable() -> Bool {
+    return score >= 60
+}
+

}

+
1
2

定义奖赏的方法,参数类型必须是遵守了此协议的结构或者类型
+

private func award(_ one: Prizable) {
if one.isPrizable() {
print(one)
print(“恭喜获得奖励”)
} else {
print(one)
print(“很遗憾”)
}
}

1
2

如果说 BasketballRecord 这个类还遵守了其他的协议,例如遵守了 `Recordable` 协议, 并且这个协议也遵守了 `CustomStringConvertible` 并且默认实现了`description` 的`get` 方法

+

// MARK: - 比赛协议,具有 赢。输。总场次。胜率 特性
protocol Recordable: CustomStringConvertible {
var wins: Int { get }
var losses: Int { get }

+
func winningPerent() -> Double
+

}

+
1
2

`Recordable` 默认实现
+

extension Recordable {
var description: String {
return String(format: “WINS: %d—LOSSES: %d”, [wins, losses])
}

+
// 默认实现总场数计算型属性
+var totalGames: Int {
+    return wins + losses
+}
+
+// 默认实现胜率
+func winningPerent() -> Double {
+    return (Double(wins) / Double(totalGames))
+}
+
+// 默认实现的方法
+func shoutWins() {
+    print("come on", wins, "times!")
+}
+

}

1
2
3
4
5

此时如果 `Student` 类还是调用 `award` 方法的话,print(one) 打印的信息将是`Recordable `中默认实现的内容,因此需要约束`award`函数的参数必须遵守两个协议让`Student` 也重写自己的`description `属性的`get`方法,不能再让 `Prizable` 扩展 默认实现

- swift 3 写法: protocol<A, B>
- swift 4 写法: A & B

+

private func award2(_ one: Prizable & CustomStringConvertible) {
if one.isPrizable() {
print(one)
print(“恭喜获得奖励”)
} else {
print(one)
print(“很遗憾”)
}
}

1
2
3
4
5
6
7
8
9
10

### 泛型约束


- 定义一个函数,找出一个学生数组中分数最大的
- 参数:一个学生数组,都遵守了 Comparable 的类型
- 返回值:某个遵守了 Comparable 的类型实例
- 此时函数报错 `Protocol 'Comparable' can only be used as a generic constraint because it has Self or associated type requirements`,

因为 Comparable 协议中定义的方法 public static func < (lhs: Self, rhs: Self) -> Bool 的参数类型是Self,是具体的某个类型

+

func maxScore(seq: [Comparable]) -> Comparable { }

1
2
3
4
5


- 如果需要定义一个函数实现在一个数组中找出需要奖励的人的名字该如何实现呢
- 参数:遵守两个协议 Comparable 和 Prizable 协议, 并且使用泛型
- 返回值:返回值是可选值,有可能没有任何奖励的对象

+

func topPrizable<T: Comparable & Prizable>(seq: [T]) -> T? {
return seq.reduce(nil) { (tempTop: T?, condender: T) in
guard condender.isPrizable() else { return tempTop }
// 解包 condender 失败, 上一层验证了他必须是奖励的那个
guard let tempTop = tempTop else { return condender }
return max(tempTop, condender)
}
}

+

`

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/Swift\345\215\217\350\256\256\346\211\251\345\261\225/index.html" "b/2019/06/18/Swift\345\215\217\350\256\256\346\211\251\345\261\225/index.html" new file mode 100644 index 0000000..af807f1 --- /dev/null +++ "b/2019/06/18/Swift\345\215\217\350\256\256\346\211\251\345\261\225/index.html" @@ -0,0 +1,244 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

Swift协议扩展

+

定义结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct Point {
var x = 0.0
var y = 0.0

}

struct Size {
var height = 0.0
var width = 0.0
}

struct Rectangle {
var origin = Point()
var size = Size()

init(origin: Point, size: Size) {
self.origin = origin
self.size = size
}
}
+

扩展

    +
  • 扩展方法
  • +
+
1
2
3
4
5
6
extension Rectangle {
mutating func translate(x: Double, y: Double) {
self.origin.x += x
self.origin.y += y
}
}
+
    +
  • 只能扩展计算型的属性,不能扩展存储型属性, 存储型属性需要在定义类或者结构体时声明
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
extension Rectangle {
var center: Point {
get {
let center_x = origin.x + size.width / 2.0
let center_y = origin.y + size.height / 2.0
return Point(x: center_x, y: center_y)
}

set {
origin.x = newValue.x - size.width / 2.0
origin.y = newValue.y - size.height / 2.0
}
}
}
+
    +
  • 扩展构造方法

    +
      +
    • 类中不能扩展指定构造方法,只能在结构体中扩展
    • +
    • 结构体中不能扩展便利构造方法m,只能在类中扩展
    • +
    +
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

extension Rectangle {
init(center: Point, size: Size) {
let origin_x = center.x - size.width / 2.0
let origin_y = center.y - size.height / 2.0
self.origin = Point(x: origin_x, y: origin_y)
self.size = size
}

// 结构体中不能扩展便利构造函数 Delegating initializers in structs are not marked with 'convenience'
// convenience init(center: Point, size: Size) {
// let origin_x = center.x - size.width / 2.0
// let origin_y = center.y - size.height / 2.0
// self.origin = Point(x: origin_x, y: origin_y)
// self.size = size
// }
}
+
    +
  • 扩展嵌套类型
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
extension Rectangle {
enum Vertex: Int {
case left_top
case left_bottom
case right_bottom
case right_top
}

// 获取某一个顶点坐标
func point(of vertex: Vertex) -> Point {
switch vertex {
case .left_top:
return origin
case .left_bottom:
return Point(x: origin.x, y: origin.y + size.height)
case .right_bottom:
return Point(x: origin.x + size.width, y: origin.y + size.height)
case .right_top:
return Point(x: origin.x + size.width, y: origin.y)
}
}
}
+
    +
  • 扩展下标,根据传入的索引获取对应顶点坐标
  • +
+
1
2
3
4
5
6
extension Rectangle {
subscript(index: Int) -> Point? {
assert(0 <= index && index < 4, "传入值非法")
return point(of: Vertex(rawValue: index)!)
}
}
+

扩展系统方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
extension Int {
/// 平方
var square: Int {
return self * self
}

/// 立方
var cube: Int {
return self * self * self
}

/// 判断数组是否在某一个范围内
func inRange(clousedLeft left: Int, openRight right: Int) -> Bool {
return self >= left && self > right
}

/// 重复执行操作
func repeatitions(task: () -> ()) {
for _ in 0..<self {
task()
}
}

/// 变长函数
func stride(from: Int, to: Int, by: Int, task: () -> ()) {
// 系统变长函数
for i in Swift.stride(from: 0, to: 21, by: 3) {
print(i)
}

for _ in Swift.stride(from: from, to: to, by: by) {
task()
}
}
}
+

扩展后使用

+
1
2
3
4
5
6
7
8
9
10
11
12
print(2.square)
print(3.cube)
print(4.inRange(clousedLeft: 0, openRight: 4))
4.repeatitions {
print("extension")
}


// 使用变长函数
4.stride(from: 0, to: 8, by: 4) {
print("stride")
}
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/Swift\346\263\233\345\236\213/index.html" "b/2019/06/18/Swift\346\263\233\345\236\213/index.html" new file mode 100644 index 0000000..3266819 --- /dev/null +++ "b/2019/06/18/Swift\346\263\233\345\236\213/index.html" @@ -0,0 +1,240 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

Swift 泛型

泛型定义–wikipedia

    +
  1. 在程序编码中一些包含类型参数的类型,也就是说泛型的参数只可以代表类,不能代表个别对象。(这是当今较常见的定义)
  2. +
  3. 在程序编码中一些包含参数的类。其参数可以代表类或对象等等。(现在人们大多把这称作模板)
  4. +
+

泛型的用处

泛型可以实现传入一个指定类型后做一些操作然后返回后还是原来指定的类型,你可以指定特定的类型(特定是指约束)

+

泛型和 Any 的区别

区别:Any能代表任何类型,但是不能返回某个具体的类型,都需要使用 as 去转化,而泛型正好相反

+
    +
  • Any 可以代表任何类型,除了class之外还可以代表struct,enum,
  • +
  • AnyObject 可以代码任何 class 的类型, swift 中基本类型, ArrayDictionary 都是 struct 类型,都不能使用 AnyObject 来接受
  • +
+

Swift中Any 和 AnyObject用法

+

泛型函数和泛型参数

swapTwoValues是一个泛型函数,可以接收或者返回任何类型

+
1
2
3
4
5
func swapTwoValues<T>(inout a: T, inout b: T) {
let temporaryA = a
a = b
b = temporaryA
}
+
    +
  • swapTwoValues 函数名后面的 <T> 表示这个 T 类型是函数所定义的一个类型, T 只是一个类型的占位符,不用关心具体类型

    +
  • +
  • (inout a: T, inout b: T) 中a和b两个参数类型T是泛型参数

    +
  • +
+

类型约束

两个泛型参数的泛型函数

+
1
2
3
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
// function body goes here
}
+
    +
  • 泛型参数T约束条件:必须是SomeClass类的子类
  • +
  • 泛型参数U约束条件:必须遵守SomeProtocol协议
  • +
  • 使用where指定约束
  • +
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/Swift\351\224\231\350\257\257\345\244\204\347\220\206/index.html" "b/2019/06/18/Swift\351\224\231\350\257\257\345\244\204\347\220\206/index.html" new file mode 100644 index 0000000..caa8bb1 --- /dev/null +++ "b/2019/06/18/Swift\351\224\231\350\257\257\345\244\204\347\220\206/index.html" @@ -0,0 +1,221 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

Swift 中错误处理

+

assert 断言

只在debug 模式下程序会自动退出

+
1
public func assert(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)
+

assertionFailure 断言错误

只在debug 模式下程序会自动退出, 没有条件

+
1
public func assertionFailure(_ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)
+

precondition

releas 模式程序也会退出

+
1
public func precondition(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)
+

throw

+ +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/Swift\351\241\271\347\233\256\344\270\255\351\233\206\346\210\220RN/index.html" "b/2019/06/18/Swift\351\241\271\347\233\256\344\270\255\351\233\206\346\210\220RN/index.html" new file mode 100644 index 0000000..7ae91c3 --- /dev/null +++ "b/2019/06/18/Swift\351\241\271\347\233\256\344\270\255\351\233\206\346\210\220RN/index.html" @@ -0,0 +1,243 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

Swift已有项目中集成ReactNative

+

本教程针对于已经有的Swift项目集成ReactNative

+

创建新的空目录

为了不影响原有的swift项目目录结构,建议先创建一个空的文件夹,例如SwiftRN

+

+

创建package.json文件

终端进入创建的SwiftRN文件目录下,创建package.json文件

+
1
touch package.json
+
修改package.json文件,指定各个版本
1
2
3
4
5
6
7
8
9
10
11
12
13
{
"name": "SwiftRN",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start",
"test": "jest"
},
"dependencies": {
"react": "16.3.1",
"react-native": "^0.56.0"
}
}
+
执行npm install

在创建package.json所在的文件目录下执行,在此过程中可以遇到的错误可能较多

+
1
npm install
+
添加index.js文件
1
touch index.js
+
修改文件内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import {
AppRegistry,
Text,
View,
} from 'react-native';

import React, { Component } from 'react';

// 用于最后在项目中测试
class SwiftRNHomeView extends Component {
render() {
return(
<View style={{backgroundColor:'white'}}>
<Text style={{fontSize: 20, marginTop: 200}}>Hellow world</Text>
</View>
);
}
}


class MyApp extends Component {
render() {
return <SwiftRNHomeView></SwiftRNHomeView>;
}
}

AppRegistry.registerComponent('SwiftRN', () => MyApp);
+

配置swift项目

将新创建的Swift文件目录下的所有文件拷贝到Swift项目所在的根目录下

+

+

使用Pod集成RN

swift项目采用了pod管理第三方库,在Podfile文件中加入下面代码

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
pod 'React', :path => './node_modules/react-native', :subspecs => [
'Core',
'RCTText',
'RCTNetwork',
'RCTWebSocket', # 这个模块是用于调试功能的
'CxxBridge', # Include this for RN >= 0.47,不引入的话会报错误或者警告
'DevSupport', # Include this to enable In-App Devmenu if RN >= 0.43,不引入的话会报错误或者警告
'RCTAnimation', 不引入的话会报错误或者警告
]


# 如果你的RN版本 >= 0.42.0,请加入下面这行,不引入的话会报错误或者警告
pod "yoga", :path => "./node_modules/react-native/ReactCommon/yoga"
pod 'DoubleConversion', :podspec => './node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
pod 'glog', :podspec => './node_modules/react-native/third-party-podspecs/glog.podspec'
pod 'Folly', :podspec => './node_modules/react-native/third-party-podspecs/Folly.podspec'
+

创建桥接文件

    +
  • 如果原项目中有SwiftDemo-Bridging-Header.h类似这个文件可以跳过。
  • +
  • 如果从来没有创建过桥接文件,创建一个OC的类会自动弹出是否需要创建桥接文件,直接创建即可,最后把无用的OC文件删掉
  • +
+

引用RN头文件

在桥接文件中导入头文件

+
1
2
#import <React/RCTRootView.h>
#import <React/RCTBundleURLProvider.h>
+

使用RN页面

在模拟器中使用RN的页面, 确保在 info.plist 文件中添加了Allow Arbitrary Loads=YES

+
    +
  • moduleName 对应的名字和index.js中 AppRegistry.registerComponent(‘SwiftRN’, () => MyApp)注册的名字必须相同
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import UIKit

class RNTestController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "http://localhost:8081/index.bundle?platform=ios")!
let rootView = RCTRootView(
bundleURL: url,
moduleName: "SwiftRN", //这里的名字必须和AppRegistry.registerComponent('MyApp', () => MyApp)中注册的名字一样
initialProperties: nil,
launchOptions: nil
)
view = rootView
}
}
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/Xcode\344\270\255\344\275\277\347\224\250Debug Memory Graph\346\243\200\346\237\245\345\206\205\345\255\230\346\263\204\346\274\217/index.html" "b/2019/06/18/Xcode\344\270\255\344\275\277\347\224\250Debug Memory Graph\346\243\200\346\237\245\345\206\205\345\255\230\346\263\204\346\274\217/index.html" new file mode 100644 index 0000000..9c341d6 --- /dev/null +++ "b/2019/06/18/Xcode\344\270\255\344\275\277\347\224\250Debug Memory Graph\346\243\200\346\237\245\345\206\205\345\255\230\346\263\204\346\274\217/index.html" @@ -0,0 +1,223 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/2019/06/18/docs/15608702427054/index.html b/2019/06/18/docs/15608702427054/index.html new file mode 100644 index 0000000..62cab3d --- /dev/null +++ b/2019/06/18/docs/15608702427054/index.html @@ -0,0 +1,215 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/flutter\345\256\211\350\243\205/index.html" "b/2019/06/18/flutter\345\256\211\350\243\205/index.html" new file mode 100644 index 0000000..b003d00 --- /dev/null +++ "b/2019/06/18/flutter\345\256\211\350\243\205/index.html" @@ -0,0 +1,216 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

flutter安装

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 导入头文件
import 'package:flutter/material.dart';

void main() => (MyApp());

// 定义类, 继承

class MyApp extends StatelessWidget {
// 重写
@override

Widget build(BuildContext context) {

// 返回一窗口
return MaterialApp(
title:'Flutter',
home: Scaffold(
appBar: AppBar(
title: Text('flutter 初体验')
),

body: Center(
child: Text('flutter 学习之路'),
),
),
);
}
}
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}

class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);

// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.

// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".

final String title;

@override
_MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;

void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}

@override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Invoke "debug painting" (press "p" in the console, choose the
// "Toggle Debug Paint" action from the Flutter Inspector in Android
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
// to see the wireframe for each widget.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'你可以点击按钮来修改文字,我是热重载的结果',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
+ +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/iOS \345\212\250\346\200\201\350\256\276\347\275\256TableHeaderView\351\253\230\345\272\246/index.html" "b/2019/06/18/iOS \345\212\250\346\200\201\350\256\276\347\275\256TableHeaderView\351\253\230\345\272\246/index.html" new file mode 100644 index 0000000..7c10824 --- /dev/null +++ "b/2019/06/18/iOS \345\212\250\346\200\201\350\256\276\347\275\256TableHeaderView\351\253\230\345\272\246/index.html" @@ -0,0 +1,230 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

iOS动态设置tableHeaderView高度

需求

动态的设置tableHeaderView的高度

+

遇到的问题:

    +
  • 设置tableHeaderViewframe无法实现,有时候会出现tableView 中间的 cell 不显示
  • +
  • 有时候会导致headerViewcell内容重叠

    +
  • +
  • 通过计算控件高度设置frame方式麻烦

    +
  • +
+

正确的步骤

    +
  • 创建HeaderView
  • +
  • 取出UITableViewtableHeaderView
  • +
  • 设置frame大小,调整headerView布局
  • +
  • 重新给UITableViewtableHeaderView
  • +
+

使用AutoLayout

UITableView增加分类

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
extension UITableView {
/// set tableHeaderView
func setTableHeaderView(_ headerView: UIView) {
headerView.translatesAutoresizingMaskIntoConstraints = false
self.tableHeaderView = headerView
// autolayout
NSLayoutConstraint(item: headerView, attribute: .centerX, relatedBy: .equal, toItem: self, attribute: .centerX, multiplier: 1.0, constant: 0).isActive = true
NSLayoutConstraint(item: headerView, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1.0, constant: 0).isActive = true
NSLayoutConstraint(item: headerView, attribute: .width, relatedBy: .equal, toItem: self, attribute: .width, multiplier: 1.0, constant: 0).isActive = true
}
/// update
func updateHeaderView() {
guard let headerView = self.tableHeaderView else { return }
headerView.layoutIfNeeded()
let header = self.tableHeaderView
self.tableHeaderView = header
}
}
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/iOS\345\274\200\345\217\221_\344\272\216\351\270\277\351\271\217/index.html" "b/2019/06/18/iOS\345\274\200\345\217\221_\344\272\216\351\270\277\351\271\217/index.html" new file mode 100644 index 0000000..beeff65 --- /dev/null +++ "b/2019/06/18/iOS\345\274\200\345\217\221_\344\272\216\351\270\277\351\271\217/index.html" @@ -0,0 +1,264 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

个人信息

+

工作经历

猎聘网 (2018.2 ~ 至今)

Swift重构猎聘App中应聘记录模块,推进项目组使用AutoLayout技术,后期负责开发迭代乐班班项目,参与每版需求分析和主要代码编写,解决复杂功能页面卡顿,内存优化,BUG修复及iOS新特性适配等。

+

北京万朝科技有限公司(2015.5 ~ 2018.2)

在该项目中担任iOS开发⼯作,参与项目需求分析、核⼼代码编写以及常用工具类的封装,BUG修复,版本迭代更新,推进组内swift的使用。

+

中盎炬华科技发展有限公司(2014.4月 ~ 2015.5)

参与iOS客户端的开发维护完成开发工作,实现一些交互动画和效果;研究最新技术,优化程序代码,集成第三方SDK。

+

项⽬经历

1. 猎聘APP (2018.2 ~ 至今)

项⽬目描述: 为中高端人才和经理人提供招聘的平台

+
    +
  • 使用 swift 重构应聘记录模块中相关页面及功能
  • +
  • 推进整个项目组使用 AutoLayout 技术
  • +
+

2. 乐班班

项⽬目描述: 音/视频播放、直播,内购,即时通信等功能,由OC、Swift,RN 技术混编,项⽬应⽤技术

+
    +
  • 使用ReactNative技术重构和开发部分页面
  • +
  • 使用Xcode、Instruments工具查找、分析、解决项目内存泄露,修复BUG
  • +
  • Swift重构通信模块,用collectionView构建消息页面,自定义emoj Keyboard,封装常用UI控件,自定义控制器转场动画
  • +
  • 使用AVFoundation 实现视频录制,上传,播放功能
  • +
  • 通过GCD dispatch semaphors 解决并发网络请求问题
  • +
  • 使用collectionView构建复杂的视频播放页面,企业大学首页
  • +
+

3. e蜂通信 (2015.5 ~ 2018.2)

项⽬目描述:整合了办公OA、CRM系统,新增即时通信、分享、远程监控、解密外发、红包、私聊等功能;由OC和swift混编, 项⽬目应⽤用技术:

+
    +
  • 使⽤CocoaAsyncSocket自定义通信协议,实现聊天,特定业务消息触达功能
  • +
  • 使⽤cocoaPod实现组件化,
  • +
  • 封装swift基础库,完善swift和Objective-C混编
  • +
  • 实现分享⼩视频、图片、点赞、评论功能
  • +
  • ⾃定义相机实现人脸识别打卡、扫描二维码功能
  • +
  • 封装常⽤UI控件,使用Core Animation实现动画效果,集成百度地图SDK
  • +
  • 利⽤JavaScriptCore完成H5和Native之间的交互
  • +
+

4. 四万公里 (2014.4 ~ 2015.5)

项目描述:针对境外旅游群体提供便捷服务,项⽬目应⽤用技术:

+
    +
  • 封装 TableViewController基类,对接服务端接口等
  • +
  • 封装基础UI控件
  • +
  • 集成科大讯飞SDK, 实现了实时翻译功能
  • +
  • 集成滴滴打车SDK, 实现用户可在国外打车功能
  • +
+

专业技能

    +
  1. 熟练使用Objective-C、Swift,及Swift与Objective-C混编,有良好的编程习惯
  2. +
  3. 熟练掌握iOS内存管理机制以及ARC技术,结合Instruments对项目进行内存优化
  4. +
  5. 熟练掌握NSThread/GCD/NSOperation等并发编程技术
  6. +
  7. 熟练掌握iOS中事件链和响应链的传递方式
  8. +
  9. 熟练使⽤AFNetworking,Moya,FMDB,SDWebImage,Masonry等第三⽅开源框架
  10. +
  11. 熟练掌握各种UI控件封装,熟悉多视图开发,能实现复杂的界⾯面交互
  12. +
  13. 掌握KVO原理理、KVC原理理、SDWebImage内部实现过程、RunLoop的应⽤
  14. +
  15. 有团队开发经验,同时能够独立完成APP开发,代码编写,调试和发布
  16. +
+

自我介绍

工作之余了解使⽤ JavaScript,RxSwift 函数响应式编程,小程序开发等知识,经常关注iOS博客和Github 上优秀代码,以及在raywenderlich上学习新知识,在个人博客做些技术积累。 计划在iOS领域深⼊同时也扩展⾃己的知识⾯,了解使⽤后端和前端技术。

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/iOS\351\200\232\350\277\207URL\350\216\267\345\217\226HTML\344\270\255\346\240\207\351\242\230/index.html" "b/2019/06/18/iOS\351\200\232\350\277\207URL\350\216\267\345\217\226HTML\344\270\255\346\240\207\351\242\230/index.html" new file mode 100644 index 0000000..7424181 --- /dev/null +++ "b/2019/06/18/iOS\351\200\232\350\277\207URL\350\216\267\345\217\226HTML\344\270\255\346\240\207\351\242\230/index.html" @@ -0,0 +1,258 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

iOS通过URL获取HTML中标题

+

获取标题

String扩展一个方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private let patternTitle = "(?<=title>).*(?=</title)"
extension String {
static func parseHTMLTitle(_ urlString: String) -> String? {
let url = URL(string: urlString)!
do {
let html = try String(contentsOf: url, encoding: .utf8)
let range = html.range(of: patternTitle, options: String.CompareOptions.regularExpression, range: html.startIndex..<html.endIndex, locale: nil)
guard let tempRange = range else { return "" }
let titleStr = html[tempRange]
return String(titleStr)
} catch {
return ""
}
}
}
+
iOS 中简单正则表达式

iOS 中 正则表达式中的\ 需要使用 \\ 来表示

+
// 获取
+let testStr = "我到底是[iOS]开发还是[产品]经理"
+
+// 获取 "[" 和 "]" 之间的所有内容, 左边[ 起一直到 右边 ] 结束的所有内容 
+let pattern1 = "\\[.*\\]" // [iOS]开发还是[产品]
+
+// 获取 "[" 和 "]" 之间的所有内容,包括[]
+let pattern2 = "\\[.*?\\]"    // [iOS]
+
+// 获取第一个 "[" 和 "]" 之间的所有内容 不包括[] 
+let pattern3 = "(?<=\\[).*?(?=\\])" // iOS
+
+// 获取html中 title 标签中内容
+let pattern5 = "(?<=title>).*(?=</title)" 
+
获取范围截取子串
let testRange1 = testStr.range(of: pattern1, options: String.CompareOptions.regularExpression, range: testStr.startIndex..<testStr.endIndex, locale: nil)
+    let result1 = String(testStr[testRange1!])
+
+
+let testRange2 = testStr.range(of: pattern2, options: String.CompareOptions.regularExpression, range: testStr.startIndex..<testStr.endIndex, locale: nil)
+let result2 = String(testStr[testRange2!])
+
+
+let testRange3 = testStr.range(of: pattern3, options: String.CompareOptions.regularExpression, range: testStr.startIndex..<testStr.endIndex, locale: nil)
+let result3 = String(testStr[testRange3!])
+
+print(result1) 
+print(result2) 
+print(result3) 
+
正则表达式

通过URL解析html中的标题

+
    +
  • 获取html字符串
  • +
  • 正则匹配
  • +
  • 生成字符串Range
  • +
  • 截取字符串
  • +
+

匹配一次直接返回结果

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 生成表达式
let regular = try NSRegularExpression(pattern: pattern5, options: .caseInsensitive)


// 开始匹配
let matchResult1 = regular.firstMatch(in: html, options: NSRegularExpression.MatchingOptions.reportProgress, range: NSMakeRange(0, html.count))

// 获取匹配结果范围
let matchRange = matchResult1?.range
let range = Range(matchRange!)!

// 生成String.Index 类型范围
let startIndex = html.index(html.startIndex, offsetBy: range.lowerBound)
let endIndex = html.index(html.startIndex, offsetBy: range.upperBound)
let range3 = Range(uncheckedBounds: (lower: startIndex, upper: endIndex))

// 截取字符串
let result1 = String(html[range3])
print(result1)
+

全部匹配后返回结果时一个集合

+
1
2
3
4
5
6
7
8
9
10
let matchResult2 = regular.matches(in: html, options: .reportProgress, range: NSMakeRange(0, html.count))
matchResult2.forEach { (result) in
let range = Range(result.range)!

let startIndex = html.index(html.startIndex, offsetBy: range.lowerBound)
let endIndex = html.index(html.startIndex, offsetBy: range.upperBound)

let subStr2 = html[Range(uncheckedBounds: (startIndex, endIndex))]
print(String(subStr2))
}
+

将匹配到的内容提还

+
1
let str3 = regular.stringByReplacingMatches(in: html, options: .reportProgress, range: NSMakeRange(0, html.count), withTemplate: "====")
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/iOS\351\235\242\350\257\225OC\350\257\255\350\250\200\347\211\271\346\200\247/index.html" "b/2019/06/18/iOS\351\235\242\350\257\225OC\350\257\255\350\250\200\347\211\271\346\200\247/index.html" new file mode 100644 index 0000000..75e2536 --- /dev/null +++ "b/2019/06/18/iOS\351\235\242\350\257\225OC\350\257\255\350\250\200\347\211\271\346\200\247/index.html" @@ -0,0 +1,330 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

OC 语言特性

分类

原理

由运行时来决议的,不同分类当中含有同名方法 谁最终生效取决于谁最终参与编译,最后参编译的同名分类方法会最终生效,假如分类方法中添加的方法和宿主类中的某一个方法名相同,分类中的方法会覆盖宿主类中的同名方法,覆盖是指在消息传递过程中优先查找数组靠前的元素,如果查找到了同名方法就直接调用,实际上宿主类的同名方法实现仍然是存在的,我们可可以通过一些手段调用到原有类的同名方法的实现

+

使用场景

    +
  • 申明私有方法,分类的 .m 文件中申明私有方法啊,对外不暴露
  • +
  • 对类中的代码进行抽取分解
  • +
  • 把Framework的私有方法公开化
  • +
+

特点

    +
  • 运行时决议,运行时通过runtime 才把分类中的方法添加到了宿主类上
  • +
  • 可以为系统类添加方法
  • +
  • 有多个分类中存在同名方法时,最后编译的方法会最先生效
  • +
  • 分类中有和宿主类同名方法时,底层中通过内存copy将分类方法“覆盖”宿主类方法
  • +
  • 名字相同的分类编译会报错
  • +
+

分类中可以添加的内容

    +
  • 实例方法
  • +
  • 类方法
  • +
  • 协议
  • +
  • 属性,实际上只是声明了 getter / setter 方法,并没有添加成员变量
  • +
+

关联对象技术

关联对象由 AssocicationsManager 管理并在 AssociationsHashMap 上存储
所有对象的关联内容都在同一个全局容器中

+
    +
  • 给分类添加成员变量,通过关联对象的方式
  • +
+

扩展

使用场景

    +
  • 声明私有属性
  • +
  • 声明私有方法
  • +
  • 声明私有成员变量
  • +
+

特点

    +
  • 编译时决议
  • +
  • 只以声明的形式存在,多数情况下寄生在宿主类的.m中
  • +
  • 不能为系统添加扩展
  • +
+

代理

    +
  • 代理设计模式,传递方式是一对一
  • +
  • 使用weak 避免循环引用
  • +
+

通知

使用观察者模式来实现的用于跨层传递消息的机制

+

KVO

KVO 是系统对观察者模式的又一实现,使用isa 混写技术(isa - swizzling)来动态运行时为某一个类添加一个子类重写了它的setter 方法,同时将原有类的isa指针指向了新创建的类上

+
    +
  • KVO 是OC对观察者模式的又一实现
  • +
  • apple 使用isa 混写技术(isa - swizzling)来实现KVO
      +
    • 给我A类注册一个观察者时本质上是调用系统的 Observer for keyPath 方法,系统会创建一个NSKVONotifiying_A 的类
    • +
    +
  • +
  • 使用 setter 方法设置值KVO才能生效
  • +
  • 使用setValue:forKey: 改变值KVO才能生效
  • +
  • 使用下划线的成员变量直接修改值需要手动添加KVO才能生效(增加 willChangeValueForKey 和 didChangeValueForKey 两个方法)
  • +
+

KVC

apple提供的键值编码技术

+
    +
  • (nullable id)valueForKey:(NSString *)key;
  • +
  • (void)setValue:(nullable id)value forKey:(NSString *)key;

    +

    以上两个方法中的key是没有任何限制的,只要知道对应的成员变量名称,就可以对私有的成员变量设置和操作,这违背了面向对象的编程思想

    +
  • +
+

(void)setValue:(nullable id)value forKey:(NSString *)key; 方法调用流程

+
    +
  • 先判断是否有跟key 相关的setter 方法,如果有就直接调用,结束调用
  • +
  • 如果没有再判断是否存在实例变量,如果存在直接给赋值,结束调用
  • +
  • 如果说实例变量不存在,会去调用 - (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key; 抛出异常
  • +
+

属性关键字

读写权限

    +
  • readonly

    +
  • +
  • readwrite 系统默认

    +
  • +
+

原子性

    +
  • atomic (系统默认)保证赋值和获取(对成员属性的直接获取和赋值)时线程安全,并不代表操作和访问,如果atomic修饰的一个数组,只能保证独具改数组的读取和赋值时线程安全,并不能保证对数据操作(增加/删除数组元素)时是安全的
  • +
  • nonatomic
  • +
+

引用技术

    +
  • assign
      +
    • 修饰基本数据类型,
    • +
    • 修饰对象时不改变其引用计数,
    • +
    • 会产生悬垂指针
    • +
    +
  • +
  • weak
      +
    • 不改变被修饰对象的引用计数,
    • +
    • 所指对象被释放之后指针自动置为nil
    • +
    +
  • +
+

两者区别:

+
    +
  • weak可以修饰对象,而assign 既可以修饰对象也可以修饰基本数据类型
  • +
  • assign 修饰的对象,对象释放后指针仍然指向原对象的内存地址,而weak 修饰的对象,释放后会自动置为nil
  • +
+

问题1

+

weak 修饰的对象为什么释放后会自动置为nil

+

问题2

+

@property (copy) NSMutableArray *array; 有什么问题

+
    +
  • 被copy修饰后,如果赋值过来的是NSMutableArray,copy之后就是NSArray,如果赋值过来的是NSAarray,copy 之后仍然是NSArray,有可能会调用array的添加元素方法会导致crash
  • +
+

copy 关键词

+
    +
  • 浅拷贝:内存地址的复制,让目标对象指针和源对象指向同一块内存空间,
      +
    • 会增加对象的引用计数
    • +
    • 并没有一个新的内存分配
    • +
    +
  • +
+
    +
  • 深拷贝:目标对象指针和源对象指针分别指向两块内容相同的内存空间
      +
    • 不会增加源对象的引用计数
    • +
    • 有新对象的内存分配
    • +
    +
  • +
+

MRC 重写 retain修饰的变量的setter 方法

1
2
3
4
5
6
7
8

- (void)setObj:(id)obj {
if (_obj != obj) {
[_obj release];
}
_obj = [obj retain];

}
+

判断是为了防止异常处理,如果不做 if 判断,当传入的 obj 对象正好是原来的_obj 对象,对原对象尽行releas 操作,实际上也会对传入的对象进行releas 操作进行释放,此时如果再通过obj指针访问废弃的对象时就会导致carsh

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/is \345\205\263\351\224\256\345\255\227\347\261\273\345\236\213\346\243\200\346\237\245/index.html" "b/2019/06/18/is \345\205\263\351\224\256\345\255\227\347\261\273\345\236\213\346\243\200\346\237\245/index.html" new file mode 100644 index 0000000..6b8ca40 --- /dev/null +++ "b/2019/06/18/is \345\205\263\351\224\256\345\255\227\347\261\273\345\236\213\346\243\200\346\237\245/index.html" @@ -0,0 +1,225 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

swift类型检查和类型转换

+

is 关键字类型检查

判断父类和子类之间的关系

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
func typeCheck() {
let programmer = Programmer(name: "老张-php")
if dev is iOSDeveloper {
print("iOS 开发者")
} else {
print("其他开发者")
}

if programmer is Coder {
print("老张-php是一农个码农")
} else {
print("")
}
}
+

as 关键字类型转换

1
2
let ios = iosDev as? Programmer
let ios2 = iosDev as! Coder
+

判断是否遵守了协议

swift 中协议可以当作是一个类型使用

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
et ios = iosDev as? Programmer
let ios2 = iosDev as Coder

// 是否遵守了 Person 协议
if iosDev is Person {
print("遵守了Person协议")
}

let devs = [dev, iosDev, programmer] as [Any]

for dev in devs {
if let dev = dev as? Person {
print(dev)
print("遵守Person协议")
} else {
print("未遵守Person协议")
}
}
+

AnyObject Any

1
2
3
4
var anyObjectArray: [AnyObject] = [CGFloat(0.5) as AnyObject,
1 as AnyObject,
"string" as AnyObject,
iOSODev]
+

swift是面向函数编程, 函数是一等公民,数组中可以存入一个函数,函数表达的是一个过程,不是一个名词或者物体,所以函数不是一个对象,需要使用 any 关键字

+
1
2
3
4
var anyArray: [Any] = [CGFloat(0.5),
1,
"string",
iOSODev]
+

放入函数

+
1
anyArray.append({ (a: Int) -> (Int) in return a * a })
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/npm\345\222\214cnpm/index.html" "b/2019/06/18/npm\345\222\214cnpm/index.html" new file mode 100644 index 0000000..40d9587 --- /dev/null +++ "b/2019/06/18/npm\345\222\214cnpm/index.html" @@ -0,0 +1,244 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

ReactNative配置.npm 和cnpm 相关

npm

Nodejs的包管理工具

+

npm 常用命令

    +
  • 更新或卸载

    +
    1
    npm update/uninstall moduleName
    +
  • +
  • 查看当前目录下已安装的包

    +
    1
    npm list
    +
  • +
  • 查看全局安装的包的路径

    +
    1
    npm root -g
    +
  • +
  • 全部命令

    +
    1
    npm help
    +
  • +
+

cnpm

因为npm安装插件是从国外服务器下载,受网络影响大,可能出现异常,cnpm 淘宝镜像

+

安装

1
npm install -g cnpm --registry=https://registry.npm.taobao.org
+

设置镜像源

1
2
cnpm set registry http://registry.cnpm.taobao.com
npm set registry http://registry.cnpm.taobao.com
+

配置 .npmrc 文件

前端项目开发离不开安装各种npm依赖包,可以选择远程的仓库也可以选择本地的仓库,但更改仓库地址需要在安装时控制台打命令,比较麻烦, 而npmrc可以很方便地解决上面问题

+

.npmrc 原理

当安装项目的依赖包时,会查找并读取项目根目录下的.npmrc文件的配置,自动指定仓库地址

+
    +
  • 打开 .npmrc

    +
    1
    open .npmrc
    +
  • +
+
    +
  • 编辑文件, 指定特定的库的镜像源, 比如以@test开头的库镜像为:http://registry.cnpm.test.com/ 而其他的正常

    +
    1
    2
    @test:registry=http://registry.cnpm.lietou.com/
    registry=https://registry.npm.taobao.org/
    +
  • +
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/swift\344\270\255\345\274\272\345\274\225\347\224\250\345\276\252\347\216\257\345\222\214weak\345\205\263\351\224\256\345\255\227/index.html" "b/2019/06/18/swift\344\270\255\345\274\272\345\274\225\347\224\250\345\276\252\347\216\257\345\222\214weak\345\205\263\351\224\256\345\255\227/index.html" new file mode 100644 index 0000000..a55ed6a --- /dev/null +++ "b/2019/06/18/swift\344\270\255\345\274\272\345\274\225\347\224\250\345\276\252\347\216\257\345\222\214weak\345\205\263\351\224\256\345\255\227/index.html" @@ -0,0 +1,299 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

Swift中内存管理

+

强引用循环和 weak 关键字

房东

+
1
2
3
4
5
6
7
8
9
10
11
12
class Landlord {
var name: String
var house: House?

init(name: String) {
self.name = name
}

deinit {
print("Landlord内存释放")
}
}
+

房子

+
1
2
3
4
5
6
7
8
9
10
11
12
class House {
var name: String
var landlord: Landlord?

init(person: String) {
self.name = person
}

deinit {
print("House内存释放")
}
}
+

循环引用

+
1
2
3
4
5
6
7
8
9
10
11
func strongRetain() {
var landlord: Landlord? = Landlord(name: "老李")
var house: House? = House(person: "望京府邸")
// 房东有个房子
landlord?.house = house
// 房子有个主人
house?.landlord = landlord

landlord = nil
house = nil
}
+
    +
  • 虽然 landlord = nil 和 house = nil,但是 Landlord 和 House 内存空间并没有释放 (并未执行 deinit 函数)

    +
  • +
  • 原因:因为两个内存空间相互引用,引用计数不为0,形成了强循环引用

    +
  • +
  • 解决办法:使用weak 关键字来修饰两者中其中一个变量

    +
      +
    • 使用weak修饰的变量必须是可选类型,可以赋值为nil
    • +
    • 使用weak修饰必须是var 类型变量
    • +
    +
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
class House {
var name: String
weak var landlord: Landlord?

init(person: String) {
self.name = person
}

deinit {
print("House内存释放")
}
}
+

unowned 关键字的使用

相互循环引用的两个变量有一个是可选的

有个身份证的类,每个身份证肯定对应一个人

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class ID {
var number: String
let person: Landlord

init(person: Landlord, number: String) {
self.person = person
self.number = number
}

deinit {
print("ID内存释放")
}
}

```

房东
+

class Landlord {
var name: String
var house: House?
/// 身份证可以为nil,
var id: ID?

+
init(name: String) {
+    self.name = name
+}
+
+deinit {
+    print("Landlord内存释放")
+}
+

}

+
1
2

循环引用
+

func unownedTest() {
var person: Landlord? = Landlord(name: “老王”)
var id: ID? = ID(person: person!, number: “10010”)

+
person?.id = id
+person = nil
+id = nil
+

}

+
1
2
3
4
5
6
7
8

- person 和 id 之间有强引用关系
- 每个id必须有一个对应的人并且是固定的只能使用let 修饰,不能为nil, 而id可以为空
- unowned和 weak 作用相同都是弱引用,只是unowned 修改的变量不能是nil (可选型的)
- unowned只能使用在 class 类型上,不能修饰函数类型,例如闭包存在循环引用时不能使用unowned修饰
- 解决方法:
- 此时可以通过使用 weak 修饰 id 的方式,因为 id 可以为nil
- let 修饰并且不能为nil 这种情况可以使用 unowned 修饰解决
+
class ID {
+    var number: String
+    unowned let person: Landlord
+
+    init(person: Landlord, number: String) {
+        self.person = person
+        self.number = number
+    }
+
+    deinit {
+        print("ID内存释放")
+    }
+}
+
1
2
3
4
	
**unowned有一定的危险性**

因为unowned 修饰的对象不能赋值为nil,所以执行下面的代码会crash,
+

func unownedTest() {
var person: Landlord? = Landlord(name: “老王”)
var id: ID? = ID(person: person!, number: “10010”)

+
person?.id = id
+
+// 提前释放 person 内存
+person = nil
+
+// 获取身份证对应的人
+print(id?.person) // 此处奔溃,
+let owner = id?.person
+id = nil
+

}

1
2

正确的写法因该是先将🆔设置为nil,再将 Person 设置为nil,再调用 print(id?.person) 就不会再报错

+

func unownedTest() {
var person: Landlord? = Landlord(name: “老王”)
var id: ID? = ID(person: person!, number: “10010”)

+
person?.id = id
+
+id = nil
+
+// 提前释放 person 内存
+person = nil
+
+// 获取身份证对应的人
+print(id?.person) // 此处奔溃,
+let owner = id?.person
+

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

#### 相互循环引用的两个变量都不是可选的

使用隐式可选类型

### 闭包中的强引用循环

- 并不是所有的闭包都会产生循环引用,只有相互引用的两个对象会产生循环引用

- 使用swift闭包捕获列表,在闭包的声明参数之前使用 [unowned self], 不需要解包

- 使用 unowned表示这个对象不能为空,可能存在风险,如果对象为空就会crash


使用 unowned 对象不能为空,可能存在风险

+

class Closure {
var closure: ((Int) -> Void)?

+
init() {
+    closure = { [unowned self] index in
+        print(self)
+        print(index)
+    }
+}
+

}

1
2

使用 weak, 此时需要解包

+

closure = { [weak self] index in
// 解包
if let self = self {
print(self)
print(index)
}
}

1
2

强制解包

+

closure = { [weak self] index in
// 强制解包
print(self!)
print(index)
}
`

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/typescripe \345\256\211\350\243\205\345\217\212\350\257\255\346\263\225/index.html" "b/2019/06/18/typescripe \345\256\211\350\243\205\345\217\212\350\257\255\346\263\225/index.html" new file mode 100644 index 0000000..0526485 --- /dev/null +++ "b/2019/06/18/typescripe \345\256\211\350\243\205\345\217\212\350\257\255\346\263\225/index.html" @@ -0,0 +1,290 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

ReactNative 中使用 TypeScript

+

从RN0.57版本开始已经可以直接支持typescript,无需任何配置。在保留根入口文件index.js的前提下,其他文件都可以直接使用.ts或.tsx后缀。但这一由babel进行的转码过程并不进行实际的类型检查,因此仍然需要编辑器和插件来共同进行类型检查

+
+

TypeScript和JavaScript对比

两者深度对比

+

最大的区别:

+
    +
  • TypeScript是强类型语⾔和静态类型语⾔(需要指定类型)
  • +
  • JavaScript是弱类型语⾔和动态类型语⾔(不需要指定类型)
  • +
+

TypeScript

由微软开发的自由和开源的编程语言,是JavaScript的一个严格超集,并添加了可选的静态类型和基于类的面向对象编程

+

TypeScript定义

+

安装及语法

教程

+
安装
1
npm install -g typescript
+
安装 typescript 依赖管理器 typings
1
npm install -g typings
+
安装 typings 依赖
1
2
typings install npm~react --save
typings install dt~react-native --globals --save
+
使用 yarn 安装

如果已经安装了 yarn 安装如下:

+
1
2
yarn add tslib @types/react @types/react-native
yarn add --dev react-native-typescript-transformer typescript
+
创建 tsconfig.json 配置文件
    +
  • cd 到 ReactNative 项目根文件夹下

    +
    1
    tsc --init
    +
  • +
  • 修改 tsconfig.json 文件

    +

    如果一个目录下存在一个tsconfig.json文件,那么它意味着这个目录是TypeScript项目的根目录。 tsconfig.json文件中指定了用来编译这个项目的根文件和编译选项

    +

    tsconfig.json文件配置详解

    +

    TypeScript配置文件tsconfig简析

    +

    务必设置”jsx”:”react”
    注意多余的注释可能会不兼容,需要移除

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    {
    "compilerOptions": {
    "target": "es6",
    "allowJs": true,
    "jsx": "react",
    "outDir": "./dist",
    "sourceMap": true,
    "noImplicitAny": false
    },

    // "files"属性 :指定一个包含相对或绝对文件路径的列表。 "include"和"exclude"属性指定一个文件glob匹配模式列表

    "include": [
    "typings/**/*.d.ts",
    "src/**/*.ts",
    "src/**/*.tsx"
    ],
    "exclude": [
    "node_modules"
    ]
    }
    +
  • +
  • cd 到项目根目录中新建存放 typescripe 源代码的文件夹

    +
  • +
+
建立 rn-cli.config.js 文件,使用支持typescript的transfomer
1
2
3
4
5
6
7
8
module.exports = {
getTransformModulePath() {
return require.resolve('react-native-typescript-transformer');
},
getSourceExts() {
return ['ts', 'tsx'];
}
}
+
修改 .babelrc 文件
1
2
3
4
{
"presets": ["react-native"],
"plugins": ["transform-decorators-legacy"]
}
+
修改项目中文件导入
    +
  • 入口index.jsApp.js的文件名请不要修改,项目根目录下新建src文件夹
  • +
  • src文件夹下新建 .tsx 后缀的文件
  • +
  • .tsx 后缀的文件引用react的写法有所区别

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import * as React from 'react'
    import { Text, View } from 'react-native';
    import { Component } from 'react';

    export default class HelloWorld extends Component{
    render() {
    return (
    <View>
    <Text>Hello!</Text>
    <Text>Typescript</Text>
    </View>
    );
    }
    }
    +
  • +
  • App.js中使用

    +
      +
    • App.js 导入方式修改为如下方式

      +
      1
      2
      3
      4
      	import './src';
      ```

      - 导入 .tsx 类型的组建
      +

      import HelloWorld from “./src/HelloWorld”;

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11

      ### 配置jsconfig

      `jsconfig.json`目录中存在文件表明该目录是JavaScript项目的根目录。该jsconfig.json文件指定根文件和JavaScript语言服务提供的功能选项

      [jsconfig详解](https://code.visualstudio.com/docs/languages/jsconfig)

      [jsconfig.json](https://jeasonstudio.gitbooks.io/vscode-cn-doc/content/md/%E8%AF%AD%E8%A8%80/javascript.html?q=)


      ##### 新建 `jsconfig.json` 文件
      +
    • +
    +
  • +
+

touch jsconfig.json

1
2

##### 编辑 `jsconfig.json` 文件

+

{
“compilerOptions”: {
“allowJs”: true,
“allowSyntheticDefaultImports”: true,
“emitDecoratorMetadata”: true
},
“exclude”: [
“node_modules”,
“rnApp/Tools” // 项目根目录下新建的存放RN中所用的一些工具类
]
}

+
1
2
3


### package.json 文件配置
+

{
“name”: “RNProject”,
“version”: “0.0.1”,
“private”: true,
“scripts”: { // 通过设置这个可以使npm调用一些命令脚本,封装一些功能
“start”: “node node_modules/react-native/local-cli/cli.js start”,
“test”: “jest”
},
“dependencies”: { // 发布后依赖的包
“@types/react”: “^16.4.16”,
“@types/react-native”: “^0.57.4”,
“react”: “16.5.0”,
“react-native”: “0.57.2”,
“react-transform-hmr”: “^1.0.4”,
“tslib”: “^1.9.3”
},

+

“devDependencies”: { // 开发中依赖的其它包
“babel-jest”: “23.6.0”,
“jest”: “23.6.0”,
“metro-react-native-babel-preset”: “0.48.0”,
“react-native-typescript-transformer”: “^1.2.10”,
“react-test-renderer”: “16.5.0”,
“typescript”: “^3.1.2”
},

+

“jest”: { // JavaScript 的单元测试框架
“preset”: “react-native”
}
}

1
2
3
4
5
6
7
8
9

##### `–save` 和 `-–save-dev` 区别

- --save参数表示将该模块写入dependencies属性,
- --save-dev表示将该模块写入devDependencies属性。

##### 添加开发依赖库

- 类型检查

+
npm install @types/jest --save-dev
+npm install @types/react --save-dev
+npm install @types/react-native --save-dev 
+
+
1
- 装饰器转化核心插件
+ +npm install babel-plugin-transform-decorators-legacy --save-dev +
1
2
	
- 测试框架
+ +npm install react-addons-test-utils --save-dev +npm install react-native-mock --save-dev +
1
2

##### 删除依赖库
+

npm uninstall @types/jest –save

1
2


+

npm uninstall @types/jest –save-dev

1
2

##### 彻底删除,-d 表示 devDependencies, -O 表示 optionalDependencies 中

+

npm uninstall -s -D -O @types/jest

+
1
2
3
4
5
6
7
8
9
10

### 报错汇总

##### 错误1 `Cannot find module 'react-transform-hmr/lib/index.js'`

[解决1](https://stackoverflow.com/questions/50142561/cannot-find-module-react-transform-hmr-lib-index-js?noredirect=1&lq=1)

[解决2](https://github.com/facebook/react-native/issues/21530)

- 安装 `react-transform-hmr`
+
npm install react-transform-hmr --save
+
1
2

- 清除缓存重新启动服务
+ +npm start --reset-cache +``` +
+ +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/\343\200\21252\344\270\252\346\226\271\346\263\225\347\254\254\344\270\200\347\253\240\343\200\213/index.html" "b/2019/06/18/\343\200\21252\344\270\252\346\226\271\346\263\225\347\254\254\344\270\200\347\253\240\343\200\213/index.html" new file mode 100644 index 0000000..229d7e6 --- /dev/null +++ "b/2019/06/18/\343\200\21252\344\270\252\346\226\271\346\263\225\347\254\254\344\270\200\347\253\240\343\200\213/index.html" @@ -0,0 +1,402 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

《52个方法-第一章》

+

一、OC起源

    +
  • OC使用的是“消息结构”而不是“函数调用”
  • +
  • 两者区别:
      +
    • 消息结构:在运行时所执行的代码由运行环境来决定,无论是否多态总会在运行时才会去查找所要执行的方法
      1
      2
      NSObject *objc = [NSObject new];
      [objc performWith: parameter1 and: parameter2];
      +
    • +
    +
  • +
+
- 函数调用:由编译器决定,如果函数多态,编译时就要查处到底应该执行那个函数实现
+
+
1
2
Object *objc = new Object;
objc -> perform(parameter1, parameter2);
+
    +
  • 消息结构中编译器不会关心接收消息的对象是何种类型,接收消息的对象问题也要在运行时处理,此过程叫做“动态绑定”
  • +
+

要点:

+
    +
  1. OC语言使用的动态绑定的消息结构,在运行时所执行的代码由运行环境来决定,无论是否多态总会在运行时才会去查找所要执行的方法,
  2. +
  3. OC 中对象总是分配在“堆空间”,而绝不会分配在”栈“上,不带 “*”的变量或者结构体等可能会使用“栈空间”
  4. +
+

二、类头文件尽量少导入其他头文件

要点:

+
    +
  1. 除非必要,否则不要引入头文件,使用向前声明降低类之间的耦合
  2. +
  3. 有时无法使用向前声明,应该把该类遵守协议的声明放到“class-continuation分类”(匿名分类)中。
  4. +
  5. 如果“class-continuation分类”也不行的话,就把协议单独放在一个文件中,再将其引入### 三、多用字面量语法,少用与之等价的方法
  6. +
+

三、多用字面量语法,少用与之等价的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
- (void)tempMethond {
// OC1.0 起,支持简单方式创建基本类型对象

// - 使用字符串字面量:简介易读
NSString *someString = @"Effective Objective-C 2.0";
// 传统方式
NSString *someStrongOld = [NSString stringWithFormat:@"Effective Objective-C 2.0"];


// - 字面数值
NSNumber *someNumber = @1;
NSNumber *floatNumber = @2.5f;
NSNumber *doubleNumber = @3.1415926;
NSNumber *boolNumber = @YES;
NSNumber *charNumber = @'a';
// 字面量语法用于表达式
int x = 5;
float y = 6.23f;
NSNumber *expressionNumber = @(x * y);


// - 字面量数组
NSArray *animals = @[@"cat", @"dog", @"mouse", @"badger"];
NSString *dog = animals[1];

// arrayWithObjects 和 字面量 的区别:
// arrayWithObjects 方法依次处理各个参数,直到发现nil为止
// 字面量数组遇到nil直接抛出异常
NSArray *animalsOld = [NSArray arrayWithObjects:@"cat", @"dog", @"mouse", @"badger", nil];
NSString *dogOld = [animalsOld objectAtIndex:1];


// - 字面量字典
// 如果有nil直接抛出异常
NSDictionary *personData = @{@"firtName": @"Matt",
@"lastName": @"Galloway",
@"age": @28};
NSString *lastName = personData[@"lastName"];

// dictionaryWithObjectsAndKeys 方法依次处理各个参数,直到发现nil为止
NSDictionary *personDataOld = [NSDictionary dictionaryWithObjectsAndKeys:@"Matt",@"firtName",
@"Galloway", @"lastName",
[NSNumber numberWithInteger:28], @"age", nil];

NSString *lastNameOld = [personData objectForKey:@"lastName"];


// - 可变数组和字典
NSMutableArray *mutableArray = animals.mutableCopy;
mutableArray[1] = @"dog";

NSMutableDictionary *mutableDict = [personData mutableCopy];
mutableDict[@"lastName"] = @"Galloway";
}
+

要点:

+
    +
  1. 使用字面量语法创建字符串,数值,数组,字典简明扼要
  2. +
  3. 通过下标操作获取数组,字典对应的元素
  4. +
  5. 使用字面量创建数组,字典时若有nil ,会抛出异常,确保值里不含nil### 五、用枚举表示状态、选项、状态码
  6. +
+

四、多用类型常量,少用#define预处理指令

宏定义
    +
  • 缺点:
      +
    • 可以把所有相同的字符串值都替换为定义的值,
        +
      • 定义出来的常量没有具体的类型信息
      • +
      • 如果有人重新定义了宏定义,不会有警告或者提醒,导致程序中值不一样
      • +
      +
    • +
    +
  • +
+
类型常量
    +
  • 命名规则:

    +
      +
    • 如果只是在实现文件.m 中使用,则在前面加字母k
    • +
    • 如果此常量在类之外也可见,则以类名为前缀,(第19条详细了解命名习惯)
    • +
    +
  • +
  • 声明位置:

    +
      +
    • 总是喜欢在头文件中声明宏定义,可能和常量名冲突,
    • +
    • 所有引入这个头文件的类都会出现宏定义或者常量,相当于声明了一个名叫kAnimationDuration的全局变量
    • +
    • 应该加上前缀,表明其所属的具体类,例如:EOCViewClassAnimationDuration
    • +
    +
  • +
  • 常量从右往左解读依次是: 具体类型,不可修改

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    	// const 修饰的是常量不希望别人修改, NSString * 指向NSString对象
    extern NSString *const EOCStringConstant;


    // 两者写法都可以
    extern const NSTimeInterval EOCAnimatedViewAnimationDuration;
    extern NSTimeInterval const EOCAnimatedViewAnimationDuration1;
    ```

    - 如果不打算公开常量,可以定义在 .m 实现文件里
    +

    static const NSTimeInterval kAnimationDuration = 0.3;

    +
    1
    2
    3

    - static关键字
    - static 修饰意味着该变量仅在定义此变量的编译单元(实现文件.m)中可见,作用域就是当前.m文件
    +
    static const NSTimeInterval kAnimationDuration = 0.3;
    +
    1
    2
    3
    - 如果需要声明一个外部可见的常值变量,需要在头文件中声明并使用 extern 关键词修饰 "extern 

    在 .h 文件中
    + +extern NSString *const EOCStringConstant; + +// 两者写法都可以 +extern const NSTimeInterval EOCAnimatedViewAnimationDuration; +extern NSTimeInterval const EOCAnimatedViewAnimationDuration1; +
    1
    2

    在 .m 文件中
    + +NSString *const EOCStringConstant = @"VALUE"; +const NSTimeInterval EOCAnimatedViewAnimationDuration = 0.3; +NSTimeInterval const EOCAnimatedViewAnimationDuration1 = 0.3; +
    1
    2
    3
    4
    5
    6
    7
    8
    9

    **要点:**

    1. 定义常量时static const定义编译单元(实现文件.m)中可见的常量,无需为其名称加前缀
    2. 在头文件中使用extern 声明全局常量,并在实现文件中定义其值,这种常量要出现在全局符号表中,所以加相关类作为前缀

    ### 五、用枚举表示状态、选项、状态码

    - 普通枚举
    +

    enum EOCConnectionState {

    +
    EOCConnectionStateDisconnected, // 断开连接
    +EOCConnectionStateConnecting,   // 连接中...
    +EOCConnectionStateConnected,    // 已连接
    +

    };

    +
    1
    普通枚举如果要使用如下:
    +

    enum EOCConnectionState status = EOCConnectionStateConnected;

    +
    1
    2

    如果每次都不需要输入enum关键字,只需要typedef关键字重新定义枚举类型
    +

    EOCConnectionState status1 = EOCConnectionStateConnecting;

    +
    1
    2

    - 设置枚举的初始值
    +

    enum EOCConnectionStateConnectionState {

    +
    EOCConnectionStateConnectionStateDisconnected = 1, // 断开连接
    +EOCConnectionStateConnectionStateConnecting,   // 连接中...
    +EOCConnectionStateConnectionStateConnected,    // 已连接
    +

    };

    +
    1
    2
    3
    4
    5

    - 可选枚举

    - << 位操作符(左移), 表示往左移动N位,使用 位运算符 | (或)来组合使用
    - 使用位运算符 &(与) 判断是否开启了某个选项
    +

    enum UIViewAutoresizing {

    +
    UIViewAutoresizingNone                 = 0,
    +UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
    +UIViewAutoresizingFlexibleWidth        = 1 << 1,
    +UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
    +UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
    +UIViewAutoresizingFlexibleHeight       = 1 << 4,
    +UIViewAutoresizingFlexibleBottomMargin = 1 << 5
    +

    }

    +
    1
    2
    3
    4

    - 使用宏定义枚举

    普通枚举类型(老式语法定义枚举)
    +

    typedef enum : NSUInteger {

    +
    EOCConnectionState2DisConnected,
    +EOCConnectionState2DisConnecting,
    +EOCConnectionState2Connected,
    +

    } EOCConnectionState2;

    +
    1
    2

    NS_ENUM 宏定义的枚举展开后:
    +

    typedef enum EOCConnectionState_3 : NSUInteger EOCConnectionState_3;
    enum EOCConnectionState_3 : NSUInteger {

    +
    EOCConnectionState_3DisConnected,
    +EOCConnectionState_3DisConnecting,
    +EOCConnectionState_3Connected,
    +

    };

    +
    1
    2

    - 定义新特性枚举
    +

    typedef NS_ENUM(NSUInteger, EOCConnectionState3) {

    +
    EOCConnectionState3DisConnected,
    +EOCConnectionState3DisConnecting,
    +EOCConnectionState3Connected,
    +

    };

    +
    1
    2
    3
    4

    - 枚举使用

    .h文件
    +

    #import <UIKit/UIKit.h>

    +

    typedef enum EOCConnectionState EOCConnectionState;

    +

    typedef NS_ENUM(NSUInteger, EOCConnectionState3) {

    +
    EOCConnectionState3DisConnected,
    +EOCConnectionState3DisConnecting,
    +EOCConnectionState3Connected,
    +

    };

    +
  • +
+
@interface EOCEnum : NSObject
+@property (nonatomic, assign) enum EOCConnectionState state;
+@property (nonatomic, assign) EOCConnectionState3 state3;
+@end
+
+
1
2

.m 文件中
+ +#import "EOCEnum.h" + +@implementation EOCEnum + +- (void)testEnum { + // 未使用typedef 关键字 + enum EOCConnectionState status = EOCConnectionStateConnected; + NSLog(@"%u", status); + + // 使用typedef 关键字 + EOCConnectionState status1 = EOCConnectionStateConnecting; + NSLog(@"%u", status1); + + UIViewAutoresizing resizing = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + if (resizing & UIViewAutoresizingFlexibleWidth) { + // 设置了 UIViewAutoresizingFlexibleWidth 约束 + } +} + +// UIKit 试图所支持的设置显示方向 +- (UIInterfaceOrientationMask)supportedInterfaceOrientations { + return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft; +} + +// 枚举使用 +- (void)userEnum { + switch (_state3) { + case EOCConnectionState3DisConnected: + // ... + break; + + case EOCConnectionState3DisConnecting: + // ... + break; + + case EOCConnectionState3Connected: + // ... + break; + + default: // 最好不用,防止以后增加一种状态机之后,编译器不发出警告 + break; + } +} + +@end +``` +

要点:

+
    +
  1. 使用枚举表示状态机的状态、传递给方法的选项以及状态码等值,给枚举值起名时要注重易懂
  2. +
  3. 如果一个类型可以使用状态机的状态来表示,并且多个选项可同时使用,那么就定义为可选的枚举类型,枚举各值定义为2的幂,以便通过“按位或”操作组合
  4. +
  5. 使用NS_ENUM 和 NS_OPTIONS 宏来定义枚举,并指明底层的数据类型。这样可以确保枚举是用开发者所选的底层数据类型实现出来的,而不会采用编译器所选的类型
  6. +
  7. switch语句处理枚举类型时不要实现default 分支,如果以后加入枚举的新状态之后,编译器会发出警告
  8. +
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/\344\270\200\350\207\252\345\212\250\345\274\225\347\224\250\350\256\241\346\225\260/index.html" "b/2019/06/18/\344\270\200\350\207\252\345\212\250\345\274\225\347\224\250\350\256\241\346\225\260/index.html" new file mode 100644 index 0000000..b922f6b --- /dev/null +++ "b/2019/06/18/\344\270\200\350\207\252\345\212\250\345\274\225\347\224\250\350\256\241\346\225\260/index.html" @@ -0,0 +1,246 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

自动引用计数

+

什么是自动引用计数

内存管理中对引用采取自动计数的技术

+

内存管理

    +
  • 自己生成的对象,自己所持有
      +
    • alloc
    • +
    • new
    • +
    • copy
    • +
    • mutableCopy
    • +
    +
  • +
  • 非自己生成的对象,自己也能持有
      +
    • retain
    • +
    +
  • +
  • 不再需要自己持有的对象时释放
      +
    • release
    • +
    +
  • +
  • 非自己持有的对象无法释放

    +
  • +
  • 废弃对象

    +
      +
    • dealloc
    • +
    +
  • +
+

__strong

__strong 修饰符表示对对象的“强引用”

+

__unsafe_unretained 修饰符

__unsafe_unretained 是不安全的所有权修饰符, 被它修饰的变量不属于编译器的内存管理对象

+
1
id __unsafe_unretained objc = [[NSObject alloc] init];
+

Xcode 提示:Assigning retained object to unsafe_unretained variable; object will be released after assignment

+

将保留对象赋给unsafe_unretain变量;对象将在赋值后释放

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
id __unsafe_unretained obj1 = nil;

{
id __unsafe_unretained obj = [[NSObject alloc] init];

// 自己生成并持有对象
// 因为obj0 变量是__strong,强引用,所以自己持有对象
id __strong obj0 = [[NSObject alloc] init];

// obj0 变量赋值给你obj1
// ojb1 变量不持有对象的强应用,也不持有弱引用
obj1 = obj0;


// 输出 obj1 变量表示的对象
NSLog(@"A: %@", obj1);
} // obj0变量 超出了其作用域,强引用失效自动释放自己持有的对象,除此之外没有其他持有者,所以废弃该对象




// 输出 obj1 变量表示的对象
// obj1 变量表示的对象因为无持有者已经销毁,坏的内存访问
NSLog(@"B: %@", obj1);
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/\347\254\254\344\270\203\347\253\240Blocks\351\235\242\350\257\225/index.html" "b/2019/06/18/\347\254\254\344\270\203\347\253\240Blocks\351\235\242\350\257\225/index.html" new file mode 100644 index 0000000..5bbe269 --- /dev/null +++ "b/2019/06/18/\347\254\254\344\270\203\347\253\240Blocks\351\235\242\350\257\225/index.html" @@ -0,0 +1,296 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

Blocks

Block 是什么?

Blocks 是 C 语言的扩充功能,将函数及其执行上下文封装起来的对象,带有自动变量(局部变量)的匿名函数

+
    +
  • 因为block 底层C++ 结构体中存在着 isa 指针,所以是一个对象
  • +
  • 因为block 底层C++ 结构体中存在着一个函数指针 FuncPtr,所以是一个函数
  • +
+

什么是Block 调用

Block 的调用就是函数的调用
在终端使用 clang 编译之后查看文件内容可得出以下结论:

+
    +
  • 对其进行一个强制转换,转换之后取出成员变量 FuncPtr
  • +
+

截获变量

以下代码运行结果是 12

+

+
原因:block 对基本数据类型的局部变量会截获其值,在定义 block 时以值的方式传到了block 对应的结构体当中,而调用block 时直接使用的是block对应结构体当中已经传过来的那个值,所以值为12
+
    +
  • 局部变量截获

    +
      +
    • 基本数据类型的局部变量截获其值
    • +
    • 对象类型的局部变量连同所有权修饰符一起截获
    • +
    +
  • +
  • 局部静态变量: 已指针形式截获

    +
  • +
  • 全局变量:不截获

    +
  • +
  • 静态全局变量: 不截获

    +
  • +
+

以下代码运行结果是 8

+

+
原因:block 对局部静态变量会以指针形式截获,所以值为8
+

__block 修饰符

    +
  • __block 修饰后的变量最后变成了对象,底层c++ 结构体中存在isa指针
  • +
  • 一般情况下,对被截获变量进行赋值操作需要添加block 修饰符,而操作是不需要添加block 修饰符的
  • +
  • 不需要__block 修饰符,全局变量和静态全局变量 block 是不截获的,所以不需要修饰,静态局部变量是通过指针截获的,修改外部的变量,也不需要添加
      +
    • 静态局部变量
    • +
    • 全局变量
    • +
    • 静态全局变量
    • +
    +
  • +
+

不需要添加__Block 修饰符

+
1
2
3
4
5
6
7
8
- (void)test {
NSMutableArray *array = [NSMutableArray array];
void(^block)(void) = ^{
[array addObject:@1234];
};

block();
}
+

需要添加__Block 修饰符

+
1
2
3
4
5
6
7
8
- (void)test {
__block NSMutableArray *tempArray = nil;
void(^block)(void) = ^{
tempArray = [NSMutableArray array];
};

block();
}
+

以下代码运行结果是 8

+
1
2
3
4
5
6
7
8
- (void)test__Block3 {
__block int multiplier = 6;
int(^Block)(int) = ^int(int num){
return multiplier * num;
};
multiplier = 4;
NSLog(@"__block--test__Block3---result is %d", Block(2));
}
+

原因

+

+

+

栈上Block 的block 变量中的 forwarding 指针指向的是自身

+

Block 的内存管理

Block 的 内存分布

+

+

Block 的 Copy 操作

+

+
    +
  • 全局block
  • +
  • 堆block
  • +
  • 栈block
  • +
+

Block 循环引用

    +
  • 如果当前block对当前对象的某一变量进行截获的话,block 会对对应变量有一个强引用,当前block因为当前的对象对其有个强引用,产生了自循环引用,通过声明为__weak变量来解决
  • +
  • 如果定义了__block 修饰符的话也会产生循环引用,在ARC 下会产生循环引用,而在MRC 下不会,在ARC 下通过打破环来消除,但是存在弊端时当block 没有机会执行时,
  • +
+

循环引用1

+

解决方法:

+

+

大环循环引用2

+

+

以上代码,在MRC 下,不会产生循环引用,在ARC 下,会产生循环应用,引起内存泄露

+

解决方法

+

如果block 没有机会执行,那么循环引用将不会被打破

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/\347\254\254\344\270\211\347\253\240UI\350\247\206\345\233\276\345\223\215\345\272\224\351\223\276\347\246\273\345\261\217\346\270\262\346\237\223\351\235\242\350\257\225/index.html" "b/2019/06/18/\347\254\254\344\270\211\347\253\240UI\350\247\206\345\233\276\345\223\215\345\272\224\351\223\276\347\246\273\345\261\217\346\270\262\346\237\223\351\235\242\350\257\225/index.html" new file mode 100644 index 0000000..7cb4bbb --- /dev/null +++ "b/2019/06/18/\347\254\254\344\270\211\347\253\240UI\350\247\206\345\233\276\345\223\215\345\272\224\351\223\276\347\246\273\345\261\217\346\270\262\346\237\223\351\235\242\350\257\225/index.html" @@ -0,0 +1,289 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

UITableView 相关

    +
  • 重用机制
  • +
  • 并发访问,数据拷贝问题:主线程删除了一条数据,子线程数据由删除之前的数据源copy而来,子线程网络请求,数据解析,预排版等操作后,回到主线程刷新时已经删除的数据又会重现

    +
      +
    • 主线程数据源删除时对数据进行记录,子线程的数据回来后再进行一次同步删除操作,然后再刷新数据

      +
    • +
    • 使用GCD中的串行队列,将数据删除和数据解析在此队列中进行

      +
    • +
    +
  • +
+

UIView 和CALayer 的关系

    +
  • UIView 为 CALayer 提供内容,负责处理触摸事件,参与响应链
  • +
  • CALayer 负责显示内容,分工明确,UIView 的Layer 实际上就是CALayer, backgroundColor 等属性本质是对CALayer 做了一层包装
  • +
+

事件传递

+
    +
  • hitTest 方法是返回响应时间的试图
  • +
  • pointInside withEvent 方法判断点击位置是否在当前视图范围内
  • +
  • 倒序遍历最终响应事件的试图,最后添加的试图会最优先被遍历到
  • +
+

需求案例

+

只让正方形的view中圆形区域内可以响应事件,四个角的位置不响应事件

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class CircularAreaResponseButton: UIButton {

override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if self.isHidden || !self.isUserInteractionEnabled || self.alpha <= 0.01 {
return nil
}

var hitView: UIView?
if self.point(inside: point, with: event) {
// 遍历当前视图的子试图
for subView in subviews {
// 坐标转换
let convertPoint = self.convert(point, to: subView)
if let hitTestView = subView.hitTest(convertPoint, with: event) {
// 找到了最终响应事件的试图
hitView = hitTestView
break;
}
}

if hitView != nil {
return hitView
}
return self
}
return nil
}


override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
let x1 = point.x
let y1 = point.y

let x2 = self.frame.width / 2.0
let y2 = self.frame.height / 2.0
// 使用平面内两点见距离公式计算距离, 并且判断是否 <= 圆的半径
return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)) <= x2
}
}
+

视图响应

UIView 都继承于 UIResponder,都有以下方法

+
1
2
3
4
func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?)
func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?)
func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?)
+

如果事件沿着视图响应链一直传递到 UIApplicationDelegate 仍然没有任何一个视图处理事件,程序将会发生什么?

+
    +
  • 忽略掉了这个事件当作什么都没有发生
  • +
+

图像显示原理

CALayer 的 contents 是位图

+

+
    +
  • CPU的工作

    +

    Layout | Display | Prepare | Commite |
    ———|———–|————|———|
    UI布局 | 绘制 | 图片编解码 | 提交位图 |
    文本计算 |

    +
  • +
  • GPU渲染管线

    +

    定点着色 | 图元转配 | 光栅化 | 片段着色 | 片段处理 |
    ——–|——–|——-|———|———|

    +
  • +
+
提交到帧缓冲区(frameBuffer)
+

UI卡顿掉帧的原因

1/60 ms 时间内, 由cpu 和GPU 协同产生最终的数据,当 CPU UI布局,文本计算所用的时间较长时,留给GPU渲染,提交到帧缓冲区的时间就会变短,这样以来就会发生掉帧情况

+

UIView 的绘制原理

CPU优化方案

+
    +
  • 对象的创建、调整、销毁放在子线程,节省cpu的一部分时间
  • +
  • 预排版(布局计算,文本计算)放在子线程中
  • +
  • 预渲染(文本等异步绘制,图片编解码)等
  • +
+

GPU优化方案

+

在屏渲染

指GPU的渲染操作是在当前用于显示的屏幕缓冲区域中进行

+

离屏渲染

指GPU在当前屏幕缓冲区域外新开劈了一个缓冲区域进行渲染操作
指定了UI视图图层的某些属性,标记为视图在未预合成之前不能用于在当前屏幕上直接显示的时候就会发生离屏渲染,例如图层的圆角设置和蒙层设置

+
    +
  • 离屏渲染何时触发

    +
      +
    • 设置layer 的圆角并且和 maskToBounds 一起使用时
    • +
    • 图层蒙版
    • +
    • 阴影
    • +
    • 光栅化
    • +
    +
  • +
  • 为何要避免离屏渲染

    +
      +
    • 离屏渲染,会增加 GPU 的工作量,GPU工作量的增加就很有可能导致CPU和GPU的总耗时超过1/60s,导致UI卡顿和掉帧
    • +
    +
  • +
  • 避免离屏渲染

    +
  • +
+

###异步绘制

+

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/\347\254\254\344\271\235\347\253\240Runloop\351\235\242\350\257\225/index.html" "b/2019/06/18/\347\254\254\344\271\235\347\253\240Runloop\351\235\242\350\257\225/index.html" new file mode 100644 index 0000000..fbaa32c --- /dev/null +++ "b/2019/06/18/\347\254\254\344\271\235\347\253\240Runloop\351\235\242\350\257\225/index.html" @@ -0,0 +1,218 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/\347\254\254\344\272\224\347\253\240Runtime\347\233\270\345\205\263\351\235\242\350\257\225/index.html" "b/2019/06/18/\347\254\254\344\272\224\347\253\240Runtime\347\233\270\345\205\263\351\235\242\350\257\225/index.html" new file mode 100644 index 0000000..360c72a --- /dev/null +++ "b/2019/06/18/\347\254\254\344\272\224\347\253\240Runtime\347\233\270\345\205\263\351\235\242\350\257\225/index.html" @@ -0,0 +1,303 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

Runtime 相关问题

数据结构

objc_object 结构体

objc_class

对象、类对象、元类对象的区别

    +
  • 类对象存储实例方法列表等信息
  • +
  • 元类对象存锤类方法列表等信息
  • +
+

消息传递

void objc_msgSend(void /* id self, SEL op, ... */)

+

经过编译器转变后
[self class] <==> objc_msgSend(self, @selector(class))
第一个参数是消息传递的接收者self, 第二个参数是传递的消息名称(选择器)

+

void objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */)
第一个参数是一个结构体指针

+

//
struuch objc_super {
__unsafe_unretained id receiver; // 就是当前的对象
}

+

[superclass] <==> objc_msgSendSuper(super, @selector(class))

+

无论调用[super class] 还是 [self class] 最终这条消息的接收者都是当前对象

+

图1

+

+

消息传递过程

图2

+

+

问题1

+

+

打印结果都是 Phone

+

原因:

+
    +
  • 无论调用[super class] 还是 [self class] 最终这条消息的接收者都是当前对象
  • +
  • [self class] 在Phone的类对象中查找class 方法,没有去父类查找,父类也没有,如图1顺次往上查找到根类,调用根类中class 的具体实现
  • +
  • [super class]只是跨越了当前类,直接从当前类的父类中查找 class 方法,父类中没有class 方法,如图1所示只能继续往上查找到根类对象,在NSObject 中有class 方法实现,所以接受者任然是当前的对象
  • +
+

缓存查找

根据给定的SEL(方法选择器)通过一个函数来映射出bucket_t 在数组当中的位置,本质上是哈希查找

+

+

当前类中查找

当前类中存在着对应的方法列表

+
    +
  • 对于已经排序好的列表,采用二分查找算法查找对应的执行函数
  • +
  • 对于没有排序的列表,采用一般遍历查找方法查找对应的执行函数
  • +
+

父类逐级查找

图3

+
    +
  • 最关键是通过当前类的 superclass 成员变量去查找父类,或者访问父类
  • +
  • curClass 的父类是否为 nil?如果是NSObject 类,他的父类为空,所以会结束查找
  • +
  • 如果当前类有父类就要去该父类的缓存中查找,如果在缓存中命中,那么就结束查找,如果缓存中没有,就需要遍历当前类的父类的方法列表,如果没有就遍历父类的父类查找,沿着superclass指针逐级向上查找,直到查找到NSObject,再去Superclass。如果还是没有为nil时就结束父类逐级查找
  • +
+

消息转发

resolveInstanceMethod:

    +
  • 该方法是类方法不是实例方法,参数是SEL 方法选择器类型
  • +
  • 返回值是一个BOOL值
  • +
  • 告诉系统是否要解决当前实例方法的实现
  • +
  • 如果返回YES,结束消息转发
  • +
  • 如果返回NO,系统会给第二次机会处理这个消息,会回掉forwardingTargetForSelector:
  • +
+

forwardingTargetForSelector:

    +
  • 参数是 SEL 方法选择器
  • +
  • 返回值是 id 类型,说明有哪个对象来处理,转发的对象是谁,如果返回了转发目标就会结束当前调用
  • +
  • 如果返回为nil,系统会调用 methodSignatureForSelector:方法,最后一次机会处理这个消息
  • +
+

methodSignatureForSelector:

    +
  • 参数是 SEL 方法选择器
  • +
  • 返回值是一个对象,实际上这个对象是对methodSignatureForSelector 方法的参数,参数个数,参数类型和返回值的包装
  • +
  • 如果返回一个方法签名的话,就会调用 forwardInvocation: 方法
  • +
  • 如果返回为nil,标记为消息无法处理,程序crash
  • +
+

forwardInvocation:

    +
  • 如果此方法不能处理消息,程序crash
  • +
+

代码示例

+

.h 文件

+
1
2
3
4
@interface RunTimeObject : NSObject
// 只声明,不实现,验证消息转发机制
- (void)test;
@end
+

.m 文件

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
@implementation RunTimeObject

// 实现消息转发流程 中的方法
/**
是否要解决当前实例方法的实现
*/
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(test)) {
NSLog(@"resolveInstanceMethod:");
// 如果返回YES,消息转发就会结束
// return YES;

return NO;
} else {

NSLog(@"super resolveInstanceMethod:");
// 返回父类的默认调用
return [super resolveInstanceMethod:sel];
}
}


/**
- 参数是 SEL 方法选择器
- 返回值是 id 类型,说明有哪个对象来处理,转发的对象是谁,如果返回了转发目标就会结束当前调用
- 如果返回为nil,系统会调用 methodSignatureForSelector:方法,最后一次机会处理这个消息
*/
- (id)forwardingTargetForSelector:(SEL)aSelector {
NSLog(@" forwardingTargetForSelector: ");
return nil;
}

/**
- 参数是 SEL 方法选择器
- 返回值是一个对象,实际上这个对象是对`methodSignatureForSelector` 方法的参数,参数个数,参数类型和返回值的包装
- 如果返回一个方法签名的话,就会调用 forwardInvocation: 方法
- 如果返回为nil,标记为消息无法处理,程序crash

*/
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if (aSelector == @selector(test)) {
NSLog(@"methodSignatureForSelector:");
/**
v: 表示这个方法返回值是 void

固定参数@: 表示参数类型是 id, 即self
固定参数`:` : 表示参数类型是选择器类型,即 @selector(test)
*/
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
} else {

NSLog(@"super methodSignatureForSelector:");
// 返回父类的默认调用
return [super methodSignatureForSelector:aSelector];
}
}


- (void)forwardInvocation:(NSInvocation *)anInvocation {
NSLog(@"forwardInvocation:");
}

@end
+

调用

+
1
2
3
4
- (void)runTiemTest {
RunTimeObject *obj = [[RunTimeObject alloc] init];
[obj test];
}
+

结果:

+
1
2
3
4
5
resolveInstanceMethod:
forwardingTargetForSelector:
methodSignatureForSelector:
super resolveInstanceMethod:
forwardInvocation:
+

为类动态添加方法

methond Swizzling

+

使用场景

    +
  • 页面中的进出添加统计信息,使用 methond Swizzling 替换viewillAppear 方法
  • +
+

动态添加方法

    +
  • perforSelector: 实际上是考察class_addMethod 的方法使用
  • +
+

动态方法解析

    +
  • @dynamic关键字
  • +
  • 动态运行时语言将函数决议推迟到运行时
  • +
  • 编译时不生成setter/getter 方法,而是在运行时才添加具体的执行函数
  • +
+

问题

[obj foo] 和 obj_msgSend()函数之间有什么关系?

    +
  • [obj foo]经过编译器处理过后会变成 obj_msgSend()有两个参数,一个是obj,第二个参数是foo 选择器
  • +
+

runtime 如何通过Selector找到对应的IMP地址的?

消息传递机制

+

能否向编译后的类增加实例变量

编译之前就完成了实例变量的布局,
runtime_r_t 表示是readonly 的,编译后的类是不能添加实例变量的,可以向动态添加的类中添加实例变量

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/\347\254\254\345\205\253\347\253\240\345\244\232\347\272\277\347\250\213\351\235\242\350\257\225/index.html" "b/2019/06/18/\347\254\254\345\205\253\347\253\240\345\244\232\347\272\277\347\250\213\351\235\242\350\257\225/index.html" new file mode 100644 index 0000000..a9e9170 --- /dev/null +++ "b/2019/06/18/\347\254\254\345\205\253\347\253\240\345\244\232\347\272\277\347\250\213\351\235\242\350\257\225/index.html" @@ -0,0 +1,215 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/\347\254\254\345\205\255\347\253\240\345\206\205\345\255\230\347\256\241\347\220\206\351\235\242\350\257\225/index.html" "b/2019/06/18/\347\254\254\345\205\255\347\253\240\345\206\205\345\255\230\347\256\241\347\220\206\351\235\242\350\257\225/index.html" new file mode 100644 index 0000000..5d19701 --- /dev/null +++ "b/2019/06/18/\347\254\254\345\205\255\347\253\240\345\206\205\345\255\230\347\256\241\347\220\206\351\235\242\350\257\225/index.html" @@ -0,0 +1,298 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

内存管理

内存布局

+
    +
  • stack 栈是从高地址向低地址扩展,所以栈是向下增长的,对象和block copy 后都会放在堆上
  • +
  • heap 堆区向上增长
  • +
+
    +
  • 散列表方式

    +
      +
    • 自旋锁
        +
      • 自旋锁是“忙等” 的锁
      • +
      +
    • +
    • 引用计数表
    • +
    • 弱应用表
    • +
    +
  • +
+

内存管理方案

    +
  • 小对象使用 TaggedPointer
  • +
  • 64 位架构下面使用的是 NONPONINT_ISA 内存管理方案,非指针型的 isa,内部存储了一些
  • +
  • 散列表结构 (Side Tables() 结构)
      +
    • 自旋锁
    • +
    • 引用计数表
    • +
    • 弱引用表
    • +
    • Side Tables() 结构
    • +
    +
  • +
+

为什么不是一个SideTable,而是多个组成了Side Tables

如果所有对象的存储都放在一张大表当中,因为每个对象是在不同的线程中创建的,要操作其中一个对象时,需要将表加锁处理保证数据安全,要等锁释放之后才能操作下一个对象,此时存在效率问题,引入分离锁技术方案

+
    +
  • 分离锁:把引用计数表分成多张表进行,
  • +
  • Side Tables 本质是一张Hash 表
  • +
+

+

自旋锁

是“忙等”的锁,如果当前锁已被其他线程获取,当前线程会不断探测是否被释放,会第一时间获取,而其他锁比如信号量当它获取不到锁时,会把自己的线程阻塞休眠,等到其他线程释放这个锁时再唤醒这个锁

+
    +
  • 适用于轻量访问
  • +
+

引用计数表

使用过Hash表来实现,hash查找提高了查找高效率,插入和获取是通过Hash 算法来是实现的,避免了for循环

+

alloc 实现

经过一系列调用,最终调用了C函数 calloc, 并没有引用计数+1

+

retain 实现

底层经过了2次Hash表查找

+

+

release 实现

和 retain 实现相反

+

dealloc 实现原理

+

+

objc_destructInstance() 实现

+

clearDeallocating() 实现

+

MRC

手动引用计数

+

ARC

    +
  • 编译器在对应位置自动插入retain 和 release 操作,并和runtime协作达到自动引用计数管理内存的效果

    +
  • +
  • ARC 中禁止手动调用 retain/release

    +
  • +
  • ARC 中新增 weeak、strong 等关键字
  • +
+

弱引用管理

被声明为__weak 的对象指针经过编译后会进过调用以下方法,在最终的weak_register_no_lock() 方法中进行弱引用变量的添加。添加的位置是通过Hash算法查找的

+

+

weak 修饰的对象,释放时置为nil 如何实现的

当一个对象被 dealloc 之后,在dealloc内部实现当中会调用弱引用清除的相关函数,在相关函数当中会根据当前对象指针查找弱引用表,把当前对象下对应的弱应用都取出,遍历弱应用指针并置为nil。

+

自动释放池

是以栈为结点通过双向链表的形式组合而成,和线程一一对应

+

Autoreleasepool

+

循环引用

自循环引用

一个对象中有拥有一个 强持有他的 obj,如果给 obj 赋值为原对象就会造成自循环引用

+

相互循环引用

    +
  • 代理
  • +
  • Block
  • +
  • NSTimer
  • +
  • 大环多循环
  • +
+

多循环引用

+

解决循环引用的方案

__weak

解决相互循环引用

+

__block

+

__unsafe_unretained

一般不建议使用

+

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/\347\254\254\345\215\201\344\270\200\347\253\240\350\256\276\350\256\241\346\250\241\345\274\217\351\235\242\350\257\225/index.html" "b/2019/06/18/\347\254\254\345\215\201\344\270\200\347\253\240\350\256\276\350\256\241\346\250\241\345\274\217\351\235\242\350\257\225/index.html" new file mode 100644 index 0000000..031af67 --- /dev/null +++ "b/2019/06/18/\347\254\254\345\215\201\344\270\200\347\253\240\350\256\276\350\256\241\346\250\241\345\274\217\351\235\242\350\257\225/index.html" @@ -0,0 +1,242 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

第十一章 设计模式

+

六大设计原则

单一职责原则:一个类只负责一件事

+

开闭原则: 对修改关闭,对扩展开放

+

接口隔离原则:使用多个专门的协议,而不是一个庞大臃肿的协议,iOS系统的UITableView的数据和代理协议分开

+

依赖倒置原则:抽象不应该依赖于具体实现,具体实现可以依赖于抽象

+

里氏替换原则:父类可以被子类无缝替换,且原有功能不受任何影响,iOS系统 的 KVO 机制遵守了这一原则,当调用 addObserver 方法时,因为系统在动态运行时为我们创建了一个子类,直观感受是仍然使用的是父类,实际上系统已经替换为创建的子类,所以KVO 机制 不但使用了管擦这模式,并且遵从了 里氏替换原则

+

迪米特法则: 一个对象应对其他对象有尽可能少的了解,平时所说的高内聚,低耦合

+

常用设计模式

责任链

有三个业务分别为A/B/C,以前的调用顺序为 A -> B -> C ,现在顺序有调整,

+

定义某一个类,有一个成员变量他的类型跟原有类类型一致,组成一个责任链,让处理具体业务的责任者去处理

+

+

桥接模式

业务解耦的问题:同一个列表和多套数据显示,多套数据通过后端来控制是共存,

+

+

类构成

+

抽象类A ——> class B

+

+

适配器模式

一个现有类需要适应变化的问题, 年代比较久远的类或者对象

+

类构成

+
    +
  • 对象适配
  • +
  • 类适配
  • +
+

单例模式

命令模式

行为参数化

+

降低代码重合度

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/\347\254\254\345\215\201\344\270\211\347\253\240\347\256\227\346\263\225\351\235\242\350\257\225/index.html" "b/2019/06/18/\347\254\254\345\215\201\344\270\211\347\253\240\347\256\227\346\263\225\351\235\242\350\257\225/index.html" new file mode 100644 index 0000000..55ebbb8 --- /dev/null +++ "b/2019/06/18/\347\254\254\345\215\201\344\270\211\347\253\240\347\256\227\346\263\225\351\235\242\350\257\225/index.html" @@ -0,0 +1,215 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/\347\254\254\345\215\201\344\272\214\347\253\240\346\236\266\346\236\204\345\222\214\346\241\206\346\236\266\351\235\242\350\257\225/index.html" "b/2019/06/18/\347\254\254\345\215\201\344\272\214\347\253\240\346\236\266\346\236\204\345\222\214\346\241\206\346\236\266\351\235\242\350\257\225/index.html" new file mode 100644 index 0000000..dc77d9f --- /dev/null +++ "b/2019/06/18/\347\254\254\345\215\201\344\272\214\347\253\240\346\236\266\346\236\204\345\222\214\346\241\206\346\236\266\351\235\242\350\257\225/index.html" @@ -0,0 +1,302 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

第十二章 架构和框架

+

目的

+
    +
  • 实现模块化
  • +
  • 分层
  • +
  • 解耦
  • +
+

图片缓存

图片缓存框架设计方案?

+

图片缓存时内存设计上需要考虑的问题

    +
  • 内存存储的size

    +
      +
    • 10kb的图片开辟的内存空间 50 张
    • +
    • 100kb图片的 20 张
    • +
    • 100kb 以上的 10 张
      通过队列的方式存储,先进先出的方式淘汰
    • +
    +
  • +
  • 淘汰策略

    +
      +
    • 以队列先进先出的方式淘汰
    • +
    • 已LRU 算法 (如 30 min 之内是否使用过)
    • +
    +
  • +
+

磁盘设计考虑的问题

    +
  • 存储方式
  • +
  • 大小限制
  • +
  • 淘汰策略(超过 7 天)
  • +
+

网络部分的设计需要考虑的问题

    +
  • 图片请求最大并发量
  • +
  • 请求超时策略,一旦超时了,可以重试,如果重试2次失败,是否还下载
  • +
  • 请求优先级,下载或者缓存的图片是否紧急需要
  • +
+

图片通过什么方式进行读写,过程是怎样的?

    +
  • 以图片的url 的单向哈希值作为key 存储
  • +
  • 读取过程
  • +
+

+

图片解码

    +
  • 对于不同格式的图片,解码采用什么方式来做?

    +
      +
    • 应用策略模式对于不同图片格式进行解码
    • +
    +
  • +
  • 在那个阶段做图片解码处理?

    +
      +
    • 再磁盘读取后未解码或者网络请求返回后,解码后再放到内存中,可以减轻主线程的压力,解码在主线程中
    • +
    +
  • +
+

线程处理

+

阅读时长统计

怎样设计一个时长统计框架?

+

为何要有不同类型的记录器,处于什么考虑?

基于不同分类场景提供的关于记录的封装、适配

+

记录的数据由于程序杀死或者断电,关机等丢失,你是怎样处理的?

    +
  • 定时写磁盘
  • +
  • 限定内存缓存条数,超过该条数就写如磁盘
  • +
+

记录器上传 中延时上传的具体场景有哪些?上传时机如何把握?

上传时机

+
    +
  • 立刻上传
  • +
  • 延时上传
  • +
  • 定时上传
  • +
+

延时上传场景

+

统计结果立即上传会重复调用接口导致资源浪费

+
    +
  • 前后台切换
  • +
  • 从无网到有网的变化时
  • +
  • 通过其他的接口捎带着上传
  • +
+

复杂页面的架构

MVVM 框架思想

+

RN 的数据流思想

+

任何一个子节点是没有权力做自己的变化更新的,它必须把这个消息传递给根结点,根结点通过自顶向下的遍历查找需要更新的节点

+

系统UIView 更新机制的思想

AsyncDisplayKit 关于预排版的设计思想

客户端整体架构

需要独立于APP 的通用层
通用的业务层
中间层
业务层

+

+

业务之间的解耦通信方式

+
    +
  • OpenURL
  • +
  • 依赖注入方式, 在中间层通过代理 的方式实现业务A 和业务B之间的通信
  • +
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/\347\254\254\345\215\201\345\233\233\347\253\240\347\254\254\344\270\211\346\226\271\345\272\223\351\235\242\350\257\225/index.html" "b/2019/06/18/\347\254\254\345\215\201\345\233\233\347\253\240\347\254\254\344\270\211\346\226\271\345\272\223\351\235\242\350\257\225/index.html" new file mode 100644 index 0000000..a75766b --- /dev/null +++ "b/2019/06/18/\347\254\254\345\215\201\345\233\233\347\253\240\347\254\254\344\270\211\346\226\271\345\272\223\351\235\242\350\257\225/index.html" @@ -0,0 +1,242 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

第十四章 第三方库

+

AFNetworking

框架图

会话NSURSession

+

主要类关系图

+

AFURLSessionManager

    +
  • 创建和管理 NSURLSession, NSURLSessionTask
  • +
  • 实现NSURLSessionDelegate 等协议的代理方法
  • +
  • 引入AFSecurityPolicy 保证请求安全,https 请求时 证书检验,公钥验证
  • +
  • 引入AFNetworkReachabilityManager 监控网络状态
  • +
+

SDWebImage

架构图

+

+

加载图片流程

+

Reactive Cocoa

+

RACStream

+

+

AsyncDisplayKit

主要解决的问题

+

+

基本原理

+

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/\347\254\254\345\215\201\347\253\240\347\275\221\347\273\234\347\233\270\345\205\263/index.html" "b/2019/06/18/\347\254\254\345\215\201\347\253\240\347\275\221\347\273\234\347\233\270\345\205\263/index.html" new file mode 100644 index 0000000..9b4d44b --- /dev/null +++ "b/2019/06/18/\347\254\254\345\215\201\347\253\240\347\275\221\347\273\234\347\233\270\345\205\263/index.html" @@ -0,0 +1,360 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

第十章 网络请求相关

+

HTTP协议

超文本传输协议

+

请求/响应报文

请求报文:

+
    +
  • 请求行
    方法 URL 协议版本(1.1版本) CRLF(回车)

    +
  • +
  • 首部字段区域
    首部字段名(key) :值(value )CRLF
    首部字段名(key) :值(value )CRLF

    +
  • +
  • 实体主体
    get 请求没有
    post 有实体主体

    +
  • +
+

响应报文:

+
    +
  • 响应行
    版本 状态码 状态码描述(短语) CRLF

    +
  • +
  • 首部字段区

    +
  • +
  • +
  • 响应实体主体
  • +
+

连接建立流程

三次握手和四次挥手

+

三次握手

+
    +
  • 客户端 发送 syn 同步报文到Server,
  • +
  • server 端收到syn报文后会,链接建立,server 端会返回一个 syn,ack 报文给客户端,
  • +
  • 客户端收到 server 端的ask 报文后,会再次回应一个确认ack 的报文
  • +
+

数据请求

+
    +
  • 接下来在已经建立的tcp 通道上进行http 的请求报文
  • +
  • server 端回复给客户端一个http响应报文
  • +
+

四次挥手

+
    +
  • 客户端发送 fin 终止报文到server 端
  • +
  • server 端收到报文后会回回复一个ack 报文到客户端
  • +
  • 客户端到server 端的tcp的链接已经断开,但是 server 端到客户端的链接还会传输数据
  • +
  • server 端向客户端发送一个fin ack 报文 ,然后客户端回应给server 一个ack 报文,释放链接
  • +
+

HTTP 的特点

    +
  • 无连接
    非持久链接 需要多次断开/建立链接,就会经历 三次握手,四次挥手

    +

    HTTP 的持久链接: 多个http请求在同一个tcp 通道上, 一定时间内不会断开tcp通道,提高了网络请求的效率

    +

    头部字段:

    +
    coonection : keep-alive 客户端是否采用持久链接
    +time :20 多长时间有效
    +max:10 最多发生多少个http请求和响应对
    +
  • +
  • 无状态
    同一个用户多次发送http请求,server 端是不知道是同一个用户的,server 端通过 Cookie/Session 规避这个问题

    +
      +
    • cookie
      cookie 主要是用来记录用户状态,区分用户,cookie由server端返回给客户端,cookie 状态保存在客户端

      +

      保证cookie 的安全

      +
        +
      • cookie 加密处理
      • +
      • 只在https 上携带Cookie
      • +
      • 设置Cookie为 httpOnly,防止跨站脚本攻击
      • +
      +
    • +
    • session 用来记录用户的状态,区分用户,状态存放在服务端

      +
    • +
    +
  • +
+

HTTPS 与网络安全

HTTPS 都使用了那些加密手段,为什么?

+
    +
  • 建立链接过程使用非对称加密,因为耗时
  • +
  • 后续通信数据传输过程中使用对称性加密
  • +
+

对称性加密

+

TCP: 传输控制协议

    +
  • 面向链接
    TCP 是全双工的所以数据传输之前需要三次握手,传输完毕需要四次挥手,
  • +
  • 可靠传输

    +
      +
    • 无差错
    • +
    • 按序到达
    • +
    • +
    +
  • +
  • 面向字节流

    +

    发送方发送数据时不管一次性提交给TCP的缓冲是多大的数据,TCP本身会根据实际情况来划分数据包再发送给接受数据方

    +
  • +
  • 流量控制

    +

    滑动窗口协议

    +
  • +
+

UDP : 用户数据协议

    +
  • 无连接
    发送数据时不需要建立链接,传输完毕也不需要释放链接

    +
  • +
  • 尽最大努力交付
    是不保证可靠传输的,

    +
  • +
  • 面向报文
    既不合并,也不拆分

    +
  • +
+

问题

GET 和post 请求的区别

    +
  • GET 获取资源的

    +
      +
    • 安全的: 不因该引起Server 端的任何状态变化的
    • +
    • 幂等的:同一个请求方法执行多次和执行一次的效果完全相同
    • +
    • 可缓存的:请求是可以缓存的,代理服务器可以缓存
    • +
    • 请求参数以? 分割拼接到URL 后面
    • +
    +
  • +
  • post 请求处理资源

    +
      +
    • 不安全的:
    • +
    • 非幂等的
    • +
    • 不可缓存的
    • +
    +
  • +
+

GET 请求参数有长度限制,是2048 个字符,post 一般没有限制

+

HTTPS 链接建立流程

客户端会发送服务端一个支持的加密算法列表
包括 TLS 的版本号 + 随机数C,
服务端再回给客户端一个证书
包括 商定的加密算法 后续首先通过非对称加密进行对称性加密的密钥传输,http的网络就通过被非对称密钥保护的对称密钥访问和传输数据

+

TCP 和 UDP的区别

tcp是面向链接的,并且支持可靠的传输,支持面向字节流, tcp 提供了流浪上的控制和拥塞的控制
UDP 提供了简单的复用/分用及差错检测的传输层的功能,

+

状态吗有哪些

    +
  • 1XX
  • +
  • 2XX:成功
  • +
  • 3XX:重定向
  • +
  • 4XX:客户端请求错误
  • +
  • 5XX: 服务器端请求错误
  • +
+

为什么需要三次握手,两次行不行?

解决同步请求链接建立时超时的情况,如果只有两次握手,

+

持久链接怎么判断一个请求是否结束

    +
  • 请求/响应报文的头部字段
  • +
+

响应报文 content-length: 1024 头部字段是由server 端返回数据大小,客户端根据所接收的数据的字节数是否达到了头部字段 cotent-length 的大小判断

+
    +
  • 通过post请求时server 端返回数据有可能需要多次响应才能返回,此时需要根据chunked 字段名 是否为空来判断
  • +
+

Charels 抓包原理是什么?

中间人攻击

+

正常步骤是 客户端 —- server

+

抓包:发送请求和响应请求都可以通过中间人修改再返回给某一端

+

客户端 —中间人—- server

+

DNS 解析

域名到IP地址的映射,DNS解析请求采用UDP数据报,且明文

+

NDS 解析存在的常见问题

    +
  • DNS 劫持问题
    钓鱼网站
  • +
  • DNS 解析转发问题
  • +
+

DNS 劫持和HTTP 的关系时怎么样的

没有关系,

+
    +
  • DNS 解析发在HTTP 建立连接之前
  • +
  • DNS 解析请求使用UDP数据报, 端口是53,跟HTTP没有任何关系
  • +
+

解决DNS 劫持问题

+
    +
  • httpDNS
  • +
  • 长连接
  • +
+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/18/\347\254\254\345\233\233\347\253\240OC\350\257\255\350\250\200\347\211\271\346\200\247\351\235\242\350\257\225/index.html" "b/2019/06/18/\347\254\254\345\233\233\347\253\240OC\350\257\255\350\250\200\347\211\271\346\200\247\351\235\242\350\257\225/index.html" new file mode 100644 index 0000000..7496d76 --- /dev/null +++ "b/2019/06/18/\347\254\254\345\233\233\347\253\240OC\350\257\255\350\250\200\347\211\271\346\200\247\351\235\242\350\257\225/index.html" @@ -0,0 +1,330 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

OC 语言特性

分类

原理

由运行时来决议的,不同分类当中含有同名方法 谁最终生效取决于谁最终参与编译,最后参编译的同名分类方法会最终生效,假如分类方法中添加的方法和宿主类中的某一个方法名相同,分类中的方法会覆盖宿主类中的同名方法,覆盖是指在消息传递过程中优先查找数组靠前的元素,如果查找到了同名方法就直接调用,实际上宿主类的同名方法实现仍然是存在的,我们可可以通过一些手段调用到原有类的同名方法的实现

+

使用场景

    +
  • 申明私有方法,分类的 .m 文件中申明私有方法啊,对外不暴露
  • +
  • 对类中的代码进行抽取分解
  • +
  • 把Framework的私有方法公开化
  • +
+

特点

    +
  • 运行时决议,运行时通过runtime 才把分类中的方法添加到了宿主类上
  • +
  • 可以为系统类添加方法
  • +
  • 有多个分类中存在同名方法时,最后编译的方法会最先生效
  • +
  • 分类中有和宿主类同名方法时,底层中通过内存copy将分类方法“覆盖”宿主类方法
  • +
  • 名字相同的分类编译会报错
  • +
+

分类中可以添加的内容

    +
  • 实例方法
  • +
  • 类方法
  • +
  • 协议
  • +
  • 属性,实际上只是声明了 getter / setter 方法,并没有添加成员变量
  • +
+

关联对象技术

关联对象由 AssocicationsManager 管理并在 AssociationsHashMap 上存储
所有对象的关联内容都在同一个全局容器中

+
    +
  • 给分类添加成员变量,通过关联对象的方式
  • +
+

扩展

使用场景

    +
  • 声明私有属性
  • +
  • 声明私有方法
  • +
  • 声明私有成员变量
  • +
+

特点

    +
  • 编译时决议
  • +
  • 只以声明的形式存在,多数情况下寄生在宿主类的.m中
  • +
  • 不能为系统添加扩展
  • +
+

代理

    +
  • 代理设计模式,传递方式是一对一
  • +
  • 使用weak 避免循环引用
  • +
+

通知

使用观察者模式来实现的用于跨层传递消息的机制

+

KVO

KVO 是系统对观察者模式的又一实现,使用isa 混写技术(isa - swizzling)来动态运行时为某一个类添加一个子类重写了它的setter 方法,同时将原有类的isa指针指向了新创建的类上

+
    +
  • KVO 是OC对观察者模式的又一实现
  • +
  • apple 使用isa 混写技术(isa - swizzling)来实现KVO
      +
    • 给我A类注册一个观察者时本质上是调用系统的 Observer for keyPath 方法,系统会创建一个NSKVONotifiying_A 的类
    • +
    +
  • +
  • 使用 setter 方法设置值KVO才能生效
  • +
  • 使用setValue:forKey: 改变值KVO才能生效
  • +
  • 使用下划线的成员变量直接修改值需要手动添加KVO才能生效(增加 willChangeValueForKey 和 didChangeValueForKey 两个方法)
  • +
+

KVC

apple提供的键值编码技术

+
    +
  • (nullable id)valueForKey:(NSString *)key;
  • +
  • (void)setValue:(nullable id)value forKey:(NSString *)key;

    +

    以上两个方法中的key是没有任何限制的,只要知道对应的成员变量名称,就可以对私有的成员变量设置和操作,这违背了面向对象的编程思想

    +
  • +
+

(void)setValue:(nullable id)value forKey:(NSString *)key; 方法调用流程

+
    +
  • 先判断是否有跟key 相关的setter 方法,如果有就直接调用,结束调用
  • +
  • 如果没有再判断是否存在实例变量,如果存在直接给赋值,结束调用
  • +
  • 如果说实例变量不存在,会去调用 - (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key; 抛出异常
  • +
+

属性关键字

读写权限

    +
  • readonly

    +
  • +
  • readwrite 系统默认

    +
  • +
+

原子性

    +
  • atomic (系统默认)保证赋值和获取(对成员属性的直接获取和赋值)时线程安全,并不代表操作和访问,如果atomic修饰的一个数组,只能保证独具改数组的读取和赋值时线程安全,并不能保证对数据操作(增加/删除数组元素)时是安全的
  • +
  • nonatomic
  • +
+

引用技术

    +
  • assign
      +
    • 修饰基本数据类型,
    • +
    • 修饰对象时不改变其引用计数,
    • +
    • 会产生悬垂指针
    • +
    +
  • +
  • weak
      +
    • 不改变被修饰对象的引用计数,
    • +
    • 所指对象被释放之后指针自动置为nil
    • +
    +
  • +
+

两者区别:

+
    +
  • weak可以修饰对象,而assign 既可以修饰对象也可以修饰基本数据类型
  • +
  • assign 修饰的对象,对象释放后指针仍然指向原对象的内存地址,而weak 修饰的对象,释放后会自动置为nil
  • +
+

问题1

+

weak 修饰的对象为什么释放后会自动置为nil

+

问题2

+

@property (copy) NSMutableArray *array; 有什么问题

+
    +
  • 被copy修饰后,如果赋值过来的是NSMutableArray,copy之后就是NSArray,如果赋值过来的是NSAarray,copy 之后仍然是NSArray,有可能会调用array的添加元素方法会导致crash
  • +
+

copy 关键词

+
    +
  • 浅拷贝:内存地址的复制,让目标对象指针和源对象指向同一块内存空间,
      +
    • 会增加对象的引用计数
    • +
    • 并没有一个新的内存分配
    • +
    +
  • +
+
    +
  • 深拷贝:目标对象指针和源对象指针分别指向两块内容相同的内存空间
      +
    • 不会增加源对象的引用计数
    • +
    • 有新对象的内存分配
    • +
    +
  • +
+

MRC 重写 retain修饰的变量的setter 方法

1
2
3
4
5
6
7
8

- (void)setObj:(id)obj {
if (_obj != obj) {
[_obj release];
}
_obj = [obj retain];

}
+

判断是为了防止异常处理,如果不做 if 判断,当传入的 obj 对象正好是原来的_obj 对象,对原对象尽行releas 操作,实际上也会对传入的对象进行releas 操作进行释放,此时如果再通过obj指针访问废弃的对象时就会导致carsh

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/19/Flutter\344\270\255\345\270\203\345\261\200/index.html" "b/2019/06/19/Flutter\344\270\255\345\270\203\345\261\200/index.html" new file mode 100644 index 0000000..bf1d2c4 --- /dev/null +++ "b/2019/06/19/Flutter\344\270\255\345\270\203\345\261\200/index.html" @@ -0,0 +1,240 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

Flutter中布局

线性布局

主轴、副轴

    +
  • main轴:如果用column组件,垂直就是主轴,如果用Row组件,那水平就是主轴

    +
  • +
  • cross轴:幅轴,是和主轴垂直的方向。用Row组件,那垂直就是幅轴,Column组件,水平方向就是副轴

    +
  • +
+

Row 水平方向布局组件

    +
  • 非自适应布局:根据元素本身大小来布局
  • +
  • 自适应布局:使用 Expanded 布局
  • +
+

非自适应布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class MyRowNoChangeLayout extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Container(
child: Row(
children: <Widget>[
new RaisedButton(
onPressed: (){
print('点击了---- 红色');
},

color: Colors.red,
child: new Text('按钮一红色'),

),

new RaisedButton(
onPressed: (){
print('点击了---- 黄色');
},

color: Colors.yellow,
child: new Text('按钮一黄色'),

),
new RaisedButton(
onPressed: (){
print('点击了---- 绿色');
},

color: Colors.green,
child: new Text('按钮一绿色'),

)
],
),
);
}
}

+

自适应布局

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class MyRowAutoLayout extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Container(
child: Row(
children: <Widget>[
Expanded(
child: new RaisedButton(
onPressed: (){
print('点击了---- 红色');
},

color: Colors.red,
child: new Text('按钮一红色'),
),
),

Expanded(
child: new RaisedButton(
onPressed: (){
print('点击了---- 黄色');
},

color: Colors.yellow,
child: new Text('按钮一黄色'),

)
),

Expanded(
child: new RaisedButton(
onPressed: (){
print('点击了---- 绿色');
},

color: Colors.green,
child: new Text('按钮一绿色'),

)
)
],
),
);
}
}
+

非自动布局和自动布局混合使用

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class MyRowLayout extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Container(
child: Row(
children: <Widget>[
new RaisedButton(
onPressed: (){
print('点击了---- 红色');
},

color: Colors.red,
child: new Text('按钮一红色'),
),

Expanded(
child: new RaisedButton(
onPressed: (){
print('点击了---- 黄色');
},

color: Colors.yellow,
child: new Text('按钮一黄色'),

)
),

new RaisedButton(
onPressed: (){
print('点击了---- 绿色');
},

color: Colors.green,
child: new Text('按钮一绿色'),

)
],
),
);
}
}
+

Column 垂直方向布局组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class MyColunmLayout extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Container(
color: Colors.cyan,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.max,
// 设置主轴的对齐方式
mainAxisAlignment: MainAxisAlignment.center,
// 设置横轴的对齐方式
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new RaisedButton (
onPressed: (){
print('点击了---- 红色');
},

color: Colors.red,
child: new Text('按钮一红色'),
),

new RaisedButton(
onPressed: (){
print('点击了---- 绿色');
},
color: Colors.green,
child: new Text('按钮一绿色'),
),

new Text('文字内容',
textAlign: TextAlign.center,
),
new Text('测试文字',
textAlign: TextAlign.right,
)
],
),
);
}
}
+

层叠布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// stack层叠布局

class MyStackLayout extends StatelessWidget {
@override
Widget build(BuildContext context) {
// 返回一个 Stack 布局
return new Stack(
// 设置位置
alignment: const FractionalOffset(0.5, .9),
// 圆角组件
children: <Widget>[
new CircleAvatar(
backgroundImage: new NetworkImage('https://p.ampmake.com/mall/product/26153760-b964-45d5-b811-7f2bf5844102.jpg'),
// backgroundImage: new Image.network('https://p.ampmake.com/mall/product/26153760-b964-45d5-b811-7f2bf5844102.jpg'),
radius: 100.0,
),

// 容器组件
new Container(
// 设置容器掩饰
decoration: BoxDecoration(
color: Colors.blue,
// border
border: Border.all(
color: Colors.red,
width: 3,
),
),

padding: const EdgeInsets.all(10.0),

child: new Text('stack 布局',
textAlign: TextAlign.center,
maxLines: 1,
// 文字样式
style: TextStyle(
color: Colors.yellow,
textBaseline: TextBaseline.ideographic,
// 下划线
decoration: TextDecoration.underline,
// 下划线颜色
decorationColor: Colors.orange,
// 下划线样式
decorationStyle: TextDecorationStyle.double,
),
),
)
],

);
}
}
+

Positioned组件

    +
  • bottom: 距离层叠组件下边的距离
  • +
  • left:距离层叠组件左边的距离
  • +
  • top:距离层叠组件上边的距离
  • +
  • right:距离层叠组件右边的距离
  • +
  • width: 层叠定位组件的宽度
  • +
  • height: 层叠定位组件的高度
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class MyMoreStackLayout extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Stack(
children: <Widget>[
new CircleAvatar(
backgroundImage: NetworkImage('https://p.ampmake.com/mall/product/26153760-b964-45d5-b811-7f2bf5844102.jpg'),
radius: 100.0,
),

new Positioned(
top: 5,
left: 50,
// right: 5,
width: 100,
child: new RaisedButton(
onPressed: (){
print("多个组件层叠布局");
},

color: Colors.deepOrange,
child: Text('测试按钮'),
),
),

new Positioned(
bottom: 10,
right: 10,
left: 10,
child: new Text('flutter 多个试图 stack布局',
maxLines: 1,
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.lightGreen,
fontSize: 20,
fontStyle: FontStyle.italic
),
)
)
],
);
}
}
+

Card 布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
class MyCardLayout extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Card(
child: Column(
children: <Widget>[
new ListTile(
title: new Text('card 布局1'),
subtitle: new Text('子标题1',
textAlign: TextAlign.right,
style: TextStyle(
color: Colors.lightGreen
),
),
leading: new Icon(Icons.account_balance, color: Colors.purple)
),

new Divider(
color: Colors.cyan
),

new ListTile(
title: new Text('card 布局2'),
subtitle: new Text('子标题2',
textAlign: TextAlign.right,
style: TextStyle(
color: Colors.lightGreen
),
),
leading: new Icon(Icons.account_box, color: Colors.yellow)
),

new Divider(
color: Colors.red,
),

new ListTile(
title: new Text('card 布局3'),
subtitle: new Text('子标题3',
textAlign: TextAlign.right,
style: TextStyle(
color: Colors.lightGreen
),
),
leading: new Icon(Icons.account_circle, color: Colors.red)
),

new Divider(
color: Colors.orangeAccent,
),

new ListTile(
title: new Text('card 布局4'),
subtitle: new Text('子标题4',
textAlign: TextAlign.right,
style: TextStyle(
color: Colors.lightGreen
),
),
leading: new Icon(Icons.camera_enhance, color: Colors.brown)
)
],
),
);
}
}
+ +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/20/Flutter\351\241\265\351\235\242\345\257\274\350\210\252/index.html" "b/2019/06/20/Flutter\351\241\265\351\235\242\345\257\274\350\210\252/index.html" new file mode 100644 index 0000000..d090a37 --- /dev/null +++ "b/2019/06/20/Flutter\351\241\265\351\235\242\345\257\274\350\210\252/index.html" @@ -0,0 +1,223 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

Flutter页面导航

导航栏的使用

程序主入口

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class MyApp extends StatelessWidget {

// 声明参数
final List<String> items;

// 构造函数
MyApp({Key key, @required this.items}):super(key: key); // 调用父类的方法


// 重写
@override

Widget build(BuildContext context) {

// 返回一窗口
return MaterialApp(
title:'Flutter --',
// 基础组件学习
// home: MyLearningFlutterHome(),

home: FirstScreen(),
);
}
}
+

导航栏页面跳转参数传递

商品类

1
2
3
4
5
6
class Product {
final String title; // 定义商品标题
final String description;
// 定义构造方法
Product(this.title, this.description);
}

+

第一级页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class FirstScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: new Text('第一个页面'),
),

body: Center(
child: RaisedButton(
child: new Text('点击跳转详情页'),
onPressed: (){
Navigator.push(context, new MaterialPageRoute(
builder: (context) => (new SecondScreen())
// builder: (context){
// new SecondScreen();
// }
));
},
),
),
);
}
}

+

详情页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class SecondScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('详情页面')
),

body: Center(
child: RaisedButton(
child: Text('返回上一级页面'),

onPressed: () => (Navigator.pop(context)),

// onPressed: (){
// Navigator.pop(context);
// },
),
),
);
}
}

+

导航栏页面返回反向参数传递

使用异步请求和等待

+

商品列表页面

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class ProductListView extends StatelessWidget {
// 定义变量,里面是一个对象
final List<Product> products;

// 使用构造方法 函数的方式接受参数
ProductListView({Key key, @required this.products}):super(key:key); // 设置必参,调用父类方法

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('商品列表'),
),

// 使用 ListView.builder 动态构建
body: ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(products[index].title),
onLongPress: (){
print("长按事件响应");
},


// 点击时传入参数
onTap: (){
print('点击事件');
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProducterDetailView(product:products[index])
)
);
},
);
},
),
);
}
}
+

商品详情页面点击按钮返回上一级页面,并带有参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class ProducterDetailView extends StatelessWidget {
final Product product;

ProducterDetailView({Key key, @required this.product}):super(key:key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Product ${product.title} 详情页面')
),

body: Center(
// 有多控件
child: Column(
children: <Widget>[
Text('Product ${product.title} desc ${product.description}'),
RaisedButton(
child: Text('back Product List'),
color: Colors.blue,
onPressed: (){
Navigator.pop(context, 'selected Id = ${product.title}');
},
)
],
),
),
);
}
}

+ + +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/23/Flutter\344\270\255\346\234\254\345\234\260\345\233\276\347\211\207\350\265\204\346\272\220\344\275\277\347\224\250/index.html" "b/2019/06/23/Flutter\344\270\255\346\234\254\345\234\260\345\233\276\347\211\207\350\265\204\346\272\220\344\275\277\347\224\250/index.html" new file mode 100644 index 0000000..2b800d7 --- /dev/null +++ "b/2019/06/23/Flutter\344\270\255\346\234\254\345\234\260\345\233\276\347\211\207\350\265\204\346\272\220\344\275\277\347\224\250/index.html" @@ -0,0 +1,220 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

Flutter中本地图片资源使用

新建图片文件目录

+

添加图片到项目

手动投入图片到新建的文件目录

+

声明图片资源

找到项目中 pubspec.yaml 文件,找到 assets: 选项 按照注释代码格式声明图片路径

+
1
2
assets:
- lib/assets/ideaOne.png
+

使用图片资源

1
Image.asset('lib/assets/ideaOne.png')
+ +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/2019/06/23/Flutter\346\211\223\345\214\205/index.html" "b/2019/06/23/Flutter\346\211\223\345\214\205/index.html" new file mode 100644 index 0000000..1f68b72 --- /dev/null +++ "b/2019/06/23/Flutter\346\211\223\345\214\205/index.html" @@ -0,0 +1,222 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+ +

Flutter 打包

app 图标替换

Android 图标更换

    +
  • 替换图中路径的图片资源即可,
  • +
  • 注意图片名字需要是 ic_launcher
  • +
  • 每个尺寸对应不同的文件大小
  • +
+
    +
  • 如果要修改启动图片名称需要先在主配置文件中修改
  • +
+

+

iOS 图标更换

修改app图标名称

    +
  • 找到 Android 主配置文件
  • +
  • 修改 android:label=”app_flutter” 中名称
  • +
+

修改app 的名称

打包

生成key

+ +
+ +
+ + + + + +
+ +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index 0381f18..0000000 --- a/README.md +++ /dev/null @@ -1,76 +0,0 @@ -# aTreey.github.io - -开发 - -## 知识小集 - -> [开发小结](https://github.com/aTreey/aTreey.github.io/issues/3) - -> [TableHeaderView动态高度](https://github.com/aTreey/aTreey.github.io/blob/hexo/source/_posts/iOS%20%E5%8A%A8%E6%80%81%E8%AE%BE%E7%BD%AETableHeaderView%E9%AB%98%E5%BA%A6.md) - -## 《52个方法学习整理》 - -> [52个方法第一章](https://github.com/aTreey/aTreey.github.io/blob/hexo/source/_posts/%E3%80%8A52%E4%B8%AA%E6%96%B9%E6%B3%95%E7%AC%AC%E4%B8%80%E7%AB%A0%E3%80%8B.md) - -## GCD信号量用法 -> [GCD信号量用法](https://github.com/aTreey/aTreey.github.io/blob/hexo/source/_posts/GCD信号量用法.md) - - -## Swift - - -> [Swift基础语法1](https://github.com/aTreey/aTreey.github.io/blob/hexo/source/_posts/Alamofire源码学习.md) - -> [Swift基础语法1](https://github.com/aTreey/aTreey.github.io/blob/hexo/source/_posts/Swift3.0学习(一).md) - -> [Swift基础语法2](https://github.com/aTreey/aTreey.github.io/blob/hexo/source/_posts/Swift3.0学习(二).md) - -> [Swift基础语法3](https://github.com/aTreey/aTreey.github.io/blob/hexo/source/_posts/Swift3.0学习(三).md) - -> [Swift基础语法4](https://github.com/aTreey/aTreey.github.io/blob/hexo/source/_posts/Swift3.0学习(四).md) - -> [Swift基础语法5](https://github.com/aTreey/aTreey.github.io/blob/hexo/source/_posts/Swift3.0学习(五).md) - -> [Swift基础](https://github.com/aTreey/aTreey.github.io/blob/hexo/source/_posts/Swift%20基础知识.md) - -> [Swift加密](https://github.com/aTreey/aTreey.github.io/blob/hexo/source/_posts/Swift加密相关.md) - -> [Swift泛型](https://github.com/aTreey/aTreey.github.io/blob/hexo/source/_posts/Swift泛型.md) - -> [Swift协议使用](https://github.com/aTreey/aTreey.github.io/blob/hexo/source/_posts/Swift%20协议使用.md) - -> [SwiftLint使用](https://github.com/aTreey/aTreey.github.io/blob/hexo/source/_posts/SwiftLint使用.md) - - - - -## ReactNative - -> [ReactNative](https://github.com/aTreey/aTreey.github.io/issues/3) - -> [ReactNativeref使用](https://github.com/aTreey/aTreey.github.io/blob/hexo/source/_posts/ReactNative中ref使用.md) - -> [ES6语法](https://github.com/aTreey/aTreey.github.io/blob/hexo/source/_posts/ES6语法.md) - -> [ReactNative集成报错及解决方法](https://github.com/aTreey/aTreey.github.io/blob/hexo/source/_posts/RN%E6%8A%A5%E9%94%99.md) - -> [Swift项目中集成RN](https://github.com/aTreey/aTreey.github.io/blob/hexo/source/_posts/Swift%E9%A1%B9%E7%9B%AE%E4%B8%AD%E9%9B%86%E6%88%90RN.md) - -> [ReactNative 中使用 TypeScript](https://github.com/aTreey/aTreey.github.io/blob/hexo/source/_posts/typescripe%20%E5%AE%89%E8%A3%85%E5%8F%8A%E8%AF%AD%E6%B3%95.md) - -> [ReactNative配置.npmrc相关](https://github.com/aTreey/aTreey.github.io/blob/hexo/source/_posts/npm%E5%92%8Ccnpm.md) - -> [React-Native开发iOS打包](https://github.com/aTreey/aTreey.github.io/blob/hexo/source/_posts/React-Native%E5%BC%80%E5%8F%91iOS%E6%89%93%E5%8C%85.md) - - -## AutoLayout - -> [原生AutoLayout](https://github.com/aTreey/aTreey.github.io/issues/1) - -> [Masonry/Snapkit](https://github.com/aTreey/aTreey.github.io/blob/hexo/source/_posts/Masonry:Snapkit%E4%BD%BF%E7%94%A8%E6%80%BB%E7%BB%93.md) - -## CocoaPods - -> [iOS静态库动态库](https://github.com/aTreey/aTreey.github.io/blob/hexo/source/_posts/iOS静态库动态库.md) - -> [CocoaPods制作私有pod](https://github.com/aTreey/aTreey.github.io/blob/hexo/source/_posts/CocoaPods制作私有pod.md) diff --git a/archives/2016/10/index.html b/archives/2016/10/index.html new file mode 100644 index 0000000..04e38d6 --- /dev/null +++ b/archives/2016/10/index.html @@ -0,0 +1,238 @@ + + + + + + + + Archives: 2016/10 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2016/11/index.html b/archives/2016/11/index.html new file mode 100644 index 0000000..efdfecf --- /dev/null +++ b/archives/2016/11/index.html @@ -0,0 +1,333 @@ + + + + + + + + Archives: 2016/11 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2016 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2016/12/index.html b/archives/2016/12/index.html new file mode 100644 index 0000000..5e71a2e --- /dev/null +++ b/archives/2016/12/index.html @@ -0,0 +1,257 @@ + + + + + + + + Archives: 2016/12 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2016/index.html b/archives/2016/index.html new file mode 100644 index 0000000..62f5347 --- /dev/null +++ b/archives/2016/index.html @@ -0,0 +1,376 @@ + + + + + + + + Archives: 2016 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2016 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2016/page/2/index.html b/archives/2016/page/2/index.html new file mode 100644 index 0000000..e61caab --- /dev/null +++ b/archives/2016/page/2/index.html @@ -0,0 +1,281 @@ + + + + + + + + Archives: 2016 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2017/01/index.html b/archives/2017/01/index.html new file mode 100644 index 0000000..49f35e0 --- /dev/null +++ b/archives/2017/01/index.html @@ -0,0 +1,200 @@ + + + + + + + + Archives: 2017/1 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2017/02/index.html b/archives/2017/02/index.html new file mode 100644 index 0000000..d5c21be --- /dev/null +++ b/archives/2017/02/index.html @@ -0,0 +1,238 @@ + + + + + + + + Archives: 2017/2 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2017/03/index.html b/archives/2017/03/index.html new file mode 100644 index 0000000..b20bf4d --- /dev/null +++ b/archives/2017/03/index.html @@ -0,0 +1,257 @@ + + + + + + + + Archives: 2017/3 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2017/04/index.html b/archives/2017/04/index.html new file mode 100644 index 0000000..debae02 --- /dev/null +++ b/archives/2017/04/index.html @@ -0,0 +1,200 @@ + + + + + + + + Archives: 2017/4 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2017/11/index.html b/archives/2017/11/index.html new file mode 100644 index 0000000..104e6ad --- /dev/null +++ b/archives/2017/11/index.html @@ -0,0 +1,200 @@ + + + + + + + + Archives: 2017/11 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2017/12/index.html b/archives/2017/12/index.html new file mode 100644 index 0000000..3e940d3 --- /dev/null +++ b/archives/2017/12/index.html @@ -0,0 +1,219 @@ + + + + + + + + Archives: 2017/12 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2017/index.html b/archives/2017/index.html new file mode 100644 index 0000000..343acfb --- /dev/null +++ b/archives/2017/index.html @@ -0,0 +1,376 @@ + + + + + + + + Archives: 2017 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2017 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2017/page/2/index.html b/archives/2017/page/2/index.html new file mode 100644 index 0000000..a9874d2 --- /dev/null +++ b/archives/2017/page/2/index.html @@ -0,0 +1,224 @@ + + + + + + + + Archives: 2017 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2018/01/index.html b/archives/2018/01/index.html new file mode 100644 index 0000000..86ef886 --- /dev/null +++ b/archives/2018/01/index.html @@ -0,0 +1,295 @@ + + + + + + + + Archives: 2018/1 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2018/02/index.html b/archives/2018/02/index.html new file mode 100644 index 0000000..c230929 --- /dev/null +++ b/archives/2018/02/index.html @@ -0,0 +1,200 @@ + + + + + + + + Archives: 2018/2 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2018/index.html b/archives/2018/index.html new file mode 100644 index 0000000..85bdcc1 --- /dev/null +++ b/archives/2018/index.html @@ -0,0 +1,314 @@ + + + + + + + + Archives: 2018 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2019/06/index.html b/archives/2019/06/index.html new file mode 100644 index 0000000..978a42c --- /dev/null +++ b/archives/2019/06/index.html @@ -0,0 +1,322 @@ + + + + + + + + Archives: 2019/6 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2019 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2019/06/page/2/index.html b/archives/2019/06/page/2/index.html new file mode 100644 index 0000000..deff3a2 --- /dev/null +++ b/archives/2019/06/page/2/index.html @@ -0,0 +1,316 @@ + + + + + + + + Archives: 2019/6 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2019 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2019/06/page/3/index.html b/archives/2019/06/page/3/index.html new file mode 100644 index 0000000..89e6c92 --- /dev/null +++ b/archives/2019/06/page/3/index.html @@ -0,0 +1,316 @@ + + + + + + + + Archives: 2019/6 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2019 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2019/06/page/4/index.html b/archives/2019/06/page/4/index.html new file mode 100644 index 0000000..65caa3b --- /dev/null +++ b/archives/2019/06/page/4/index.html @@ -0,0 +1,316 @@ + + + + + + + + Archives: 2019/6 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2019 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2019/06/page/5/index.html b/archives/2019/06/page/5/index.html new file mode 100644 index 0000000..ba599aa --- /dev/null +++ b/archives/2019/06/page/5/index.html @@ -0,0 +1,309 @@ + + + + + + + + Archives: 2019/6 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2019 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2019/index.html b/archives/2019/index.html new file mode 100644 index 0000000..b26fea1 --- /dev/null +++ b/archives/2019/index.html @@ -0,0 +1,322 @@ + + + + + + + + Archives: 2019 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2019 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2019/page/2/index.html b/archives/2019/page/2/index.html new file mode 100644 index 0000000..3c37b42 --- /dev/null +++ b/archives/2019/page/2/index.html @@ -0,0 +1,316 @@ + + + + + + + + Archives: 2019 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2019 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2019/page/3/index.html b/archives/2019/page/3/index.html new file mode 100644 index 0000000..4169a72 --- /dev/null +++ b/archives/2019/page/3/index.html @@ -0,0 +1,316 @@ + + + + + + + + Archives: 2019 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2019 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2019/page/4/index.html b/archives/2019/page/4/index.html new file mode 100644 index 0000000..9860847 --- /dev/null +++ b/archives/2019/page/4/index.html @@ -0,0 +1,316 @@ + + + + + + + + Archives: 2019 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2019 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/2019/page/5/index.html b/archives/2019/page/5/index.html new file mode 100644 index 0000000..da295a7 --- /dev/null +++ b/archives/2019/page/5/index.html @@ -0,0 +1,309 @@ + + + + + + + + Archives: 2019 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2019 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/index.html b/archives/index.html new file mode 100644 index 0000000..fffd9a4 --- /dev/null +++ b/archives/index.html @@ -0,0 +1,322 @@ + + + + + + + + Archives | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2019 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/page/2/index.html b/archives/page/2/index.html new file mode 100644 index 0000000..7e467d2 --- /dev/null +++ b/archives/page/2/index.html @@ -0,0 +1,316 @@ + + + + + + + + Archives | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2019 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/page/3/index.html b/archives/page/3/index.html new file mode 100644 index 0000000..2307928 --- /dev/null +++ b/archives/page/3/index.html @@ -0,0 +1,316 @@ + + + + + + + + Archives | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2019 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/page/4/index.html b/archives/page/4/index.html new file mode 100644 index 0000000..619d838 --- /dev/null +++ b/archives/page/4/index.html @@ -0,0 +1,316 @@ + + + + + + + + Archives | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2019 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/page/5/index.html b/archives/page/5/index.html new file mode 100644 index 0000000..b0de8aa --- /dev/null +++ b/archives/page/5/index.html @@ -0,0 +1,338 @@ + + + + + + + + Archives | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2019 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ 2018 +
+
+ + + + +
+ + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/page/6/index.html b/archives/page/6/index.html new file mode 100644 index 0000000..fbab4ff --- /dev/null +++ b/archives/page/6/index.html @@ -0,0 +1,386 @@ + + + + + + + + Archives | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2018 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ 2017 +
+
+ + + + + + + + + + + + + + + + +
+ + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/page/7/index.html b/archives/page/7/index.html new file mode 100644 index 0000000..50463fc --- /dev/null +++ b/archives/page/7/index.html @@ -0,0 +1,386 @@ + + + + + + + + Archives | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2017 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ 2016 +
+
+ + + + + + + + +
+ + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/page/8/index.html b/archives/page/8/index.html new file mode 100644 index 0000000..0f06596 --- /dev/null +++ b/archives/page/8/index.html @@ -0,0 +1,376 @@ + + + + + + + + Archives | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + +
+
+ 2016 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/archives/page/9/index.html b/archives/page/9/index.html new file mode 100644 index 0000000..37f82ca --- /dev/null +++ b/archives/page/9/index.html @@ -0,0 +1,243 @@ + + + + + + + + Archives | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/themes/landscape/source/css/fonts/FontAwesome.otf b/css/fonts/FontAwesome.otf similarity index 100% rename from themes/landscape/source/css/fonts/FontAwesome.otf rename to css/fonts/FontAwesome.otf diff --git a/themes/landscape/source/css/fonts/fontawesome-webfont.eot b/css/fonts/fontawesome-webfont.eot similarity index 100% rename from themes/landscape/source/css/fonts/fontawesome-webfont.eot rename to css/fonts/fontawesome-webfont.eot diff --git a/themes/landscape/source/css/fonts/fontawesome-webfont.svg b/css/fonts/fontawesome-webfont.svg similarity index 100% rename from themes/landscape/source/css/fonts/fontawesome-webfont.svg rename to css/fonts/fontawesome-webfont.svg diff --git a/themes/landscape/source/css/fonts/fontawesome-webfont.ttf b/css/fonts/fontawesome-webfont.ttf similarity index 100% rename from themes/landscape/source/css/fonts/fontawesome-webfont.ttf rename to css/fonts/fontawesome-webfont.ttf diff --git a/themes/landscape/source/css/fonts/fontawesome-webfont.woff b/css/fonts/fontawesome-webfont.woff similarity index 100% rename from themes/landscape/source/css/fonts/fontawesome-webfont.woff rename to css/fonts/fontawesome-webfont.woff diff --git a/themes/landscape/source/css/images/banner.jpg b/css/images/banner.jpg similarity index 100% rename from themes/landscape/source/css/images/banner.jpg rename to css/images/banner.jpg diff --git a/css/style.css b/css/style.css new file mode 100644 index 0000000..0cfa2d3 --- /dev/null +++ b/css/style.css @@ -0,0 +1,1374 @@ +body { + width: 100%; +} +body:before, +body:after { + content: ""; + display: table; +} +body:after { + clear: both; +} +html, +body, +div, +span, +applet, +object, +iframe, +h1, +h2, +h3, +h4, +h5, +h6, +p, +blockquote, +pre, +a, +abbr, +acronym, +address, +big, +cite, +code, +del, +dfn, +em, +img, +ins, +kbd, +q, +s, +samp, +small, +strike, +strong, +sub, +sup, +tt, +var, +dl, +dt, +dd, +ol, +ul, +li, +fieldset, +form, +label, +legend, +table, +caption, +tbody, +tfoot, +thead, +tr, +th, +td { + margin: 0; + padding: 0; + border: 0; + outline: 0; + font-weight: inherit; + font-style: inherit; + font-family: inherit; + font-size: 100%; + vertical-align: baseline; +} +body { + line-height: 1; + color: #000; + background: #fff; +} +ol, +ul { + list-style: none; +} +table { + border-collapse: separate; + border-spacing: 0; + vertical-align: middle; +} +caption, +th, +td { + text-align: left; + font-weight: normal; + vertical-align: middle; +} +a img { + border: none; +} +input, +button { + margin: 0; + padding: 0; +} +input::-moz-focus-inner, +button::-moz-focus-inner { + border: 0; + padding: 0; +} +@font-face { + font-family: FontAwesome; + font-style: normal; + font-weight: normal; + src: url("fonts/fontawesome-webfont.eot?v=#4.0.3"); + src: url("fonts/fontawesome-webfont.eot?#iefix&v=#4.0.3") format("embedded-opentype"), url("fonts/fontawesome-webfont.woff?v=#4.0.3") format("woff"), url("fonts/fontawesome-webfont.ttf?v=#4.0.3") format("truetype"), url("fonts/fontawesome-webfont.svg#fontawesomeregular?v=#4.0.3") format("svg"); +} +html, +body, +#container { + height: 100%; +} +body { + background: #eee; + font: 14px -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; + -webkit-text-size-adjust: 100%; +} +.outer { + max-width: 1220px; + margin: 0 auto; + padding: 0 20px; +} +.outer:before, +.outer:after { + content: ""; + display: table; +} +.outer:after { + clear: both; +} +.inner { + display: inline; + float: left; + width: 98.33333333333333%; + margin: 0 0.833333333333333%; +} +.left, +.alignleft { + float: left; +} +.right, +.alignright { + float: right; +} +.clear { + clear: both; +} +#container { + position: relative; +} +.mobile-nav-on { + overflow: hidden; +} +#wrap { + height: 100%; + width: 100%; + position: absolute; + top: 0; + left: 0; + -webkit-transition: 0.2s ease-out; + -moz-transition: 0.2s ease-out; + -ms-transition: 0.2s ease-out; + transition: 0.2s ease-out; + z-index: 1; + background: #eee; +} +.mobile-nav-on #wrap { + left: 280px; +} +@media screen and (min-width: 768px) { + #main { + display: inline; + float: left; + width: 73.33333333333333%; + margin: 0 0.833333333333333%; + } +} +.article-date, +.article-category-link, +.archive-year, +.widget-title { + text-decoration: none; + text-transform: uppercase; + letter-spacing: 2px; + color: #999; + margin-bottom: 1em; + margin-left: 5px; + line-height: 1em; + text-shadow: 0 1px #fff; + font-weight: bold; +} +.article-inner, +.archive-article-inner { + background: #fff; + -webkit-box-shadow: 1px 2px 3px #ddd; + box-shadow: 1px 2px 3px #ddd; + border: 1px solid #ddd; + border-radius: 3px; +} +.article-entry h1, +.widget h1 { + font-size: 2em; +} +.article-entry h2, +.widget h2 { + font-size: 1.5em; +} +.article-entry h3, +.widget h3 { + font-size: 1.3em; +} +.article-entry h4, +.widget h4 { + font-size: 1.2em; +} +.article-entry h5, +.widget h5 { + font-size: 1em; +} +.article-entry h6, +.widget h6 { + font-size: 1em; + color: #999; +} +.article-entry hr, +.widget hr { + border: 1px dashed #ddd; +} +.article-entry strong, +.widget strong { + font-weight: bold; +} +.article-entry em, +.widget em, +.article-entry cite, +.widget cite { + font-style: italic; +} +.article-entry sup, +.widget sup, +.article-entry sub, +.widget sub { + font-size: 0.75em; + line-height: 0; + position: relative; + vertical-align: baseline; +} +.article-entry sup, +.widget sup { + top: -0.5em; +} +.article-entry sub, +.widget sub { + bottom: -0.2em; +} +.article-entry small, +.widget small { + font-size: 0.85em; +} +.article-entry acronym, +.widget acronym, +.article-entry abbr, +.widget abbr { + border-bottom: 1px dotted; +} +.article-entry ul, +.widget ul, +.article-entry ol, +.widget ol, +.article-entry dl, +.widget dl { + margin: 0 20px; + line-height: 1.6em; +} +.article-entry ul ul, +.widget ul ul, +.article-entry ol ul, +.widget ol ul, +.article-entry ul ol, +.widget ul ol, +.article-entry ol ol, +.widget ol ol { + margin-top: 0; + margin-bottom: 0; +} +.article-entry ul, +.widget ul { + list-style: disc; +} +.article-entry ol, +.widget ol { + list-style: decimal; +} +.article-entry dt, +.widget dt { + font-weight: bold; +} +#header { + height: 300px; + position: relative; + border-bottom: 1px solid #ddd; +} +#header:before, +#header:after { + content: ""; + position: absolute; + left: 0; + right: 0; + height: 40px; +} +#header:before { + top: 0; + background: -webkit-linear-gradient(rgba(0,0,0,0.2), transparent); + background: -moz-linear-gradient(rgba(0,0,0,0.2), transparent); + background: -ms-linear-gradient(rgba(0,0,0,0.2), transparent); + background: linear-gradient(rgba(0,0,0,0.2), transparent); +} +#header:after { + bottom: 0; + background: -webkit-linear-gradient(transparent, rgba(0,0,0,0.2)); + background: -moz-linear-gradient(transparent, rgba(0,0,0,0.2)); + background: -ms-linear-gradient(transparent, rgba(0,0,0,0.2)); + background: linear-gradient(transparent, rgba(0,0,0,0.2)); +} +#header-outer { + height: 100%; + position: relative; +} +#header-inner { + position: relative; + overflow: hidden; +} +#banner { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: url("images/banner.jpg") center #000; + -webkit-background-size: cover; + -moz-background-size: cover; + background-size: cover; + z-index: -1; +} +#header-title { + text-align: center; + height: 40px; + position: absolute; + top: 50%; + left: 0; + margin-top: -20px; +} +#logo, +#subtitle { + text-decoration: none; + color: #fff; + font-weight: 300; + text-shadow: 0 1px 4px rgba(0,0,0,0.3); +} +#logo { + font-size: 40px; + line-height: 40px; + letter-spacing: 2px; +} +#subtitle { + font-size: 16px; + line-height: 16px; + letter-spacing: 1px; +} +#subtitle-wrap { + margin-top: 16px; +} +#main-nav { + float: left; + margin-left: -15px; +} +.nav-icon, +.main-nav-link { + float: left; + color: #fff; + opacity: 0.6; + text-decoration: none; + text-shadow: 0 1px rgba(0,0,0,0.2); + -webkit-transition: opacity 0.2s; + -moz-transition: opacity 0.2s; + -ms-transition: opacity 0.2s; + transition: opacity 0.2s; + display: block; + padding: 20px 15px; +} +.nav-icon:hover, +.main-nav-link:hover { + opacity: 1; +} +.nav-icon { + font-family: FontAwesome; + text-align: center; + font-size: 14px; + width: 14px; + height: 14px; + padding: 20px 15px; + position: relative; + cursor: pointer; +} +.main-nav-link { + font-weight: 300; + letter-spacing: 1px; +} +@media screen and (max-width: 479px) { + .main-nav-link { + display: none; + } +} +#main-nav-toggle { + display: none; +} +#main-nav-toggle:before { + content: "\f0c9"; +} +@media screen and (max-width: 479px) { + #main-nav-toggle { + display: block; + } +} +#sub-nav { + float: right; + margin-right: -15px; +} +#nav-rss-link:before { + content: "\f09e"; +} +#nav-search-btn:before { + content: "\f002"; +} +#search-form-wrap { + position: absolute; + top: 15px; + width: 150px; + height: 30px; + right: -150px; + opacity: 0; + -webkit-transition: 0.2s ease-out; + -moz-transition: 0.2s ease-out; + -ms-transition: 0.2s ease-out; + transition: 0.2s ease-out; +} +#search-form-wrap.on { + opacity: 1; + right: 0; +} +@media screen and (max-width: 479px) { + #search-form-wrap { + width: 100%; + right: -100%; + } +} +.search-form { + position: absolute; + top: 0; + left: 0; + right: 0; + background: #fff; + padding: 5px 15px; + border-radius: 15px; + -webkit-box-shadow: 0 0 10px rgba(0,0,0,0.3); + box-shadow: 0 0 10px rgba(0,0,0,0.3); +} +.search-form-input { + border: none; + background: none; + color: #555; + width: 100%; + font: 13px -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; + outline: none; +} +.search-form-input::-webkit-search-results-decoration, +.search-form-input::-webkit-search-cancel-button { + -webkit-appearance: none; +} +.search-form-submit { + position: absolute; + top: 50%; + right: 10px; + margin-top: -7px; + font: 13px FontAwesome; + border: none; + background: none; + color: #bbb; + cursor: pointer; +} +.search-form-submit:hover, +.search-form-submit:focus { + color: #777; +} +.article { + margin: 50px 0; +} +.article-inner { + overflow: hidden; +} +.article-meta:before, +.article-meta:after { + content: ""; + display: table; +} +.article-meta:after { + clear: both; +} +.article-date { + float: left; +} +.article-category { + float: left; + line-height: 1em; + color: #ccc; + text-shadow: 0 1px #fff; + margin-left: 8px; +} +.article-category:before { + content: "\2022"; +} +.article-category-link { + margin: 0 12px 1em; +} +.article-header { + padding: 20px 20px 0; +} +.article-title { + text-decoration: none; + font-size: 2em; + font-weight: bold; + color: #555; + line-height: 1.1em; + -webkit-transition: color 0.2s; + -moz-transition: color 0.2s; + -ms-transition: color 0.2s; + transition: color 0.2s; +} +a.article-title:hover { + color: #258fb8; +} +.article-entry { + color: #555; + padding: 0 20px; +} +.article-entry:before, +.article-entry:after { + content: ""; + display: table; +} +.article-entry:after { + clear: both; +} +.article-entry p, +.article-entry table { + line-height: 1.6em; + margin: 1.6em 0; +} +.article-entry h1, +.article-entry h2, +.article-entry h3, +.article-entry h4, +.article-entry h5, +.article-entry h6 { + font-weight: bold; +} +.article-entry h1, +.article-entry h2, +.article-entry h3, +.article-entry h4, +.article-entry h5, +.article-entry h6 { + line-height: 1.1em; + margin: 1.1em 0; +} +.article-entry a { + color: #258fb8; + text-decoration: none; +} +.article-entry a:hover { + text-decoration: underline; +} +.article-entry ul, +.article-entry ol, +.article-entry dl { + margin-top: 1.6em; + margin-bottom: 1.6em; +} +.article-entry img, +.article-entry video { + max-width: 100%; + height: auto; + display: block; + margin: auto; +} +.article-entry iframe { + border: none; +} +.article-entry table { + width: 100%; + border-collapse: collapse; + border-spacing: 0; +} +.article-entry th { + font-weight: bold; + border-bottom: 3px solid #ddd; + padding-bottom: 0.5em; +} +.article-entry td { + border-bottom: 1px solid #ddd; + padding: 10px 0; +} +.article-entry blockquote { + font-family: Georgia, "Times New Roman", serif; + font-size: 1.4em; + margin: 1.6em 20px; + text-align: center; +} +.article-entry blockquote footer { + font-size: 14px; + margin: 1.6em 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; +} +.article-entry blockquote footer cite:before { + content: "—"; + padding: 0 0.5em; +} +.article-entry .pullquote { + text-align: left; + width: 45%; + margin: 0; +} +.article-entry .pullquote.left { + margin-left: 0.5em; + margin-right: 1em; +} +.article-entry .pullquote.right { + margin-right: 0.5em; + margin-left: 1em; +} +.article-entry .caption { + color: #999; + display: block; + font-size: 0.9em; + margin-top: 0.5em; + position: relative; + text-align: center; +} +.article-entry .video-container { + position: relative; + padding-top: 56.25%; + height: 0; + overflow: hidden; +} +.article-entry .video-container iframe, +.article-entry .video-container object, +.article-entry .video-container embed { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + margin-top: 0; +} +.article-more-link a { + display: inline-block; + line-height: 1em; + padding: 6px 15px; + border-radius: 15px; + background: #eee; + color: #999; + text-shadow: 0 1px #fff; + text-decoration: none; +} +.article-more-link a:hover { + background: #258fb8; + color: #fff; + text-decoration: none; + text-shadow: 0 1px #1e7293; +} +.article-footer { + font-size: 0.85em; + line-height: 1.6em; + border-top: 1px solid #ddd; + padding-top: 1.6em; + margin: 0 20px 20px; +} +.article-footer:before, +.article-footer:after { + content: ""; + display: table; +} +.article-footer:after { + clear: both; +} +.article-footer a { + color: #999; + text-decoration: none; +} +.article-footer a:hover { + color: #555; +} +.article-tag-list-item { + float: left; + margin-right: 10px; +} +.article-tag-list-link:before { + content: "#"; +} +.article-comment-link { + float: right; +} +.article-comment-link:before { + content: "\f075"; + font-family: FontAwesome; + padding-right: 8px; +} +.article-share-link { + cursor: pointer; + float: right; + margin-left: 20px; +} +.article-share-link:before { + content: "\f064"; + font-family: FontAwesome; + padding-right: 6px; +} +#article-nav { + position: relative; +} +#article-nav:before, +#article-nav:after { + content: ""; + display: table; +} +#article-nav:after { + clear: both; +} +@media screen and (min-width: 768px) { + #article-nav { + margin: 50px 0; + } + #article-nav:before { + width: 8px; + height: 8px; + position: absolute; + top: 50%; + left: 50%; + margin-top: -4px; + margin-left: -4px; + content: ""; + border-radius: 50%; + background: #ddd; + -webkit-box-shadow: 0 1px 2px #fff; + box-shadow: 0 1px 2px #fff; + } +} +.article-nav-link-wrap { + text-decoration: none; + text-shadow: 0 1px #fff; + color: #999; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + margin-top: 50px; + text-align: center; + display: block; +} +.article-nav-link-wrap:hover { + color: #555; +} +@media screen and (min-width: 768px) { + .article-nav-link-wrap { + width: 50%; + margin-top: 0; + } +} +@media screen and (min-width: 768px) { + #article-nav-newer { + float: left; + text-align: right; + padding-right: 20px; + } +} +@media screen and (min-width: 768px) { + #article-nav-older { + float: right; + text-align: left; + padding-left: 20px; + } +} +.article-nav-caption { + text-transform: uppercase; + letter-spacing: 2px; + color: #ddd; + line-height: 1em; + font-weight: bold; +} +#article-nav-newer .article-nav-caption { + margin-right: -2px; +} +.article-nav-title { + font-size: 0.85em; + line-height: 1.6em; + margin-top: 0.5em; +} +.article-share-box { + position: absolute; + display: none; + background: #fff; + -webkit-box-shadow: 1px 2px 10px rgba(0,0,0,0.2); + box-shadow: 1px 2px 10px rgba(0,0,0,0.2); + border-radius: 3px; + margin-left: -145px; + overflow: hidden; + z-index: 1; +} +.article-share-box.on { + display: block; +} +.article-share-input { + width: 100%; + background: none; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + font: 14px -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; + padding: 0 15px; + color: #555; + outline: none; + border: 1px solid #ddd; + border-radius: 3px 3px 0 0; + height: 36px; + line-height: 36px; +} +.article-share-links { + background: #eee; +} +.article-share-links:before, +.article-share-links:after { + content: ""; + display: table; +} +.article-share-links:after { + clear: both; +} +.article-share-twitter, +.article-share-facebook, +.article-share-pinterest, +.article-share-google { + width: 50px; + height: 36px; + display: block; + float: left; + position: relative; + color: #999; + text-shadow: 0 1px #fff; +} +.article-share-twitter:before, +.article-share-facebook:before, +.article-share-pinterest:before, +.article-share-google:before { + font-size: 20px; + font-family: FontAwesome; + width: 20px; + height: 20px; + position: absolute; + top: 50%; + left: 50%; + margin-top: -10px; + margin-left: -10px; + text-align: center; +} +.article-share-twitter:hover, +.article-share-facebook:hover, +.article-share-pinterest:hover, +.article-share-google:hover { + color: #fff; +} +.article-share-twitter:before { + content: "\f099"; +} +.article-share-twitter:hover { + background: #00aced; + text-shadow: 0 1px #008abe; +} +.article-share-facebook:before { + content: "\f09a"; +} +.article-share-facebook:hover { + background: #3b5998; + text-shadow: 0 1px #2f477a; +} +.article-share-pinterest:before { + content: "\f0d2"; +} +.article-share-pinterest:hover { + background: #cb2027; + text-shadow: 0 1px #a21a1f; +} +.article-share-google:before { + content: "\f0d5"; +} +.article-share-google:hover { + background: #dd4b39; + text-shadow: 0 1px #be3221; +} +.article-gallery { + background: #000; + position: relative; +} +.article-gallery-photos { + position: relative; + overflow: hidden; +} +.article-gallery-img { + display: none; + max-width: 100%; +} +.article-gallery-img:first-child { + display: block; +} +.article-gallery-img.loaded { + position: absolute; + display: block; +} +.article-gallery-img img { + display: block; + max-width: 100%; + margin: 0 auto; +} +#comments { + background: #fff; + -webkit-box-shadow: 1px 2px 3px #ddd; + box-shadow: 1px 2px 3px #ddd; + padding: 20px; + border: 1px solid #ddd; + border-radius: 3px; + margin: 50px 0; +} +#comments a { + color: #258fb8; +} +.archives-wrap { + margin: 50px 0; +} +.archives:before, +.archives:after { + content: ""; + display: table; +} +.archives:after { + clear: both; +} +.archive-year-wrap { + margin-bottom: 1em; +} +.archives { + -webkit-column-gap: 10px; + -moz-column-gap: 10px; + column-gap: 10px; +} +@media screen and (min-width: 480px) and (max-width: 767px) { + .archives { + -webkit-column-count: 2; + -moz-column-count: 2; + column-count: 2; + } +} +@media screen and (min-width: 768px) { + .archives { + -webkit-column-count: 3; + -moz-column-count: 3; + column-count: 3; + } +} +.archive-article { + -webkit-column-break-inside: avoid; + page-break-inside: avoid; + overflow: hidden; + break-inside: avoid-column; +} +.archive-article-inner { + padding: 10px; + margin-bottom: 15px; +} +.archive-article-title { + text-decoration: none; + font-weight: bold; + color: #555; + -webkit-transition: color 0.2s; + -moz-transition: color 0.2s; + -ms-transition: color 0.2s; + transition: color 0.2s; + line-height: 1.6em; +} +.archive-article-title:hover { + color: #258fb8; +} +.archive-article-footer { + margin-top: 1em; +} +.archive-article-date { + color: #999; + text-decoration: none; + font-size: 0.85em; + line-height: 1em; + margin-bottom: 0.5em; + display: block; +} +#page-nav { + margin: 50px auto; + background: #fff; + -webkit-box-shadow: 1px 2px 3px #ddd; + box-shadow: 1px 2px 3px #ddd; + border: 1px solid #ddd; + border-radius: 3px; + text-align: center; + color: #999; + overflow: hidden; +} +#page-nav:before, +#page-nav:after { + content: ""; + display: table; +} +#page-nav:after { + clear: both; +} +#page-nav a, +#page-nav span { + padding: 10px 20px; + line-height: 1; + height: 2ex; +} +#page-nav a { + color: #999; + text-decoration: none; +} +#page-nav a:hover { + background: #999; + color: #fff; +} +#page-nav .prev { + float: left; +} +#page-nav .next { + float: right; +} +#page-nav .page-number { + display: inline-block; +} +@media screen and (max-width: 479px) { + #page-nav .page-number { + display: none; + } +} +#page-nav .current { + color: #555; + font-weight: bold; +} +#page-nav .space { + color: #ddd; +} +#footer { + background: #262a30; + padding: 50px 0; + border-top: 1px solid #ddd; + color: #999; +} +#footer a { + color: #258fb8; + text-decoration: none; +} +#footer a:hover { + text-decoration: underline; +} +#footer-info { + line-height: 1.6em; + font-size: 0.85em; +} +.article-entry pre, +.article-entry .highlight { + background: #2d2d2d; + margin: 0 -20px; + padding: 15px 20px; + border-style: solid; + border-color: #ddd; + border-width: 1px 0; + overflow: auto; + color: #ccc; + line-height: 22.400000000000002px; +} +.article-entry .highlight .gutter pre, +.article-entry .gist .gist-file .gist-data .line-numbers { + color: #666; + font-size: 0.85em; +} +.article-entry pre, +.article-entry code { + font-family: "Source Code Pro", Consolas, Monaco, Menlo, Consolas, monospace; +} +.article-entry code { + background: #eee; + text-shadow: 0 1px #fff; + padding: 0 0.3em; +} +.article-entry pre code { + background: none; + text-shadow: none; + padding: 0; +} +.article-entry .highlight pre { + border: none; + margin: 0; + padding: 0; +} +.article-entry .highlight table { + margin: 0; + width: auto; +} +.article-entry .highlight td { + border: none; + padding: 0; +} +.article-entry .highlight figcaption { + font-size: 0.85em; + color: #999; + line-height: 1em; + margin-bottom: 1em; +} +.article-entry .highlight figcaption:before, +.article-entry .highlight figcaption:after { + content: ""; + display: table; +} +.article-entry .highlight figcaption:after { + clear: both; +} +.article-entry .highlight figcaption a { + float: right; +} +.article-entry .highlight .gutter pre { + text-align: right; + padding-right: 20px; +} +.article-entry .highlight .line { + height: 22.400000000000002px; +} +.article-entry .highlight .line.marked { + background: #515151; +} +.article-entry .gist { + margin: 0 -20px; + border-style: solid; + border-color: #ddd; + border-width: 1px 0; + background: #2d2d2d; + padding: 15px 20px 15px 0; +} +.article-entry .gist .gist-file { + border: none; + font-family: "Source Code Pro", Consolas, Monaco, Menlo, Consolas, monospace; + margin: 0; +} +.article-entry .gist .gist-file .gist-data { + background: none; + border: none; +} +.article-entry .gist .gist-file .gist-data .line-numbers { + background: none; + border: none; + padding: 0 20px 0 0; +} +.article-entry .gist .gist-file .gist-data .line-data { + padding: 0 !important; +} +.article-entry .gist .gist-file .highlight { + margin: 0; + padding: 0; + border: none; +} +.article-entry .gist .gist-file .gist-meta { + background: #2d2d2d; + color: #999; + font: 0.85em -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; + text-shadow: 0 0; + padding: 0; + margin-top: 1em; + margin-left: 20px; +} +.article-entry .gist .gist-file .gist-meta a { + color: #258fb8; + font-weight: normal; +} +.article-entry .gist .gist-file .gist-meta a:hover { + text-decoration: underline; +} +pre .comment, +pre .title { + color: #999; +} +pre .variable, +pre .attribute, +pre .tag, +pre .regexp, +pre .ruby .constant, +pre .xml .tag .title, +pre .xml .pi, +pre .xml .doctype, +pre .html .doctype, +pre .css .id, +pre .css .class, +pre .css .pseudo { + color: #f2777a; +} +pre .number, +pre .preprocessor, +pre .built_in, +pre .literal, +pre .params, +pre .constant { + color: #f99157; +} +pre .class, +pre .ruby .class .title, +pre .css .rules .attribute { + color: #9c9; +} +pre .string, +pre .value, +pre .inheritance, +pre .header, +pre .ruby .symbol, +pre .xml .cdata { + color: #9c9; +} +pre .css .hexcolor { + color: #6cc; +} +pre .function, +pre .python .decorator, +pre .python .title, +pre .ruby .function .title, +pre .ruby .title .keyword, +pre .perl .sub, +pre .javascript .title, +pre .coffeescript .title { + color: #69c; +} +pre .keyword, +pre .javascript .function { + color: #c9c; +} +@media screen and (max-width: 479px) { + #mobile-nav { + position: absolute; + top: 0; + left: 0; + width: 280px; + height: 100%; + background: #191919; + border-right: 1px solid #fff; + } +} +@media screen and (max-width: 479px) { + .mobile-nav-link { + display: block; + color: #999; + text-decoration: none; + padding: 15px 20px; + font-weight: bold; + } + .mobile-nav-link:hover { + color: #fff; + } +} +@media screen and (min-width: 768px) { + #sidebar { + display: inline; + float: left; + width: 23.333333333333332%; + margin: 0 0.833333333333333%; + } +} +.widget-wrap { + margin: 50px 0; +} +.widget { + color: #777; + text-shadow: 0 1px #fff; + background: #ddd; + -webkit-box-shadow: 0 -1px 4px #ccc inset; + box-shadow: 0 -1px 4px #ccc inset; + border: 1px solid #ccc; + padding: 15px; + border-radius: 3px; +} +.widget a { + color: #258fb8; + text-decoration: none; +} +.widget a:hover { + text-decoration: underline; +} +.widget ul ul, +.widget ol ul, +.widget dl ul, +.widget ul ol, +.widget ol ol, +.widget dl ol, +.widget ul dl, +.widget ol dl, +.widget dl dl { + margin-left: 15px; + list-style: disc; +} +.widget { + line-height: 1.6em; + word-wrap: break-word; + font-size: 0.9em; +} +.widget ul, +.widget ol { + list-style: none; + margin: 0; +} +.widget ul ul, +.widget ol ul, +.widget ul ol, +.widget ol ol { + margin: 0 20px; +} +.widget ul ul, +.widget ol ul { + list-style: disc; +} +.widget ul ol, +.widget ol ol { + list-style: decimal; +} +.category-list-count, +.tag-list-count, +.archive-list-count { + padding-left: 5px; + color: #999; + font-size: 0.85em; +} +.category-list-count:before, +.tag-list-count:before, +.archive-list-count:before { + content: "("; +} +.category-list-count:after, +.tag-list-count:after, +.archive-list-count:after { + content: ")"; +} +.tagcloud a { + margin-right: 5px; + display: inline-block; +} diff --git a/themes/landscape/source/fancybox/blank.gif b/fancybox/blank.gif similarity index 100% rename from themes/landscape/source/fancybox/blank.gif rename to fancybox/blank.gif diff --git a/themes/landscape/source/fancybox/fancybox_loading.gif b/fancybox/fancybox_loading.gif similarity index 100% rename from themes/landscape/source/fancybox/fancybox_loading.gif rename to fancybox/fancybox_loading.gif diff --git a/themes/landscape/source/fancybox/fancybox_loading@2x.gif b/fancybox/fancybox_loading@2x.gif similarity index 100% rename from themes/landscape/source/fancybox/fancybox_loading@2x.gif rename to fancybox/fancybox_loading@2x.gif diff --git a/themes/landscape/source/fancybox/fancybox_overlay.png b/fancybox/fancybox_overlay.png similarity index 100% rename from themes/landscape/source/fancybox/fancybox_overlay.png rename to fancybox/fancybox_overlay.png diff --git a/themes/landscape/source/fancybox/fancybox_sprite.png b/fancybox/fancybox_sprite.png similarity index 100% rename from themes/landscape/source/fancybox/fancybox_sprite.png rename to fancybox/fancybox_sprite.png diff --git a/themes/landscape/source/fancybox/fancybox_sprite@2x.png b/fancybox/fancybox_sprite@2x.png similarity index 100% rename from themes/landscape/source/fancybox/fancybox_sprite@2x.png rename to fancybox/fancybox_sprite@2x.png diff --git a/themes/landscape/source/fancybox/helpers/fancybox_buttons.png b/fancybox/helpers/fancybox_buttons.png similarity index 100% rename from themes/landscape/source/fancybox/helpers/fancybox_buttons.png rename to fancybox/helpers/fancybox_buttons.png diff --git a/themes/landscape/source/fancybox/helpers/jquery.fancybox-buttons.css b/fancybox/helpers/jquery.fancybox-buttons.css similarity index 100% rename from themes/landscape/source/fancybox/helpers/jquery.fancybox-buttons.css rename to fancybox/helpers/jquery.fancybox-buttons.css diff --git a/themes/landscape/source/fancybox/helpers/jquery.fancybox-buttons.js b/fancybox/helpers/jquery.fancybox-buttons.js similarity index 100% rename from themes/landscape/source/fancybox/helpers/jquery.fancybox-buttons.js rename to fancybox/helpers/jquery.fancybox-buttons.js diff --git a/themes/landscape/source/fancybox/helpers/jquery.fancybox-media.js b/fancybox/helpers/jquery.fancybox-media.js similarity index 100% rename from themes/landscape/source/fancybox/helpers/jquery.fancybox-media.js rename to fancybox/helpers/jquery.fancybox-media.js diff --git a/themes/landscape/source/fancybox/helpers/jquery.fancybox-thumbs.css b/fancybox/helpers/jquery.fancybox-thumbs.css similarity index 100% rename from themes/landscape/source/fancybox/helpers/jquery.fancybox-thumbs.css rename to fancybox/helpers/jquery.fancybox-thumbs.css diff --git a/themes/landscape/source/fancybox/helpers/jquery.fancybox-thumbs.js b/fancybox/helpers/jquery.fancybox-thumbs.js similarity index 100% rename from themes/landscape/source/fancybox/helpers/jquery.fancybox-thumbs.js rename to fancybox/helpers/jquery.fancybox-thumbs.js diff --git a/themes/landscape/source/fancybox/jquery.fancybox.css b/fancybox/jquery.fancybox.css similarity index 100% rename from themes/landscape/source/fancybox/jquery.fancybox.css rename to fancybox/jquery.fancybox.css diff --git a/themes/landscape/source/fancybox/jquery.fancybox.js b/fancybox/jquery.fancybox.js similarity index 100% rename from themes/landscape/source/fancybox/jquery.fancybox.js rename to fancybox/jquery.fancybox.js diff --git a/themes/landscape/source/fancybox/jquery.fancybox.pack.js b/fancybox/jquery.fancybox.pack.js similarity index 100% rename from themes/landscape/source/fancybox/jquery.fancybox.pack.js rename to fancybox/jquery.fancybox.pack.js diff --git a/index.html b/index.html new file mode 100644 index 0000000..5ec7081 --- /dev/null +++ b/index.html @@ -0,0 +1,890 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ +
+ + +
+ +

Flutter 打包

app 图标替换

Android 图标更换

    +
  • 替换图中路径的图片资源即可,
  • +
  • 注意图片名字需要是 ic_launcher
  • +
  • 每个尺寸对应不同的文件大小
  • +
+
    +
  • 如果要修改启动图片名称需要先在主配置文件中修改
  • +
+

+

iOS 图标更换

修改app图标名称

    +
  • 找到 Android 主配置文件
  • +
  • 修改 android:label=”app_flutter” 中名称
  • +
+

修改app 的名称

打包

生成key

+ +
+ +
+ +
+ + + +
+ +
+ + +
+ +

Flutter中本地图片资源使用

新建图片文件目录

+

添加图片到项目

手动投入图片到新建的文件目录

+

声明图片资源

找到项目中 pubspec.yaml 文件,找到 assets: 选项 按照注释代码格式声明图片路径

+
1
2
assets:
- lib/assets/ideaOne.png
+

使用图片资源

1
Image.asset('lib/assets/ideaOne.png')
+ +
+ +
+ +
+ + + +
+ +
+ + +
+ +

Flutter页面导航

导航栏的使用

程序主入口

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class MyApp extends StatelessWidget {

// 声明参数
final List<String> items;

// 构造函数
MyApp({Key key, @required this.items}):super(key: key); // 调用父类的方法


// 重写
@override

Widget build(BuildContext context) {

// 返回一窗口
return MaterialApp(
title:'Flutter --',
// 基础组件学习
// home: MyLearningFlutterHome(),

home: FirstScreen(),
);
}
}
+

导航栏页面跳转参数传递

商品类

1
2
3
4
5
6
class Product {
final String title; // 定义商品标题
final String description;
// 定义构造方法
Product(this.title, this.description);
}

+

第一级页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class FirstScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: new Text('第一个页面'),
),

body: Center(
child: RaisedButton(
child: new Text('点击跳转详情页'),
onPressed: (){
Navigator.push(context, new MaterialPageRoute(
builder: (context) => (new SecondScreen())
// builder: (context){
// new SecondScreen();
// }
));
},
),
),
);
}
}

+

详情页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class SecondScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('详情页面')
),

body: Center(
child: RaisedButton(
child: Text('返回上一级页面'),

onPressed: () => (Navigator.pop(context)),

// onPressed: (){
// Navigator.pop(context);
// },
),
),
);
}
}

+

导航栏页面返回反向参数传递

使用异步请求和等待

+

商品列表页面

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class ProductListView extends StatelessWidget {
// 定义变量,里面是一个对象
final List<Product> products;

// 使用构造方法 函数的方式接受参数
ProductListView({Key key, @required this.products}):super(key:key); // 设置必参,调用父类方法

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('商品列表'),
),

// 使用 ListView.builder 动态构建
body: ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(products[index].title),
onLongPress: (){
print("长按事件响应");
},


// 点击时传入参数
onTap: (){
print('点击事件');
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProducterDetailView(product:products[index])
)
);
},
);
},
),
);
}
}
+

商品详情页面点击按钮返回上一级页面,并带有参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class ProducterDetailView extends StatelessWidget {
final Product product;

ProducterDetailView({Key key, @required this.product}):super(key:key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Product ${product.title} 详情页面')
),

body: Center(
// 有多控件
child: Column(
children: <Widget>[
Text('Product ${product.title} desc ${product.description}'),
RaisedButton(
child: Text('back Product List'),
color: Colors.blue,
onPressed: (){
Navigator.pop(context, 'selected Id = ${product.title}');
},
)
],
),
),
);
}
}

+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ +

Flutter中布局

线性布局

主轴、副轴

    +
  • main轴:如果用column组件,垂直就是主轴,如果用Row组件,那水平就是主轴

    +
  • +
  • cross轴:幅轴,是和主轴垂直的方向。用Row组件,那垂直就是幅轴,Column组件,水平方向就是副轴

    +
  • +
+

Row 水平方向布局组件

    +
  • 非自适应布局:根据元素本身大小来布局
  • +
  • 自适应布局:使用 Expanded 布局
  • +
+

非自适应布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class MyRowNoChangeLayout extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Container(
child: Row(
children: <Widget>[
new RaisedButton(
onPressed: (){
print('点击了---- 红色');
},

color: Colors.red,
child: new Text('按钮一红色'),

),

new RaisedButton(
onPressed: (){
print('点击了---- 黄色');
},

color: Colors.yellow,
child: new Text('按钮一黄色'),

),
new RaisedButton(
onPressed: (){
print('点击了---- 绿色');
},

color: Colors.green,
child: new Text('按钮一绿色'),

)
],
),
);
}
}

+

自适应布局

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class MyRowAutoLayout extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Container(
child: Row(
children: <Widget>[
Expanded(
child: new RaisedButton(
onPressed: (){
print('点击了---- 红色');
},

color: Colors.red,
child: new Text('按钮一红色'),
),
),

Expanded(
child: new RaisedButton(
onPressed: (){
print('点击了---- 黄色');
},

color: Colors.yellow,
child: new Text('按钮一黄色'),

)
),

Expanded(
child: new RaisedButton(
onPressed: (){
print('点击了---- 绿色');
},

color: Colors.green,
child: new Text('按钮一绿色'),

)
)
],
),
);
}
}
+

非自动布局和自动布局混合使用

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class MyRowLayout extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Container(
child: Row(
children: <Widget>[
new RaisedButton(
onPressed: (){
print('点击了---- 红色');
},

color: Colors.red,
child: new Text('按钮一红色'),
),

Expanded(
child: new RaisedButton(
onPressed: (){
print('点击了---- 黄色');
},

color: Colors.yellow,
child: new Text('按钮一黄色'),

)
),

new RaisedButton(
onPressed: (){
print('点击了---- 绿色');
},

color: Colors.green,
child: new Text('按钮一绿色'),

)
],
),
);
}
}
+

Column 垂直方向布局组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class MyColunmLayout extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Container(
color: Colors.cyan,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.max,
// 设置主轴的对齐方式
mainAxisAlignment: MainAxisAlignment.center,
// 设置横轴的对齐方式
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new RaisedButton (
onPressed: (){
print('点击了---- 红色');
},

color: Colors.red,
child: new Text('按钮一红色'),
),

new RaisedButton(
onPressed: (){
print('点击了---- 绿色');
},
color: Colors.green,
child: new Text('按钮一绿色'),
),

new Text('文字内容',
textAlign: TextAlign.center,
),
new Text('测试文字',
textAlign: TextAlign.right,
)
],
),
);
}
}
+

层叠布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// stack层叠布局

class MyStackLayout extends StatelessWidget {
@override
Widget build(BuildContext context) {
// 返回一个 Stack 布局
return new Stack(
// 设置位置
alignment: const FractionalOffset(0.5, .9),
// 圆角组件
children: <Widget>[
new CircleAvatar(
backgroundImage: new NetworkImage('https://p.ampmake.com/mall/product/26153760-b964-45d5-b811-7f2bf5844102.jpg'),
// backgroundImage: new Image.network('https://p.ampmake.com/mall/product/26153760-b964-45d5-b811-7f2bf5844102.jpg'),
radius: 100.0,
),

// 容器组件
new Container(
// 设置容器掩饰
decoration: BoxDecoration(
color: Colors.blue,
// border
border: Border.all(
color: Colors.red,
width: 3,
),
),

padding: const EdgeInsets.all(10.0),

child: new Text('stack 布局',
textAlign: TextAlign.center,
maxLines: 1,
// 文字样式
style: TextStyle(
color: Colors.yellow,
textBaseline: TextBaseline.ideographic,
// 下划线
decoration: TextDecoration.underline,
// 下划线颜色
decorationColor: Colors.orange,
// 下划线样式
decorationStyle: TextDecorationStyle.double,
),
),
)
],

);
}
}
+

Positioned组件

    +
  • bottom: 距离层叠组件下边的距离
  • +
  • left:距离层叠组件左边的距离
  • +
  • top:距离层叠组件上边的距离
  • +
  • right:距离层叠组件右边的距离
  • +
  • width: 层叠定位组件的宽度
  • +
  • height: 层叠定位组件的高度
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class MyMoreStackLayout extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Stack(
children: <Widget>[
new CircleAvatar(
backgroundImage: NetworkImage('https://p.ampmake.com/mall/product/26153760-b964-45d5-b811-7f2bf5844102.jpg'),
radius: 100.0,
),

new Positioned(
top: 5,
left: 50,
// right: 5,
width: 100,
child: new RaisedButton(
onPressed: (){
print("多个组件层叠布局");
},

color: Colors.deepOrange,
child: Text('测试按钮'),
),
),

new Positioned(
bottom: 10,
right: 10,
left: 10,
child: new Text('flutter 多个试图 stack布局',
maxLines: 1,
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.lightGreen,
fontSize: 20,
fontStyle: FontStyle.italic
),
)
)
],
);
}
}
+

Card 布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
class MyCardLayout extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Card(
child: Column(
children: <Widget>[
new ListTile(
title: new Text('card 布局1'),
subtitle: new Text('子标题1',
textAlign: TextAlign.right,
style: TextStyle(
color: Colors.lightGreen
),
),
leading: new Icon(Icons.account_balance, color: Colors.purple)
),

new Divider(
color: Colors.cyan
),

new ListTile(
title: new Text('card 布局2'),
subtitle: new Text('子标题2',
textAlign: TextAlign.right,
style: TextStyle(
color: Colors.lightGreen
),
),
leading: new Icon(Icons.account_box, color: Colors.yellow)
),

new Divider(
color: Colors.red,
),

new ListTile(
title: new Text('card 布局3'),
subtitle: new Text('子标题3',
textAlign: TextAlign.right,
style: TextStyle(
color: Colors.lightGreen
),
),
leading: new Icon(Icons.account_circle, color: Colors.red)
),

new Divider(
color: Colors.orangeAccent,
),

new ListTile(
title: new Text('card 布局4'),
subtitle: new Text('子标题4',
textAlign: TextAlign.right,
style: TextStyle(
color: Colors.lightGreen
),
),
leading: new Icon(Icons.camera_enhance, color: Colors.brown)
)
],
),
);
}
}
+ +
+ +
+ +
+ + + +
+ +
+ + +
+ + +

+ GridView组件使用.md +

+ + +
+ +
+ +

GridView 组件使用

    +
  • padding: const EdgeInsets.all(10.0) 设置边距为10
  • +
  • crossAxisCount:横轴子元素的数量crossAxisCount。
  • +
  • mainAxisSpacing:主轴方向的间距。
  • +
  • crossAxisSpacing:横轴元素的间距。
  • +
  • childAspectRatio:元素的宽高比
  • +
+

简单例子代码

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class MyGridView2 extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return Container(
child: new GridView(
// 设置边距
padding: const EdgeInsets.all(10.0),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(

// 横轴 数量
crossAxisCount: 3,
crossAxisSpacing: 10,
mainAxisSpacing: 10.0,
// 宽高比:默认是1.0
childAspectRatio: 1.5,
),

children: <Widget>[
new Image.network('https://github.com/Jinxiansen/SwiftUI/raw/master/images/example/Text.png', fit: BoxFit.cover),
new Image.network('https://p.ampmake.com/mall/product/9666f00e-f02a-46ce-9b7c-090bf6aba9aa.png', fit: BoxFit.cover),
new Image.network('https://p.ampmake.com/mall/product/d6fdc891-eec3-47ce-b837-df726c73a9b8.png', fit: BoxFit.cover),
new Image.network('https://p.ampmake.com/mall/product/0cccf224-57d1-4a3e-bf06-9df5fed0f4e0.png', fit: BoxFit.cover),
new Image.network('https://p.ampmake.com/mall/product/2bf5c0d7-dc3a-45d2-ab1b-40fe737aa79a.jpg', fit: BoxFit.cover),
new Image.network('https://p.ampmake.com/mall/product/26153760-b964-45d5-b811-7f2bf5844102.jpg', fit: BoxFit.cover),
new Image.network('https://p.ampmake.com/mall/product/a20bb53c-b710-4b39-9607-91bef06ee54e.png', fit: BoxFit.cover),
],
)
);
}
}
+

以前的写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class MyGridView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
child: new GridView.count(
padding: const EdgeInsets.all(15.0),
// 横轴 item 数量
crossAxisCount: 3,
// 横轴 间距
crossAxisSpacing: 10.0,
children: <Widget>[
const Text('我是第一个'),
const Text('我是第二个'),
const Text('我是第三个'),
const Text('我是第四个'),
const Text('我是第五个'),
const Text('我是第六个'),
const Text('我是第七个'),
],
),
);
}
}

+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ +

第十四章 第三方库

+

AFNetworking

框架图

会话NSURSession

+

主要类关系图

+

AFURLSessionManager

    +
  • 创建和管理 NSURLSession, NSURLSessionTask
  • +
  • 实现NSURLSessionDelegate 等协议的代理方法
  • +
  • 引入AFSecurityPolicy 保证请求安全,https 请求时 证书检验,公钥验证
  • +
  • 引入AFNetworkReachabilityManager 监控网络状态
  • +
+

SDWebImage

架构图

+

+

加载图片流程

+

Reactive Cocoa

+

RACStream

+

+

AsyncDisplayKit

主要解决的问题

+

+

基本原理

+

+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ +

OC 语言特性

分类

原理

由运行时来决议的,不同分类当中含有同名方法 谁最终生效取决于谁最终参与编译,最后参编译的同名分类方法会最终生效,假如分类方法中添加的方法和宿主类中的某一个方法名相同,分类中的方法会覆盖宿主类中的同名方法,覆盖是指在消息传递过程中优先查找数组靠前的元素,如果查找到了同名方法就直接调用,实际上宿主类的同名方法实现仍然是存在的,我们可可以通过一些手段调用到原有类的同名方法的实现

+

使用场景

    +
  • 申明私有方法,分类的 .m 文件中申明私有方法啊,对外不暴露
  • +
  • 对类中的代码进行抽取分解
  • +
  • 把Framework的私有方法公开化
  • +
+

特点

    +
  • 运行时决议,运行时通过runtime 才把分类中的方法添加到了宿主类上
  • +
  • 可以为系统类添加方法
  • +
  • 有多个分类中存在同名方法时,最后编译的方法会最先生效
  • +
  • 分类中有和宿主类同名方法时,底层中通过内存copy将分类方法“覆盖”宿主类方法
  • +
  • 名字相同的分类编译会报错
  • +
+

分类中可以添加的内容

    +
  • 实例方法
  • +
  • 类方法
  • +
  • 协议
  • +
  • 属性,实际上只是声明了 getter / setter 方法,并没有添加成员变量
  • +
+

关联对象技术

关联对象由 AssocicationsManager 管理并在 AssociationsHashMap 上存储
所有对象的关联内容都在同一个全局容器中

+
    +
  • 给分类添加成员变量,通过关联对象的方式
  • +
+

扩展

使用场景

    +
  • 声明私有属性
  • +
  • 声明私有方法
  • +
  • 声明私有成员变量
  • +
+

特点

    +
  • 编译时决议
  • +
  • 只以声明的形式存在,多数情况下寄生在宿主类的.m中
  • +
  • 不能为系统添加扩展
  • +
+

代理

    +
  • 代理设计模式,传递方式是一对一
  • +
  • 使用weak 避免循环引用
  • +
+

通知

使用观察者模式来实现的用于跨层传递消息的机制

+

KVO

KVO 是系统对观察者模式的又一实现,使用isa 混写技术(isa - swizzling)来动态运行时为某一个类添加一个子类重写了它的setter 方法,同时将原有类的isa指针指向了新创建的类上

+
    +
  • KVO 是OC对观察者模式的又一实现
  • +
  • apple 使用isa 混写技术(isa - swizzling)来实现KVO
      +
    • 给我A类注册一个观察者时本质上是调用系统的 Observer for keyPath 方法,系统会创建一个NSKVONotifiying_A 的类
    • +
    +
  • +
  • 使用 setter 方法设置值KVO才能生效
  • +
  • 使用setValue:forKey: 改变值KVO才能生效
  • +
  • 使用下划线的成员变量直接修改值需要手动添加KVO才能生效(增加 willChangeValueForKey 和 didChangeValueForKey 两个方法)
  • +
+

KVC

apple提供的键值编码技术

+
    +
  • (nullable id)valueForKey:(NSString *)key;
  • +
  • (void)setValue:(nullable id)value forKey:(NSString *)key;

    +

    以上两个方法中的key是没有任何限制的,只要知道对应的成员变量名称,就可以对私有的成员变量设置和操作,这违背了面向对象的编程思想

    +
  • +
+

(void)setValue:(nullable id)value forKey:(NSString *)key; 方法调用流程

+
    +
  • 先判断是否有跟key 相关的setter 方法,如果有就直接调用,结束调用
  • +
  • 如果没有再判断是否存在实例变量,如果存在直接给赋值,结束调用
  • +
  • 如果说实例变量不存在,会去调用 - (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key; 抛出异常
  • +
+

属性关键字

读写权限

    +
  • readonly

    +
  • +
  • readwrite 系统默认

    +
  • +
+

原子性

    +
  • atomic (系统默认)保证赋值和获取(对成员属性的直接获取和赋值)时线程安全,并不代表操作和访问,如果atomic修饰的一个数组,只能保证独具改数组的读取和赋值时线程安全,并不能保证对数据操作(增加/删除数组元素)时是安全的
  • +
  • nonatomic
  • +
+

引用技术

    +
  • assign
      +
    • 修饰基本数据类型,
    • +
    • 修饰对象时不改变其引用计数,
    • +
    • 会产生悬垂指针
    • +
    +
  • +
  • weak
      +
    • 不改变被修饰对象的引用计数,
    • +
    • 所指对象被释放之后指针自动置为nil
    • +
    +
  • +
+

两者区别:

+
    +
  • weak可以修饰对象,而assign 既可以修饰对象也可以修饰基本数据类型
  • +
  • assign 修饰的对象,对象释放后指针仍然指向原对象的内存地址,而weak 修饰的对象,释放后会自动置为nil
  • +
+

问题1

+

weak 修饰的对象为什么释放后会自动置为nil

+

问题2

+

@property (copy) NSMutableArray *array; 有什么问题

+
    +
  • 被copy修饰后,如果赋值过来的是NSMutableArray,copy之后就是NSArray,如果赋值过来的是NSAarray,copy 之后仍然是NSArray,有可能会调用array的添加元素方法会导致crash
  • +
+

copy 关键词

+
    +
  • 浅拷贝:内存地址的复制,让目标对象指针和源对象指向同一块内存空间,
      +
    • 会增加对象的引用计数
    • +
    • 并没有一个新的内存分配
    • +
    +
  • +
+
    +
  • 深拷贝:目标对象指针和源对象指针分别指向两块内容相同的内存空间
      +
    • 不会增加源对象的引用计数
    • +
    • 有新对象的内存分配
    • +
    +
  • +
+

MRC 重写 retain修饰的变量的setter 方法

1
2
3
4
5
6
7
8

- (void)setObj:(id)obj {
if (_obj != obj) {
[_obj release];
}
_obj = [obj retain];

}
+

判断是为了防止异常处理,如果不做 if 判断,当传入的 obj 对象正好是原来的_obj 对象,对原对象尽行releas 操作,实际上也会对传入的对象进行releas 操作进行释放,此时如果再通过obj指针访问废弃的对象时就会导致carsh

+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ +

第十章 网络请求相关

+

HTTP协议

超文本传输协议

+

请求/响应报文

请求报文:

+
    +
  • 请求行
    方法 URL 协议版本(1.1版本) CRLF(回车)

    +
  • +
  • 首部字段区域
    首部字段名(key) :值(value )CRLF
    首部字段名(key) :值(value )CRLF

    +
  • +
  • 实体主体
    get 请求没有
    post 有实体主体

    +
  • +
+

响应报文:

+
    +
  • 响应行
    版本 状态码 状态码描述(短语) CRLF

    +
  • +
  • 首部字段区

    +
  • +
  • +
  • 响应实体主体
  • +
+

连接建立流程

三次握手和四次挥手

+

三次握手

+
    +
  • 客户端 发送 syn 同步报文到Server,
  • +
  • server 端收到syn报文后会,链接建立,server 端会返回一个 syn,ack 报文给客户端,
  • +
  • 客户端收到 server 端的ask 报文后,会再次回应一个确认ack 的报文
  • +
+

数据请求

+
    +
  • 接下来在已经建立的tcp 通道上进行http 的请求报文
  • +
  • server 端回复给客户端一个http响应报文
  • +
+

四次挥手

+
    +
  • 客户端发送 fin 终止报文到server 端
  • +
  • server 端收到报文后会回回复一个ack 报文到客户端
  • +
  • 客户端到server 端的tcp的链接已经断开,但是 server 端到客户端的链接还会传输数据
  • +
  • server 端向客户端发送一个fin ack 报文 ,然后客户端回应给server 一个ack 报文,释放链接
  • +
+

HTTP 的特点

    +
  • 无连接
    非持久链接 需要多次断开/建立链接,就会经历 三次握手,四次挥手

    +

    HTTP 的持久链接: 多个http请求在同一个tcp 通道上, 一定时间内不会断开tcp通道,提高了网络请求的效率

    +

    头部字段:

    +
    coonection : keep-alive 客户端是否采用持久链接
    +time :20 多长时间有效
    +max:10 最多发生多少个http请求和响应对
    +
  • +
  • 无状态
    同一个用户多次发送http请求,server 端是不知道是同一个用户的,server 端通过 Cookie/Session 规避这个问题

    +
      +
    • cookie
      cookie 主要是用来记录用户状态,区分用户,cookie由server端返回给客户端,cookie 状态保存在客户端

      +

      保证cookie 的安全

      +
        +
      • cookie 加密处理
      • +
      • 只在https 上携带Cookie
      • +
      • 设置Cookie为 httpOnly,防止跨站脚本攻击
      • +
      +
    • +
    • session 用来记录用户的状态,区分用户,状态存放在服务端

      +
    • +
    +
  • +
+

HTTPS 与网络安全

HTTPS 都使用了那些加密手段,为什么?

+
    +
  • 建立链接过程使用非对称加密,因为耗时
  • +
  • 后续通信数据传输过程中使用对称性加密
  • +
+

对称性加密

+

TCP: 传输控制协议

    +
  • 面向链接
    TCP 是全双工的所以数据传输之前需要三次握手,传输完毕需要四次挥手,
  • +
  • 可靠传输

    +
      +
    • 无差错
    • +
    • 按序到达
    • +
    • +
    +
  • +
  • 面向字节流

    +

    发送方发送数据时不管一次性提交给TCP的缓冲是多大的数据,TCP本身会根据实际情况来划分数据包再发送给接受数据方

    +
  • +
  • 流量控制

    +

    滑动窗口协议

    +
  • +
+

UDP : 用户数据协议

    +
  • 无连接
    发送数据时不需要建立链接,传输完毕也不需要释放链接

    +
  • +
  • 尽最大努力交付
    是不保证可靠传输的,

    +
  • +
  • 面向报文
    既不合并,也不拆分

    +
  • +
+

问题

GET 和post 请求的区别

    +
  • GET 获取资源的

    +
      +
    • 安全的: 不因该引起Server 端的任何状态变化的
    • +
    • 幂等的:同一个请求方法执行多次和执行一次的效果完全相同
    • +
    • 可缓存的:请求是可以缓存的,代理服务器可以缓存
    • +
    • 请求参数以? 分割拼接到URL 后面
    • +
    +
  • +
  • post 请求处理资源

    +
      +
    • 不安全的:
    • +
    • 非幂等的
    • +
    • 不可缓存的
    • +
    +
  • +
+

GET 请求参数有长度限制,是2048 个字符,post 一般没有限制

+

HTTPS 链接建立流程

客户端会发送服务端一个支持的加密算法列表
包括 TLS 的版本号 + 随机数C,
服务端再回给客户端一个证书
包括 商定的加密算法 后续首先通过非对称加密进行对称性加密的密钥传输,http的网络就通过被非对称密钥保护的对称密钥访问和传输数据

+

TCP 和 UDP的区别

tcp是面向链接的,并且支持可靠的传输,支持面向字节流, tcp 提供了流浪上的控制和拥塞的控制
UDP 提供了简单的复用/分用及差错检测的传输层的功能,

+

状态吗有哪些

    +
  • 1XX
  • +
  • 2XX:成功
  • +
  • 3XX:重定向
  • +
  • 4XX:客户端请求错误
  • +
  • 5XX: 服务器端请求错误
  • +
+

为什么需要三次握手,两次行不行?

解决同步请求链接建立时超时的情况,如果只有两次握手,

+

持久链接怎么判断一个请求是否结束

    +
  • 请求/响应报文的头部字段
  • +
+

响应报文 content-length: 1024 头部字段是由server 端返回数据大小,客户端根据所接收的数据的字节数是否达到了头部字段 cotent-length 的大小判断

+
    +
  • 通过post请求时server 端返回数据有可能需要多次响应才能返回,此时需要根据chunked 字段名 是否为空来判断
  • +
+

Charels 抓包原理是什么?

中间人攻击

+

正常步骤是 客户端 —- server

+

抓包:发送请求和响应请求都可以通过中间人修改再返回给某一端

+

客户端 —中间人—- server

+

DNS 解析

域名到IP地址的映射,DNS解析请求采用UDP数据报,且明文

+

NDS 解析存在的常见问题

    +
  • DNS 劫持问题
    钓鱼网站
  • +
  • DNS 解析转发问题
  • +
+

DNS 劫持和HTTP 的关系时怎么样的

没有关系,

+
    +
  • DNS 解析发在HTTP 建立连接之前
  • +
  • DNS 解析请求使用UDP数据报, 端口是53,跟HTTP没有任何关系
  • +
+

解决DNS 劫持问题

+
    +
  • httpDNS
  • +
  • 长连接
  • +
+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ +

第十一章 设计模式

+

六大设计原则

单一职责原则:一个类只负责一件事

+

开闭原则: 对修改关闭,对扩展开放

+

接口隔离原则:使用多个专门的协议,而不是一个庞大臃肿的协议,iOS系统的UITableView的数据和代理协议分开

+

依赖倒置原则:抽象不应该依赖于具体实现,具体实现可以依赖于抽象

+

里氏替换原则:父类可以被子类无缝替换,且原有功能不受任何影响,iOS系统 的 KVO 机制遵守了这一原则,当调用 addObserver 方法时,因为系统在动态运行时为我们创建了一个子类,直观感受是仍然使用的是父类,实际上系统已经替换为创建的子类,所以KVO 机制 不但使用了管擦这模式,并且遵从了 里氏替换原则

+

迪米特法则: 一个对象应对其他对象有尽可能少的了解,平时所说的高内聚,低耦合

+

常用设计模式

责任链

有三个业务分别为A/B/C,以前的调用顺序为 A -> B -> C ,现在顺序有调整,

+

定义某一个类,有一个成员变量他的类型跟原有类类型一致,组成一个责任链,让处理具体业务的责任者去处理

+

+

桥接模式

业务解耦的问题:同一个列表和多套数据显示,多套数据通过后端来控制是共存,

+

+

类构成

+

抽象类A ——> class B

+

+

适配器模式

一个现有类需要适应变化的问题, 年代比较久远的类或者对象

+

类构成

+
    +
  • 对象适配
  • +
  • 类适配
  • +
+

单例模式

命令模式

行为参数化

+

降低代码重合度

+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ +

第十二章 架构和框架

+

目的

+
    +
  • 实现模块化
  • +
  • 分层
  • +
  • 解耦
  • +
+

图片缓存

图片缓存框架设计方案?

+

图片缓存时内存设计上需要考虑的问题

    +
  • 内存存储的size

    +
      +
    • 10kb的图片开辟的内存空间 50 张
    • +
    • 100kb图片的 20 张
    • +
    • 100kb 以上的 10 张
      通过队列的方式存储,先进先出的方式淘汰
    • +
    +
  • +
  • 淘汰策略

    +
      +
    • 以队列先进先出的方式淘汰
    • +
    • 已LRU 算法 (如 30 min 之内是否使用过)
    • +
    +
  • +
+

磁盘设计考虑的问题

    +
  • 存储方式
  • +
  • 大小限制
  • +
  • 淘汰策略(超过 7 天)
  • +
+

网络部分的设计需要考虑的问题

    +
  • 图片请求最大并发量
  • +
  • 请求超时策略,一旦超时了,可以重试,如果重试2次失败,是否还下载
  • +
  • 请求优先级,下载或者缓存的图片是否紧急需要
  • +
+

图片通过什么方式进行读写,过程是怎样的?

    +
  • 以图片的url 的单向哈希值作为key 存储
  • +
  • 读取过程
  • +
+

+

图片解码

    +
  • 对于不同格式的图片,解码采用什么方式来做?

    +
      +
    • 应用策略模式对于不同图片格式进行解码
    • +
    +
  • +
  • 在那个阶段做图片解码处理?

    +
      +
    • 再磁盘读取后未解码或者网络请求返回后,解码后再放到内存中,可以减轻主线程的压力,解码在主线程中
    • +
    +
  • +
+

线程处理

+

阅读时长统计

怎样设计一个时长统计框架?

+

为何要有不同类型的记录器,处于什么考虑?

基于不同分类场景提供的关于记录的封装、适配

+

记录的数据由于程序杀死或者断电,关机等丢失,你是怎样处理的?

    +
  • 定时写磁盘
  • +
  • 限定内存缓存条数,超过该条数就写如磁盘
  • +
+

记录器上传 中延时上传的具体场景有哪些?上传时机如何把握?

上传时机

+
    +
  • 立刻上传
  • +
  • 延时上传
  • +
  • 定时上传
  • +
+

延时上传场景

+

统计结果立即上传会重复调用接口导致资源浪费

+
    +
  • 前后台切换
  • +
  • 从无网到有网的变化时
  • +
  • 通过其他的接口捎带着上传
  • +
+

复杂页面的架构

MVVM 框架思想

+

RN 的数据流思想

+

任何一个子节点是没有权力做自己的变化更新的,它必须把这个消息传递给根结点,根结点通过自顶向下的遍历查找需要更新的节点

+

系统UIView 更新机制的思想

AsyncDisplayKit 关于预排版的设计思想

客户端整体架构

需要独立于APP 的通用层
通用的业务层
中间层
业务层

+

+

业务之间的解耦通信方式

+
    +
  • OpenURL
  • +
  • 依赖注入方式, 在中间层通过代理 的方式实现业务A 和业务B之间的通信
  • +
+ + +
+ +
+ +
+ + + + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/themes/landscape/source/js/script.js b/js/script.js similarity index 100% rename from themes/landscape/source/js/script.js rename to js/script.js diff --git a/package.json b/package.json deleted file mode 100644 index d779c33..0000000 --- a/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "hexo-site", - "version": "0.0.0", - "private": true, - "hexo": { - "version": "3.9.0" - }, - "dependencies": { - "hexo": "^3.8.0", - "hexo-deployer-git": "^0.3.1", - "hexo-generator-archive": "^0.1.4", - "hexo-generator-category": "^0.1.3", - "hexo-generator-index": "^0.2.0", - "hexo-generator-search": "^2.2.1", - "hexo-generator-tag": "^0.2.0", - "hexo-helper-post-top": "0.0.1", - "hexo-helper-qrcode": "^1.0.2", - "hexo-renderer-ejs": "^0.3.0", - "hexo-renderer-marked": "^0.3.0", - "hexo-renderer-stylus": "^0.3.1", - "hexo-server": "^0.2.0", - "npm": "^5.7.1" - } -} \ No newline at end of file diff --git a/page/2/index.html b/page/2/index.html new file mode 100644 index 0000000..d93a1e8 --- /dev/null +++ b/page/2/index.html @@ -0,0 +1,806 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + + + + + + + + +
+ +
+ + +
+ +

内存管理

内存布局

+
    +
  • stack 栈是从高地址向低地址扩展,所以栈是向下增长的,对象和block copy 后都会放在堆上
  • +
  • heap 堆区向上增长
  • +
+
    +
  • 散列表方式

    +
      +
    • 自旋锁
        +
      • 自旋锁是“忙等” 的锁
      • +
      +
    • +
    • 引用计数表
    • +
    • 弱应用表
    • +
    +
  • +
+

内存管理方案

    +
  • 小对象使用 TaggedPointer
  • +
  • 64 位架构下面使用的是 NONPONINT_ISA 内存管理方案,非指针型的 isa,内部存储了一些
  • +
  • 散列表结构 (Side Tables() 结构)
      +
    • 自旋锁
    • +
    • 引用计数表
    • +
    • 弱引用表
    • +
    • Side Tables() 结构
    • +
    +
  • +
+

为什么不是一个SideTable,而是多个组成了Side Tables

如果所有对象的存储都放在一张大表当中,因为每个对象是在不同的线程中创建的,要操作其中一个对象时,需要将表加锁处理保证数据安全,要等锁释放之后才能操作下一个对象,此时存在效率问题,引入分离锁技术方案

+
    +
  • 分离锁:把引用计数表分成多张表进行,
  • +
  • Side Tables 本质是一张Hash 表
  • +
+

+

自旋锁

是“忙等”的锁,如果当前锁已被其他线程获取,当前线程会不断探测是否被释放,会第一时间获取,而其他锁比如信号量当它获取不到锁时,会把自己的线程阻塞休眠,等到其他线程释放这个锁时再唤醒这个锁

+
    +
  • 适用于轻量访问
  • +
+

引用计数表

使用过Hash表来实现,hash查找提高了查找高效率,插入和获取是通过Hash 算法来是实现的,避免了for循环

+

alloc 实现

经过一系列调用,最终调用了C函数 calloc, 并没有引用计数+1

+

retain 实现

底层经过了2次Hash表查找

+

+

release 实现

和 retain 实现相反

+

dealloc 实现原理

+

+

objc_destructInstance() 实现

+

clearDeallocating() 实现

+

MRC

手动引用计数

+

ARC

    +
  • 编译器在对应位置自动插入retain 和 release 操作,并和runtime协作达到自动引用计数管理内存的效果

    +
  • +
  • ARC 中禁止手动调用 retain/release

    +
  • +
  • ARC 中新增 weeak、strong 等关键字
  • +
+

弱引用管理

被声明为__weak 的对象指针经过编译后会进过调用以下方法,在最终的weak_register_no_lock() 方法中进行弱引用变量的添加。添加的位置是通过Hash算法查找的

+

+

weak 修饰的对象,释放时置为nil 如何实现的

当一个对象被 dealloc 之后,在dealloc内部实现当中会调用弱引用清除的相关函数,在相关函数当中会根据当前对象指针查找弱引用表,把当前对象下对应的弱应用都取出,遍历弱应用指针并置为nil。

+

自动释放池

是以栈为结点通过双向链表的形式组合而成,和线程一一对应

+

Autoreleasepool

+

循环引用

自循环引用

一个对象中有拥有一个 强持有他的 obj,如果给 obj 赋值为原对象就会造成自循环引用

+

相互循环引用

    +
  • 代理
  • +
  • Block
  • +
  • NSTimer
  • +
  • 大环多循环
  • +
+

多循环引用

+

解决循环引用的方案

__weak

解决相互循环引用

+

__block

+

__unsafe_unretained

一般不建议使用

+

+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ +

Runtime 相关问题

数据结构

objc_object 结构体

objc_class

对象、类对象、元类对象的区别

    +
  • 类对象存储实例方法列表等信息
  • +
  • 元类对象存锤类方法列表等信息
  • +
+

消息传递

void objc_msgSend(void /* id self, SEL op, ... */)

+

经过编译器转变后
[self class] <==> objc_msgSend(self, @selector(class))
第一个参数是消息传递的接收者self, 第二个参数是传递的消息名称(选择器)

+

void objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */)
第一个参数是一个结构体指针

+

//
struuch objc_super {
__unsafe_unretained id receiver; // 就是当前的对象
}

+

[superclass] <==> objc_msgSendSuper(super, @selector(class))

+

无论调用[super class] 还是 [self class] 最终这条消息的接收者都是当前对象

+

图1

+

+

消息传递过程

图2

+

+

问题1

+

+

打印结果都是 Phone

+

原因:

+
    +
  • 无论调用[super class] 还是 [self class] 最终这条消息的接收者都是当前对象
  • +
  • [self class] 在Phone的类对象中查找class 方法,没有去父类查找,父类也没有,如图1顺次往上查找到根类,调用根类中class 的具体实现
  • +
  • [super class]只是跨越了当前类,直接从当前类的父类中查找 class 方法,父类中没有class 方法,如图1所示只能继续往上查找到根类对象,在NSObject 中有class 方法实现,所以接受者任然是当前的对象
  • +
+

缓存查找

根据给定的SEL(方法选择器)通过一个函数来映射出bucket_t 在数组当中的位置,本质上是哈希查找

+

+

当前类中查找

当前类中存在着对应的方法列表

+
    +
  • 对于已经排序好的列表,采用二分查找算法查找对应的执行函数
  • +
  • 对于没有排序的列表,采用一般遍历查找方法查找对应的执行函数
  • +
+

父类逐级查找

图3

+
    +
  • 最关键是通过当前类的 superclass 成员变量去查找父类,或者访问父类
  • +
  • curClass 的父类是否为 nil?如果是NSObject 类,他的父类为空,所以会结束查找
  • +
  • 如果当前类有父类就要去该父类的缓存中查找,如果在缓存中命中,那么就结束查找,如果缓存中没有,就需要遍历当前类的父类的方法列表,如果没有就遍历父类的父类查找,沿着superclass指针逐级向上查找,直到查找到NSObject,再去Superclass。如果还是没有为nil时就结束父类逐级查找
  • +
+

消息转发

resolveInstanceMethod:

    +
  • 该方法是类方法不是实例方法,参数是SEL 方法选择器类型
  • +
  • 返回值是一个BOOL值
  • +
  • 告诉系统是否要解决当前实例方法的实现
  • +
  • 如果返回YES,结束消息转发
  • +
  • 如果返回NO,系统会给第二次机会处理这个消息,会回掉forwardingTargetForSelector:
  • +
+

forwardingTargetForSelector:

    +
  • 参数是 SEL 方法选择器
  • +
  • 返回值是 id 类型,说明有哪个对象来处理,转发的对象是谁,如果返回了转发目标就会结束当前调用
  • +
  • 如果返回为nil,系统会调用 methodSignatureForSelector:方法,最后一次机会处理这个消息
  • +
+

methodSignatureForSelector:

    +
  • 参数是 SEL 方法选择器
  • +
  • 返回值是一个对象,实际上这个对象是对methodSignatureForSelector 方法的参数,参数个数,参数类型和返回值的包装
  • +
  • 如果返回一个方法签名的话,就会调用 forwardInvocation: 方法
  • +
  • 如果返回为nil,标记为消息无法处理,程序crash
  • +
+

forwardInvocation:

    +
  • 如果此方法不能处理消息,程序crash
  • +
+

代码示例

+

.h 文件

+
1
2
3
4
@interface RunTimeObject : NSObject
// 只声明,不实现,验证消息转发机制
- (void)test;
@end
+

.m 文件

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
@implementation RunTimeObject

// 实现消息转发流程 中的方法
/**
是否要解决当前实例方法的实现
*/
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(test)) {
NSLog(@"resolveInstanceMethod:");
// 如果返回YES,消息转发就会结束
// return YES;

return NO;
} else {

NSLog(@"super resolveInstanceMethod:");
// 返回父类的默认调用
return [super resolveInstanceMethod:sel];
}
}


/**
- 参数是 SEL 方法选择器
- 返回值是 id 类型,说明有哪个对象来处理,转发的对象是谁,如果返回了转发目标就会结束当前调用
- 如果返回为nil,系统会调用 methodSignatureForSelector:方法,最后一次机会处理这个消息
*/
- (id)forwardingTargetForSelector:(SEL)aSelector {
NSLog(@" forwardingTargetForSelector: ");
return nil;
}

/**
- 参数是 SEL 方法选择器
- 返回值是一个对象,实际上这个对象是对`methodSignatureForSelector` 方法的参数,参数个数,参数类型和返回值的包装
- 如果返回一个方法签名的话,就会调用 forwardInvocation: 方法
- 如果返回为nil,标记为消息无法处理,程序crash

*/
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if (aSelector == @selector(test)) {
NSLog(@"methodSignatureForSelector:");
/**
v: 表示这个方法返回值是 void

固定参数@: 表示参数类型是 id, 即self
固定参数`:` : 表示参数类型是选择器类型,即 @selector(test)
*/
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
} else {

NSLog(@"super methodSignatureForSelector:");
// 返回父类的默认调用
return [super methodSignatureForSelector:aSelector];
}
}


- (void)forwardInvocation:(NSInvocation *)anInvocation {
NSLog(@"forwardInvocation:");
}

@end
+

调用

+
1
2
3
4
- (void)runTiemTest {
RunTimeObject *obj = [[RunTimeObject alloc] init];
[obj test];
}
+

结果:

+
1
2
3
4
5
resolveInstanceMethod:
forwardingTargetForSelector:
methodSignatureForSelector:
super resolveInstanceMethod:
forwardInvocation:
+

为类动态添加方法

methond Swizzling

+

使用场景

    +
  • 页面中的进出添加统计信息,使用 methond Swizzling 替换viewillAppear 方法
  • +
+

动态添加方法

    +
  • perforSelector: 实际上是考察class_addMethod 的方法使用
  • +
+

动态方法解析

    +
  • @dynamic关键字
  • +
  • 动态运行时语言将函数决议推迟到运行时
  • +
  • 编译时不生成setter/getter 方法,而是在运行时才添加具体的执行函数
  • +
+

问题

[obj foo] 和 obj_msgSend()函数之间有什么关系?

    +
  • [obj foo]经过编译器处理过后会变成 obj_msgSend()有两个参数,一个是obj,第二个参数是foo 选择器
  • +
+

runtime 如何通过Selector找到对应的IMP地址的?

消息传递机制

+

能否向编译后的类增加实例变量

编译之前就完成了实例变量的布局,
runtime_r_t 表示是readonly 的,编译后的类是不能添加实例变量的,可以向动态添加的类中添加实例变量

+ + +
+ +
+ +
+ + + + + + + +
+ +
+ + +
+ +

Blocks

Block 是什么?

Blocks 是 C 语言的扩充功能,将函数及其执行上下文封装起来的对象,带有自动变量(局部变量)的匿名函数

+
    +
  • 因为block 底层C++ 结构体中存在着 isa 指针,所以是一个对象
  • +
  • 因为block 底层C++ 结构体中存在着一个函数指针 FuncPtr,所以是一个函数
  • +
+

什么是Block 调用

Block 的调用就是函数的调用
在终端使用 clang 编译之后查看文件内容可得出以下结论:

+
    +
  • 对其进行一个强制转换,转换之后取出成员变量 FuncPtr
  • +
+

截获变量

以下代码运行结果是 12

+

+
原因:block 对基本数据类型的局部变量会截获其值,在定义 block 时以值的方式传到了block 对应的结构体当中,而调用block 时直接使用的是block对应结构体当中已经传过来的那个值,所以值为12
+
    +
  • 局部变量截获

    +
      +
    • 基本数据类型的局部变量截获其值
    • +
    • 对象类型的局部变量连同所有权修饰符一起截获
    • +
    +
  • +
  • 局部静态变量: 已指针形式截获

    +
  • +
  • 全局变量:不截获

    +
  • +
  • 静态全局变量: 不截获

    +
  • +
+

以下代码运行结果是 8

+

+
原因:block 对局部静态变量会以指针形式截获,所以值为8
+

__block 修饰符

    +
  • __block 修饰后的变量最后变成了对象,底层c++ 结构体中存在isa指针
  • +
  • 一般情况下,对被截获变量进行赋值操作需要添加block 修饰符,而操作是不需要添加block 修饰符的
  • +
  • 不需要__block 修饰符,全局变量和静态全局变量 block 是不截获的,所以不需要修饰,静态局部变量是通过指针截获的,修改外部的变量,也不需要添加
      +
    • 静态局部变量
    • +
    • 全局变量
    • +
    • 静态全局变量
    • +
    +
  • +
+

不需要添加__Block 修饰符

+
1
2
3
4
5
6
7
8
- (void)test {
NSMutableArray *array = [NSMutableArray array];
void(^block)(void) = ^{
[array addObject:@1234];
};

block();
}
+

需要添加__Block 修饰符

+
1
2
3
4
5
6
7
8
- (void)test {
__block NSMutableArray *tempArray = nil;
void(^block)(void) = ^{
tempArray = [NSMutableArray array];
};

block();
}
+

以下代码运行结果是 8

+
1
2
3
4
5
6
7
8
- (void)test__Block3 {
__block int multiplier = 6;
int(^Block)(int) = ^int(int num){
return multiplier * num;
};
multiplier = 4;
NSLog(@"__block--test__Block3---result is %d", Block(2));
}
+

原因

+

+

+

栈上Block 的block 变量中的 forwarding 指针指向的是自身

+

Block 的内存管理

Block 的 内存分布

+

+

Block 的 Copy 操作

+

+
    +
  • 全局block
  • +
  • 堆block
  • +
  • 栈block
  • +
+

Block 循环引用

    +
  • 如果当前block对当前对象的某一变量进行截获的话,block 会对对应变量有一个强引用,当前block因为当前的对象对其有个强引用,产生了自循环引用,通过声明为__weak变量来解决
  • +
  • 如果定义了__block 修饰符的话也会产生循环引用,在ARC 下会产生循环引用,而在MRC 下不会,在ARC 下通过打破环来消除,但是存在弊端时当block 没有机会执行时,
  • +
+

循环引用1

+

解决方法:

+

+

大环循环引用2

+

+

以上代码,在MRC 下,不会产生循环引用,在ARC 下,会产生循环应用,引起内存泄露

+

解决方法

+

如果block 没有机会执行,那么循环引用将不会被打破

+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ +

UITableView 相关

    +
  • 重用机制
  • +
  • 并发访问,数据拷贝问题:主线程删除了一条数据,子线程数据由删除之前的数据源copy而来,子线程网络请求,数据解析,预排版等操作后,回到主线程刷新时已经删除的数据又会重现

    +
      +
    • 主线程数据源删除时对数据进行记录,子线程的数据回来后再进行一次同步删除操作,然后再刷新数据

      +
    • +
    • 使用GCD中的串行队列,将数据删除和数据解析在此队列中进行

      +
    • +
    +
  • +
+

UIView 和CALayer 的关系

    +
  • UIView 为 CALayer 提供内容,负责处理触摸事件,参与响应链
  • +
  • CALayer 负责显示内容,分工明确,UIView 的Layer 实际上就是CALayer, backgroundColor 等属性本质是对CALayer 做了一层包装
  • +
+

事件传递

+
    +
  • hitTest 方法是返回响应时间的试图
  • +
  • pointInside withEvent 方法判断点击位置是否在当前视图范围内
  • +
  • 倒序遍历最终响应事件的试图,最后添加的试图会最优先被遍历到
  • +
+

需求案例

+

只让正方形的view中圆形区域内可以响应事件,四个角的位置不响应事件

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class CircularAreaResponseButton: UIButton {

override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if self.isHidden || !self.isUserInteractionEnabled || self.alpha <= 0.01 {
return nil
}

var hitView: UIView?
if self.point(inside: point, with: event) {
// 遍历当前视图的子试图
for subView in subviews {
// 坐标转换
let convertPoint = self.convert(point, to: subView)
if let hitTestView = subView.hitTest(convertPoint, with: event) {
// 找到了最终响应事件的试图
hitView = hitTestView
break;
}
}

if hitView != nil {
return hitView
}
return self
}
return nil
}


override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
let x1 = point.x
let y1 = point.y

let x2 = self.frame.width / 2.0
let y2 = self.frame.height / 2.0
// 使用平面内两点见距离公式计算距离, 并且判断是否 <= 圆的半径
return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)) <= x2
}
}
+

视图响应

UIView 都继承于 UIResponder,都有以下方法

+
1
2
3
4
func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?)
func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?)
func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?)
+

如果事件沿着视图响应链一直传递到 UIApplicationDelegate 仍然没有任何一个视图处理事件,程序将会发生什么?

+
    +
  • 忽略掉了这个事件当作什么都没有发生
  • +
+

图像显示原理

CALayer 的 contents 是位图

+

+
    +
  • CPU的工作

    +

    Layout | Display | Prepare | Commite |
    ———|———–|————|———|
    UI布局 | 绘制 | 图片编解码 | 提交位图 |
    文本计算 |

    +
  • +
  • GPU渲染管线

    +

    定点着色 | 图元转配 | 光栅化 | 片段着色 | 片段处理 |
    ——–|——–|——-|———|———|

    +
  • +
+
提交到帧缓冲区(frameBuffer)
+

UI卡顿掉帧的原因

1/60 ms 时间内, 由cpu 和GPU 协同产生最终的数据,当 CPU UI布局,文本计算所用的时间较长时,留给GPU渲染,提交到帧缓冲区的时间就会变短,这样以来就会发生掉帧情况

+

UIView 的绘制原理

CPU优化方案

+
    +
  • 对象的创建、调整、销毁放在子线程,节省cpu的一部分时间
  • +
  • 预排版(布局计算,文本计算)放在子线程中
  • +
  • 预渲染(文本等异步绘制,图片编解码)等
  • +
+

GPU优化方案

+

在屏渲染

指GPU的渲染操作是在当前用于显示的屏幕缓冲区域中进行

+

离屏渲染

指GPU在当前屏幕缓冲区域外新开劈了一个缓冲区域进行渲染操作
指定了UI视图图层的某些属性,标记为视图在未预合成之前不能用于在当前屏幕上直接显示的时候就会发生离屏渲染,例如图层的圆角设置和蒙层设置

+
    +
  • 离屏渲染何时触发

    +
      +
    • 设置layer 的圆角并且和 maskToBounds 一起使用时
    • +
    • 图层蒙版
    • +
    • 阴影
    • +
    • 光栅化
    • +
    +
  • +
  • 为何要避免离屏渲染

    +
      +
    • 离屏渲染,会增加 GPU 的工作量,GPU工作量的增加就很有可能导致CPU和GPU的总耗时超过1/60s,导致UI卡顿和掉帧
    • +
    +
  • +
  • 避免离屏渲染

    +
  • +
+

###异步绘制

+

+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ +

自动引用计数

+

什么是自动引用计数

内存管理中对引用采取自动计数的技术

+

内存管理

    +
  • 自己生成的对象,自己所持有
      +
    • alloc
    • +
    • new
    • +
    • copy
    • +
    • mutableCopy
    • +
    +
  • +
  • 非自己生成的对象,自己也能持有
      +
    • retain
    • +
    +
  • +
  • 不再需要自己持有的对象时释放
      +
    • release
    • +
    +
  • +
  • 非自己持有的对象无法释放

    +
  • +
  • 废弃对象

    +
      +
    • dealloc
    • +
    +
  • +
+

__strong

__strong 修饰符表示对对象的“强引用”

+

__unsafe_unretained 修饰符

__unsafe_unretained 是不安全的所有权修饰符, 被它修饰的变量不属于编译器的内存管理对象

+
1
id __unsafe_unretained objc = [[NSObject alloc] init];
+

Xcode 提示:Assigning retained object to unsafe_unretained variable; object will be released after assignment

+

将保留对象赋给unsafe_unretain变量;对象将在赋值后释放

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
id __unsafe_unretained obj1 = nil;

{
id __unsafe_unretained obj = [[NSObject alloc] init];

// 自己生成并持有对象
// 因为obj0 变量是__strong,强引用,所以自己持有对象
id __strong obj0 = [[NSObject alloc] init];

// obj0 变量赋值给你obj1
// ojb1 变量不持有对象的强应用,也不持有弱引用
obj1 = obj0;


// 输出 obj1 变量表示的对象
NSLog(@"A: %@", obj1);
} // obj0变量 超出了其作用域,强引用失效自动释放自己持有的对象,除此之外没有其他持有者,所以废弃该对象




// 输出 obj1 变量表示的对象
// obj1 变量表示的对象因为无持有者已经销毁,坏的内存访问
NSLog(@"B: %@", obj1);
+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ +

swift类型检查和类型转换

+

is 关键字类型检查

判断父类和子类之间的关系

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
func typeCheck() {
let programmer = Programmer(name: "老张-php")
if dev is iOSDeveloper {
print("iOS 开发者")
} else {
print("其他开发者")
}

if programmer is Coder {
print("老张-php是一农个码农")
} else {
print("")
}
}
+

as 关键字类型转换

1
2
let ios = iosDev as? Programmer
let ios2 = iosDev as! Coder
+

判断是否遵守了协议

swift 中协议可以当作是一个类型使用

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
et ios = iosDev as? Programmer
let ios2 = iosDev as Coder

// 是否遵守了 Person 协议
if iosDev is Person {
print("遵守了Person协议")
}

let devs = [dev, iosDev, programmer] as [Any]

for dev in devs {
if let dev = dev as? Person {
print(dev)
print("遵守Person协议")
} else {
print("未遵守Person协议")
}
}
+

AnyObject Any

1
2
3
4
var anyObjectArray: [AnyObject] = [CGFloat(0.5) as AnyObject,
1 as AnyObject,
"string" as AnyObject,
iOSODev]
+

swift是面向函数编程, 函数是一等公民,数组中可以存入一个函数,函数表达的是一个过程,不是一个名词或者物体,所以函数不是一个对象,需要使用 any 关键字

+
1
2
3
4
var anyArray: [Any] = [CGFloat(0.5),
1,
"string",
iOSODev]
+

放入函数

+
1
anyArray.append({ (a: Int) -> (Int) in return a * a })
+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ +

ReactNative配置.npm 和cnpm 相关

npm

Nodejs的包管理工具

+

npm 常用命令

    +
  • 更新或卸载

    +
    1
    npm update/uninstall moduleName
    +
  • +
  • 查看当前目录下已安装的包

    +
    1
    npm list
    +
  • +
  • 查看全局安装的包的路径

    +
    1
    npm root -g
    +
  • +
  • 全部命令

    +
    1
    npm help
    +
  • +
+

cnpm

因为npm安装插件是从国外服务器下载,受网络影响大,可能出现异常,cnpm 淘宝镜像

+

安装

1
npm install -g cnpm --registry=https://registry.npm.taobao.org
+

设置镜像源

1
2
cnpm set registry http://registry.cnpm.taobao.com
npm set registry http://registry.cnpm.taobao.com
+

配置 .npmrc 文件

前端项目开发离不开安装各种npm依赖包,可以选择远程的仓库也可以选择本地的仓库,但更改仓库地址需要在安装时控制台打命令,比较麻烦, 而npmrc可以很方便地解决上面问题

+

.npmrc 原理

当安装项目的依赖包时,会查找并读取项目根目录下的.npmrc文件的配置,自动指定仓库地址

+
    +
  • 打开 .npmrc

    +
    1
    open .npmrc
    +
  • +
+
    +
  • 编辑文件, 指定特定的库的镜像源, 比如以@test开头的库镜像为:http://registry.cnpm.test.com/ 而其他的正常

    +
    1
    2
    @test:registry=http://registry.cnpm.lietou.com/
    registry=https://registry.npm.taobao.org/
    +
  • +
+ + +
+ +
+ +
+ + + + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/page/3/index.html b/page/3/index.html new file mode 100644 index 0000000..e5abc36 --- /dev/null +++ b/page/3/index.html @@ -0,0 +1,1039 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ +
+ + +
+ +

Swift中内存管理

+

强引用循环和 weak 关键字

房东

+
1
2
3
4
5
6
7
8
9
10
11
12
class Landlord {
var name: String
var house: House?

init(name: String) {
self.name = name
}

deinit {
print("Landlord内存释放")
}
}
+

房子

+
1
2
3
4
5
6
7
8
9
10
11
12
class House {
var name: String
var landlord: Landlord?

init(person: String) {
self.name = person
}

deinit {
print("House内存释放")
}
}
+

循环引用

+
1
2
3
4
5
6
7
8
9
10
11
func strongRetain() {
var landlord: Landlord? = Landlord(name: "老李")
var house: House? = House(person: "望京府邸")
// 房东有个房子
landlord?.house = house
// 房子有个主人
house?.landlord = landlord

landlord = nil
house = nil
}
+
    +
  • 虽然 landlord = nil 和 house = nil,但是 Landlord 和 House 内存空间并没有释放 (并未执行 deinit 函数)

    +
  • +
  • 原因:因为两个内存空间相互引用,引用计数不为0,形成了强循环引用

    +
  • +
  • 解决办法:使用weak 关键字来修饰两者中其中一个变量

    +
      +
    • 使用weak修饰的变量必须是可选类型,可以赋值为nil
    • +
    • 使用weak修饰必须是var 类型变量
    • +
    +
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
class House {
var name: String
weak var landlord: Landlord?

init(person: String) {
self.name = person
}

deinit {
print("House内存释放")
}
}
+

unowned 关键字的使用

相互循环引用的两个变量有一个是可选的

有个身份证的类,每个身份证肯定对应一个人

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class ID {
var number: String
let person: Landlord

init(person: Landlord, number: String) {
self.person = person
self.number = number
}

deinit {
print("ID内存释放")
}
}

```

房东
+

class Landlord {
var name: String
var house: House?
/// 身份证可以为nil,
var id: ID?

+
init(name: String) {
+    self.name = name
+}
+
+deinit {
+    print("Landlord内存释放")
+}
+

}

+
1
2

循环引用
+

func unownedTest() {
var person: Landlord? = Landlord(name: “老王”)
var id: ID? = ID(person: person!, number: “10010”)

+
person?.id = id
+person = nil
+id = nil
+

}

+
1
2
3
4
5
6
7
8

- person 和 id 之间有强引用关系
- 每个id必须有一个对应的人并且是固定的只能使用let 修饰,不能为nil, 而id可以为空
- unowned和 weak 作用相同都是弱引用,只是unowned 修改的变量不能是nil (可选型的)
- unowned只能使用在 class 类型上,不能修饰函数类型,例如闭包存在循环引用时不能使用unowned修饰
- 解决方法:
- 此时可以通过使用 weak 修饰 id 的方式,因为 id 可以为nil
- let 修饰并且不能为nil 这种情况可以使用 unowned 修饰解决
+
class ID {
+    var number: String
+    unowned let person: Landlord
+
+    init(person: Landlord, number: String) {
+        self.person = person
+        self.number = number
+    }
+
+    deinit {
+        print("ID内存释放")
+    }
+}
+
1
2
3
4
	
**unowned有一定的危险性**

因为unowned 修饰的对象不能赋值为nil,所以执行下面的代码会crash,
+

func unownedTest() {
var person: Landlord? = Landlord(name: “老王”)
var id: ID? = ID(person: person!, number: “10010”)

+
person?.id = id
+
+// 提前释放 person 内存
+person = nil
+
+// 获取身份证对应的人
+print(id?.person) // 此处奔溃,
+let owner = id?.person
+id = nil
+

}

1
2

正确的写法因该是先将🆔设置为nil,再将 Person 设置为nil,再调用 print(id?.person) 就不会再报错

+

func unownedTest() {
var person: Landlord? = Landlord(name: “老王”)
var id: ID? = ID(person: person!, number: “10010”)

+
person?.id = id
+
+id = nil
+
+// 提前释放 person 内存
+person = nil
+
+// 获取身份证对应的人
+print(id?.person) // 此处奔溃,
+let owner = id?.person
+

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

#### 相互循环引用的两个变量都不是可选的

使用隐式可选类型

### 闭包中的强引用循环

- 并不是所有的闭包都会产生循环引用,只有相互引用的两个对象会产生循环引用

- 使用swift闭包捕获列表,在闭包的声明参数之前使用 [unowned self], 不需要解包

- 使用 unowned表示这个对象不能为空,可能存在风险,如果对象为空就会crash


使用 unowned 对象不能为空,可能存在风险

+

class Closure {
var closure: ((Int) -> Void)?

+
init() {
+    closure = { [unowned self] index in
+        print(self)
+        print(index)
+    }
+}
+

}

1
2

使用 weak, 此时需要解包

+

closure = { [weak self] index in
// 解包
if let self = self {
print(self)
print(index)
}
}

1
2

强制解包

+

closure = { [weak self] index in
// 强制解包
print(self!)
print(index)
}
`

+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ +

ReactNative 中使用 TypeScript

+

从RN0.57版本开始已经可以直接支持typescript,无需任何配置。在保留根入口文件index.js的前提下,其他文件都可以直接使用.ts或.tsx后缀。但这一由babel进行的转码过程并不进行实际的类型检查,因此仍然需要编辑器和插件来共同进行类型检查

+
+

TypeScript和JavaScript对比

两者深度对比

+

最大的区别:

+
    +
  • TypeScript是强类型语⾔和静态类型语⾔(需要指定类型)
  • +
  • JavaScript是弱类型语⾔和动态类型语⾔(不需要指定类型)
  • +
+

TypeScript

由微软开发的自由和开源的编程语言,是JavaScript的一个严格超集,并添加了可选的静态类型和基于类的面向对象编程

+

TypeScript定义

+

安装及语法

教程

+
安装
1
npm install -g typescript
+
安装 typescript 依赖管理器 typings
1
npm install -g typings
+
安装 typings 依赖
1
2
typings install npm~react --save
typings install dt~react-native --globals --save
+
使用 yarn 安装

如果已经安装了 yarn 安装如下:

+
1
2
yarn add tslib @types/react @types/react-native
yarn add --dev react-native-typescript-transformer typescript
+
创建 tsconfig.json 配置文件
    +
  • cd 到 ReactNative 项目根文件夹下

    +
    1
    tsc --init
    +
  • +
  • 修改 tsconfig.json 文件

    +

    如果一个目录下存在一个tsconfig.json文件,那么它意味着这个目录是TypeScript项目的根目录。 tsconfig.json文件中指定了用来编译这个项目的根文件和编译选项

    +

    tsconfig.json文件配置详解

    +

    TypeScript配置文件tsconfig简析

    +

    务必设置”jsx”:”react”
    注意多余的注释可能会不兼容,需要移除

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    {
    "compilerOptions": {
    "target": "es6",
    "allowJs": true,
    "jsx": "react",
    "outDir": "./dist",
    "sourceMap": true,
    "noImplicitAny": false
    },

    // "files"属性 :指定一个包含相对或绝对文件路径的列表。 "include"和"exclude"属性指定一个文件glob匹配模式列表

    "include": [
    "typings/**/*.d.ts",
    "src/**/*.ts",
    "src/**/*.tsx"
    ],
    "exclude": [
    "node_modules"
    ]
    }
    +
  • +
  • cd 到项目根目录中新建存放 typescripe 源代码的文件夹

    +
  • +
+
建立 rn-cli.config.js 文件,使用支持typescript的transfomer
1
2
3
4
5
6
7
8
module.exports = {
getTransformModulePath() {
return require.resolve('react-native-typescript-transformer');
},
getSourceExts() {
return ['ts', 'tsx'];
}
}
+
修改 .babelrc 文件
1
2
3
4
{
"presets": ["react-native"],
"plugins": ["transform-decorators-legacy"]
}
+
修改项目中文件导入
    +
  • 入口index.jsApp.js的文件名请不要修改,项目根目录下新建src文件夹
  • +
  • src文件夹下新建 .tsx 后缀的文件
  • +
  • .tsx 后缀的文件引用react的写法有所区别

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import * as React from 'react'
    import { Text, View } from 'react-native';
    import { Component } from 'react';

    export default class HelloWorld extends Component{
    render() {
    return (
    <View>
    <Text>Hello!</Text>
    <Text>Typescript</Text>
    </View>
    );
    }
    }
    +
  • +
  • App.js中使用

    +
      +
    • App.js 导入方式修改为如下方式

      +
      1
      2
      3
      4
      	import './src';
      ```

      - 导入 .tsx 类型的组建
      +

      import HelloWorld from “./src/HelloWorld”;

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11

      ### 配置jsconfig

      `jsconfig.json`目录中存在文件表明该目录是JavaScript项目的根目录。该jsconfig.json文件指定根文件和JavaScript语言服务提供的功能选项

      [jsconfig详解](https://code.visualstudio.com/docs/languages/jsconfig)

      [jsconfig.json](https://jeasonstudio.gitbooks.io/vscode-cn-doc/content/md/%E8%AF%AD%E8%A8%80/javascript.html?q=)


      ##### 新建 `jsconfig.json` 文件
      +
    • +
    +
  • +
+

touch jsconfig.json

1
2

##### 编辑 `jsconfig.json` 文件

+

{
“compilerOptions”: {
“allowJs”: true,
“allowSyntheticDefaultImports”: true,
“emitDecoratorMetadata”: true
},
“exclude”: [
“node_modules”,
“rnApp/Tools” // 项目根目录下新建的存放RN中所用的一些工具类
]
}

+
1
2
3


### package.json 文件配置
+

{
“name”: “RNProject”,
“version”: “0.0.1”,
“private”: true,
“scripts”: { // 通过设置这个可以使npm调用一些命令脚本,封装一些功能
“start”: “node node_modules/react-native/local-cli/cli.js start”,
“test”: “jest”
},
“dependencies”: { // 发布后依赖的包
“@types/react”: “^16.4.16”,
“@types/react-native”: “^0.57.4”,
“react”: “16.5.0”,
“react-native”: “0.57.2”,
“react-transform-hmr”: “^1.0.4”,
“tslib”: “^1.9.3”
},

+

“devDependencies”: { // 开发中依赖的其它包
“babel-jest”: “23.6.0”,
“jest”: “23.6.0”,
“metro-react-native-babel-preset”: “0.48.0”,
“react-native-typescript-transformer”: “^1.2.10”,
“react-test-renderer”: “16.5.0”,
“typescript”: “^3.1.2”
},

+

“jest”: { // JavaScript 的单元测试框架
“preset”: “react-native”
}
}

1
2
3
4
5
6
7
8
9

##### `–save` 和 `-–save-dev` 区别

- --save参数表示将该模块写入dependencies属性,
- --save-dev表示将该模块写入devDependencies属性。

##### 添加开发依赖库

- 类型检查

+
npm install @types/jest --save-dev
+npm install @types/react --save-dev
+npm install @types/react-native --save-dev 
+
+
1
- 装饰器转化核心插件
+ +npm install babel-plugin-transform-decorators-legacy --save-dev +
1
2
	
- 测试框架
+ +npm install react-addons-test-utils --save-dev +npm install react-native-mock --save-dev +
1
2

##### 删除依赖库
+

npm uninstall @types/jest –save

1
2


+

npm uninstall @types/jest –save-dev

1
2

##### 彻底删除,-d 表示 devDependencies, -O 表示 optionalDependencies 中

+

npm uninstall -s -D -O @types/jest

+
1
2
3
4
5
6
7
8
9
10

### 报错汇总

##### 错误1 `Cannot find module 'react-transform-hmr/lib/index.js'`

[解决1](https://stackoverflow.com/questions/50142561/cannot-find-module-react-transform-hmr-lib-index-js?noredirect=1&lq=1)

[解决2](https://github.com/facebook/react-native/issues/21530)

- 安装 `react-transform-hmr`
+
npm install react-transform-hmr --save
+
1
2

- 清除缓存重新启动服务
+ +npm start --reset-cache +``` +
+ +
+ +
+ +
+ + + +
+ +
+ + +
+ +

《52个方法-第一章》

+

一、OC起源

    +
  • OC使用的是“消息结构”而不是“函数调用”
  • +
  • 两者区别:
      +
    • 消息结构:在运行时所执行的代码由运行环境来决定,无论是否多态总会在运行时才会去查找所要执行的方法
      1
      2
      NSObject *objc = [NSObject new];
      [objc performWith: parameter1 and: parameter2];
      +
    • +
    +
  • +
+
- 函数调用:由编译器决定,如果函数多态,编译时就要查处到底应该执行那个函数实现
+
+
1
2
Object *objc = new Object;
objc -> perform(parameter1, parameter2);
+
    +
  • 消息结构中编译器不会关心接收消息的对象是何种类型,接收消息的对象问题也要在运行时处理,此过程叫做“动态绑定”
  • +
+

要点:

+
    +
  1. OC语言使用的动态绑定的消息结构,在运行时所执行的代码由运行环境来决定,无论是否多态总会在运行时才会去查找所要执行的方法,
  2. +
  3. OC 中对象总是分配在“堆空间”,而绝不会分配在”栈“上,不带 “*”的变量或者结构体等可能会使用“栈空间”
  4. +
+

二、类头文件尽量少导入其他头文件

要点:

+
    +
  1. 除非必要,否则不要引入头文件,使用向前声明降低类之间的耦合
  2. +
  3. 有时无法使用向前声明,应该把该类遵守协议的声明放到“class-continuation分类”(匿名分类)中。
  4. +
  5. 如果“class-continuation分类”也不行的话,就把协议单独放在一个文件中,再将其引入### 三、多用字面量语法,少用与之等价的方法
  6. +
+

三、多用字面量语法,少用与之等价的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
- (void)tempMethond {
// OC1.0 起,支持简单方式创建基本类型对象

// - 使用字符串字面量:简介易读
NSString *someString = @"Effective Objective-C 2.0";
// 传统方式
NSString *someStrongOld = [NSString stringWithFormat:@"Effective Objective-C 2.0"];


// - 字面数值
NSNumber *someNumber = @1;
NSNumber *floatNumber = @2.5f;
NSNumber *doubleNumber = @3.1415926;
NSNumber *boolNumber = @YES;
NSNumber *charNumber = @'a';
// 字面量语法用于表达式
int x = 5;
float y = 6.23f;
NSNumber *expressionNumber = @(x * y);


// - 字面量数组
NSArray *animals = @[@"cat", @"dog", @"mouse", @"badger"];
NSString *dog = animals[1];

// arrayWithObjects 和 字面量 的区别:
// arrayWithObjects 方法依次处理各个参数,直到发现nil为止
// 字面量数组遇到nil直接抛出异常
NSArray *animalsOld = [NSArray arrayWithObjects:@"cat", @"dog", @"mouse", @"badger", nil];
NSString *dogOld = [animalsOld objectAtIndex:1];


// - 字面量字典
// 如果有nil直接抛出异常
NSDictionary *personData = @{@"firtName": @"Matt",
@"lastName": @"Galloway",
@"age": @28};
NSString *lastName = personData[@"lastName"];

// dictionaryWithObjectsAndKeys 方法依次处理各个参数,直到发现nil为止
NSDictionary *personDataOld = [NSDictionary dictionaryWithObjectsAndKeys:@"Matt",@"firtName",
@"Galloway", @"lastName",
[NSNumber numberWithInteger:28], @"age", nil];

NSString *lastNameOld = [personData objectForKey:@"lastName"];


// - 可变数组和字典
NSMutableArray *mutableArray = animals.mutableCopy;
mutableArray[1] = @"dog";

NSMutableDictionary *mutableDict = [personData mutableCopy];
mutableDict[@"lastName"] = @"Galloway";
}
+

要点:

+
    +
  1. 使用字面量语法创建字符串,数值,数组,字典简明扼要
  2. +
  3. 通过下标操作获取数组,字典对应的元素
  4. +
  5. 使用字面量创建数组,字典时若有nil ,会抛出异常,确保值里不含nil### 五、用枚举表示状态、选项、状态码
  6. +
+

四、多用类型常量,少用#define预处理指令

宏定义
    +
  • 缺点:
      +
    • 可以把所有相同的字符串值都替换为定义的值,
        +
      • 定义出来的常量没有具体的类型信息
      • +
      • 如果有人重新定义了宏定义,不会有警告或者提醒,导致程序中值不一样
      • +
      +
    • +
    +
  • +
+
类型常量
    +
  • 命名规则:

    +
      +
    • 如果只是在实现文件.m 中使用,则在前面加字母k
    • +
    • 如果此常量在类之外也可见,则以类名为前缀,(第19条详细了解命名习惯)
    • +
    +
  • +
  • 声明位置:

    +
      +
    • 总是喜欢在头文件中声明宏定义,可能和常量名冲突,
    • +
    • 所有引入这个头文件的类都会出现宏定义或者常量,相当于声明了一个名叫kAnimationDuration的全局变量
    • +
    • 应该加上前缀,表明其所属的具体类,例如:EOCViewClassAnimationDuration
    • +
    +
  • +
  • 常量从右往左解读依次是: 具体类型,不可修改

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    	// const 修饰的是常量不希望别人修改, NSString * 指向NSString对象
    extern NSString *const EOCStringConstant;


    // 两者写法都可以
    extern const NSTimeInterval EOCAnimatedViewAnimationDuration;
    extern NSTimeInterval const EOCAnimatedViewAnimationDuration1;
    ```

    - 如果不打算公开常量,可以定义在 .m 实现文件里
    +

    static const NSTimeInterval kAnimationDuration = 0.3;

    +
    1
    2
    3

    - static关键字
    - static 修饰意味着该变量仅在定义此变量的编译单元(实现文件.m)中可见,作用域就是当前.m文件
    +
    static const NSTimeInterval kAnimationDuration = 0.3;
    +
    1
    2
    3
    - 如果需要声明一个外部可见的常值变量,需要在头文件中声明并使用 extern 关键词修饰 "extern 

    在 .h 文件中
    + +extern NSString *const EOCStringConstant; + +// 两者写法都可以 +extern const NSTimeInterval EOCAnimatedViewAnimationDuration; +extern NSTimeInterval const EOCAnimatedViewAnimationDuration1; +
    1
    2

    在 .m 文件中
    + +NSString *const EOCStringConstant = @"VALUE"; +const NSTimeInterval EOCAnimatedViewAnimationDuration = 0.3; +NSTimeInterval const EOCAnimatedViewAnimationDuration1 = 0.3; +
    1
    2
    3
    4
    5
    6
    7
    8
    9

    **要点:**

    1. 定义常量时static const定义编译单元(实现文件.m)中可见的常量,无需为其名称加前缀
    2. 在头文件中使用extern 声明全局常量,并在实现文件中定义其值,这种常量要出现在全局符号表中,所以加相关类作为前缀

    ### 五、用枚举表示状态、选项、状态码

    - 普通枚举
    +

    enum EOCConnectionState {

    +
    EOCConnectionStateDisconnected, // 断开连接
    +EOCConnectionStateConnecting,   // 连接中...
    +EOCConnectionStateConnected,    // 已连接
    +

    };

    +
    1
    普通枚举如果要使用如下:
    +

    enum EOCConnectionState status = EOCConnectionStateConnected;

    +
    1
    2

    如果每次都不需要输入enum关键字,只需要typedef关键字重新定义枚举类型
    +

    EOCConnectionState status1 = EOCConnectionStateConnecting;

    +
    1
    2

    - 设置枚举的初始值
    +

    enum EOCConnectionStateConnectionState {

    +
    EOCConnectionStateConnectionStateDisconnected = 1, // 断开连接
    +EOCConnectionStateConnectionStateConnecting,   // 连接中...
    +EOCConnectionStateConnectionStateConnected,    // 已连接
    +

    };

    +
    1
    2
    3
    4
    5

    - 可选枚举

    - << 位操作符(左移), 表示往左移动N位,使用 位运算符 | (或)来组合使用
    - 使用位运算符 &(与) 判断是否开启了某个选项
    +

    enum UIViewAutoresizing {

    +
    UIViewAutoresizingNone                 = 0,
    +UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
    +UIViewAutoresizingFlexibleWidth        = 1 << 1,
    +UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
    +UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
    +UIViewAutoresizingFlexibleHeight       = 1 << 4,
    +UIViewAutoresizingFlexibleBottomMargin = 1 << 5
    +

    }

    +
    1
    2
    3
    4

    - 使用宏定义枚举

    普通枚举类型(老式语法定义枚举)
    +

    typedef enum : NSUInteger {

    +
    EOCConnectionState2DisConnected,
    +EOCConnectionState2DisConnecting,
    +EOCConnectionState2Connected,
    +

    } EOCConnectionState2;

    +
    1
    2

    NS_ENUM 宏定义的枚举展开后:
    +

    typedef enum EOCConnectionState_3 : NSUInteger EOCConnectionState_3;
    enum EOCConnectionState_3 : NSUInteger {

    +
    EOCConnectionState_3DisConnected,
    +EOCConnectionState_3DisConnecting,
    +EOCConnectionState_3Connected,
    +

    };

    +
    1
    2

    - 定义新特性枚举
    +

    typedef NS_ENUM(NSUInteger, EOCConnectionState3) {

    +
    EOCConnectionState3DisConnected,
    +EOCConnectionState3DisConnecting,
    +EOCConnectionState3Connected,
    +

    };

    +
    1
    2
    3
    4

    - 枚举使用

    .h文件
    +

    #import <UIKit/UIKit.h>

    +

    typedef enum EOCConnectionState EOCConnectionState;

    +

    typedef NS_ENUM(NSUInteger, EOCConnectionState3) {

    +
    EOCConnectionState3DisConnected,
    +EOCConnectionState3DisConnecting,
    +EOCConnectionState3Connected,
    +

    };

    +
  • +
+
@interface EOCEnum : NSObject
+@property (nonatomic, assign) enum EOCConnectionState state;
+@property (nonatomic, assign) EOCConnectionState3 state3;
+@end
+
+
1
2

.m 文件中
+ +#import "EOCEnum.h" + +@implementation EOCEnum + +- (void)testEnum { + // 未使用typedef 关键字 + enum EOCConnectionState status = EOCConnectionStateConnected; + NSLog(@"%u", status); + + // 使用typedef 关键字 + EOCConnectionState status1 = EOCConnectionStateConnecting; + NSLog(@"%u", status1); + + UIViewAutoresizing resizing = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + if (resizing & UIViewAutoresizingFlexibleWidth) { + // 设置了 UIViewAutoresizingFlexibleWidth 约束 + } +} + +// UIKit 试图所支持的设置显示方向 +- (UIInterfaceOrientationMask)supportedInterfaceOrientations { + return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft; +} + +// 枚举使用 +- (void)userEnum { + switch (_state3) { + case EOCConnectionState3DisConnected: + // ... + break; + + case EOCConnectionState3DisConnecting: + // ... + break; + + case EOCConnectionState3Connected: + // ... + break; + + default: // 最好不用,防止以后增加一种状态机之后,编译器不发出警告 + break; + } +} + +@end +``` +

要点:

+
    +
  1. 使用枚举表示状态机的状态、传递给方法的选项以及状态码等值,给枚举值起名时要注重易懂
  2. +
  3. 如果一个类型可以使用状态机的状态来表示,并且多个选项可同时使用,那么就定义为可选的枚举类型,枚举各值定义为2的幂,以便通过“按位或”操作组合
  4. +
  5. 使用NS_ENUM 和 NS_OPTIONS 宏来定义枚举,并指明底层的数据类型。这样可以确保枚举是用开发者所选的底层数据类型实现出来的,而不会采用编译器所选的类型
  6. +
  7. switch语句处理枚举类型时不要实现default 分支,如果以后加入枚举的新状态之后,编译器会发出警告
  8. +
+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ +

iOS通过URL获取HTML中标题

+

获取标题

String扩展一个方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private let patternTitle = "(?<=title>).*(?=</title)"
extension String {
static func parseHTMLTitle(_ urlString: String) -> String? {
let url = URL(string: urlString)!
do {
let html = try String(contentsOf: url, encoding: .utf8)
let range = html.range(of: patternTitle, options: String.CompareOptions.regularExpression, range: html.startIndex..<html.endIndex, locale: nil)
guard let tempRange = range else { return "" }
let titleStr = html[tempRange]
return String(titleStr)
} catch {
return ""
}
}
}
+
iOS 中简单正则表达式

iOS 中 正则表达式中的\ 需要使用 \\ 来表示

+
// 获取
+let testStr = "我到底是[iOS]开发还是[产品]经理"
+
+// 获取 "[" 和 "]" 之间的所有内容, 左边[ 起一直到 右边 ] 结束的所有内容 
+let pattern1 = "\\[.*\\]" // [iOS]开发还是[产品]
+
+// 获取 "[" 和 "]" 之间的所有内容,包括[]
+let pattern2 = "\\[.*?\\]"    // [iOS]
+
+// 获取第一个 "[" 和 "]" 之间的所有内容 不包括[] 
+let pattern3 = "(?<=\\[).*?(?=\\])" // iOS
+
+// 获取html中 title 标签中内容
+let pattern5 = "(?<=title>).*(?=</title)" 
+
获取范围截取子串
let testRange1 = testStr.range(of: pattern1, options: String.CompareOptions.regularExpression, range: testStr.startIndex..<testStr.endIndex, locale: nil)
+    let result1 = String(testStr[testRange1!])
+
+
+let testRange2 = testStr.range(of: pattern2, options: String.CompareOptions.regularExpression, range: testStr.startIndex..<testStr.endIndex, locale: nil)
+let result2 = String(testStr[testRange2!])
+
+
+let testRange3 = testStr.range(of: pattern3, options: String.CompareOptions.regularExpression, range: testStr.startIndex..<testStr.endIndex, locale: nil)
+let result3 = String(testStr[testRange3!])
+
+print(result1) 
+print(result2) 
+print(result3) 
+
正则表达式

通过URL解析html中的标题

+
    +
  • 获取html字符串
  • +
  • 正则匹配
  • +
  • 生成字符串Range
  • +
  • 截取字符串
  • +
+

匹配一次直接返回结果

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 生成表达式
let regular = try NSRegularExpression(pattern: pattern5, options: .caseInsensitive)


// 开始匹配
let matchResult1 = regular.firstMatch(in: html, options: NSRegularExpression.MatchingOptions.reportProgress, range: NSMakeRange(0, html.count))

// 获取匹配结果范围
let matchRange = matchResult1?.range
let range = Range(matchRange!)!

// 生成String.Index 类型范围
let startIndex = html.index(html.startIndex, offsetBy: range.lowerBound)
let endIndex = html.index(html.startIndex, offsetBy: range.upperBound)
let range3 = Range(uncheckedBounds: (lower: startIndex, upper: endIndex))

// 截取字符串
let result1 = String(html[range3])
print(result1)
+

全部匹配后返回结果时一个集合

+
1
2
3
4
5
6
7
8
9
10
let matchResult2 = regular.matches(in: html, options: .reportProgress, range: NSMakeRange(0, html.count))
matchResult2.forEach { (result) in
let range = Range(result.range)!

let startIndex = html.index(html.startIndex, offsetBy: range.lowerBound)
let endIndex = html.index(html.startIndex, offsetBy: range.upperBound)

let subStr2 = html[Range(uncheckedBounds: (startIndex, endIndex))]
print(String(subStr2))
}
+

将匹配到的内容提还

+
1
let str3 = regular.stringByReplacingMatches(in: html, options: .reportProgress, range: NSMakeRange(0, html.count), withTemplate: "====")
+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ +

OC 语言特性

分类

原理

由运行时来决议的,不同分类当中含有同名方法 谁最终生效取决于谁最终参与编译,最后参编译的同名分类方法会最终生效,假如分类方法中添加的方法和宿主类中的某一个方法名相同,分类中的方法会覆盖宿主类中的同名方法,覆盖是指在消息传递过程中优先查找数组靠前的元素,如果查找到了同名方法就直接调用,实际上宿主类的同名方法实现仍然是存在的,我们可可以通过一些手段调用到原有类的同名方法的实现

+

使用场景

    +
  • 申明私有方法,分类的 .m 文件中申明私有方法啊,对外不暴露
  • +
  • 对类中的代码进行抽取分解
  • +
  • 把Framework的私有方法公开化
  • +
+

特点

    +
  • 运行时决议,运行时通过runtime 才把分类中的方法添加到了宿主类上
  • +
  • 可以为系统类添加方法
  • +
  • 有多个分类中存在同名方法时,最后编译的方法会最先生效
  • +
  • 分类中有和宿主类同名方法时,底层中通过内存copy将分类方法“覆盖”宿主类方法
  • +
  • 名字相同的分类编译会报错
  • +
+

分类中可以添加的内容

    +
  • 实例方法
  • +
  • 类方法
  • +
  • 协议
  • +
  • 属性,实际上只是声明了 getter / setter 方法,并没有添加成员变量
  • +
+

关联对象技术

关联对象由 AssocicationsManager 管理并在 AssociationsHashMap 上存储
所有对象的关联内容都在同一个全局容器中

+
    +
  • 给分类添加成员变量,通过关联对象的方式
  • +
+

扩展

使用场景

    +
  • 声明私有属性
  • +
  • 声明私有方法
  • +
  • 声明私有成员变量
  • +
+

特点

    +
  • 编译时决议
  • +
  • 只以声明的形式存在,多数情况下寄生在宿主类的.m中
  • +
  • 不能为系统添加扩展
  • +
+

代理

    +
  • 代理设计模式,传递方式是一对一
  • +
  • 使用weak 避免循环引用
  • +
+

通知

使用观察者模式来实现的用于跨层传递消息的机制

+

KVO

KVO 是系统对观察者模式的又一实现,使用isa 混写技术(isa - swizzling)来动态运行时为某一个类添加一个子类重写了它的setter 方法,同时将原有类的isa指针指向了新创建的类上

+
    +
  • KVO 是OC对观察者模式的又一实现
  • +
  • apple 使用isa 混写技术(isa - swizzling)来实现KVO
      +
    • 给我A类注册一个观察者时本质上是调用系统的 Observer for keyPath 方法,系统会创建一个NSKVONotifiying_A 的类
    • +
    +
  • +
  • 使用 setter 方法设置值KVO才能生效
  • +
  • 使用setValue:forKey: 改变值KVO才能生效
  • +
  • 使用下划线的成员变量直接修改值需要手动添加KVO才能生效(增加 willChangeValueForKey 和 didChangeValueForKey 两个方法)
  • +
+

KVC

apple提供的键值编码技术

+
    +
  • (nullable id)valueForKey:(NSString *)key;
  • +
  • (void)setValue:(nullable id)value forKey:(NSString *)key;

    +

    以上两个方法中的key是没有任何限制的,只要知道对应的成员变量名称,就可以对私有的成员变量设置和操作,这违背了面向对象的编程思想

    +
  • +
+

(void)setValue:(nullable id)value forKey:(NSString *)key; 方法调用流程

+
    +
  • 先判断是否有跟key 相关的setter 方法,如果有就直接调用,结束调用
  • +
  • 如果没有再判断是否存在实例变量,如果存在直接给赋值,结束调用
  • +
  • 如果说实例变量不存在,会去调用 - (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key; 抛出异常
  • +
+

属性关键字

读写权限

    +
  • readonly

    +
  • +
  • readwrite 系统默认

    +
  • +
+

原子性

    +
  • atomic (系统默认)保证赋值和获取(对成员属性的直接获取和赋值)时线程安全,并不代表操作和访问,如果atomic修饰的一个数组,只能保证独具改数组的读取和赋值时线程安全,并不能保证对数据操作(增加/删除数组元素)时是安全的
  • +
  • nonatomic
  • +
+

引用技术

    +
  • assign
      +
    • 修饰基本数据类型,
    • +
    • 修饰对象时不改变其引用计数,
    • +
    • 会产生悬垂指针
    • +
    +
  • +
  • weak
      +
    • 不改变被修饰对象的引用计数,
    • +
    • 所指对象被释放之后指针自动置为nil
    • +
    +
  • +
+

两者区别:

+
    +
  • weak可以修饰对象,而assign 既可以修饰对象也可以修饰基本数据类型
  • +
  • assign 修饰的对象,对象释放后指针仍然指向原对象的内存地址,而weak 修饰的对象,释放后会自动置为nil
  • +
+

问题1

+

weak 修饰的对象为什么释放后会自动置为nil

+

问题2

+

@property (copy) NSMutableArray *array; 有什么问题

+
    +
  • 被copy修饰后,如果赋值过来的是NSMutableArray,copy之后就是NSArray,如果赋值过来的是NSAarray,copy 之后仍然是NSArray,有可能会调用array的添加元素方法会导致crash
  • +
+

copy 关键词

+
    +
  • 浅拷贝:内存地址的复制,让目标对象指针和源对象指向同一块内存空间,
      +
    • 会增加对象的引用计数
    • +
    • 并没有一个新的内存分配
    • +
    +
  • +
+
    +
  • 深拷贝:目标对象指针和源对象指针分别指向两块内容相同的内存空间
      +
    • 不会增加源对象的引用计数
    • +
    • 有新对象的内存分配
    • +
    +
  • +
+

MRC 重写 retain修饰的变量的setter 方法

1
2
3
4
5
6
7
8

- (void)setObj:(id)obj {
if (_obj != obj) {
[_obj release];
}
_obj = [obj retain];

}
+

判断是为了防止异常处理,如果不做 if 判断,当传入的 obj 对象正好是原来的_obj 对象,对原对象尽行releas 操作,实际上也会对传入的对象进行releas 操作进行释放,此时如果再通过obj指针访问废弃的对象时就会导致carsh

+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ +

iOS动态设置tableHeaderView高度

需求

动态的设置tableHeaderView的高度

+

遇到的问题:

    +
  • 设置tableHeaderViewframe无法实现,有时候会出现tableView 中间的 cell 不显示
  • +
  • 有时候会导致headerViewcell内容重叠

    +
  • +
  • 通过计算控件高度设置frame方式麻烦

    +
  • +
+

正确的步骤

    +
  • 创建HeaderView
  • +
  • 取出UITableViewtableHeaderView
  • +
  • 设置frame大小,调整headerView布局
  • +
  • 重新给UITableViewtableHeaderView
  • +
+

使用AutoLayout

UITableView增加分类

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
extension UITableView {
/// set tableHeaderView
func setTableHeaderView(_ headerView: UIView) {
headerView.translatesAutoresizingMaskIntoConstraints = false
self.tableHeaderView = headerView
// autolayout
NSLayoutConstraint(item: headerView, attribute: .centerX, relatedBy: .equal, toItem: self, attribute: .centerX, multiplier: 1.0, constant: 0).isActive = true
NSLayoutConstraint(item: headerView, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1.0, constant: 0).isActive = true
NSLayoutConstraint(item: headerView, attribute: .width, relatedBy: .equal, toItem: self, attribute: .width, multiplier: 1.0, constant: 0).isActive = true
}
/// update
func updateHeaderView() {
guard let headerView = self.tableHeaderView else { return }
headerView.layoutIfNeeded()
let header = self.tableHeaderView
self.tableHeaderView = header
}
}
+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ +

个人信息

+

工作经历

猎聘网 (2018.2 ~ 至今)

Swift重构猎聘App中应聘记录模块,推进项目组使用AutoLayout技术,后期负责开发迭代乐班班项目,参与每版需求分析和主要代码编写,解决复杂功能页面卡顿,内存优化,BUG修复及iOS新特性适配等。

+

北京万朝科技有限公司(2015.5 ~ 2018.2)

在该项目中担任iOS开发⼯作,参与项目需求分析、核⼼代码编写以及常用工具类的封装,BUG修复,版本迭代更新,推进组内swift的使用。

+

中盎炬华科技发展有限公司(2014.4月 ~ 2015.5)

参与iOS客户端的开发维护完成开发工作,实现一些交互动画和效果;研究最新技术,优化程序代码,集成第三方SDK。

+

项⽬经历

1. 猎聘APP (2018.2 ~ 至今)

项⽬目描述: 为中高端人才和经理人提供招聘的平台

+
    +
  • 使用 swift 重构应聘记录模块中相关页面及功能
  • +
  • 推进整个项目组使用 AutoLayout 技术
  • +
+

2. 乐班班

项⽬目描述: 音/视频播放、直播,内购,即时通信等功能,由OC、Swift,RN 技术混编,项⽬应⽤技术

+
    +
  • 使用ReactNative技术重构和开发部分页面
  • +
  • 使用Xcode、Instruments工具查找、分析、解决项目内存泄露,修复BUG
  • +
  • Swift重构通信模块,用collectionView构建消息页面,自定义emoj Keyboard,封装常用UI控件,自定义控制器转场动画
  • +
  • 使用AVFoundation 实现视频录制,上传,播放功能
  • +
  • 通过GCD dispatch semaphors 解决并发网络请求问题
  • +
  • 使用collectionView构建复杂的视频播放页面,企业大学首页
  • +
+

3. e蜂通信 (2015.5 ~ 2018.2)

项⽬目描述:整合了办公OA、CRM系统,新增即时通信、分享、远程监控、解密外发、红包、私聊等功能;由OC和swift混编, 项⽬目应⽤用技术:

+
    +
  • 使⽤CocoaAsyncSocket自定义通信协议,实现聊天,特定业务消息触达功能
  • +
  • 使⽤cocoaPod实现组件化,
  • +
  • 封装swift基础库,完善swift和Objective-C混编
  • +
  • 实现分享⼩视频、图片、点赞、评论功能
  • +
  • ⾃定义相机实现人脸识别打卡、扫描二维码功能
  • +
  • 封装常⽤UI控件,使用Core Animation实现动画效果,集成百度地图SDK
  • +
  • 利⽤JavaScriptCore完成H5和Native之间的交互
  • +
+

4. 四万公里 (2014.4 ~ 2015.5)

项目描述:针对境外旅游群体提供便捷服务,项⽬目应⽤用技术:

+
    +
  • 封装 TableViewController基类,对接服务端接口等
  • +
  • 封装基础UI控件
  • +
  • 集成科大讯飞SDK, 实现了实时翻译功能
  • +
  • 集成滴滴打车SDK, 实现用户可在国外打车功能
  • +
+

专业技能

    +
  1. 熟练使用Objective-C、Swift,及Swift与Objective-C混编,有良好的编程习惯
  2. +
  3. 熟练掌握iOS内存管理机制以及ARC技术,结合Instruments对项目进行内存优化
  4. +
  5. 熟练掌握NSThread/GCD/NSOperation等并发编程技术
  6. +
  7. 熟练掌握iOS中事件链和响应链的传递方式
  8. +
  9. 熟练使⽤AFNetworking,Moya,FMDB,SDWebImage,Masonry等第三⽅开源框架
  10. +
  11. 熟练掌握各种UI控件封装,熟悉多视图开发,能实现复杂的界⾯面交互
  12. +
  13. 掌握KVO原理理、KVC原理理、SDWebImage内部实现过程、RunLoop的应⽤
  14. +
  15. 有团队开发经验,同时能够独立完成APP开发,代码编写,调试和发布
  16. +
+

自我介绍

工作之余了解使⽤ JavaScript,RxSwift 函数响应式编程,小程序开发等知识,经常关注iOS博客和Github 上优秀代码,以及在raywenderlich上学习新知识,在个人博客做些技术积累。 计划在iOS领域深⼊同时也扩展⾃己的知识⾯,了解使⽤后端和前端技术。

+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ +

Xcode中使用Debug Memory Graph检查内存泄漏

勾选如下图所示选项

+

运行程序后点击

+

内存泄露定位

+

+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ +

flutter安装

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 导入头文件
import 'package:flutter/material.dart';

void main() => (MyApp());

// 定义类, 继承

class MyApp extends StatelessWidget {
// 重写
@override

Widget build(BuildContext context) {

// 返回一窗口
return MaterialApp(
title:'Flutter',
home: Scaffold(
appBar: AppBar(
title: Text('flutter 初体验')
),

body: Center(
child: Text('flutter 学习之路'),
),
),
);
}
}
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}

class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);

// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.

// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".

final String title;

@override
_MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;

void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}

@override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Invoke "debug painting" (press "p" in the console, choose the
// "Toggle Debug Paint" action from the Flutter Inspector in Android
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
// to see the wireframe for each widget.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'你可以点击按钮来修改文字,我是热重载的结果',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
+ +
+ +
+ +
+ + + +
+ +
+ + +
+ +

Swift已有项目中集成ReactNative

+

本教程针对于已经有的Swift项目集成ReactNative

+

创建新的空目录

为了不影响原有的swift项目目录结构,建议先创建一个空的文件夹,例如SwiftRN

+

+

创建package.json文件

终端进入创建的SwiftRN文件目录下,创建package.json文件

+
1
touch package.json
+
修改package.json文件,指定各个版本
1
2
3
4
5
6
7
8
9
10
11
12
13
{
"name": "SwiftRN",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start",
"test": "jest"
},
"dependencies": {
"react": "16.3.1",
"react-native": "^0.56.0"
}
}
+
执行npm install

在创建package.json所在的文件目录下执行,在此过程中可以遇到的错误可能较多

+
1
npm install
+
添加index.js文件
1
touch index.js
+
修改文件内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import {
AppRegistry,
Text,
View,
} from 'react-native';

import React, { Component } from 'react';

// 用于最后在项目中测试
class SwiftRNHomeView extends Component {
render() {
return(
<View style={{backgroundColor:'white'}}>
<Text style={{fontSize: 20, marginTop: 200}}>Hellow world</Text>
</View>
);
}
}


class MyApp extends Component {
render() {
return <SwiftRNHomeView></SwiftRNHomeView>;
}
}

AppRegistry.registerComponent('SwiftRN', () => MyApp);
+

配置swift项目

将新创建的Swift文件目录下的所有文件拷贝到Swift项目所在的根目录下

+

+

使用Pod集成RN

swift项目采用了pod管理第三方库,在Podfile文件中加入下面代码

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
pod 'React', :path => './node_modules/react-native', :subspecs => [
'Core',
'RCTText',
'RCTNetwork',
'RCTWebSocket', # 这个模块是用于调试功能的
'CxxBridge', # Include this for RN >= 0.47,不引入的话会报错误或者警告
'DevSupport', # Include this to enable In-App Devmenu if RN >= 0.43,不引入的话会报错误或者警告
'RCTAnimation', 不引入的话会报错误或者警告
]


# 如果你的RN版本 >= 0.42.0,请加入下面这行,不引入的话会报错误或者警告
pod "yoga", :path => "./node_modules/react-native/ReactCommon/yoga"
pod 'DoubleConversion', :podspec => './node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
pod 'glog', :podspec => './node_modules/react-native/third-party-podspecs/glog.podspec'
pod 'Folly', :podspec => './node_modules/react-native/third-party-podspecs/Folly.podspec'
+

创建桥接文件

    +
  • 如果原项目中有SwiftDemo-Bridging-Header.h类似这个文件可以跳过。
  • +
  • 如果从来没有创建过桥接文件,创建一个OC的类会自动弹出是否需要创建桥接文件,直接创建即可,最后把无用的OC文件删掉
  • +
+

引用RN头文件

在桥接文件中导入头文件

+
1
2
#import <React/RCTRootView.h>
#import <React/RCTBundleURLProvider.h>
+

使用RN页面

在模拟器中使用RN的页面, 确保在 info.plist 文件中添加了Allow Arbitrary Loads=YES

+
    +
  • moduleName 对应的名字和index.js中 AppRegistry.registerComponent(‘SwiftRN’, () => MyApp)注册的名字必须相同
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import UIKit

class RNTestController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "http://localhost:8081/index.bundle?platform=ios")!
let rootView = RCTRootView(
bundleURL: url,
moduleName: "SwiftRN", //这里的名字必须和AppRegistry.registerComponent('MyApp', () => MyApp)中注册的名字一样
initialProperties: nil,
launchOptions: nil
)
view = rootView
}
}
+ + +
+ +
+ +
+ + + + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/page/4/index.html b/page/4/index.html new file mode 100644 index 0000000..885b07f --- /dev/null +++ b/page/4/index.html @@ -0,0 +1,936 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ +
+ + +
+ +

Swift 中错误处理

+

assert 断言

只在debug 模式下程序会自动退出

+
1
public func assert(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)
+

assertionFailure 断言错误

只在debug 模式下程序会自动退出, 没有条件

+
1
public func assertionFailure(_ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)
+

precondition

releas 模式程序也会退出

+
1
public func precondition(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)
+

throw

+ +
+ +
+ +
+ + + +
+ +
+ + +
+ +

Swift 泛型

泛型定义–wikipedia

    +
  1. 在程序编码中一些包含类型参数的类型,也就是说泛型的参数只可以代表类,不能代表个别对象。(这是当今较常见的定义)
  2. +
  3. 在程序编码中一些包含参数的类。其参数可以代表类或对象等等。(现在人们大多把这称作模板)
  4. +
+

泛型的用处

泛型可以实现传入一个指定类型后做一些操作然后返回后还是原来指定的类型,你可以指定特定的类型(特定是指约束)

+

泛型和 Any 的区别

区别:Any能代表任何类型,但是不能返回某个具体的类型,都需要使用 as 去转化,而泛型正好相反

+
    +
  • Any 可以代表任何类型,除了class之外还可以代表struct,enum,
  • +
  • AnyObject 可以代码任何 class 的类型, swift 中基本类型, ArrayDictionary 都是 struct 类型,都不能使用 AnyObject 来接受
  • +
+

Swift中Any 和 AnyObject用法

+

泛型函数和泛型参数

swapTwoValues是一个泛型函数,可以接收或者返回任何类型

+
1
2
3
4
5
func swapTwoValues<T>(inout a: T, inout b: T) {
let temporaryA = a
a = b
b = temporaryA
}
+
    +
  • swapTwoValues 函数名后面的 <T> 表示这个 T 类型是函数所定义的一个类型, T 只是一个类型的占位符,不用关心具体类型

    +
  • +
  • (inout a: T, inout b: T) 中a和b两个参数类型T是泛型参数

    +
  • +
+

类型约束

两个泛型参数的泛型函数

+
1
2
3
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
// function body goes here
}
+
    +
  • 泛型参数T约束条件:必须是SomeClass类的子类
  • +
  • 泛型参数U约束条件:必须遵守SomeProtocol协议
  • +
  • 使用where指定约束
  • +
+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ +

Swift协议和别名

+

协议

某些属性的集合,可以有多个协议组成,具有的特点

+
    +
  1. 可以声明函数,但是不能实现,由实现这协议的具体结构或者类来实现
  2. +
  3. 声明的函数如果有参数也不能设置参数的默认值, 默认参数被认为也是函的一种实现
  4. +
  5. 可以声明属性,必须是计算型属性 只能使用var 修饰,不能是存储型属,必须在 {} 中明确指出属性的读取类型 例如 {get set}
  6. +
  7. 遵守协议的类可选择存储型属性或者计算型属性来实现协议中定义的属性
  8. +
  9. protocol Person: class { } 表示此协议只能被类来遵守,结构体能遵守
  10. +
  11. 协议可以看着是一种类型,如果协议中的属性是只读的,如果是协议类型只能访问get 方法, 但是遵守协议的具体类或者结构可以设置属性的读和写

    +
  12. +
  13. 协议中的构造函数

    +
    - 如果父类中遵守了协议中的构造函数,子类中必须重写父类中的构造函数并且使用require修饰构造函数,表示继承此类的子类也必须实现该构造函数
    +- 如果class 使用了final 修饰,表示不能被继承,此时不需要 require 关键词修饰
    +
  14. +
+

类型别名

    +
  • 定义别名 typealias, 供扩展使用

    +
    1
    2
    3
    4
    5
    6
    7
    8
    typealias Length = Double
    extension Double {
    var km: Length {return self * 1_000.0}
    var m: Length {return self * 100.0}
    var cm: Length {return self / 10.0}
    /// 英尺
    var ft: Length { return self / 3.28084}
    }
    +
  • +
+
使用
+
+
1
let distance: Length = 10.5.km
+
    +
  • 定义别名防止硬编码

    +
    1
    2
    	// 音频采样率
    typealias AudioSample = UInt64
    +
  • +
+

协议中 typealias

协议中定义一个属性,该属性的类型根据不同的情况返回不同的类型(我理解为类似泛型),具体的类型由遵守此协议的类或者结构指定

+
    +
  • 定义协议中别名
  • +
+
1
2
3
4
5
protocol WeightCalculable {
// 属性重量根据不同体积返回的可能是Int,可以可能是double类型,所以使用别名, 协议中使用associatedtype关键词,
associatedtype WeightType
var weight: WeightType { get }
}
+

扩展 Int 中的吨单位

+
1
2
3
4
extension Int {
typealias Weight = Int
var t: Weight {return self * 1_000}
}
+
    +
  • 使用协议中别名
  • +
+
    +
  • 一部手机的时使用该协议WeightCalculable
  • +
+
1
2
3
4
5
6
class iPhone: WeightCalculable {
typealias WeightType = Double
var weight: WeightType {
return 0.5
}
}
+
    +
  • 一辆大卡时使用该协议WeightCalculable
  • +
+
1
2
3
4
5
6
7
8
class Car: WeightCalculable {
typealias WeightType = Int
let weight: WeightType

init(weight: Int) {
self.weight = weight
}
}
+
1
let bigCar = Car(weight: 8_000.t) // 8 吨
+

面向协议编程

    +
  • 定义比赛协议,具有 赢/输/总场次/胜率 等特性
  • +
+
1
2
3
4
5
6
protocol Recordable: CustomStringConvertible {
var wins: Int { get }
var losses: Int { get }

func winningPerent() -> Double
}
+
    +
  • 协议中方法或者属性的默认实现
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
extension Recordable {
// CustomStringConvertible 协议默认实现
var description: String {
return String(format: "WINS: %d---LOSSES: %d", [wins, losses])
}

// 默认实现总场数计算型属性
var totalGames: Int {
return wins + losses
}

// 默认实现的方法
func shoutWins() {
print("come on", wins, "times!")
}
}
+

篮球比赛:

    +
  • Equatable 协议 - 相等比较 需要重载 ==
  • +
  • Comparable 协议 - 是否可比较 ,重载 < 可直接调用sort()函数
  • +
  • CustomStringConvertible 协议 - 打印信息,需重写 description
  • +
  • Recordable协议 - 比赛协议,没有平局
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
struct BasketballRecord:Equatable, Comparable, CustomStringConvertible, Recordable {
var wins: Int
var losses: Int


/// 在具体定义的类型中实现 协议中的属性或者方法就会覆盖协议中的
var totalGames: Int = 200

var description: String {
return "本次比赛胜: " + String(wins) + "负" + "\(losses)"
}

// 胜率
func winningPerent() -> Double {
return (Double(wins) / Double(totalGames))
}

// swift4.1 后,不需要再重写此方法,swift 会自动合成,如果不想让某一个属性参与比较,就需要重写该方法
static func == (lhs: BasketballRecord, rhs: BasketballRecord) -> Bool {
return lhs.wins == rhs.wins && lhs.losses == rhs.losses
}

// Comparable
static func < (lhs: BasketballRecord, rhs: BasketballRecord) -> Bool {
if lhs.wins != rhs.wins {
return lhs.wins < rhs.wins
}
return lhs.losses > rhs.losses
}
}
```

#### 足球比赛:

有平局的情况,原来的协议不能满足,需要增加一个平局的协议

- 定义平局协议
+

protocol Tieable {
/// 平局,可读写
var ties: Int { get set }

+

}

1
2
3
4

- 足球比赛需要遵守的协议 `Recordable` 和 `Tieable`

- 总场数需要加上平局的场数,需要实现totalGames的get方法

+

struct FootableRecord: Recordable, Tieable {
var wins: Int
var losses: Int
// 平局数
var ties: Int

+
// 总场数
+var totalGames: Int {
+    return wins + losses + ties
+}
+
+var description: String {
+    return "本次比赛胜: " + String(wins) + "负" + "\(losses)"
+}
+
+func winningPerent() -> Double {
+    return (Double(wins) / Double(totalGames))
+}
+

}

1
2
3
4
5
6
7
8
9
10
11
12

- 足球比赛中以上写法存在的问题

- 所有有平局的比赛计算总场次的逻辑都是一样,每次都要写一次实现
- 单一修改 Recordable 协议不能解决问题,
- 存在平局和没有平局两种情况 totalGames 的计算逻辑不相同

- 解决办法:
- 扩展遵守了`Recordable`协议的类型,前提条件是:这个类型遵守了 Tieable 同时也遵守了 `Recordable ` 协议,可以理解为如果该类型遵守了 `Tieable `协议之后,`Recordable` 协议中的 totalGames 属性实现是另一种方式了,不在是以前的 `totalGames` 中直接返回 `wins + losses` 的值


定义协议

+

protocol Recordable: CustomStringConvertible {
var wins: Int { get }
var losses: Int { get }

+
func winningPerent() -> Double
+

}

1
2

协议默认实现

+

extension Recordable {
var description: String {
return String(format: “WINS: %d—LOSSES: %d”, [wins, losses])
}

+
// 默认实现总场数计算型属性
+var totalGames: Int {
+    return wins + losses
+}
+
+// 默认实现胜率
+func winningPerent() -> Double {
+    return (Double(wins) / Double(totalGames))
+}
+
+// 默认实现的方法
+func shoutWins() {
+    print("come on", wins, "times!")
+}
+

}

+
1
2

扩展遵守了Tieable 的 类型的 Recordable 协议
+

extension Recordable where Self: Tieable {
var totalGames: Int {
return wins + losses + ties
}
}

+
1
2
3
4
5
6

### 协议聚合

如果对某一类型需要限制他必须遵守某些协议,可以使用协议聚合来定义

比如: 有个奖赏协议
+

protocol Prizable {
func isPrizable() -> Bool
}

+
1
2

篮球比赛遵守此协议
+

// 篮球比赛奖赏
extension BasketballRecord: Prizable {
func isPrizable() -> Bool {
return totalGames > 10 && winningPerent() > 0.5
}
}

+
1
2

足球比赛奖赏遵守此协议
+

extension FootableRecord: Prizable {
func isPrizable() -> Bool {
return wins > 1
}
}

1
2

现在有某一个学生也遵守了此协议

+

struct Student: Prizable {
var name: String
var score: Int

+
func isPrizable() -> Bool {
+    return score >= 60
+}
+

}

+
1
2

定义奖赏的方法,参数类型必须是遵守了此协议的结构或者类型
+

private func award(_ one: Prizable) {
if one.isPrizable() {
print(one)
print(“恭喜获得奖励”)
} else {
print(one)
print(“很遗憾”)
}
}

1
2

如果说 BasketballRecord 这个类还遵守了其他的协议,例如遵守了 `Recordable` 协议, 并且这个协议也遵守了 `CustomStringConvertible` 并且默认实现了`description` 的`get` 方法

+

// MARK: - 比赛协议,具有 赢。输。总场次。胜率 特性
protocol Recordable: CustomStringConvertible {
var wins: Int { get }
var losses: Int { get }

+
func winningPerent() -> Double
+

}

+
1
2

`Recordable` 默认实现
+

extension Recordable {
var description: String {
return String(format: “WINS: %d—LOSSES: %d”, [wins, losses])
}

+
// 默认实现总场数计算型属性
+var totalGames: Int {
+    return wins + losses
+}
+
+// 默认实现胜率
+func winningPerent() -> Double {
+    return (Double(wins) / Double(totalGames))
+}
+
+// 默认实现的方法
+func shoutWins() {
+    print("come on", wins, "times!")
+}
+

}

1
2
3
4
5

此时如果 `Student` 类还是调用 `award` 方法的话,print(one) 打印的信息将是`Recordable `中默认实现的内容,因此需要约束`award`函数的参数必须遵守两个协议让`Student` 也重写自己的`description `属性的`get`方法,不能再让 `Prizable` 扩展 默认实现

- swift 3 写法: protocol<A, B>
- swift 4 写法: A & B

+

private func award2(_ one: Prizable & CustomStringConvertible) {
if one.isPrizable() {
print(one)
print(“恭喜获得奖励”)
} else {
print(one)
print(“很遗憾”)
}
}

1
2
3
4
5
6
7
8
9
10

### 泛型约束


- 定义一个函数,找出一个学生数组中分数最大的
- 参数:一个学生数组,都遵守了 Comparable 的类型
- 返回值:某个遵守了 Comparable 的类型实例
- 此时函数报错 `Protocol 'Comparable' can only be used as a generic constraint because it has Self or associated type requirements`,

因为 Comparable 协议中定义的方法 public static func < (lhs: Self, rhs: Self) -> Bool 的参数类型是Self,是具体的某个类型

+

func maxScore(seq: [Comparable]) -> Comparable { }

1
2
3
4
5


- 如果需要定义一个函数实现在一个数组中找出需要奖励的人的名字该如何实现呢
- 参数:遵守两个协议 Comparable 和 Prizable 协议, 并且使用泛型
- 返回值:返回值是可选值,有可能没有任何奖励的对象

+

func topPrizable<T: Comparable & Prizable>(seq: [T]) -> T? {
return seq.reduce(nil) { (tempTop: T?, condender: T) in
guard condender.isPrizable() else { return tempTop }
// 解包 condender 失败, 上一层验证了他必须是奖励的那个
guard let tempTop = tempTop else { return condender }
return max(tempTop, condender)
}
}

+

`

+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ +

Swift协议扩展

+

定义结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct Point {
var x = 0.0
var y = 0.0

}

struct Size {
var height = 0.0
var width = 0.0
}

struct Rectangle {
var origin = Point()
var size = Size()

init(origin: Point, size: Size) {
self.origin = origin
self.size = size
}
}
+

扩展

    +
  • 扩展方法
  • +
+
1
2
3
4
5
6
extension Rectangle {
mutating func translate(x: Double, y: Double) {
self.origin.x += x
self.origin.y += y
}
}
+
    +
  • 只能扩展计算型的属性,不能扩展存储型属性, 存储型属性需要在定义类或者结构体时声明
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
extension Rectangle {
var center: Point {
get {
let center_x = origin.x + size.width / 2.0
let center_y = origin.y + size.height / 2.0
return Point(x: center_x, y: center_y)
}

set {
origin.x = newValue.x - size.width / 2.0
origin.y = newValue.y - size.height / 2.0
}
}
}
+
    +
  • 扩展构造方法

    +
      +
    • 类中不能扩展指定构造方法,只能在结构体中扩展
    • +
    • 结构体中不能扩展便利构造方法m,只能在类中扩展
    • +
    +
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

extension Rectangle {
init(center: Point, size: Size) {
let origin_x = center.x - size.width / 2.0
let origin_y = center.y - size.height / 2.0
self.origin = Point(x: origin_x, y: origin_y)
self.size = size
}

// 结构体中不能扩展便利构造函数 Delegating initializers in structs are not marked with 'convenience'
// convenience init(center: Point, size: Size) {
// let origin_x = center.x - size.width / 2.0
// let origin_y = center.y - size.height / 2.0
// self.origin = Point(x: origin_x, y: origin_y)
// self.size = size
// }
}
+
    +
  • 扩展嵌套类型
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
extension Rectangle {
enum Vertex: Int {
case left_top
case left_bottom
case right_bottom
case right_top
}

// 获取某一个顶点坐标
func point(of vertex: Vertex) -> Point {
switch vertex {
case .left_top:
return origin
case .left_bottom:
return Point(x: origin.x, y: origin.y + size.height)
case .right_bottom:
return Point(x: origin.x + size.width, y: origin.y + size.height)
case .right_top:
return Point(x: origin.x + size.width, y: origin.y)
}
}
}
+
    +
  • 扩展下标,根据传入的索引获取对应顶点坐标
  • +
+
1
2
3
4
5
6
extension Rectangle {
subscript(index: Int) -> Point? {
assert(0 <= index && index < 4, "传入值非法")
return point(of: Vertex(rawValue: index)!)
}
}
+

扩展系统方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
extension Int {
/// 平方
var square: Int {
return self * self
}

/// 立方
var cube: Int {
return self * self * self
}

/// 判断数组是否在某一个范围内
func inRange(clousedLeft left: Int, openRight right: Int) -> Bool {
return self >= left && self > right
}

/// 重复执行操作
func repeatitions(task: () -> ()) {
for _ in 0..<self {
task()
}
}

/// 变长函数
func stride(from: Int, to: Int, by: Int, task: () -> ()) {
// 系统变长函数
for i in Swift.stride(from: 0, to: 21, by: 3) {
print(i)
}

for _ in Swift.stride(from: from, to: to, by: by) {
task()
}
}
}
+

扩展后使用

+
1
2
3
4
5
6
7
8
9
10
11
12
print(2.square)
print(3.cube)
print(4.inRange(clousedLeft: 0, openRight: 4))
4.repeatitions {
print("extension")
}


// 使用变长函数
4.stride(from: 0, to: 8, by: 4) {
print("stride")
}
+ + +
+ +
+ +
+ + + + + + + +
+ +
+ + +
+ +

Swift下标和运算符重载

+

字典数组下标

var array = [1,2,3,4,5,6]
+let temp = array[0]     // 通过下标访问
+let temp2 = array[2]    // 通过下标访问
+

结构体下标

    +
  • 对于结构体不是直接使用下标访问,会直接报错
    直接使用下标获取属性值报错: Type 'Vector_3' has no subscript members

    +
  • +
  • 可以通过实现 subscript(index: Int) -> Double?方法让结构体支持下标访问,可以理解为一个特殊的函数,需要传入参数和返回值

    +
  • +
  • 可以增加第二种下标–根据坐标轴获取属性值
  • +
  • 重写 subscript 的 set 方法 可以使用下标修改属性值
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
struct Vector_3 {
var x = 0.0
var y = 0.0
var z = 0.0

// 根据索引获取
subscript(index: Int) -> Double? {

// 重写了get方法 实现通过下标获取属性值
get {
switch index {
case 0: return x
case 1: return y
case 2: return z
default: return nil
}
}

// 重写 set 方法,实现g通过下标设置属性值,使用系统默认的newValue
set {
guard let newValue = newValue else {return}
switch index {
case 0: x = newValue
case 1: y = newValue
case 2: z = newValue
default: return
}
}
}

// 增加第二种下标--根据坐标轴获取属性值
subscript(axis: String) -> Double? {

get {
switch axis {
case "X", "x": return x
case "Y", "y": return y
case "Z", "z": return z
default: return nil
}
}

set {
guard let newValue = newValue else {return}
switch axis {
case "X", "x": x = newValue
case "Y", "y": y = newValue
case "Z", "z": z = newValue
default: return
}
}
}
}

```

### 下标访问

结构体使用下标获取值
+

var v = Vector_3(x: 1, y: 2, z: 3)
print(v[0], v[1], v[2], v[3], v[100]) // Optional(1.0) Optional(2.0) Optional(3.0) nil nil
print(v[“x”], v[“y”], v[“z”], v[“i”]) // Optional(1.0) Optional(2.0) Optional(3.0) nil

+
1
2

结构体使用下标修改值
+

v[0] = 101
v[1] = 202
v[2] = 303
v[3] = 400
v[100] = 51
print(v[0], v[1], v[2], v[3], v[100]) // Optional(101.0) Optional(202.0) Optional(303.0) nil nil

1
 

+

v[“x”] = 100
v[“y”] = 200
v[“z”] = 300
v[“i”] = 50
print(v[“x”], v[“y”], v[“z”], v[“i”]) // Optional(100.0) Optional(200.0) Optional(300.0) nil

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

### 多维下标

定义一个关于矩阵的结构体

struct Matrix {
var data: [[Double]]
let row: Int
let column: Int

init(row: Int, column: Int) {
self.row = row
self.column = column
data = [[Double]]()
for _ in 0..<row {
let aRow = Array(repeating: 1.0, count: column)
data.append(aRow)
}
}

// 通过下标 [row,column] 方式访问
subscript(row: Int, column: Int) -> Double {
get {
assert(row >= 0 && row < self.row && column >= 0 && column < self.column, "下标不合法")
return data[row][column]
}

set {
assert(row >= 0 && row < self.row && column >= 0 && column < self.column, "下标不合法")
data[row][column] = newValue
}
}

// 通过下标 [row][column] 方式访问
subscript(row: Int) -> [Double] {
get {
assert(row >= 0 && row < self.row, "下标不合法")
// 直接返回数组,数组本身有下标
return data[row]
}

set {
assert(newValue.count == column, "下标不合法")
data[row] = newValue
}
}
}


### 运算符重载

- 重载 + 运算符
+

func +(one: Vector_3, other: Vector_3) -> Vector_3 {
return Vector_3(x: one[0]! + other[0]!, y: one[1]! + other[1]!, z: one[2]! + other[2]!)

+

// return Vector_3(x: one.x + other.x, y: one.y + other.y, z: one.z + other.z)
}

+
1
2

- 两个参数时相减
+

func -(one: Vector_3, other: Vector_3) -> Vector_3 {
return Vector_3(x: one.x - other.x, y: one.y - other.y, z: one.z - other.z)
}

+
1
- 一个参数时去反, 需要 prefix 修饰
+

prefix func -(a: Vector_3) -> Vector_3 {
return Vector_3(x: -a.x, y: -a.y, z: -a.z)
}

1
2

- 向量相乘/向量和常量相乘

+

func (one: Vector_3, other: Vector_3) -> Double {
return (one.x
other.x) + (one.y other.y) + (one.z other.z)
}

+
1
2

- 两个参数不能交换,需要重载两次 `*`
+

func (one: Vector_3, a: Double) -> Vector_3 {
return Vector_3(x: a
one.x, y: a one.y, z: a one.z)
}

+

func (a: Double, one: Vector_3) -> Vector_3 {
return one
a

+
// 也可采用下面写法
+

// return Vector_3(x: a one.x, y: a one.y, z: a * one.z)
}

+
1
2

- 修改自身参数,不需要返回值
+

func +=(one: inout Vector_3, other: Vector_3) {
// 已经重载过 + 运算符,可以直接调用
one = one + other
}

+

func ==(one: Vector_3, other: Vector_3) -> Bool {
return one.x == other.x &&
one.y == other.y &&
one.z == other.z
}

+

func !=(one: Vector_3, other: Vector_3) -> Bool {
return !(one == other)

+
// 也可采用下面写法
+return one.x != other.x ||
+        one.y != other.y ||
+        one.z != other.z
+

}

+

func <(one: Vector_3, other: Vector_3) -> Bool {
if one.x != other.x {return one.x < other.x}
if one.y != other.y {return one.y < other.y}
if one.z != other.z {return one.z < other.z}
return false
}

+

func <=(one: Vector_3, other: Vector_3) -> Bool {
return one < other || one == other

+
// 也可采用下面写法
+return one.x > other.x &&
+        one.y > other.x &&
+        one.z > other.z
+

}

+

func >(one: Vector_3, other: Vector_3) -> Bool {
return (one <= other)
}

+
1
2
3
4
5
6

### 自定义操作符

`postfix` 声明前后缀关键词, `operator ` 操作符关键词

- a+++
+

声明后置操作符
postfix operator +++
postfix func +++(vector: inout Vector_3) -> Vector_3 {
vector += Vector_3(x: 1.0, y: 1.0, z: 1.0)
return vector
}

+
1
2

- +++a
+

// 声明前置操作符
prefix operator +++
prefix func +++(vector: inout Vector_3) -> Vector_3 {
let temp = vector
vector += Vector_3(x: 1.0, y: 1.0, z: 1.0)
return temp
}

+

`

+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ +

Swift 协议使用

对UIView 扩展

按钮/文本框抖动动画

    +
  • 声明协议

    +
      +
    • 协议中定义属性:遵循该协议的类型都具有此属性

      +
        +
      • 必须明确规定该属性是可读的 {get} 或者可写的 {set},或是可读可写的 {get set}
      • +
      • 使用static修饰声明一个类类型属性
      • +
      +
    • +
    • 协议中定义方法

      +
    • +
    +
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
protocol Shakable {}
extension Shakable where Self: UIView {
func shakeAnimation() {
let animation = CABasicAnimation(keyPath: "position")
animation.duration = 0.08
animation.repeatCount = 5
animation.autoreverses = true
animation.fromValue = NSValue(cgPoint: CGPoint(x: self.center.x - 4, y: self.center.y))
animation.toValue = NSValue(cgPoint: CGPoint(x: self.center.x + 4, y: self.center.y))
layer.add(animation, forKey: "position")
}
}
+
    +
  • 自定义UI控件并遵守协议
  • +
+
1
2
3
class MyButton: UIButton, Shakable {  }
class MySwitch: UISwitch, Shakable { }
class MyTextField: UITextField, Shakable { }
+
    +
  • 使用
  • +
+
1
2
3
4
5
6
7
8
9
10
let mySwitch = MySwitch()
mySwitch.isOn = true
self.mySwitch = mySwitch
view.addSubview(mySwitch)
mySwitch.translatesAutoresizingMaskIntoConstraints = false
mySwitch.topAnchor.constraint(equalTo: view.bottomAnchor, constant: 10).isActive = true
mySwitch.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10).isActive = true

// 调用
mySwitch.shakeAnimation()
+

UITableViewCell使用

定义协议

1
2
3
4
5
6
7
8
9
10
11
protocol ReusableView: class {	}

extension ReusableView where Self: UIView {
static var reuseIdentifier: String {
return String(describing: self)
}
}

```

### 对tableView扩展
+

extension UITableView {
/// 注册cell
func register<T: UITableViewCell>(T: T.Type) where T: ReusableView {
print(T.reuseIdentifier)
self.register(T.self, forCellReuseIdentifier: T.reuseIdentifier)
}

+
/// 调用cell
+func dequeueReusableCell<T: UITableViewCell>(_ indexPath: IndexPath) -> T where T: ReusableView {
+    print(T.reuseIdentifier)
+    return self.dequeueReusableCell(withIdentifier: T.reuseIdentifier, for: indexPath) as! T
+}
+

}

1
2
3
4

### 用法

- 创建自定义的cell,需要遵守协议

+

class TableProtocolCell: UITableViewCell, ReusableView {
override var reuseIdentifier: String? {
return “cell”
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.backgroundColor = .cyan
self.textLabel?.text = “通过协议和泛型创建的cell”
self.textLabel?.textColor = .blue
}
required init?(coder aDecoder: NSCoder) {
fatalError(“init(coder:) has not been implemented”)
}
}

+

class TableProtocolCell2: UITableViewCell, ReusableView {
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.backgroundColor = .red
self.textLabel?.text = “cell1 - 通过协议和泛型创建的”
self.textLabel?.textAlignment = .right
}
required init?(coder aDecoder: NSCoder) {
fatalError(“init(coder:) has not been implemented”)
}
}

1
2

- 注册和使用

+

class TableViewProtocolController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(T: TableProtocolCell.self)
tableView.register(T: TableProtocolCell2.self)
}

+
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+    return 10
+}
+
+override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+    var cell = UITableViewCell()
+    if indexPath.row % 2 == 0 {
+        cell = tableView.dequeueReusableCell(indexPath) as TableProtocolCell
+    } else {
+        cell = tableView.dequeueReusableCell(indexPath) as TableProtocolCell2
+    }
+    return cell
+}
+

}

+

`

+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ +

Swift 学习

+

枚举

1. 定义 rawValue 为 Int 类型,初始值为1,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
enum Month: Int {
case January = 1
case February
case March
case April
case May
case June
case July
case August
case September
case October
case November
case December
}
+

2. rawValue 的整型值可以不连续

1
2
3
4
5
6
enum Season: Int {
case Spring = 1
case Summer = 5
case Autumn = 10
case Winter = 40
}
+

3. 枚举可以是字符串,字符串是变量名rawValue 的值相等

1
2
3
4
5
6
7
enum ProgrammerLanguae: String {
case Swift
case OC = "Objective-C"
case C = "语言"
case RN = "React-Native"
case Java
}
+

使用rawValue

+
1
2
3
func residueNewYear(month: Month) -> Int {
return 12 - month.rawValue
}
+

调用枚举的Moth的构造函数生成一个Month

+
1
2
3
4
let month = Month(rawValue: 5)
let swift = ProgrammerLanguae.Swift.rawValue
let oc = ProgrammerLanguae.OC
let RN = ProgrammerLanguae.RN.rawValue
+

4. 枚举关联值 associate value

1
2
3
4
5
6
7
8
9
10
11
12
enum Status {
case success(Int)
case fail(String)
case null // 未关联值
}

func associateValueTest(isSuccess: Bool) -> Status {
if isSuccess {
return .success(200)
}
return .fail("失败")
}
+

5. 枚举associateValue多个值

    +
  • 本质是关联了一个元祖(value0, value1, value2…)
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
enum Shape {
case Square(width: Double)
case Reactangle(width: Double, height: Double)
case Circle(x: Double, y: Double, radius: Double)
case Point
}

let square = Shape.Square(width: 20)
let reactangle = Shape.Reactangle(width: 10, height: 20)
let circle = Shape.Circle(x: 10, y: 10, radius: 20)
let point = Shape.Point

private func area(shape: Shape) -> Double {
switch shape {
case let .Square(width):
return width * width
case let .Reactangle(width, height):
return width * height
case let .Circle(_, _, radius):
return radius * radius * Double.pi
case .Point:
return 0
}
}
+

6. 递归枚举

    +
  • 定义一个递归算术表达式
  • +
  • 使用 indirect 来修饰
  • +
+
1
2
3
4
5
6
7
8
indirect enum ArithmeticalExpression {
case Number(Int)
case Addition(ArithmeticalExpression, ArithmeticalExpression) // + 时两边也是一个表达式
case Multiplication(ArithmeticalExpression, ArithmeticalExpression) // * 时两边也是一个表达式

// indirect case Addition(ArithmeticalExpression, ArithmeticalExpression) // + 时两边也是一个表达式
// indirect case Multiplication(ArithmeticalExpression, ArithmeticalExpression) // * 时两边也是一个表达式
}
+
    +
  • 递归表达式使用 (2+3) * 4
  • +
+
1
2
3
4
let two = ArithmeticalExpression.Number(2)
let one = ArithmeticalExpression.Number(3)
let sum = ArithmeticalExpression.Addition(two, one)
let indirectEnumResult = ArithmeticalExpression.Multiplication(sum, ArithmeticalExpression.Number(4))
+
    +
  • 计算表达式值的函数
  • +
+
1
2
3
4
5
6
7
8
9
10
private func calculate(expression: ArithmeticalExpression) -> Int {
switch expression {
case let .Number(value):
return value
case let .Addition(left, right):
return calculate(expression: left) + calculate(expression: right)
case let .Multiplication(left, right):
return calculate(expression: left) * calculate(expression: right)
}
}
+

结构体

    +
  • 结构体中的属性值没有初始化时必须使用构造函数来初始化,否则报错
  • +
  • +
+
1
2
3
4
5
6
7
struct Location {
var latitude: Double
var longitude: Double
var placeName: String?
}

let location1 = Location(latitude: 37.3230, longitude: -122.0322, placeName: "测试")
+
    +
  • 结构体中属性如果都赋了初始值就可以直接初始化
  • +
+
1
2
3
4
5
6
struct Location2 {
var latitude: Double = 0
var longitude: Double = 0
}

let location2 = Location2()
+
    +
  • 如果未指定初始值,swift 就不会默认初始化为(不初始化)
  • +
  • 当属性值为可选值时此时允许值为nil,那么就允许不通过构造函数来初始化赋值
  • +
  • let 只有一次赋值机会
  • +
  • 不管是类还是结构体都应该提供一个全参数的构造函数 init(latitude: Double, longitude: Double, placeName: String?)
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct Location3 {
let latitude: Double
let longtitude: Double
var placeName: String?

init(coordinateString: String) {
let index = coordinateString.index(of: ",")
let index1 = coordinateString.index(after: index!)
let test_1 = coordinateString.prefix(upTo: index!)
let test_2 = coordinateString.suffix(from: index1)

latitude = Double(test_1)!
longtitude = Double(test_2)!
}

init(latitude: Double, longitude: Double, placeName: String?) {
self.latitude = latitude
self.longtitude = longitude
self.placeName = placeName
}
}

let test3 = Location3(coordinateString: "12,45")
+

属性和方法

计算型属性

类型属性

类型方法

属性管擦器

延迟属性

访问控制

单利模式

继承和构造函数

swift 中继承

多态性

属性和函数重载

子类两段式构造

    +
  • 子类构造函数分为两段,第一段是构造自己,第二段是构造父类

    +
      +
    • 父类中构造函数
    • +
    • 子类要设置一个自己的构造函数
    • +
    • 子类构造函数中需要先初始化自己的属性
    • +
    • 然后在构造函数中调用父类初始化父类构造函数中的相关属性

      +

      父类

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      class Father {
      var name: String
      var sex: Int = 0
      var old: Int = 32
      var desc: String {
      return "我是\(name)"
      }
      init(name: String) {
      self.name = name
      }

      func run() {
      print("Father can Running")
      }
      }
      +
    • +
    +
  • +
+
子类
+
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Son: Father {

var isSwimming = true

var computer: String

override var desc: String {
return "Son is \(name)"
}

init(name: String, computer: String) {
self.computer = computer
super.init(name: name)
}
}
+
    +
  • 子类两段构造都完成后子类的初始化才完成,可以使用self调用属性或者方法

    +
    1
    2
    3
    4
    5
    init(name: String, computer: String) {
    self.computer = computer
    self.getToys() // 此代码报错,子类初始化未完成
    super.init(name: name)
    }
    +
  • +
+

便利构造函数和指定构造函数

    +
  • 便利构造函数只能调用自己的指定构造函数
  • +
  • 指定构造函数通过一系列的调用都会调用super.init()
  • +
  • 便利构造函数无法调用super.init()
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Father {
var name: String
var sex: Int = 0
var old: Int = 32
var desc: String {
return "我是\(name)"
}
init(name: String) {
self.name = name
}

init(name: String, old: Int) {
self.name = name
self.old = old
}

func run() {
print("Father can Running")
}
}

class Son: Father {
var isSwimming = true
var computer: String

override var desc: String {
return "Son is \(name)"
}

// 子类指定构造函数,调用了父类的指定构造函数
init(name: String, computer: String) {
self.computer = computer
super.init(name: name)
}

// 子类便利构造函数,调用了指定构造函数
convenience override init(name: String) {
let computer = "iMac"
self.init(name: name, computer: computer)
}
}
+

构造函数继承

    +
  • 子类有可能会继承父类的构造函数
  • +
  • 子类没有实现父类的任何指定构造函数;则自动继承父类的所有指定构造函数, 因为继承了指定构造函数所以同时便利构造函数也被继承
  • +
+

父类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Father {
var name: String
var sex: Int = 0
var old: Int = 32
var desc: String {
return "我是\(name)"
}

/// Father指定构造函数-1
init(name: String) {
self.name = name
}

/// Father指定构造函数-2
init(name: String, old: Int) {
self.name = name
self.old = old
}

/// Father便利构造函数
convenience init(old: Int) {
self.init(name: "Father", old: old)
}

func run() {
print("Father can Running")
}
}

+

子类

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Son: Father {
var isSwimming = true
var computer: String
var job: String

override var desc: String {
return "Son is \(name)"
}

/// 子类重载的指定构造函数-1
convenience override init(name: String) {
self.init(name: name, computer: "Dell")
}

/// 子类重载的指定构造函数-2
override convenience init(name: String, old: Int) {
self.init(name: name, computer: "acer")
}


/// 子类自己的指定构造函数,调用了父类的指定构造函数
init(name: String, computer: String) {
self.computer = computer
self.job = "C#"
super.init(name: name)
}

// 子类便利构造函数,调用了自己指定构造函数
convenience init(computer: String) {
let name = "小张"
self.init(name: name, computer: computer)
}
}
+

未实现父类任何的指定构造函数

+
1
2
3
class Grandson: Son {
var toy: String = "dog toys"
}
+

子类可以调用的构造函数

+
1
2
3
4
5
let grandSon0 = Grandson(old: 4)
let grandSon1 = Grandson(computer: "Mi")
let grandSon2 = Grandson(name: "小王")
let grandSon3 = Grandson(name: "小虎", computer: "👽")
let grandSon4 = Grandson(name: "小李", old: 8)
+
    +
  • 子类实现了父类所有的指定构造函数,则自动继承父类的所有便利构造函数
  • +
+
1
2
3
4
5
let son0 = Son(old: 30)
let son1 = Son(computer: "Mi")
let son2 = Son(name: "小王")
let son3 = Son(name: "小虎", computer: "👽")
let son4 = Son(name: "小李", old: 8)
+

required 构造函数

    +
  • 父类中有被 required 关键词修饰的构造函数子类必须实现此构造函数
  • +
  • 子类实现的此构造函数不需要再使用 override 关键词修饰,需要 required 修饰
  • +
+

子类

+
1
2
3
4
5
6
7
8
class Father {
var name: String
/// Father指定构造函数-1
// required 修饰的指定构造函数子类中必须实现
required init(name: String) {
self.name = name
}
}
+

父类

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Son: Father {
var isSwimming = true
var computer: String

/// 子类重载的指定构造函数-1
convenience required init(name: String) {
self.init(name: name, computer: "Dell")
}

/// 子类自己的指定构造函数,调用了父类的指定构造函数
init(name: String, computer: String) {
self.computer = computer
self.job = "C#"
super.init(name: name)
}
}
+
    +
  • 子类如果实现了自己的指定构造函数,那么 required 修饰指定构造函数就初始化失败,因为自己实现了指定构造函数所以不能继承父类中的构造函数,此时可以自己实现被required 修饰的便利构造函数
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Son1: Son {
var sport: String

// 指定构造函数,需要调用父类的指定构造函数
init(name: String, sport: String) {
self.sport = sport
Son.init(name: name)
}

// 父类中有被 required 关键词修饰的必须实现的构造函数
convenience required init(name: String) {
// fatalError("init(name:) has not been implemented")
// 调用自己的指定构造函数,
self.init(name: name, sport: "")
}
}
+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ +

RN学习中遇到的错误总结

+

执行 npm install 时报错

错误1. Unexpected end of JSON input while parsing near '...er":"0.4.0"},"bin":{"'

本地有package.json文件,执行 npm install 报错,或者clone 的项目执行npm install 也报同样的错误, 可能是因为缓存导致,

+

解决办法:

+
    +
  1. 设置镜像源,以前可能大部分都是用的淘宝

    +
    1
    npm set registry https://registry.npmjs.org/
    +
  2. +
  3. 清除缓存

    +
    1
    npm cache clean –-force
    +
  4. +
  5. 如果提示失败,使用sudo

    +
    1
    sudo npm cache clean --force
    +
  6. +
+

错误2. xcrun: error: unable to find utility "simctl", not a developer tool or in PATH

可能是因为 Xcode 版本问题引起,XCode 偏好设置中 Command line Tools 中为选择版本问题导致(我的情况是安装了Xcode 10 beta版导致的)

+

解决办法:

+

+
1
2
3
4
5
6
7
8
9
10
11
12
The following build commands failed:
CompileC /Users/liepin/LiePinWorkspace/RNDemo/GitHubPopular/ios/build/Build/Intermediates.noindex/RCTWebSocket.build/Debug-iphonesimulator/RCTWebSocket.build/Objects-normal/x86_64/RCTSRWebSocket.o RCTSRWebSocket.m normal x86_64 objective-c com.apple.compilers.llvm.clang.1_0.compiler
(1 failure)
Installing build/Build/Products/Debug-iphonesimulator/GitHubPopular.app
An error was encountered processing the command (domain=NSPOSIXErrorDomain, code=2):
Failed to install the requested application
An application bundle was not found at the provided path.
Provide a valid path to the desired application bundle.
Print: Entry, ":CFBundleIdentifier", Does Not Exist

Command failed: /usr/libexec/PlistBuddy -c Print:CFBundleIdentifier build/Build/Products/Debug-iphonesimulator/GitHubPopular.app/Info.plist
Print: Entry, ":CFBundleIdentifier", Does Not Exist
+

错误3. Error: Cannot find module '../lib/utils/unsupported.js'

安装的node 版本不是稳定的版本,需要删除后重新安装

+
1
2
sudo rm -rf /usr/local/lib/node_modules/npm
brew reinstall node
+

错误4. Couldn't find preset "module:metro-react-native-babel-preset" when running jest

解决方法

+

已有项目集成RN

报错1. 文件路径错误,查看你的node_modules 文件夹是在当前目录还是上级目录

+
[!] No podspec found for `React` in `../node_modules/react-native
+
    +
  • ../ 是指父级目录

    +
  • +
  • ./ 是指当前目录

    +
  • +
+

报错 2. yoga Y 大小写为问题

+
[!] The name of the given podspec `yoga` doesn't match the expected one `Yoga`_modules/react-native/ReactCommon/yoga"
+

pod install 之后

报错 1.

+

解决办法: pod ‘React’, :subspecs 中加入 ‘CxxBridge’, ‘DevSupport’

+
pod 'React', :path => './node_modules/react-native', :subspecs => [
+'Core',
+'RCTText',
+'RCTNetwork',
+'RCTWebSocket', # 这个模块是用于调试功能的
+'CxxBridge', # Include this for RN >= 0.47
+'DevSupport', # Include this to enable In-App Devmenu if RN >= 0.43
+

]

+

报错 2.

RCTAnimation/RCTValueAnimatedNode.h' file not found

+

解决方法:

+

+
1
#import <RCTAnimation/RCTValueAnimatedNode.h>
+

替换为

+
1
#import "RCTValueAnimatedNode.h"
+

修改头文件导入方式

+

报错3 版本不匹配 react native version mismatch javascript version 0.54.3 native 0.57.2

    +
  • 关闭所有的 terminal,或者是集成开发环境的命令行窗口

    +
    1
    2
    watchman watch-del-all
    react-native start --reset-cache
    +
  • +
  • 重新打开一个terminal窗口

    +
    1
    react-native run-ios
    +

    问题描述和解决1

    +

    问题描述和解决2

    +
  • +
+

报错4 Unable to resolve module "schedule/tracking"

缺少一些开发依赖的库

+
npm i schedule@0.4.0 --save-dev
+

问题描述和解决

+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ +

React-Native开发iOS打包

打jsbudnle包

创建打包后存放文件目录

    +
  • cd 到 React-Native 项目根目录下 并且 进入 ios 文件目录下
  • +
  • 创建 bundle 文件夹

    +
    1
    mkdir bundle
    +
  • +
+

配置 React-Native 打包命令

    +
  • 打包命令

    +
    1
    react-native bundle
    +
  • +
  • 在React Native项目的根目录下执行命令

    +
    1
    react-native bundle --entry-file index.js --platform ios --dev false --bundle-output ./ios/bundle/index.jsbundle --assets-dest ./ios/bundle
    +
  • +
+
- `--entry-file`: ios或者android入口的js名称,比如 `index.js`
+
+- `--platform`: 平台名称(ios或者android)
+
+- `--dev`: 设置为`false`的时候将会对`JavaScript`代码进行优化处理。
+
+- `--bundle-output`: 生成的jsbundle文件的名称和路径,比如 `./ios/bundle/index.jsbundle`
+
+- `--assets-dest`: 图片以及其他资源存放的目录
+

使用打包命令

    +
  • 将打包命令添加到 packger.json

    +
    1
    2
    3
    4
    5
    "scripts": {
    "start": "node node_modules/react-native/local-cli/cli.js start",
    "test": "jest",
    "bundle-ios": "node node_modules/react-native/local-cli/cli.js bundle --entry-file index.js --platform ios --dev false --bundle-output ./ios/bundle/index.jsbundle --assets-dest ./ios/bundle"
    },
    +
  • +
+
    +
  • 再次打包只需要 React Native项目的根目录下执行命令,不用再次输入类似上面的命令

    +
    1
    npm run bundle-ios
    +
  • +
+

集成到Xcode(iOS原生项目中)

    +
  • 添加已经打好的.jsbundle 离线包

    +

    +
  • +
+
    +
  • iOS 项目代码配置

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    private func loadReactNativeTest() {
    // debug 测试加载本地文件
    let url = URL(string: "http://localhost:8081/index.bundle?platform=ios")!

    // 加载已经加入的离线包
    let url2 = Bundle.main.url(forResource: "index", withExtension: "jsbundle")

    let rootView = RCTRootView(
    bundleURL: url,
    moduleName: "MyApp", //这里的名字要和index.js中相同
    initialProperties: nil,
    launchOptions: nil
    )
    view = rootView
    }
    +
  • +
+ + +
+ +
+ +
+ + + + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/page/5/index.html b/page/5/index.html new file mode 100644 index 0000000..158002f --- /dev/null +++ b/page/5/index.html @@ -0,0 +1,951 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ +
+ + +
+ +

Reat Native ref 使用

+

State 状态的使用

在父控件中指定,整个生命周期讲不再改变
需求:一段闪烁的文字, Props 属性显示文字内容创建时初始化,State控制随时间变化是否显示,在父控件中

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Blink extends Component{
// 构造
constructor(props) {
super(props);
// 初始状态
this.state = {showWithText:true};
this.state = {}
setInterval(() => {
this.setState(previousState => {
return {showWithText: !previousState.showWithText}
});
}, 100);
}
render() {
let display= this.state.showWithText ? this.props.text : ' ';
return(
<Text>{display}</Text>
);
}
}
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
export default class App extends Component<Props> {
render() {
return(
<View style={styles.container}>
<Blink
text='I love to blink'
/>

<Blink
text='猜猜猜'
/>

<Blink
text='Blink is great'
/>
</View>
);
}
}
+

Props 属性的使用

1
2
3
4
5
6
7
8
9
// 定义一个组件
class Greeting extends Component {
render() {
return(
<Text> Hello {this.props.name}!
</Text>
);
}
}
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
export default class App extends Component<Props> {
render() {
return(
<View style={styles.container}>
<Image source={{uri:'https://upload.wikimedia.org/wikipedia/commons/d/de/Bananavarieties.jpg'}}
style={{width: 190, height: 110}}
/>
<Text style={styles.instructions}>
使用Props属性
</Text>
<View>
<Greeting name='React-Native'
/>
<Greeting name='iOS'
/>
<Greeting name='Swift'
/>
</View>

</View>
);
}
}
+

ref的使用

    +
  • 可以理解为组件渲染后指向组件的一个应用,可以通过ref获取到真实的组件(类似指针?)
  • +
+

定义

+
1
ref='scrollView'
+

获取

+
1
2
let scrollView = this.refs.scrollView
let scrollView = this.refs['scrollView']
+
    +
  • 可以通过ref访问组件的属性和方法
  • +
+

ES6 定时器

    +
  • 开启定时器
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let scrollView = this.refs.scrollView;
// 2.4timer 定时器的写法
this.timer = setInterval(
()=>{
var tempPage = 0;
// 修改banner索引
if ((this.state.currentPage+1) >= ImageData.data.length) {
tempPage = 0;
} else {
tempPage = this.state.currentPage+1;
}

// 更新状态
this.setState({
currentPage: tempPage
})

let offSet_x = width * tempPage
scrollView.scrollResponderScrollTo({x:offSet_x, y:0, animated: true})
},
this.state.duration
);
+
    +
  • 暂停定时器
  • +
+
1
this.timer && clearInterval(this.timer)
+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ +

js 学习

+

数据类型

1
2
3
4
5
6
7
8
var fullName;
// undefined

fullName
// undefined

fullName + 2
// NaN
+
    +
  • 判断类型名称

    +
    1
    2
    3
    4
    fullName = '张'
    "张"
    typeof(fullName)
    // "string"
    +
  • +
+
1
2
3
4
var weight = 100;
undefined
typeof(weight)
// "number"
+ + + +
1
2
3
4
var firstName = '王', lastName = '张'
undefined
firstName + lastName
// "王张"
+
    +
  • 类型不相同时转换后在拼接

    +
    1
    2
    3
    4
    var weightIncrease = '2.5斤'
    undefined
    weight + weightIncrease
    // "1002.5斤"
    +
  • +
+

数组

var array = [];
+undefined
+array.length
+// 0
+typeof(array)
+// "object"
+
    +
  • 增加元素

    +
    1
    2
    3
    4
    array.push('1')
    // 4
    array
    // (4) ["ab", "cbd", "fcg", "1"]
    +
  • +
  • 数组前面添加

    +
  • +
+
1
2
3
4
array.unshift('0')
// 6
array
// (6) ["0", "ab", "cbd", "fcg", "1", Array(2)]
+
    +
  • 删除最后元素

    +
    1
    2
    3
    4
    array.pop() // 返回删除的元素
    // (2) ["2", "3"]
    array
    // (5) ["0", "ab", "cbd", "fcg", "1"]
    +
  • +
  • 删除第一个元素

    +
    1
    2
    3
    4
    5
    6
    	array.shift()
    // "0"
    array
    // (4) ["ab", "cbd", "fcg", "1"]
    ```
    - 删除指定的值,但数组元素没有被删
    +

    delete array[2]
    // true
    array
    // (4) [“ab”, “cbd”, empty, “1”]

    +
    1
    2
    	
    - 删除指定的元素
    +

    array.splice(1)
    // (3) [“cbd”, empty, “1”]
    array
    // [“ab”]

    +
    1
    2
    3
    4
    	
    ## 函数

    ### 调用调用
    +
  • +
+

alertMessage();

+

function alertMessageWithParamter(message) {
alert(message)
}

+

alertMessageWithParamter(‘有参数的函数’)
alertMessageWithParamter(250)
alertMessageWithParamter(
console.log(‘test’)
)

+
1
### 函数表达式,使用函数声明的方式
+

var alertMessage_expression = function expression_alert (message) {
alert(message)
}

+

alertMessage_expression(‘匿名函数调用’)

+

expression_alert(‘函数表达式’)
`

+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ +

Masonry使用

基本使用官网Demo非常详细,以下总结的是常见问题和技巧

+

安装遇到的问题

    +
  • pod 安装后出现⚠️,项目运行报错

    +
    1
    [UIView mas_makeConstraints:]: unrecognized selector sent to instance
    +

    github

    +

    解决方法

    +
  • +
  • 使用lastBaseline属性crash

    +

    解决办法

    +
  • +
+

使用时注意点

    +
  • edgesForExtendedLayout 方法

    +

    重写此方法的目的是为了确定布局位置从什么位置开始什么位置结束,

    +
    1
    2
    3
    - (UIRectEdge)edgesForExtendedLayout {
    return UIRectEdgeNone;
    }
    +
  • +
+
**重写后**
+![](https://ws1.sinaimg.cn/large/006tNc79ly1fsgcn3fmo0j30s61iyalk.jpg)
+
+**重写前**
+![](https://ws4.sinaimg.cn/large/006tNc79ly1fsgcokvpdwj30s61iy7eq.jpg)
+
    +
  • translucent 设置UINavigationBar是否半透明, 如果设置了导航栏的styleUIBarStyleBlackTranslucent 就为true

    +

    设置为false不透明效果

    +
  • +
+
    +
  • extendedLayoutIncludesOpaqueBars 导航栏不透明条件下是否可以扩展,默认是NO不可以扩展

    +
    1
    2
    self.navigationController?.navigationBar.isTranslucent = false
    self.extendedLayoutIncludesOpaqueBars = true
    +
  • +
+
**设置不透明,设置可以扩展此时从坐标的(0,0)点开始布局**
+![](https://ws2.sinaimg.cn/large/006tNc79ly1fsggem13mjj30ow0j83zy.jpg)    
+
    +
  • inset 的使用,当约束控件的属性相同时不用再考虑正负值的问题,例如要约束一个控件左边距离另一控件的右边位置时还需要使用负值情况

    +
    1
    2
    3
    4
    5
    6
    7
    [yellowdView makeConstraints:^(MASConstraintMaker *make) {
    make.height.equalTo(@150);
    make.top.equalTo(self).inset(220);
    make.leading.equalTo(self).inset(10);
    make.trailing.equalTo(greenView.leading).inset(10);
    make.width.equalTo(greenView);
    }];
    +
  • +
  • edges的使用,约束边距更方便

    +
    1
    2
    3
    [edgeView remakeConstraints:^(MASConstraintMaker *make) {
    make.edges.equalTo(lasView).insets(UIEdgeInsetsMake(10, 5, 5, 5));
    }];
    +
  • +
+
    +
  • Priority 设置约束的优先级

    +
      +
    • priorityHigh
    • +
    • priorityMedium
    • +
    • priorityLow
    • +
    +
  • +
  • makeConstraintsupdateConstraintsremakeConstraints三者区别

    +
      +
    • makeConstraints 添加约束
    • +
    • updateConstraints 更新约束,更新之前会查找一边控件已经存在的约束,没有就添加,有就更新到最新的约束
    • +
    • remakeConstraints删除控件以前的所有约束重新添加约束
    • +
    +
  • +
  • 设置控件或者某一约束的’key’ 方便冲突时⚠️查找

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    	greenView.mas_key = @"greenView";
    [greenView makeConstraints:^(MASConstraintMaker *make) {
    make.height.equalTo(yellowdView).key(@"greenView-height");
    make.top.equalTo(yellowdView).key(@"greenView-top");
    make.width.equalTo(yellowdView).key(@"greenView-width");
    make.trailing.equalTo(self).inset(10).key(@"greenView-trailing");
    // 冲突约束
    // make.leading.equalTo(greenView.trailing).inset(10).key(@"greenView-leading");
    }];
    ```

    - 使用 `MASAttachKeys`批量给`view`设置key
    +

    MASAttachKeys(greenView, yellowdView);

    +
    1
    2
    3
    4
    	
    ![](https://ws3.sinaimg.cn/large/006tNc79ly1fsgalvd4l6j31a20modnp.jpg)

    - `dividedBy`和 `multipliedBy` 约束宽高比
    +

    [topInnerView makeConstraints:^(MASConstraintMaker *make) {

    +
    // 设置自己的宽高比是3:1
    +make.width.equalTo( topInnerView.height).multipliedBy(3); // 乘因数
    +
    +// 设置宽高并且设置他们的优先级最低
    +make.width.height.lessThanOrEqualTo(topView);
    +make.width.height.equalTo(topView).priorityLow();
    +
    +// 设置位置
    +make.top.equalTo(topView);
    +

    }];

    +
    1
    - 多个控件批量约束
    +

    NSValue sizeValue = [NSValue valueWithCGSize:CGSizeMake(100, 100)];
    [@[blueView, redView, yellowView] makeConstraints:^(MASConstraintMaker
    make) {

    +
    make.size.equalTo(sizeValue);
    +

    }];

    +

    [@[blueView, redView, yellowView] mas_makeConstraints:^(MASConstraintMaker *make) {

    +
    make.top.equalTo(self).inset(20);
    +

    }];

    +

    [blueView makeConstraints:^(MASConstraintMaker *make) {

    +
    make.left.equalTo(self).inset(20);
    +

    }];

    +

    [redView makeConstraints:^(MASConstraintMaker *make) {

    +
    make.left.equalTo(blueView.right).inset(10);
    +

    }];

    +

    [yellowView makeConstraints:^(MASConstraintMaker *make) {

    +
    make.left.equalTo(redView.right).inset(10);
    +

    }];

    +
    1
    2
    3
    4

    ![批量约束](https://ws1.sinaimg.cn/large/006tNc79ly1fsgaz670hvj30s61iyn2l.jpg)

    **以上代码也可以简化为**
    +

    [redView makeConstraints:^(MASConstraintMaker *make) {

    +
    make.left.equalTo(blueView.right).inset(10);
    +

    }];

    +

    [yellowView makeConstraints:^(MASConstraintMaker *make) {

    +
    make.left.equalTo(redView.right).inset(10);
    +

    }];

    +
  • +
+
[blueView makeConstraints:^(MASConstraintMaker *make) {
+    make.top.left.equalTo(self);
+    make.size.equalTo(@[redView, yellowView, sizeValue]);
+}];
+
+
1
2
3
4
5
	![](https://ws2.sinaimg.cn/large/006tNc79ly1fsgbhz8lw5j30s61iyte5.jpg)

## Snapkit 使用

- Swift 中`edgesForExtendedLayout:UIRectEdge` 扩展布局的边缘是一个属性,默认是`All`
+ +self.edgesForExtendedLayout = [] +
1
2
3
4

在Swift中闭包参数可以使用$0、$1、$2 一次代表第一个,第二个,第三个参数

- 使用$0 来代替闭包参数
+ +nullDataImageView.snp.makeConstraints { + $0.top.equalTo(view).offset(44) + $0.centerX.equalTo(view) + $0.width.height.equalTo(200) +} +
1
2
3
4
5
6
7
	
- `deactivate` 和 `activate` 使用

- `activate` 激活指定的约束
- `deactivate` 移除指定的约束

添加约束
+ +nullDataImageView.snp.makeConstraints { + nullDataViewTopConstraint = $0.top.equalTo(view).offset(80).constraint + nullDataViewLeftConstraint = $0.left.equalTo(view).inset(10).constraint + $0.width.height.equalTo(200) +} +
1
2

移除对应约束,设置约束再激活约束
+ +nullDataViewLeftConstraint?.deactivate() +nullDataViewLeftConstraint?.update(inset: 100) +nullDataViewLeftConstraint?.activate() +UIView.animate(withDuration: 1.5) { + self.view.layoutIfNeeded() +} +
1
2
3
4

- 更新约束

- 添加约束
+ +nullDataImageView.snp.makeConstraints { + nullDataViewTopConstraint = $0.top.equalTo(view).offset(64).constraint + $0.centerX.equalTo(view) + $0.width.height.equalTo(200) +} +
1
2

- 更新约束
+ +nullDataViewTopConstraint?.update(inset: 84) +UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseOut, animations: { + self.view.layoutIfNeeded() +}) { (finished) in + print("动画执行完毕") +} +
1
2
	
- 添加`debug`模式中的 `key`使用`labeled()`方法
+ +nullDataImageView.snp.makeConstraints { + nullDataViewTopConstraint = $0.top.equalTo(view).offset(80).constraint + nullDataViewLeftConstraint = $0.left.equalTo(view).inset(10).constraint + $0.width.height.equalTo(200).labeled("width和height约束") +} +``` +
    +
  • 其他注意点都和Masnory相同
  • +
+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ +

JS ES6 语法

1. let var 变量,作用域

var a = 10;
+
+if (a > 10) {
+    let b = 5; // let 只能在代码块内被访问
+
+    console.log(b);
+}
+
+// 访问 b 报错
+console.log(b);
+console.log(a);
+

2. const 只能分配一次值

{
const const_a = 1;
console.log(const_a)

+
// 报错
+const_a = 2;
+
+// 声明一个常量数组
+const const_array = [];
+const_array.push(1);
+const_array.push(2);
+// 再次赋值时会报错
+const_array = [3];
+

}

+

3. 解构语法

{
// 1.解构数组
function getArray() {
return [10, 20, 30];
}

+
let [a,b,c] = getArray()
+console.log(a, b, c)
+
+
+// 函数返回一个对象
+function getObj() {
+    return {dessert: '面包', drink: '水', play: 'Game'};
+}
+
+// 2.解构对象
+let {des: dessert,  dri: drink, pla: play} = getObj()
+console.log(des, dri, pla);
+

}

+

4. 模版字符串

{
let name = ‘姓名:’, password = ‘密码:’
let str_4 = name + ‘李老师’ + ‘—-‘ + password + ‘北京太热。。。’
console.log(str_4)

+
// 使用 `${}` 包裹会生成字符串
+let str_41 = `${name}李老师----${password}北京太热`
+console.log(str_41)
+
+// 多行显示时直接回车换行
+let str_42 = `${name}李老师
+----
+${password}北京太热`
+console.log(str_42)
+

}

+

5. 带标签的模版字符串

{
let name = ‘姓名:’, password = ‘密码:’
// 多行显示时直接回车换行
let str_5 = strTest${name}李老师 ---- ${password}北京太热
console.log(str_5)

+
// strings 字符中包含的被插值分割的子字符串已数组形式展现
+// values 字符串中的插值数组
+function strTest(strings, ...values) {
+    console.log(strings)
+    console.log(values)
+}
+
+function strTest1(strings, ...values) {
+    let result = '';
+    for (let i = 0; i < values.length; i++) {
+        result += strings[i]
+        result += values[i]
+    }
+    return result
+}
+

}

+

6. 判断字符串是否包含其他字符

{
let name = ‘姓名:’, password = ‘密码:’
let str_6 = ${name}李老师---${password}北京真热
console.log(str_6.startsWith(${name}))
console.log(str_6.endsWith(${name}))
console.log(str_6.includes(‘李老师’))
}

+

7. 函数默认参数

{
function funcDefaultParame(param1 = ‘a’, param2 = ‘b’, param3 = ‘c’) {
return ${param1}--${param2}--${param3}
}
console.log(funcDefaultParame(1, 2,3))
}

+

8. “…”展开操作符

{
let a = [1,2,3];
console.log(a);
console.log(…a)

+
let b = [...a, 4, 5];
+console.log(a);
+console.log(b)
+

}

+

9. ‘… ‘剩余操作符, 一般用在函数参数, 可以看作是一个可以展开的参数

//  ... param3表示函数可以接收除了param1, param2,两个参数外,多个参数,都会放在param3中
+function operator(param1, param2, ...param3) {
+    console.log(param1, param2, param3)
+}
+operator('a','b','c','d','e','f','g')
+

10. 解构对象解构函数

// 当函数参数是对象是可以按照对象解构
+function getObjcValue(param1, param2, {value1, value2} = {}) {
+    console.log(param1, param2, value1, value2)
+}
+
+getObjcValue(1,2,{value1: -3,value2: -4})
+

11. 获取函数名称

function getObjcValue(param1, param2, {value1, value2} = {}) {
+    console.log(param1, param2, value1, value2)
+}
+console.log(getObjcValue.name)
+
+let funName = function getfunctionName() {  }
+console.log(funName.name)
+

12. 箭头函数

// 原来普通写法
+var funType = function arrowfunction(param) {
+    return param
+}
+
+// 函数参数 => 函数返回值
+// '=>' 的左边表示箭头函数的参数,多个时表示为 (param1, param2, param3)
+// '=>' 的右边表示箭头函数的返回值,如果返回值是一个对象时用 { } 表示
+let arrowFunction = (param1, param2) => {
+    return `${param1}${param2}`
+

}

+

13

+ +
+ +
+ +
+ + + +
+ +
+ + +
+ +

flutter 常用组件

+

组件

    +
  • StatefulWidget
      +
    • 具有可变状态的窗口部件,使用应用时可随时变化,例如进度条
    • +
    +
  • +
  • StatelessWidget
      +
    • 不可变窗口部件
    • +
    +
  • +
+

Text 组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//  Text 组件
class MyText extends StatelessWidget{
@override
Widget build(BuildContext context){
return Text('reacctNative、 flutter 学习之路是非常坚信不疑的,作为一个程序员,因该对于技术有着追求,不断的提高自己才是王道,可是道理都懂依然过不好这一生',
maxLines: 2,
overflow: TextOverflow.fade,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20.0,
color: Colors.deepOrange,
decoration: TextDecoration.underline,
decorationColor: Colors.redAccent,
decorationStyle: TextDecorationStyle.dotted
),
);
}
}
+

COntainer 组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

// Container容器组件
class MyContainer extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
child: Text('Container 容器中有文字',
style: TextStyle(
backgroundColor: Colors.cyan,
),
),
alignment: Alignment.center, // 容器中内容对其方式
width: 300.0,
height: 200.0,
padding: const EdgeInsets.all(10), // 容器内边距
margin: const EdgeInsets.fromLTRB(10, 5, 10, 5), // container 和外部控件的边距
decoration: new BoxDecoration(
gradient: const LinearGradient(
colors: [Colors.lightBlue, Colors.greenAccent, Colors.yellow]
),

// 设置 border 的宽度
border: Border.all(width: 5, color: Colors.black)

),
);
}
}
+

Image 组件

fit属性

    +
  • BoxFit.fill:全图显示,图片会被拉伸,并充满父容器。
  • +
  • BoxFit.contain:全图显示,显示原比例,可能会有空隙。
  • +
  • BoxFit.cover:显示可能拉伸,可能裁切,充满(图片要充满整个容器,还不变形)。
  • +
  • BoxFit.fitWidth:宽度充满(横向充满),显示可能拉伸,可能裁切。
  • +
  • BoxFit.fitHeight :高度充满(竖向充满),显示可能拉伸,可能裁切。
  • +
  • BoxFit.scaleDown:效果和contain差不多,但是此属性不允许显示超过源图片大小,可小不可大。
  • +
+

图片的混合模式

    +
  • color:是要混合的颜色,如果你只设置color是没有意义的。
  • +
  • colorBlendMode: 是混合模式。
  • +
+

repeat 图片填充模式

    +
  • ImageRepeat.repeat : 横向和纵向都进行重复,直到铺满整个画布。
  • +
  • ImageRepeat.repeatX: 横向重复,纵向不重复。
  • +
  • ImageRepeat.repeatY:纵向重复,横向不重复
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class MyImage extends StatelessWidget {
@override
Widget build(BuildContext context){
return Container(
child: new Image.network(
// 'https://images.xiaozhuanlan.com/photo/2019/28ed76123023e46483fcf0ae5c2ba11b.png',
'https://p.ampmake.com/mall/product/9666f00e-f02a-46ce-9b7c-090bf6aba9aa.png',

// 1. 设置填充模式
// fit: BoxFit.scaleDown,
// fit: BoxFit.none,
// fit: BoxFit.fill,
// fit: BoxFit.fitHeight,
// fit: BoxFit.fitWidth,

// 保持原图比例
// fit: BoxFit.contain,

// 充满整个容器,图片被裁切
// fit: BoxFit.cover,
// scale: 1.0,

// 2. 图片混合
color: Colors.red,
colorBlendMode: BlendMode.overlay,

// 3. reap
repeat: ImageRepeat.repeat,
),
width: 300.0,
height: 200.0,
color: Colors.lightBlue,
);
}
}
+

ListView 列表组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class MyListView extends StatelessWidget{
@override
Widget build(BuildContext contex){

return ListView(
children: <Widget>[
new ListTile(
leading: new Icon(Icons.camera),
title: Text('相册'),
),

new ListTile(
leading: new Icon(Icons.camera_alt),
title: Text('相机'),
),

new ListTile(
leading: new Icon(Icons.camera_enhance),
title: Text('相机1'),
),

new ListTile(
leading: new Icon(Icons.camera_front),
title: Text('相机2'),
),

new ListTile(
leading: new Icon(Icons.camera_rear),
title: Text('相机3'),
),
],
);
}
}
+

ListView.builder 适合列表项比较多(或者无限)的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
class MyListViewBuilder extends StatelessWidget {

@override
Widget build(BuildContext context){
return ListView.builder(
itemCount: 100, // 数量
itemExtent: 40, // Tile 高度
itemBuilder: (BuildContext context, int index){
return ListTile(title: Text("$index"));
}
);
}
}
+

横向列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class MyHorizontalListView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
height: 100,
child: ListView(
// 设置滚动方式
scrollDirection: Axis.horizontal,
children: <Widget>[
new Container(
width: 180.0,
color: Colors.green,
),
new Container(
width: 180.0,
color: Colors.yellow,
),
new Container(
width: 180.0,
color: Colors.blue,
),
new Container(
width: 180.0,
color: Colors.cyan,
),
new Container(
width: 180.0,
color: Colors.purple,
),
],
),
);
}
}
+

动态列表和构造方法的创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class MyDynamicListView extends StatelessWidget {
// 声明参数
final List<String> items;
// 构造方法
MyDynamicListView({Key key, @required this.items}):super(key:key);
@override
Widget build(BuildContext context) {
return Container(
child: new ListView.builder(
itemCount: items.length,
itemBuilder: (context, index){
return ListTile(
title: new Text('通过构造方法创建的动态列表${items[index]}'),
);
},
),
);
}
}
+

GridView 网格列表的使用

+ +
+ +
+ +
+ + + +
+ +
+ + +
+ +

GCD信号量用法

+

对于异步网络请求相互依赖问题一般用三种方式解决:

+
    +
  • 网络请求嵌套
  • +
  • 使用 BlockOperation
  • +
  • GCD DispatchGroup
  • +
  • GCD semaphore(信号量)
  • +
+

GCD 信号量

    +
  • semaphore 值 <= 0 阻塞
  • +
  • semaphore semaphore.signal() 使信号量值增加
  • +
  • semaphore.wait() 等待,可设置时长
  • +
+

request – 3 依赖 request – 1 和 request – 2,1 和 2 无需关注顺序

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private func gcdSemaphore() {
let semaphore = DispatchSemaphore(value: 0)
DispatchQueue.global().async {
for i in 0...300 {
print("request -- 1")
}
semaphore.signal()
}

DispatchQueue.global().async {
for i in 0...200 {
print("request -- 2")
}
semaphore.signal()
}


DispatchQueue.global().async {
semaphore.wait()
semaphore.wait()
for i in 0...160 {
print("request -- 3")
}
}
}
+

request – 3 ,request – 2 和 request – 1,依次相互依赖

+
private func gcdSemaphore() {
+        let semaphore = DispatchSemaphore(value: 0)
+        DispatchQueue.global().async {
+            for i in 0...300 {
+                print("request -- 1")
+            }
+            semaphore.signal()
+        }
+
+        DispatchQueue.global().async {
+            semaphore.wait()
+            for i in 0...200 {
+                print("request -- 2")
+            }
+            semaphore.signal()
+        }
+
+
+        DispatchQueue.global().async {
+            semaphore.wait()
+            for i in 0...160 {
+                print("request -- 3")
+            }
+        }
+    }
+
+
+ +
+ +
+ +
+ + + +
+ +
+ + +
+ +

Charles 抓包

+

原理

简单理解如下:

+
    +
  • 正常请求

    +

    客户端 ——> 服务器

    +
  • +
+
    +
  • Charles 抓包

    +

    客户端 ——> 代理 (Charles)——> 服务器

    +
  • +
+

安装

Charles官网

+

破解版本请自行百度

+

重要说明安装之前请暂时关闭电脑的VPN

+

重要说明安装之前请暂时关闭电脑的VPN

+

重要说明安装之前请暂时关闭电脑的VPN

+

启动Charles

安装Charles证书

+
如果安装失败,请检查以前是否已经安装过,如果已经安装过并且已经失效,请删除失效证书后再安装
+

打开钥匙串–> 选择Charles CA -> 双击证书 –> 信任证书

+

设置 https

- Proxy -> SSL Proxying Settings -> SSL Proxying -> Add
+- Host: * 为需要过滤的域名地址,
+- *: 表示不过滤Port, 固定为443 `*`表示任意端口
+

+

Mac 抓包

    +
  • Proxy -> macOS Proxy (☑️)

    +

    +
  • +
+

真机抓包

此时可以将 macOS Proxy 选项中的 ✅ 去掉,

+

Mac与iPhone连接必须同一网络

设置代理端口号

+

手机安装证书

    +
  • 安装手机证书

    +

    +
  • +
  • 安装完成后弹窗

    +

    +
  • +
+
    +
  • 查看mac 地址,为手机设置代理

    +
      +
    1. 根据安装手机证书后的弹窗显示IP配置

      +
    2. +
    3. Charles –> Help –> Loca IP Address

      +

      +
    4. +
    5. 打开电脑网络设置查看

      +

      +
    6. +
    +
  • +
+
    +
  • 设置代理

    +

    第一步

    +

    第二步

    +

    第三步

    +
  • +
  • 根据弹窗提示,在手机浏览器下载并安装证书

    +

    +
  • +
+
    +
  • 手机信任证书

    +
      +
    • 设置 –> 通用 –> 关于本机 –> 证书信任设置

      +

      +
    • +
    +
  • +
+

模拟器抓包

    +
  • 安装模拟器证书

    +

    +
  • +
  • 勾选 macOS Proxy 选项

    +

    +
  • +
+

如果失败,请先关闭模拟器,重新启动Charles 再打开模拟器

+

使用浏览器打开网页提示不是私密链接

解决办法

+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ + +

+ Flutter 入门.md +

+ + +
+ +
+ +

flutter安装

+

flutter入门

+

flutter 安装

flutter 项目代码

fluter 中一切皆是组件

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 导入头文件
import 'package:flutter/material.dart';

// 程序的入口,main 函数
void main() => (MyApp());

// 定义类, 继承

class MyApp extends StatelessWidget {
// 重写
@override

Widget build(BuildContext context) {

// 返回一窗口
return MaterialApp(
title:'Flutter',
home: Scaffold(
appBar: AppBar(
title: Text('flutter 初体验')
),

body: Center(
child: Text('flutter 学习之路'),
),
),
);
}
}
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}

class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);

// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.

// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".

final String title;

@override
_MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;

void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}

@override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Invoke "debug painting" (press "p" in the console, choose the
// "Toggle Debug Paint" action from the Flutter Inspector in Android
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
// to see the wireframe for each widget.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'你可以点击按钮来修改文字,我是热重载的结果',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
+ +
+ +
+ +
+ + + + + + + +
+ +
+ + +
+ + +

+ Mac安装VirtualBox连接U盘 +

+ + +
+ +
+ +

最近因为某些原因需要再Mac上安装虚拟机,所以选择了VirtualBox安装了
win10系统,但是需要再虚拟机的win10中访问U盘数据,遇到的问题是不能连接并且读取到U盘,错误提示不能分配USB内存。。。

+

网上找了好多办法都胡扯,根本没有解决…

+

解决方法:
1.升级VirtualBox到最新版
2.关闭当前的虚拟机中的操作系统
3.安装增强工具
4.重启VirtualBox,回到Mac桌面推出U盘即可

+

最重要的就是在第四步

+ + +
+ +
+ +
+ + + + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/page/6/index.html b/page/6/index.html new file mode 100644 index 0000000..0d0d9f0 --- /dev/null +++ b/page/6/index.html @@ -0,0 +1,1178 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ +
+ + +
+ + +

+ Block学习探究 +

+ + +
+ +
+ +

代码及结果

    +
  • 普通局部变量

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    - (void)block1 {

    NSInteger num = 100;

    // 定义一个block
    dispatch_block_t block = ^ {
    NSLog(@"block1 -- numer = %zd", num);
    };

    num = 200;

    block();
    }
    +
  • +
  • 使用__block修饰变量

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    - (void)block2 {

    __block NSInteger num = 100;

    // 定义一个block
    dispatch_block_t block = ^ {
    NSLog(@"block2 -- numer = %zd", num);
    };

    num = 200;

    block();
    }
    +
  • +
  • 全局变量

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    - (void)block3 {


    // 定义一个block
    dispatch_block_t block = ^ {
    NSLog(@"block3 -- numer = %zd", blockNum);
    };

    blockNum = 200;

    block();
    }
    +
  • +
  • 静态常量

    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    - (void)block4 {

    static NSInteger num = 100;

    // 定义一个block
    dispatch_block_t block = ^ {
    NSLog(@"block4 -- numer = %zd", num);
    };

    num = 200;

    block();
    }
    +
  • +
+

运行结果

2018-01-19 00:04:13.759416+0800 CycleRetain[91251:2382380] block1 -- numer = 100
+2018-01-19 00:04:13.760206+0800 CycleRetain[91251:2382380] block2 -- numer = 200
+2018-01-19 00:04:13.760473+0800 CycleRetain[91251:2382380] block3 -- numer = 200
+2018-01-19 00:04:13.760603+0800 CycleRetain[91251:2382380] block4 -- numer = 200
+

原理及本质

block 根据创建位置不同,共有三种:栈block,堆block,全局block

+

使用block本质就是为了保证栈上和堆上block内访问和修改的是同一个变量,具体的实现是将block 修饰的变动自动封装成一个结构体,让他在堆上创建
基于OC 底层的runtime 机制
block 在内部会有一个指向结构体的指针,当调用block的时候其实就是让block找出对应的指针所指的函数地址进行调用。并传入了block自己本身

+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ + +

+ 面试中常见问题总结(二) +

+ + +
+ +
+ +

非正式协议和正式协议

非正式协议:凡是给NSObject或者其子类添加的类别都是非正式协议
正式协议:使用 @protocol 方式声明的方法列表,有 @require 和 @optional 两种类型

+

KVC 键值编码

之所以可以使用就是因为给 NObject 增加了 NSKeyValueCoding 非正式协议,NSObject 实现了协议,OC中几乎所有的对象都支持KVC,获取值的方法是不通过属性的setter、getter 方法,而是通过属性名称的key间接访问了实例变量,可以访问私有变量

+
1
2
3
4
 // 官方注释
/* Send -setObject:forKey: to the receiver, unless the value is nil, in which case send -removeObjectForKey:.
*/
- (void)setValue:(nullable ObjectType)value forKey:(NSString *)key;
+

如果为nil, 就会自动过滤掉, 此方法常用在字典中过滤空值

+

// forKeyPath: 用于符合属性,能够通过层层访问内部属性,相当于调用属性点语法, 拥有forKey 的所有功能

+
1
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;
+
    +
  • 实现原理
      +
    • 利用 setValue: forKeyPath: 赋值
    • +
    +
      +
    1. 底层实现还是使用 runtime 给对象发消息,先去查找属性是否有setter方法,存在setter 就会通过 setter 方法复制,
    2. +
    3. 如果没有setter方法,接着查找下是否划线的成员变量,如果有直接给赋值
    4. +
    5. 如果没有下划线的成员变量,查找是否存在和key值相同的属性,如果有就直接给属性赋值
    6. +
    7. 如果没有和key值相同的属性,就会执行 setValue: forUndefinedKey: 抛出一个异常程序奔溃,这也是字典转模型中需要实现这个方法的原因
    8. +
    +
  • +
+

KVO 键值监听

利用一个key值来找到某个属性并监听某个属性的值的变化发送给观察者,可以理解为简单的观察者模式

+
    +
  • KVO 使用

    +
      +
    1. 为目标对象的属性添加观察者

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      - (void)testKVO {
      Person *p = [[Person alloc] init];
      self.person = p;
      p.name = @"哈哈";

      [p addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];

      p.name = @"我不是原来的我!你能监听到我 ???";
      p.name = @"看我七十二变";
      }
      +
    2. +
    +
  • +
+
2. 实现监听的方法
+
+
1
2
3
4
5
#pragma mark - kvo 回调方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {

NSLog(@"KVO监听到的对象是 = %@, 值 = %@ , change = %@", [object class], keyPath, change);
}
+ +3. 移除观察者 + +
1
2
3
4
5
- (void)dealloc {

[self removeObserver:self forKeyPath:@"name"];
NSLog(@"-----控制器销毁-----");
}
+
    +
  • KVO原理

    +

    类的属性被添加了观察者后,OC底层就会通过Runtime 动态的创建一个这个类的派生类,当前类内部的isa 指针指向了这个派生类,在派生类中重写基类被观察属性的 setter 方法,调用前后会调用 willChangeValueForKey, setValueForKey, didChangeValue 方法, 从而达到监听属性值的方法

    +
  • +
+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ + +

+ 面试中常见问题总结 +

+ + +
+ +
+ +

OC 的动态性

OC是基于C语言的,C语言在编译阶段就会确定了调用具体的那个函数,OC它的底层是通过runtime 运行时机制来调用函数,在OC中成为消息发送,具体来说就是在编译阶段OC可以调用任何函数,即时这个函数是没有实现的,只有在运行的时候才会根据isa指针和已经注册的方法列表中找到 IMP(函数指针)真正要调用的那个函数,我们平时写的OC代码,在运行的时候也都是转换成了runtime 的方式进行的,不管是什么方法本质都是发送一个消息

+

runtime

    +
  • 消息机制原理: 根据方法编号列表 SEL 去映射表中查找对应方法的实现
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Person *p = [Person allo] init];

// 底层的写法
// alloc
Person *p = objc_msgSend(objc_getClass("Person"), sel_registerName("alloc"),);

// init
p = objc_msgSend(p, sel-registerName("init"));

// 调用对象方法本质就是:让对象发消息
objc_msgSend(p, @selector(eat));

// 调用类方法本质: 让类发消息
objc_msgSend([Person class], @selector(run:), 20);

// alloc init 也可以理解为:

id objc = objc_msgSend([NSObject class], @selector(alloc));

objc = objc_msgSend(objc, @selector(alloc));
+
    +
  • runtime 消息机制调用流程

    +

    每个对象内部都有一个isa指针,指向他自己真实的类来确定调用的是那个类的方法; 每个方法sel都有一个IMP指针(指向现实函数的指针),不管是对象方法还是类方法,都会有一个方法列表

    +
      +
    1. 发消息时根据对象内部的isa 指针去该类所对应的方法类表中查找方法,如果没有去父类中查找
    2. +
    3. 注册每个方法编号,为了以后快速查找调用
    4. +
    5. 根据方法编号查找对应方法
    6. +
    7. 通过isa指针和IMP指针确定最终调用的函数
    8. +
    +
  • +
  • runtime 常见使用

    +
      +
    1. 运行是动态添加方法
    2. +
    3. 给分类添加属性,(关联属性)
    4. +
    5. 字典转模型中从先遍历模型属性再从字典中查找相关值实现模型属性赋值
    6. +
    7. NSCoding 自动归档解档时对象属性过多时 encodeWithCoder:initWithCoder 两个方法实现
    8. +
    +
  • +
+

Runloop

    +
  • 概念

    +

    NSRunloop 是封装 CFRunloopRef 一套纯C的函数,

    +
  • +
  • 作用

    +
      +
    1. 保持程序持续运行
    2. +
    3. 处理App中各种事件,具体的有:toches事件, NSTimer 事件, Selector 事件, PerformSelector 事件,source 源等等
    4. +
    5. 有事件触发时就运行,没有就休眠
    6. +
    7. 渲染app 中唯一一个主线程上所有UI界面的更新
    8. +
    +
  • +
  • 使用和要点

    +
      +
    1. Runloop 在程序入口 main 函数中就会开启,并且开启的是主线程不会销毁除非退出程序,保证了程序一直运行并能响应事件和交互
    2. +
    3. 每一条线程都是唯一对应一个Runloop
    4. +
    5. 主线程不需要创建,因为在程序启动的时候就已经创建了,而子线程的需要手动创建 直接获取currentRunloop 方式(本质是使用了懒加载)
    6. +
    7. Runloop 对象是利用字典进行存储,因为一一对应,所有key 和value 就是线程和 他所对应的runloop

      +
    8. +
    9. Runloop 中 NSDefaultRunLoopMode 一般情况下主线程运行的 Mode

      +
    10. +
    11. UIInitializationRunLoopMode 初始化mode,通常不用
    12. +
    13. UITrackingRunLoopMode 界面跟踪, 用于scrollView 追踪触摸滑动,保证界面不受其他mode影响
    14. +
    15. GSEventReceiveRunLoopMode 用于接收系统事件内部 mode, 通常不用

      +
    16. +
    17. NSRunLoopCommonModes, 并不是某种具体的 mode ,开发中使用最多,他是一个组合 mode,组合了 NSDefaultRunLoopMode和 NSRunLoopCommonModes

      +
    18. +
    +
  • +
  • Runloop 相关类

    +
      +
    1. Source 事件源/输入源可以理解为 激发源
    2. +
    3. Timer 基于事件触发,CADisplayLink,NSTimer 都是加到了 Runloop,受 Mode 的影响,GCD 定时器不受Runloop 影响
    4. +
    5. Observer 相当于runloop 循环中的监听器,时刻监听当前Runloop的运行状态
    6. +
    7. 休眠没事情做的时候可以停下来,处于休眠状态
    8. +
    +
  • +
  • 使用场景

    +
      +
    1. 创建NSTimer
    2. +
    3. PersforSelector方法
    4. +
    5. 常驻线程:(创建后处于等待状态,有事件时响应事件)
        +
      • 实现后台收集用户停留时间或者是某个按钮的点击次数,如果使用主线程会不方便,使用runloop解决
      • +
      +
    6. +
    7. AutoreleasePool 自动释放池
    8. +
    9. UI更新
    10. +
    +
  • +
+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ + +

+ iOS即时通讯实现二 +

+ + +
+ +
+ +

实现IM通信所用协议

几个协议理解

+

xmpp 是基于xml协议主要是易于扩展,早起用于PC时代的产品使用,流量大,耗电大交互复杂其实并不适合移动互联网时代的IM产品

+

MQTT 协议 适配多平台,需自己扩展好友,群组等功能

+

protobut 协议

+

封装socket,自己实现

所要考虑的问题

+
    +
  • 传输协议选择

    +
      +
    • TCP ? 一般公司都选用(技术不是很成熟)
    • +
    • UDP ?QQ 腾讯增加自己的私有协议,保证数据传递的可靠性,解决了丢包,乱序的问题
    • +
    +
  • +
  • 聊天协议选择及代表框架

    +
      +
    • Socket ? CocoaAsyncSocket
    • +
    • WebSockt ?是传输通讯协议,基于socket封装的一个协议 SocketRocket
    • +
    • MQTT ?MQTTKit 聊天协议为上层协议
    • +
    • protobut 协议 ?
    • +
    • XMPP ?XMPPFramework 聊天协议为上层协议
    • +
    • 自定义
    • +
    +
  • +
  • 传输数据格式

    +
      +
    • Json?
    • +
    • XML?
    • +
    • ProtocolBuffer?
    • +
    +
  • +
  • 细节相关

    +
      +
    • TCP 长连接如何保持,
    • +
    • 心跳机制
    • +
    • 重连机制
    • +
    • 数据安全机制
    • +
    +
  • +
+

CocoaAsyncSocket

    +
  • CocoaAsyncSocket 的 delegate 用来设置代理处理各个回调
  • +
  • delegateQueue
  • +
  • socketQueue 串行队列,贯穿全类并没有任何加锁,确保了socket 操作中每一步都是线程安全的
  • +
  • 创建了两个读写队列(本质是数组)

    +
      +
    • NSMutableArray *readQueue;
    • +
    • NSMutableArray *writeQueue;
    • +
    +
  • +
  • 全局数据缓冲: 当前socket未获取完的数据大小

    +
      +
    • GCDAsyncSocketPreBuffer *preBuffer;
    • +
    +
  • +
  • 交替延时变量: 用于进行另一服务端地址请求的延时

    +
      +
    • alternateAddressDelay
    • +
    +
  • +
  • connect

    +
  • +
  • disconnect

    +
  • +
+

数据粘包,断包

    +
  • 粘包: 如果客户端同一时间发送几条数据,而服务器只收到一大条数据

    +
      +
    • 原因:
    • +
    • 由于传输的是数据流,经过TCP传输后,TCP使用了优化算法将多次间隔较小且数据量小的数据合并成一个大的数据块,因此三条数据合并成了一条

      +

      TCP,UDP都可能造成粘包的原因

      +
    • +
    • 发送端需要等缓冲区满了才发送出去,做成粘包

      +
    • +
    • 接收方不及时接收缓冲区的包,造成多个包接收
    • +
    +
  • +
  • 断包: 因为一次发送很大的数据包,缓冲区有限,会分段发送或读取数据

    +
  • +
+

CocoaAsyncSocket的封包,拆包处理

    +
  • 读取数据方法:

    +
    // 有数据时调用,读取当前消息队列中的未读消息
    +- (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag
    +
  • +
+
**每次读取数据,每次都必须手动调用上述 readData 方法超时设置为不超时才能触发消息回调的代理**
+
+**因此在第一连接时调用,再在接到消息时调用,上述方法就可以达到每次收到消息都会触发读取消息的代理**
+
+以上做法存在的问题就是没有考虑数据的拆包会有粘包情况,这时候需要用下面两个`read`方法,1. 读取指定长度、2.读取指定边界
+
+    // 读取特定长度数据时调用
+    - (void)readDataToLength:(NSUInteger)length withTimeout:(NSTimeInterval)timeout tag:(long)tag
+
+    // 读到特定的 data 边界时调用
+    - (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag
+
+
+**解决办法及具体思路**
+
+- 封包:给每个数据包增加一个长度或者加一个开始结束标记,表明数据的长度和类型(根据自己的项目:文本、图片、语音、红包、视频、话题、提问等等)
+    - 可以在数据包之后加一个结束标识符,解决了传输过程中丢包,丢失头部信息的错误包读取,读到这样的就丢弃直接读下一个数据包
+
+- 拆包:获取每个包的标记,根据标记的数据长度获取数据,最后根据类型处理数据,最后读取数据包头部的边界
+
    +
  • 利用缓冲区对数据进行读取

    +
      +
    • 创建读取数据包
    • +
    • 添加到读取的队列中
    • +
    • 从队列中取出读取任务包
    • +
    • 使用偏移量 maxLength 读取数据

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      		- (void)readDataWithTimeout:(NSTimeInterval)timeout
      buffer:(NSMutableData *)buffer
      bufferOffset:(NSUInteger)offset
      maxLength:(NSUInteger)length
      tag:(long)tag
      {
      if (offset > [buffer length]) {
      LogWarn(@"Cannot read: offset > [buffer length]");
      return;
      }

      // 1. 创建读取数据包
      GCDAsyncReadPacket *packet = [[GCDAsyncReadPacket alloc] initWithData:buffer
      startOffset:offset
      maxLength:length
      timeout:timeout
      readLength:0
      terminator:nil
      tag:tag];

      dispatch_async(socketQueue, ^{ @autoreleasepool {

      LogTrace();

      if ((flags & kSocketStarted) && !(flags & kForbidReadsWrites))
      {
      // 2. 向读的队列添加任务包
      [readQueue addObject:packet];
      // 3. 从队列中取出读取任务包
      [self maybeDequeueRead];
      }
      }});

      // Do not rely on the block being run in order to release the packet,
      // as the queue might get released without the block completing.
      }
      +
    • +
    +
  • +
+

doReadData 方法时读取数据的核心方法

+
    +
  • 如果读取对列中没有数据,就去判是否是上次已经读取了数据,但是因为没有目前还没有读取到数据的标记,这是正好设置了 read 方法中的超时时间,所以要断开socket 连接
  • +
+

+
    +
  • 接收到数据后在 socket系统进程的数据缓冲区中
      +
    • (基于TLS的分为两种)他们在各自管道中流动,完成数据解密后又流向了全局数据缓冲区
        +
      • CFStreem
      • +
      • SSLPrebuffer
      • +
      +
    • +
    +
  • +
+

处理数据逻辑: 如果当前读取包长度给明了,则直接流向currentRead,如果数据长度不清楚,那么则去判断这一次读取的长度,和currentRead可用空间长度去对比,如果长度比currentRead可用空间小,则流向currentRead,否则先用prebuffer来缓冲。

+

心跳机制

    +
  • TCP 自带的keep-alive 使用空闲时间来发并且默认超时时间太长是2 小时,
  • +
  • 如果应用使用了socks ,socks proxy会让tcp keep-alive失效,因为socks 协议只管转发TCP层具体的数据包,并不会转发TCP协议内的实现具体细节数据包
  • +
  • TCP 的长连接理论上是一直保持连接的,可以设置 TCP keep-alive的时间 但是实际情况下中,可能出现故障或者是因为防火墙的原因会自动把一定时间段内没有数据交互的连接给断开,心跳机制就可以维持长连接,保持活跃状态

    +
  • +
  • iOS 中使用 NSTimer scheduledTimerWithTimeInterval 需要在调用之前的关键位置设置 fireDate 为未来的某个时间, 然后再需要的时候开启

    +
  • +
  • 或者直接使用 timerWithTimeInterval 创建,然后添加到runloop
  • +
+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ + +

+ iOS 即使通讯网络知识篇 +

+ + +
+ +
+ +

网络七层协议

读《图解TCP/IP》第5版以及看过的关于网络文章之后自己的理解

+

OSI (Open System Interconnec) 译为 开发式系统互联 七层模型从下到上一次是:物理层 -> 数据链路层 —> 网络层 -> 传输层 -> 会话层 -> 表示层 -》 应用层

+

数据每次经过一层之后就会在数据头部增加一个首部信息

+
物理层:网卡,网线,集线器,中继器,调制解调器
    +
  • 定义设备标准,如网线,光纤接口类型,将0和1转化为电信号的强和弱,达到传输比特流目的,这层数据叫比特
  • +
+
数据链路层:网桥,交换机
    +
  • 传输的地址帧,并且有检测错误功能,保证数据可靠传输, 这层数据叫帧
  • +
+
网络层:路由器
    +
  • 两个计算机通信时可能会经过多个数据链路或者通信子网,网络层将数据链路层的帧组成数据包,包中有封装好的包头,其中含有逻辑地址信息(源站点和目的站点),此层为数据包选择合适的路由和交换结点,IP协议产生,这层数据叫着数据包
  • +
+
传输层:
    +
  • 解决数据如何在网络中传输,这个层负责获取全部信息,因此,它必须跟踪数据单元碎片、乱序到达的 数据包和其它在传输过程中可能发生的危险。第4层为上层提供端到端(最终用户到最终用户)的透明的、可靠的数据传输服务。所为透明的传输是指在通信过程中 传输层对上层屏蔽了通信传输系统的具体细节。传输层协议的代表包括:TCP、UDP、SPX等
  • +
+
会话层:
    +
  • 这一层也可以称为会晤层或对话层,在会话层及以上的高层次中,数据传送的单位不再另外命名,而是统称为报文。会话层不参与具体的传输,它提供包括访问验证和会话管理在内的建立和维护应用之间通信的机制。如服务器验证用户登录便是由会话层完成的
  • +
+
表示层:
    +
  • 这一层主要解决拥护信息的语法表示问题。它将欲交换的数据从适合于某一用户的抽象语法,转换为适合于OSI系统内部使用的传送语法。即提供格式化的表示和转换数据服务。数据的压缩和解压缩, 加密和解密等工作都由表示层负责。
  • +
+
应用层:HTTP 协议
    +
  • 如何包装数据
  • +
+

通常从上到下会分成四层

+

TCP/IP 通常是指TCP/IP协议族,是一组不同协议组合在一起构成的协议族

+
    +
  • 应用层:FTP 应用层协议
  • +
  • 传输层:TCP,UDP 传输层协议,TCP 提供了一个可靠的服务
  • +
  • 网络层:IP,ICMP 网络层协议,传输时不可靠的
  • +
  • 链路层:以太网协议
  • +
+

Socket

为了解决不同计算机上进程间通信问题
服务器在指定的端口上监听,然后生成一对儿新的socket 进行通讯,一个线程对应一个socket

+

英文翻译为“插座”通常称为”套接字”, 本质是对于TCP/IP的封装,有客户端 socket 和 服务端socket

+
    +
  • 流式 Socket (STREAM): 是一种面向连接的socket,针对于面向连接的TCP服务应用,因为安全所以效率低
  • +
  • 数据报式Socket (DATAGRAM): 是一种无连接的SOcket,正对于无连接的UDP服务应用,无序,不安全,需要在接收端分析重排或者要求重发,所以效率高
  • +
+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ + +

+ Swift加密相关 +

+ + +
+ +
+ +

最近公司项目开发中引用了 SwiftOC 混编, 涉及到了加密相关, 使用了第三方 CryptoSwift, 以下是使用过程中的简单记录

+

CryptoSwift简介

+

数组转换

    +
  • data 转化为字节数组

    +
    1
    2
    let bytes = data.bytes
    print(bytes)
    +
  • +
  • 字节数组转化为 data

    +
    1
    2
    let data = Data(bytes: [0x01, 0x02, 0x03])
    print(data)
    +
  • +
  • 十六进制创建字节

    +
    let bytes_Hex = Array<UInt8>(hex: "0x010203")
    +print(bytes_Hex)
    +
  • +
  • 字节转十六进制数

    +
    let hex = bytes_Hex.toHexString()
    +print(hex)       
    +
  • +
+
    +
  • 字符串生成字节数组

    +
    let string2bytes = "string".bytes
    +print(string2bytes)
    +
  • +
+
    +
  • 字节数组 base64

    +
    let bytes1: [UInt8] = [1, 2, 3]
    +let base64Str = bytes1.toBase64()
    +print(base64Str!)
    +
  • +
  • 字符串base64

    +
    let str0 = "test"
    +let base64Str1 = str0.bytes.toBase64()
    +print(base64Str1!)
    +
  • +
+

MD5加密

    +
  • MD5(RFC1321)诞生于 1991 年,全称是“Message-Digest Algorithm(信息摘要算法)5”,由 MIT 的计算机安全实验室和 RSA 安全公司共同提出。
  • +
  • 之前已经有 MD2、MD3 和 MD4 几种算法。MD5 克服了 MD4 的缺陷,生成 128bit 的摘要信息串,出现之后迅速成为主流算法

    +
  • +
  • 字节数组MD5

    +
    let str = "测试test"
    +let bytes = str.bytes
    +
    +// 写法一
    +let digest = bytes.md5().toHexString()
    +print(digest)
    +// 04f47b69e3573867a5b3c1e5edb00789
    +
    +// 写法二
    +let digest1 = Digest.md5(bytes).toHexString()
    +print(digest1)
    +// 04f47b69e3573867a5b3c1e5edb00789
    +
  • +
  • Data的MD5值

    +
    /// data MD5
    +let data = Data(str.bytes)
    +let digest3 = data.md5().toHexString()
    +print(digest3)
    +// 04f47b69e3573867a5b3c1e5edb00789
    +
  • +
  • 字符串MD5

    +
    ///写法 一
    +let digest4 = str.md5()
    +print(digest4)
    +// 04f47b69e3573867a5b3c1e5edb00789
    +
    +// 写法二
    +do {
    +    var digest4_2 = MD5()
    +
    +    let _ = try digest4_2.update(withBytes: "测试".bytes)
    +    let _ = try digest4_2.update(withBytes: "test".bytes)
    +    let result = try digest4_2.finish()
    +    print(result.toHexString())
    +    //04f47b69e3573867a5b3c1e5edb00789
    +} catch {
    +
    +}    
    +
  • +
+

哈希算法

    +
  • 字节数组SHA值

    +
    let str = "测试test"
    +let bytes = str.bytes
    +
    +//方式一
    +let digest1 = bytes.sha1().toHexString()
    +let digest2 = bytes.sha224().toHexString()
    +let digest3 = bytes.sha256().toHexString()
    +let digest4 = bytes.sha384().toHexString()
    +let digest5 = bytes.sha512().toHexString()
    +print(digest1, digest2, digest3, digest4 ,digest5, separator: "\n")
    +
    +//方式二
    +let digest6 = Digest.sha1(bytes).toHexString()
    +let digest7 = Digest.sha224(bytes).toHexString()
    +let digest8 = Digest.sha256(bytes).toHexString()
    +let digest9 = Digest.sha384(bytes).toHexString()
    +let digest10 = Digest.sha512(bytes).toHexString()
    +print(digest6, digest7, digest8, digest9 ,digest10, separator: "\n")
    +
  • +
+
    +
  • Data的SHA值

    +
    let data = Data(bytes: str.bytes)
    +let digest11 = data.sha1().toHexString()
    +let digest12 = data.sha224().toHexString()
    +let digest13 = data.sha256().toHexString()
    +let digest14 = data.sha384().toHexString()
    +let digest15 = data.sha512().toHexString()
    +print(digest11, digest12, digest13, digest14 ,digest15, separator: "\n")
    +
  • +
+
    +
  • 字符串的SHA值

    +
    let digest16 = str.sha1()
    +let digest17 = str.sha224()
    +let digest18 = str.sha256()
    +let digest19 = str.sha384()
    +let digest20 = str.sha512()
    +print(digest16, digest17, digest18, digest19 ,digest20, separator: "\n")
    +
  • +
+
// 方式一
+let digest_str = str.sha1()
+print(digest_str)
+
+// 方式二
+do {
+    var digest_str = SHA1()
+    let _ = try digest_str.update(withBytes: Array("测试".bytes))
+    let _ = try digest_str.update(withBytes: Array("test".bytes))
+    let result = try digest_str.finish()
+    print(result.toHexString())
+} catch {
+
+}
+

AES 加密

    +
  • key:公共密钥

    +
  • +
  • IV: 密钥偏移量

    +
  • +
  • padding:key 的补码方式

    +
    key 长度不够16 字节时,需要手动填充, zeroPadding 将其补齐至 blockSize 的整数倍
    +zeroPadding 补齐规则:
    +- 将长度补齐至 blockSize 参数的整数倍。比如我们将 blockSize 设置为 AES.blockSize(16)
    +- 如果长度小于 16 字节:则尾部补 0,直到满足 16 字节。
    +- 如果长度大于等于 16 字节,小于 32 字节:则尾部补 0,直到满足 32 字节。
    +- 如果长度大于等于 32 字节,小于 48 字节:则尾部补 0,直到满足 48 字节。 以此类推......
    +
  • +
  • 补码方式padding, noPadding, zeroPadding, pkcs7, pkcs5, 默认使用 pkcs7,两者写法结果相同

    +
  • +
+
let iv_paddding = "1234567890123456" // 默认是
+let aes_padding1 = try AES(key: key.bytes, blockMode: .CBC(iv: iv_paddding.bytes))
+let aes_padding2 = try AES(key: key.bytes, blockMode: .CBC(iv: iv_paddding.bytes), padding: .pkcs7)            
+
    +
  • ECB 模式

    +
    let key = "hangge.com123456"
    +let str = "E蜂通信"
    +print("加密前 \(str)")
    +
    +//MARK: 使用ECB 模式, , 使用aes128 加密
    +let aes = try AES(key: key.bytes, blockMode: .ECB)
    +let encrpyted = try aes.encrypt(str.bytes)
    +print("加密后 \(encrpyted)", separator: "-----")
    +
    + /// 解密
    + let decrypted = try aes.decrypt(encrpyted)
    + print("解密后 \(decrypted)")
    + let decryptedString = String(data: Data(decrypted), encoding: .utf8)!
    + print("解密后字符串 \(decryptedString)")
    +
  • +
+
    +
  • CBC 模式

    +
    //MARK: 使用CBC 模式, 需要提供一个额外的密钥偏移量 iv
    +
    +    /// 写法 1 --  便捷写法
    +    // 偏移量
    +    let iv = "1234567890123456" // 默认是
    +
    +    // TODO: 随机密钥偏移量
    +    let data = Data(bytes: AES.randomIV(AES.blockSize))
    +    let random_iv = String(data: data, encoding: .utf8)
    +
  • +
+
/// 写法 2 --
+// 创建密码器
+let cbc_aes = try AES(key: key.bytes, blockMode: .CBC(iv: iv.bytes))
+
+let cbc_aes2 = try AES(key: key, iv: iv)
+
+
+let cbc_aes3 = try AES(key: key, iv: iv)
+
+let cbc_encrypted = try cbc_aes.encrypt(str.bytes)
+
+let cbc_encrypted2 = try cbc_aes2.encrypt(str.bytes)
+
+let cbc_encrypted3 = try cbc_aes3.encrypt(str.bytes)
+
+
+// 便捷写法解密
+let cbc_decrypted = try cbc_aes.decrypt(cbc_encrypted)
+let cbc_decryptedData = Data(cbc_decrypted)
+let cbc_decryptedStr = String(data: cbc_decryptedData, encoding: .utf8)
+print(cbc_decryptedStr)
+
+// 正常写法2 解密
+let cbc_decrypted2 = try cbc_aes2.decrypt(cbc_encrypted2)
+let cbc_decryptedStr2 = String(data: Data(cbc_decrypted2), encoding: .utf8)
+print(cbc_decryptedStr2)
+
+
+// 随机密钥偏移量解密
+let cbc_decrypted3 = try cbc_aes3.decrypt(cbc_encrypted3)
+let cbc_decryptedStr3 = String(data: Data(cbc_decrypted3), encoding: .utf8)
+print(cbc_decryptedStr3)
+
+ +
+ +
+ +
+ + + +
+ +
+ + +
+ + +

+ Mac 安装MySQL +

+ + +
+ +
+ +

搭建 XMPP 即使通讯

+

安装MySQL数据库

    +
  • 安装MySQL 数据库

    +
      +
    • 官网 下载安装包

      +

      特别强调:不要一直下一步最后的时候会有个弹窗提示你生成root的临时密码,记录一下

      +
    • +
    +
  • +
  • 安装数据库管理工具 MySQLWorkbench

    +

    官网安装包下载安装

    +
  • +
  • 修改数据库 root 密码,

    +

    打开MySQLWorkbench 随便打开一个SQL 输入安装时记录的密码,确认后提示你修改密码,

    +
  • +
  • 如果为记住安装时密码,参考下面链接修改

    +

    +
  • +
+

安装 openfire

    +
  • 官网 下载安装包安装

    +
  • +
  • 安装后需重启系统偏好设置

    +
  • +
  • 如果出现右边的管理员配置按钮不能点击,请使用 brew 安装 或者更新 Java 环境,openfire 需要 Java 环境
  • +
+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ + +

+ 本地已有项目添加Pod +

+ + +
+ +
+ +

上一篇中主要记录了如何使用 pod 工具自动创建 pod 库并且生成一个测试Demo,这边主要记录给已有的项目添加 pod

+

创建 .podsepc 文件

    +
  • cd 到已有项目的根目录执行命令: pod spec create [ProjectName].podspec

    +
    1
    pod spec create EmotionKeyboard.podspec
    +
  • +
+

修改编辑 .podsepc 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
	s.name         = "EmotionKeyboard"
s.version = "0.0.1"
s.summary = "A Emotional Keyboard."

# 此处的格式不能修改
s.description = <<-DESC

Swift Emotional Keyboard
DESC

s.homepage = "https://github.com/aTreey/EmotionKeyboard"

# 两种写法,使用下面第二种
# s.license = "MIT (example)"
s.license = { :type => "MIT", :file => "LICENSE" }

s.author = { "aTree" => "480814177@qq.com" }

s.platform = :ios, "8.0"

s.source = { :git => "https://github.com/aTreey/EmotionKeyboard.git", :tag => "#{s.version}" }


# 从 .podspec 同级别的目录下起匹配
s.source_files = "EmotionKeyboard", "EmotionKeyboard/Classes/**/*.{h,m}"

# 设置自己项目中依赖的系统库
s.framework = "UIKit"

```

## 调整文件的目录结构

- 在项目根目录下新建一个放组件代码的文件夹
- 如果项目中包含 `bundle` 文件, 需要在组件文件夹下新建一个 `Assets` 文件夹,用来存放组件中的资源

最终结果如下:

![](https://ws1.sinaimg.cn/large/a1641e1bly1fm2jl7ykslj21gc0o80xt.jpg))

- 创建并添加必要文件

- `LICENSE` 文件: 开源许可文件, 如果创建项目的时候选择了开源许可就可以自动生成忽略此步骤,如果没有直接拷贝一份其他的工程中的即可
- `README.md` 文件: 主要用来说明你开源库的信息及使用方法


## 验证本地 `.podspec` 文件

- 错误1: `.podspec `中的文件路径设置错误

![](https://ws1.sinaimg.cn/large/a1641e1bly1fm2jlboq82j20v807ejug.jpg)

- 错误2: bundle 文件资源路径错误

![](https://ws1.sinaimg.cn/large/a1641e1bly1fm2jl4shnnj20zq0cwgqf.jpg)


- 本地验证结果

- **EmotionKeyboard passed validation.** 本地验证验证通过

## 网络验证 `.podspec` 文件

- 给 `master` 分支打上 `.podspec` 文件中对应的 `tag` 标签, 提交推送,
- 执行命令
+ +pod spec lint +
1
- 网络验证结果
+ +EmotionKeyboard.podspec passed validation. +``` +

本文参考

+

给 Pod 添加资源文件

+

基于 Swift 创建 CocoaPods 完全指南

+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ + +

+ CocoaPods制作私有pod +

+ + +
+ +
+ +

CocoaPods原理

本文主要记录制作自己的pod库过程

+

CocoaPods 是自动化工具,主要用来管理一些开源的第三方框架,或者是制作自己的私有库,开源库,也可以使用它讲自己的项目组件化。

+

具体可以参考:

+
    +
  1. 细聊 Cocoapods 与 Xcode 工程配置
  2. +
  3. 你真的会用 CocoaPods 吗?
  4. +
+

CocoaPods 安装

安装教程

+

创建私有索引仓库

每次使用第三方开源库的时候只需要在 Podfile 文件中指定所用的库然后 Pod install ,这是因为安装pod的时候 pod setup远程仓库(索引库) clone到了本地,此仓库中存放了所有支持pod框架的描述信息(包括每个库的各个版本)

+

文件目录:

+
~/.cocoapods/repos/master
+
    +
  • 码云 或者 coding 上创建一个私有仓库

    +
  • +
  • 添加私有库到CocoaPods

    +
      +
    • 格式 pod repo add [repoName] (URL)

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      	pod repo add PrivateSpec https://gitee.com/aapenga/PrivateSpec.git 
      ```


      ## 创建本地私有库

      - `cd` 到任意一个文件夹下, 执行 `pod lib create [项目名]`

      ```
      pod lib create PPExcelView
      +
    • +
    +
  • +
+
    +
  • 将文件放入到生成的 Demo 文件夹 的 Classes 文件目录下
  • +
  • cdExample 下(含有 Podfile 文件) 执行

    +
    1
    pod install
    +

    或者

    +
    1
    pod update
    +
  • +
+

此时添加的文件出现在 Dome Pod 的 Development Pods 目录中

+
    +
  • 从本地验证 pod 是否通过

    +
    1
    pod lib lint
    +

    如果有警告,可以使用 --private 或者 --allow-warnings 忽略

    +
    1
    2
    pod lib lint --allow-warnings
    pod lib lint --private
    +

    出现 PPExcelView passed validation. 为通过

    +
  • +
  • 从远程仓库验证 pod

    +
    1
    pod spec lint
    +

    出现 PPExcelView.podspec passed validation 为通过

    +
  • +
+

推送 .podspec 文件到私有仓库

cd.podspec 所在的文件下

+
pod repo push MyGitSpec PPExcelView.podspec
+
+ +
+ +
+ +
+ + + +
+ +
+ + +
+ + +

+ Alamofire源码学习总结 +

+ + +
+ +
+ +

网络请求时Path和Query 之间是用 ‘?’ 号隔开,后边是传给服务器的参数,GET请求Query是放在URL之后,POST请求是放在Body中

    +
  • 如果参数是一个 key-value 形式,Query 格式为:key=value

    +
  • +
  • 如果参数是一个数组 key = [value1, value2, value3 ….],

    +
  • +
  • Query 格式为 key[]=value1&key[]=value2&key[]=value3

    +
  • +
  • 如果参数是一个字典

    +
  • +
+
1
key = [“subKey1”:”value1”, “subKey2”:”value2”, “subKey3”:”value3”….],
+
    +
  • Query 的格式为
  • +
+
1
key[subKey1]=value1&key[subKey2]=value2&key[subKey3]=value3
+
    +
  • Alamfire中的编码
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
private func query(_ parameters: [String: Any]) -> String {
var components: [(String, String)] = []
for key in parameters.keys.sorted(by: <) {
let value = parameters[key]!
components += queryComponents(fromKey: key, value: value)
}
return components.map { "\($0)=\($1)" }.joined(separator: "&")
}
public func queryComponents(fromKey key: String, value: Any) -> [(String, String)] {
var components: [(String, String)] = [] // 元祖数组
if let dictionary = value as? [String: Any] { // value 为字典,key[subKey]=value 形式
for (nestedKey, value) in dictionary {
components += queryComponents(fromKey: "\(key)[\(nestedKey)]", value: value)
}
} else if let array = value as? [Any] { // value为数组, key[]=value 形式
for value in array {
components += queryComponents(fromKey: "\(key)[]", value: value)
}
} else if let value = value as? NSNumber { // value为 NSNumber
if value.isBool {
components.append((escape(key), escape((value.boolValue ? "1" : "0"))))
} else {
components.append((escape(key), escape("\(value)")))
}
} else if let bool = value as? Bool { // value 为 Bool
components.append((escape(key), escape((bool ? "1" : "0"))))
} else { // value 为字符串时 直接转义
components.append((escape(key), escape("\(value)")))
}
return components
}
+ +
+ +
+ +
+ + + + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/page/7/index.html b/page/7/index.html new file mode 100644 index 0000000..8b7ad50 --- /dev/null +++ b/page/7/index.html @@ -0,0 +1,1007 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ +
+ + +
+ + +

+ 知识点总结 +

+ + +
+ +
+ +

开发中的知识点总结

+

textField

+

使用textField自定义搜索框,textField及leftView等属性
使用leftView 时需要设置model为始终显示
可以设置leftView的frame 调整图片大小
IBInsepectable IBDesignable 的使用

+

给属性检查器面板中添加属性设置选项
IBDesignalbe 实时显示UI控件,需要讲对应的xib和对应类绑定

+

增加分类

+

新建一个swift 文件,

+

使用extension,重写 get set方法
使用newValue 时需要判断,注意返回值的类型是否匹配
试图切换

+

一个控制器中加载不同视图,可以在loadView 方法中自定义根视图

+

loadView 使用注意事项
如果实现loadView 未调用 super loadView ,sb/xib实效
loadView调用时,如果根视图是nil(本质是未调用super loadView),会自动调用loadView 方法初始化根视图,形成递归调用
判断当前登陆标识的状态确定是调用 super loadView 还是 把创建自定义的根视图赋值给 view
VFL 的使用

+

使用之前需要先 关闭frame 的布局才能手动的添加约束 translatesAutoresizingMaskIntoConstraints = false
基本动画使用

+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ + +

+ Swift3.0学习(三) +

+ + +
+ +
+ +

面向对象–必选属性

+

构造函数时是分段构造,先构造子类,给’必选属性’设置初始值,然后再构造父类,
定义全局变量(属性)时,需要在构造函数中设置初始化变量值
super.init()是一个类构造结束
重写: override 函数重写(覆盖),如果在重写的方法中 没有super, 父类相关的一些操作就不会被执行
重载:函数名相同, 参数的类型 或者参数的个数不同 就形成了函数的重载
注意:重载构造函数,并且父类默认的构造函数 init()不重写, 父类默认的构造函数就不能够被访问,不能够确保必选属性设置初始值,只能调用重载的构造函数
面向对象–可选属性

+

使用KVC给对象设置值
注意: 在swift中使用KVC 基本数据类型不能声明为可选项,必须设置为必选项给定初始值

+

KVC实现的流程
遍历字典的键值 给对象发送 setValue: forKey消息
如果key 对象的属性不存在就将消息转发给 setVale: forUndefinedKey:
如果存在就直接设置值
注意:setVale: forUndefinedKey: 默认抛出异常 不能够super,如果super 相当于没有实现此方法

+

面向对象–便利构造函数

+

作用:方便快捷的创建对象
场景:
可检查参数是否正确来实例化
实例化控件
特点
必须以self来调用构造函数
指定的构造函数不能被重写,也不能被super调用
便利构造函数可被子类继承
可以构造失败,返回nil
面向对象 – 描述信息

+

重写 description 方法,返回值是string类型
使用kvc讲对象属性转换为字典
将字典转化为string类型

+
override var description: String {
+        let keys = ["name","age","number","sex"]
+
+        // 对象转为字典
+        let dict = self.dictionaryWithValuesForKeys(keys)
+
+        // 每个对象都有描述属性
+        return dict.description
+    }
+

面向对象 – 存储属性

+

面向对象 – 计算属性(readOnly)

+

只有getter,没有setter
只能取值不能赋值
每次调用都会被执行,消耗cpu
不占内存空间,依赖其他的属性来计算
面向对象 – didset 属性设置监察器

+

能获取 oldValue 和 newValue 的值
通常用来重写setter方法,实现视图绑定模型
在一个属性的didset中计算其他属性

+

缺点:消耗内存,被计算的属性占内存
优点:减少了cpu消耗
懒加载

+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ + +

+ Swift3.0学习(二) +

+ + +
+ +
+ +

字符串

+

字符串遍历
字符串字节长度
字符串长度

+

let string = “我是switf,语言”

+

// 字符串遍历
for s in string.characters {
print(s)
}

+

// 字符串的长度
let length = string.characters.count;
print(length)

+

// 字符串字节长度 汉字对应3个字节,英文是1个字节
let characterLength = string.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
print(characterLength)

+

字符串拼接

+
let str1 = "你好"
+   let str2 = "不好"
+   let a = 20
+
+   // 第一种拼接
+   print("\(str1)\(str2)\(a)")
+
+   // 第二种拼接
+   let str = str1 + str2
+   let strA = str1 + String(a)
+   print(str)
+   print(strA)
+

截取子串 as 的使用

+
let subString = str1.substringToIndex("小程生生世世".endIndex);
+   print(subString)
+
+   let subStr = str1.substringFromIndex("听说".startIndex)
+   print(subStr)
+
+   let range = str1.rangeOfString("小程序")
+   let subStr1 = str1.substringWithRange(range!)
+
+   print(subStr1)
+
+   // 截取到某一个位置
+   let subStr2 = str1.substringFromIndex(str1.endIndex.advancedBy(-2))
+   print(subStr2)
+
+   // 限制截取几个字符
+   let subStr3 = str1.substringToIndex(str1.startIndex.advancedBy(5, limit: str1.characters.endIndex))
+   print(subStr3)
+String 和 NSString 之间的 使用 as 无缝转换
+let helloString = "我们一起飞"
+(helloString as NSString).substringWithRange(NSMakeRange(2, 3))
+数组
+

可以放不同类型的元素
let不可变数组,var可变数组

+
// 初始化一个空数组 [String]声明是一个装有字符串的数组类型
+   var emptyArray: [String] = [String]()
+
+// 可变数组
+   var array = ["哈哈","呵呵","嘿嘿"]
+   array.append("咯咯")
+   print("array = \(array)")
+
+   // 不可变数组
+   let array2 = ["可可","噗噗"]
+
+   // 数组拼接
+   let arr = array + array2
+   print("arr = \(arr)")
+
+   array += array2
+   print("array = \(array)")
+

数组遍历

+
// 1. 
+   for i in 0 ..< tempArray.count {
+       print(tempArray[i])
+   }
+
+
+   // 2. 
+   for obj in tempArray {
+       print("第二种方法:\(obj)")
+   }
+
+
+   // 3. 快速便利
+   for (index,value) in tempArray.enumerate() {
+       print("index = \(index), value = \(value)")
+   }
+

字典和OC类似

+

函数调用

+

必须在函数声明的下面调用内部函数

+

可以定义函数的外部参数和内部参数

+

可以在参数前面加上 ‘# ’ 表明函数的外部参数和内部参数名称一样 swift3.0 以后废弃

+

无参无返回值
三种写法

+
func demo9() -> Void {
+       print("无参无返回值第一种写法")
+   }
+
+
+   func demo_9() -> () {
+       print("无参无返回值第二种写法")
+   }
+
+   func demo_99() {
+       print("无参无返回值第三种写法")
+   }
+

有参有返回值

+
func demo10(width a: Int, height b: Int) -> Int {
+   return a * b
+

}

+

闭包

+

和OC中block类似,
和在函数中调用一个内部函数原理相同
可当作参数传递
在需要时执行闭包实现回调

+
// 定义闭包
+let closure = {() -> () in
+       print("闭包实现")
+   }
+
+   // 执行闭包
+   closure()
+

注意循环引用

+

只有两个对象相互强引用或者三个对象相互强引用形成闭环时才会形成循环引用

+

weak 和 unsafe_unretained的区别

+

__weak iOS5.0 推出,当对象被系统回收时,对象的地址会自动指向nil

+

____unsafe_unretained iOS4.0 推出,当对象被系统回收时,对象的地址不会自动指向nil,会造成野指针访问
解决办法:(官方推荐)weak - strong -dance 解决循环引用,AFNetworking中

+

尾随闭包

+

函数的最后一个参数时闭包的时候,函数的参数 ‘()’可以提前关闭,闭包写在 ‘()’后面,当作尾随闭包来使用

+

swift 和 OC 区别

+

swift 和 OC语法的快速的对比

+

XXX.init(xxx) ==> XXX.(xxx)
selector 类型 ==> ‘函数名’
对象方法的调用 是 ‘.’ self是可以省略的
枚举 枚举名 + 枚举值的名 => .枚举值的名
常量 和变量

+

let 声明常量 var 声明变量
变量/常量的类型是自动推到的
不同类型之间不能够直接运算 需要手动转换数据类型
可选项
‘?’ 表示表示可选项 可能有值 可能为 nil
可选项会自动带上 Optional 字样
可选项不能够直接参与运算 需要强制解包
‘!’ 表示强制解包 获取可选项中具体的值
‘??’ 快速判断可选项是否为nil 如果为 nil 就去取 ‘??’后面的默认值
控制流
if 没有非零即真的概念 必须制定明确的条件
if let 快速赋值 并且判断赋值对象是否为 nil 如果不为nil 就进入分之执行相关逻辑代码
guard let 作用和 if let 相反 好处: 可以减少一层分支嵌套
where 多重判断 注意 和 && || 不一样的
循环

+

for in
0..<10
0…10
字符串

+

更加轻量级 更加高效 是 结构体
支持快速遍历
长度
拼接
截取 as
集合 let 声明不可变的集合 var 声明可变的集合

+

声明 []
声明一个 空的集合
增删改查
合并
遍历
函数

+

func 函数名称(外部参数1 内部参数1: 类型, 外部参数2 内部参数2: 类型) -> Int {执行的代码}
函数没有返回值的三种方式
函数内部函数
闭包 () -> () 没有参数没有返回值的闭包类型

+

提前准备好的一段可以执行代码块
可以当做参数传递
在需要的时候执行闭包产生回调的效果
在闭包中使用self 需要考虑循环引用
函数是一种特殊的闭包

+

block 的循环引用的解除
weak 和 weak关键字作用类似 当属性对象被回收是 会自动指向nil
_
unsafeunretained 和 assgin关键字作用类似 当属性对象被回收是 不会自动指向nil, 会造成野指针访问 weak -strong - dance

+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ + +

+ Swift 3.0 学习 +

+ + +
+ +
+ +

和OC区别

    +
  • 没有了.h 和 .m 文件,没有main.m 文件
  • +
  • 程序入口发生变化 在appdelegate中的 @UIApplicationMain
  • +
  • 每行代码可以不写 ‘;’
  • +
  • self可以不写,建议不写self,因为闭包中必须要写
  • +
  • 真假表示只有 true/ false,没有非0即真的概念
  • +
  • 类型自动推到
  • +
+

类型自动推到

提前指定类型

1
2
let float :Double = 9.6 
print(float)
+

常量和变量

    +
  • let 声明常量,有且只有一次赋值机会
  • +
  • var 声明变量
  • +
  • 尽量选择使用let如需修改值再使用var 系统会报警告
  • +
+

可选项

    +
  • Optional: 表示一个常量或者是变量可能有值可能为nil,在打印的时候会带有Optional字样
  • +
  • ? 表示是一个可选项
  • +
  • 可选项不能参与运算
  • +
  • ! 表示可选项中一定要有值,可以强制解包,解包时如果没有值会boom

    +
  • +
  • ?? 合并空选项 快速判断可选项是否为nil, 如果为nil取后面的默认值,在基本数据类型和字符串使用较多

    +
  • +
+

条件分支

    +
  • if let
  • +
+

快速赋值,判断赋值对象是否为nil,如果不为nil,就赋值进入分支
生成request是url是一个可选项,需要强制解包或者合并空选项

+
1
2
3
4
let urlString = "http://www.baidu.com"
let url = NSURL(string: urlString)
let request = NSURLRequest(URL: url!)
print(request)
+
    +
  • 以上代码使用if let实现
  • +
+
1
2
3
4
if let u = NSURL(string: urlString) {
let request = NSURLRequest(URL: u)
print(request)
}
+
    +
  • where条件语句配合多重判断
  • +
+
1
2
3
4
5
6
if url != nil  {
if url?.host == "www.baidu.com" {
let request = NSURLRequest(URL: url!)
print(request)
}
}
+
    +
  • 使用if let
  • +
+
1
2
3
4
if let u = url where u.host == "www.baidu.com" {
let request = NSURLRequest(URL: u)
print(request)
}
+
    +
  • 多重判断
  • +
+
1
2
3
4
if let u = url, s = string {
let request = NSURLRequest(URL: u)
print(request)
}
+
    +
  • guard let 和 if let 相反如果为nil,可以直接返回
  • +
+
1
2
3
4
5
6
7
let urlString  = "http://www.baidi.com"
let url = NSURL(string: urlString)
guard let u = url else { return }

//程序走到这个地方就表示 u 一定有值
let request = NSURLRequest(URL: u)
print(request)
+
    +
  • swith语句

    +
      +
    • swift语句中可以case任意类型,
    • +
    • 在OC中只能case整数
    • +
    • 不需要写break,会自动跳出,
    • +
    • 如果需要实现case穿透,需要加入fallthrough语句
    • +
    • case语句中临时变量不需要写‘{ }’
    • +
    • 可同时case多个值
    • +
    • 每句case语句中至少要有一行可执行的代码
    • +
    +
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let string = "string1"
switch string {
case "string", "string1":
let str = "stringOne"
print(str)

case "string2":
let str = "stringTWO"
print(str)

case "string3":
print(string)

case "string4":
print(string)
default:
print("empty")
}
+
    +
  • switch 中同样能够赋值和使用 where 子句
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
let point = CGPoint(x: 10, y: 10)
switch point {
case let p where p.x == 0 && p.y == 0:
print("中心点")
case let p where p.x == 0:
print("Y轴")
case let p where p.y == 0:
print("X轴")
case let p where abs(p.x) == abs(p.y):
print("对角线")
default:
print("其他")
}
+
    +
  • 循环语句
  • +
+
1
2
3
4
5
6
7
for i in 0 ..< 10 { // 不包含10  循环范围中两边的值格式必须一直(空格问题)
print(i)
}

for _ in 0...5 {// 包含5 _ 表示忽略,不关心,只占位
print("循环语句")
}
+ +
+ +
+ +
+ + + +
+ +
+ + +
+ + +

+ OC和JS交互 +

+ + +
+ +
+ +

WebView相关属性

autoResizingMask 是UIView的属性,作用是实现子控件相对于父控件的自动布局

+
1
2
3
4
5
6
7
8
9
autoResizingMask 是UIViewAutoresizing 默认是 UIViewAutoresizingNone

UIViewAutoresizingNone = 0,
UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
UIViewAutoresizingFlexibleWidth = 1 << 1,
UIViewAutoresizingFlexibleRightMargin = 1 << 2,
UIViewAutoresizingFlexibleTopMargin = 1 << 3,
UIViewAutoresizingFlexibleHeight = 1 << 4,
UIViewAutoresizingFlexibleBottomMargin = 1 << 5
+

各属性解释:

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
UIViewAutoresizingNone
不会随父视图的改变而改变

UIViewAutoresizingFlexibleLeftMargin

自动调整view与父视图左边距,以保证右边距不变

UIViewAutoresizingFlexibleWidth
自动调整view的宽度,保证左边距和右边距不变

UIViewAutoresizingFlexibleRightMargin
自动调整view与父视图右边距,以保证左边距不变

UIViewAutoresizingFlexibleTopMargin
自动调整view与父视图上边距,以保证下边距不变

UIViewAutoresizingFlexibleHeight
自动调整view的高度,以保证上边距和下边距不变

UIViewAutoresizingFlexibleBottomMargin
自动调整view与父视图的下边距,以保证上边距不变
+

OC 调用js

webViewDidFinishLoad:(UIWebView *)webView方法中

+
    +
  • 删除增加结点
  • +
  • 添加点击事件
  • +
+

js调用OC

webView:(UIWebview )webView shouldStartLoadWithRequest:(NSURLRequest )request方法中

+

调用js页面点击按钮后保存图片到相册,必须调用系统提供的方法,不能自己自定义方法

+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ + +

+ SDWebImage的用法及原理 +

+ + +
+ +
+ +

SDWebImage 介绍

SDWebImage 是用于网络中下载且缓存图片,并设置图片到对应的控件或上

+

Demo: https://github.com/aTreey/DownImage.git

+
    +
  • 提供了UIImageView的category用来加载网络图片并且下载的图片的缓存进行管理
  • +
  • 采用异步方式来下载,确保不会阻塞主线程,memory+disk来缓存网络图片,自动管理缓存
  • +
  • 支持GIF动画,[self.imageView setImageWithURL:[[NSBundle mainBundle] URLForResource:@”xx.gif” withExtension:nil];
  • +
  • 支持WebP格式
  • +
  • 同一个URL的网络图片不会被重复下载
  • +
  • 失效的URL不会被无限重试
  • +
+

SDWebImage 的使用

克隆

+

git clone https://github.com/rs/SDWebImage.git
使用以上命令克隆会报错,框架中Vendors文件夹中的文件未能全部下载导致报错

+

解决办法:https://github.com/rs/SDWebImage/blob/master/Docs/ManualInstallation.md

+

git clone –recursive https://github.com/rs/SDWebImage.git

+

文件全部下载

+

自定义operation 加入NSOperationQueue中

+
    +
  • 自定义NSOperation,
    重写main方法
  • +
+
1
2
3
4
5
6
7
8
9
10
11
- (void)main {
@autoreleasepool {
NSURL *url = [NSURL URLWithString:_urlStr];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];

if (_finishBlock) {
_finishBlock(image);
}
}
}
+
    +
  • 一个NSOperation对象可以通过调用start方法来执行任务,默认是同步执行的。也可以将NSOperation添加到一个NSOperationQueue(操作队列)中去执行,而且是异步执行的
    创建队列
  • +
+
1
2
3
4
- (NSOperationQueue *)downLoadQueue {
if (!_downLoadQueue) _downLoadQueue = [[NSOperationQueue alloc] init];
return _downLoadQueue;
}
+
    +
  • 添加一个任务到队列
  • +
+
1
2
3
4
5
6
7
8
9
[_downLoadQueue addOperation:operation];
添加一组operation, 是否阻塞当前线程

[_downLoadQueue addOperations:@[operation] waitUntilFinished:NO];
添加一个block 形式的operation

[_downLoadQueue addOperationWithBlock:^{
NSLog(@"执行一个新的线程");
}];
+

注意:

+
    +
  1. NSOperation 添加到 queue之后,通常短时间内就会执行,但是如果存在依赖,或者整个queue被暂停等原因,也可能会需要等待

    +
  2. +
  3. NSOperation添加到queue之后,绝不要修改NSOperation对象的状态,因为NSOperation对象可能会在任何时候运行,因此改变NSOperation对象的依赖或者数据会产生不利的影响,只能查看NSOperation 对象的状态,比如是否正在运行、等待运行、已经完成等

    +
  4. +
+
    +
  • NSOperation 添加依赖,
    依赖关系不局限于相同的queue 中的NSOperation对象,可以夸队列进行依赖,但是不能循环依赖,
  • +
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// NSOperation 添加依赖
{
NSOperationQueue *testQueue = [[NSOperationQueue alloc] init];
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"operation1 NSThread = %@", [NSThread currentThread]);
}];

NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"operation2 NSThead = %@", [NSThread currentThread]);
}];

// 添加依赖,1 依赖于 2,只有2 执行完之后才执行1
[operation1 addDependency:operation2];
// 加入到队列
[testQueue addOperation:operation1];
[testQueue addOperation:operation2];
}
+
    +
  • 修改Operation的执行顺序
    对于添加到queue中的operation执行顺序有一下2点决定
    operation 是否已经准备好,是否添加了依赖
    根据多有operations的相对优先性来决定,优先等级是operation对象本身的一个属性,默认都是“普通”优先级,可以通过setQueuePriority方法提高和降低优先级,优先级只能用于相同 queue 中的 operation,多个queue中operation的优先级相互独立,因此不同queue中的低优先级的operation可能比高优先级的operation更早执行
    注意优先级和依赖不能相互替代,优先级只是对已经准备好的operation确定执行顺序,先满足依赖,然后再看已经准备好的operation中的优先级

    +
  • +
  • 设置 queue 的最大并发数,队列中最多同时运行几条线程
    虽然NSOperationQueue类设计用于并发执行Operations,你也可以强制单个queue一次只能执行一个Operation。setMaxConcurrentOperationCount:方法可以配置queue的最大并发操作数量。设为1就表示queue每次只能执行一个操作。不过operation执行的顺序仍然依赖于其它因素,比如operation是否准备好和operation的优先级等。因此串行化的operation queue并不等同于GCD中的串行dispatch queue

    +
  • +
+
1
2
3
4
// 每次只能执行一个操作  
queue.maxConcurrentOperationCount = 1;
// 或者这样写
[queue setMaxConcurrentOperationCount:1];
+
    +
  • 取消 operations
    一旦添加到operation queue,queue就拥有了这个Operation对象并且不能被删除,只能取消。调用Operation对象的cancel方法取消单个操作,也可以调用operation queue的cancelAllOperations方法取消当前queue中的所有操作, 使用cancel属性来判断是否已经取消了
  • +
+

[operation cacel] 只是打了一个死亡标记, 并没有正真意义上的取消,称为 “自杀“,需要在被取消的任务中时时判断是否取消(在程序的关键处), 如果取消,结束任务, 具体是指在自定义的 operation 的main方法中结束

+
1
2
3
4
5
6
[queue cancelAllOperations] 由队列来取消,称为 “他杀”

// 取消单个操作
[operation cancel];
// 取消queue中所有的操作
[queue cancelAllOperations];
+
    +
  • 等待operation 完成
  • +
+

为了最佳的性能,你应该设计你的应用尽可能地异步操作,让应用在Operation正在执行时可以去处理其它事情。如果需要在当前线程中处理operation完成后的结果,可以使用NSOperation的waitUntilFinished方法阻塞当前线程,等待operation完成。通常我们应该避免编写这样的代码,阻塞当前线程可能是一种简便的解决方案,但是它引入了更多的串行代码,限制了整个应用的并发性,同时也降低了用户体验。绝对不要在应用主线程中等待一个Operation,只能在第二或次要线程中等待。阻塞主线程将导致应用无法响应用户事件,应用也将表现为无响应。

1
2
// 会阻塞当前线程,等到某个operation执行完毕  
[operation waitUntilFinished];

+

除了等待单个Operation完成,你也可以同时等待一个queue中的所有操作,使用NSOperationQueue的waitUntilAllOperationsAreFinished方法。注意:在等待一个 queue时,应用的其它线程仍然可以往queue中添加Operation,因此可能会加长线程的等待时间。

+

// 阻塞当前线程,等待queue的所有操作执行完毕
[queue waitUntilAllOperationsAreFinished];
暂停和继续operation
如果你想临时暂停Operations的执行,可以使用queue的setSuspended:方法暂停queue。不过暂停一个queue不会导致正在执行的operation在任务中途暂停,只是简单地阻止调度新Operation执行。你可以在响应用户请求时,暂停一个queue来暂停等待中的任务。稍后根据用户的请求,可以再次调用setSuspended:方法继续queue中operation的执行

1
2
3
4
5
// 暂停queue  
[queue setSuspended:YES];

// 继续queue
[queue setSuspended:NO];

+

增加Manager管理类,

作用:

+
    +
  • 下载队列
  • +
  • 图像缓存
  • +
  • 操作缓存(内存缓存)
  • +
  • 沙盒缓存
  • +
  • 防止错乱,取消老的未开始下载的操作
  • +
  • 图片显示逻辑:内存缓存 – 沙盒缓存 – 网络下载
  • +
  • 图片缓存逻辑:下载完成 – 缓存沙盒 – 加载到内存
  • +
+

注意:缓存到本地时只能保存property列表里的对象(NSData, NSDate, NSNumber, NSString, NSArray, NSDictory), 图片需要转化为二进制数据

+

SDWebImage

    +
  • SDWebImageDownloader 下完图片后都需要手动设置给UIImageView
  • +
  • SDWebImageManager 下完图片后都需要手动设置给UIImageView
  • +
  • 下载后要显示可以使用 sd_setImageWithURL 方法
  • +
+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ + +

+ WKWebiView 使用 +

+ + +
+ +
+ +

iOS中静态库动态库介绍及编译

    +
  • 静态库
  • +
+

iOS中根据源码的公开情况可分为2种

+
    +
  • 开源库
    源代码公开,能够看到具体的代码实现
  • +
+

比如 SDWebImage AFNetworking。。。

+
    +
  • 闭源库
    不公开源代码, 是经过编译后的二进制文件, 看不到具体实现
    主要分为:静态库、动态库

    +
  • +
  • 存在形式

    +
  • +
+

静态库: .a 和 .framework
动态库:.tbd 和 .framework (iOS 9 之前是 .dylib, iOS 9 之后取消了 .dylib 而使用 .tbd 来代替 .dylib )

+

.framework 的库可以是静态库也可以是静态库, 凡是系统的 .framework 都是动态库,第三方的基本上全是静态中

+
    +
  • 两者区别
  • +
+

静态库:链接时会被完整的复制到可执行文件中,每次被使用都会被复制
动态库:链接时不复制,程序运行时由系统动态加载到内存中,供程序调用,系统只加载一次,多个程序可以公用,节省内存

+

静态库编译

编译时如果使用的是模拟器,生成的 .a 只能模拟器用,真机不可用,生成的 .a 文件在 Debug-iphonesimulator 文件夹下

+

编译时如果使用 generic iOS Device,生成的 .a 只能真机而模拟器不可用,生成的 .a 文件在 Debug-iphoneos 文件夹下

+

编译静态库时,自己手动创建的类的头文件不会被导出,只是默认导出创建工程时类的头文件

+

导出手动添加类的头文件,点击工程名称 –> build Phases –> Copy Files 添加头文件,然后编译

+
    +
  • 架构种类
  • +
+

模拟器:i386 32位架构 (4s~5) / x86_64 64位架构 (5s以后机型)
真机:armv7 32位架构 (4~4s) / armv7s 特殊架构 (5~5C) / arm64 64位架构 (5s 以后机型)

+
    +
  • 架构查看
    lipo -info xxxx.a
    默认生成模拟器只生成一种架构 i386 x86_64;真机默认导出两种架构 armv7 / arm64

    +
  • +
  • 合并多个架构

    +
  • +
+

将模拟器的多个架构合并,项目名称–>Build Settings –> Achitectures –> Build Active Architecture Only –> Debug设置为NO
合并真机多个架构 按照以上方法运行真机即可
合并真机和模拟器架构 cd products lipo -create Debug-iphoneos/xxx.a Debug-iphonessimulator/xxx.a -output xx.a
合成 5 个架构 在Build Setting –> Architectures 中手动添加三种架构,再编译合并

+
    +
  • 静态库报错
  • +
+

Undefined symbols for architecture (arm64 / armv7 / armv7s / i386 / x86_64),架构包导入出错,静态库有真机与模拟器

+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ + +

+ 异步下载图片 +

+ + +
+ +
+ +

异步下载

同步发送网络请求会卡UI线程,采用异步来下载图片。

+

异步下载存在的问题

+
    +
  • 错行问题

    +
      +
    • 解决:使用MVC模式,使数据一对一可解决,
    • +
    • 在MVC中增加image属性后,会导致单个模式的体积增大
    • +
    • 内存占用
    • +
    • 当接收到内存警告时清空图片时需要遍历模型中将image置为nil
    • +
    • 将图片用字典缓存起来达到一对一, 清空内存中的图片时只需要remove字典就ok
    • +
    • 创建下载任务的字典,在开始下载时将正在下载的URL写入到字典中下载完成后将字典中对应的值移除,写入防止来回滑动时重复发送网络请求,下载完后移除是为了防止如果接受到内存警告后下载的图片都已清除,但是下载任务的字典的值为移除就不会再一次去下载图片
    • +
    • 缓存下载的图片到沙盒中

      +
    • +
    • 获取图片下载路径最后一个分隔符后面的字符串作为图片名保存

      +
    • +
    +
  • +
+

stringByAppendingPathComponentstringByAppendingString 两个方法有区别

+

stringByAppendingString 方法是获取caches文件名,在文件夹名后面拼接字符串,在caches上一级目录下创建文件,和caches文件夹属于同一个级别

+
// 获取caches文件路径
+NSString *cachesPath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
+
+NSString *URL = @"http://wwww.baidu.com/image/123456.png";
+
+NSString *imageName = [URL lastPathComponent];
+
+// 拼接路径
+// 此方法生成的路径在caches文件下目录下
+NSString *filePath = [cachesPath stringByAppendingPathComponent];
+
+
+// 读取图片
+if (内存中没有) {
+    // 沙盒读取
+    UIImage *sandBoxImage = [UIImage imageWithContetsOfFile:filePath];
+
+    **加入到内存中,方便下次加载**
+} 
+
+if (sandBoxImage == nil) {
+    // 下载图片
+}
+
+// 写入到沙盒
+if (imageData != nil) {
+    [imageData writeToFile: filePath atomically:NO];
+}
+
+ +
+ +
+ +
+ + + +
+ +
+ + +
+ + +

+ Quartz 2D绘制形状(二) +

+ + +
+ +
+ +

使用Quartz 2D绘制圆弧,扇形,下载进度以及绘制的图形的填充和描边属性的设置.
将贝塞尔路径添加到图形上下文中
图形上下文中的状态保存和恢复
对图形上下文进行矩阵操作(平移,缩放,选转),实现一些特殊效果

+

使用贝塞尔曲线绘制矩形

1
2
3
4
5
6
7
{
UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, 20, 30)];
[[UIColor magentaColor] setStroke];
[[UIColor brownColor] setFill];
[path fill]; // 填充
[path stroke]; // 描边
}
+

圆角矩形

1
2
3
4
5
6
7
8
// 绘制圆角矩形
{
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(35, 10, 50, 70) cornerRadius:5.0];
[[UIColor magentaColor] setStroke];
[[UIColor cyanColor] setFill];
[path fill];
[path stroke];
}
+

绘制圆

1
2
3
4
5
6
7
8
// 画圆
{
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(90, 5, 40, 40) cornerRadius:20.0];
[[UIColor purpleColor] setStroke];
[[UIColor magentaColor] setFill];
[path fill];
[path stroke];
}
+

绘制扇形

1
2
3
4
5
6
CGPoint center = CGPointMake(200, 50);
UIBezierPath *sectorPath = [UIBezierPath bezierPathWithArcCenter:center radius:100 startAngle:0 endAngle:M_PI_4 clockwise:YES];
[sectorPath addLineToPoint:center];
[[UIColor brownColor] setStroke];
[sectorPath fill];
[sectorPath stroke];
+

绘制圆弧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 绘制圆弧
* 参数1: 圆心坐标
* 参数2: 半径
* 参数3: 开始角度
* 参数4: 结束角度 使用弧度制表示
* 参数5: clockwis顺时针方法的
*/

UIBezierPath *circuLarArcPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(100, 60) radius:50.0 startAngle:0 endAngle:M_PI_2 clockwise:YES];

[circuLarArcPath moveToPoint:CGPointMake(100, 60)];

[[UIColor magentaColor] setStroke];
[[UIColor brownColor] setFill];
[circuLarArcPath stroke];
[circuLarArcPath fill];
+

绘制文字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
* 绘制文字
* drawAtPoint 不能换行,当文字过多过长时不会有效果
* 使用 drawInRect 可以实现换行
*/


NSString *text = @"我是绘制文字价格就看过几个框架刚开始大家高考的感觉是快乐的关键时刻大家赶快来时的结果看了风水格局来看就哭了感觉树大根深来对抗肌肤抵抗力";

NSMutableDictionary *attributeDict = [NSMutableDictionary dictionary];
attributeDict[NSFontAttributeName] = [UIFont systemFontOfSize:18];

// 字体颜色
attributeDict[NSForegroundColorAttributeName] = [UIColor greenColor];
// 描边颜色
attributeDict[NSStrokeColorAttributeName] = [UIColor yellowColor];
// 描边宽度
attributeDict[NSStrokeWidthAttributeName] = @1;
// 阴影
NSShadow *shadow = [[NSShadow alloc] init];
shadow.shadowColor = [UIColor redColor];
shadow.shadowOffset = CGSizeMake(1.0, 1.0);

attributeDict[NSShadowAttributeName] = shadow;

// 可以换行
[text drawInRect:self.bounds withAttributes:attributeDict];

// 不换行
[text drawAtPoint:CGPointZero withAttributes:attributeDict];
+

绘制图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 绘制图片
* drawAtPoint 默认绘制的内容尺寸和图片的尺寸一样大
*
*/

- (void)drawImage {

UIImage *image = [UIImage imageNamed:@"wc_dk"];
UIImage *image1 = [UIImage imageNamed:@"wc_hq"];

[image drawAtPoint:CGPointMake(10, 90)];
[image1 drawAtPoint:CGPointMake(70, 80)];

// 绘制在控件内部,和控件大小一致
[image1 drawInRect:self.bounds];

// 以平铺的方式绘制在控件的内部
[image1 drawAsPatternInRect:self.bounds];
}
+

圆形下载进度的绘制

drawRect 方法不能手动调用,因为图形上下文不能手动创建
需要调用setNeedsDisplay方法来重绘,系统会创建图形上下文
绘制时用到的定时器,一般不使用NSTimer定时器,因为调度优先级比较低,不会被准时带,界面会出现卡顿的现象
CADisplayLink 每次刷新屏幕的时候就会调用,1s刷新 60 次
setNeedsDisplay调用此方法时并不是立即调用drawRect方法,只是给给当前控件添加刷新标记,直到下一次屏幕刷新的时候才调用drawRect 方法,正好与CADisplayLink 方法一致,所以界面流畅不会卡顿
图形上下文状态栈

+

UIBezierPath图形上下文和CGContextRef的图形上下文不是同一个图形上下文

+

可以将贝塞尔路径添加到图形上下文中

+

也可以保存当前的图形上下文,CGContextSaveGState(ctx);
在下一次使用时可以恢复,CGContextRestoreGState(ctx);
画两种不同状态的线条

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
/**
* 图形上下文栈的理解
* 画两条状态不同的线段
*/

// 获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();

// 第一条
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(10, 60)];
[path addLineToPoint:CGPointMake(240, 60)];

// 把路径添加到上下文
CGContextAddPath(ctx, path.CGPath);

// 保存上下的状态
CGContextSaveGState(ctx);

// 设置当前状态
[[UIColor greenColor] setStroke];
//此时设置path 的状态无效,需要设置当前上下文的状态
path.lineWidth = 5.0;
CGContextSetLineWidth(ctx, 5.0);

// 渲染上下文
CGContextStrokePath(ctx);


// 第二根

// 可使用第一根的路径,因为每次绘制完成之后都会清除
path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(60, 10)];
[path addLineToPoint:CGPointMake(60, 240)];

// 添加路径到上下文 需要转换为CGPath
CGContextAddPath(ctx, path.CGPath);

// 还原之前保存的上下文状态
CGContextRestoreGState(ctx);
CGContextStrokePath(ctx);
图形上下文矩阵

// 使用矩阵

// 获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();

// 使用贝塞尔路径
UIBezierPath *ovalPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(-150, -100, 300, 200)];
[[UIColor cyanColor] setFill];



// 以上绘制的图形有一部分看不到,通过对上下文矩阵操作来实现,平移上下文
// 操作矩阵必须在添加路径之前

// 平移
CGContextTranslateCTM(ctx, 150, 100);

// 缩放
CGContextScaleCTM(ctx, 0.5, 0.5);

// 旋转
// 可以实现点无法计算时的效果
CGContextRotateCTM(ctx, M_PI_4);



// 添加路径到上下文
CGContextAddPath(ctx, ovalPath.CGPath);

// 操作矩阵放在添加路径之后无效果
// CGContextTranslateCTM(ctx, 150, 100);

CGContextFillPath(ctx);

// 正常的绘制椭圆
// UIBezierPath *ovalPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(50, 20, 200, 100)];
// ovalPath.lineWidth = 4.0;
// [[UIColor magentaColor] setFill];
// [[UIColor yellowColor] setStroke];
// [ovalPath stroke];
// [ovalPath fill];
+ +
+ +
+ +
+ + + +
+ +
+ + +
+ + +

+ GCD实现按钮定时亮起 +

+ + +
+ +
+ +

某些时候需要按钮不能连续点击,需要在一定的时间之后才可以允许交互,或者是实现类似发送验证码的按钮效果,具体做法是采用定时器

+

使用GCD定时器

创建定时器对象

__block NSInteger time = 30; // 需要强应用
+

创建队列

dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
+

创建dispatch源(定时器)

(dispatach_source…timer..)
+
    +
  • 01参数:要创建的source 是什么类型的,

    +

    (DISPATCH_SOURCE_TYPE_TIMER)定时器

    +
  • +
  • 04参数:队列 —-线程 决定block 在哪个线程中调用

    +
  • +
+

代码

+
dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
+

设置定时器

    +
  • 01参数:定时器对象

    +
  • +
  • 02参数:开始时间 (DISPATCH_TIME_NOW) 什么时候开始执行第一次任务

    +
  • +
  • 03参数:间隔时间 GCD时间单位:纳秒

    +
  • +
  • 04参数:leewayInSeconds精准度:允许的误差: 0 表示绝对精准

    +
  • +
+

code:

+
dispatch_source_set_timer(_timer, dispatch_walltime(NULL, 0), 1.0 * NSEC_PER_SEC, 0);
+

定时器每隔一段时间就要执行任务(block回调)

dispatch_source_set_event_handler(_timer, ^{
+    if (time <= 0) {
+        dispatch_source_cancel(_timer);
+        dispatch_async(dispatch_get_main_queue(), ^{
+
+            // 设置按钮的样式
+            [self.button setTitle:@"重新获取验证码" forState:UIControlStateNormal];
+            [self.button setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
+            [self.button setUserInteractionEnabled:YES];
+        });
+    } else {
+
+        NSInteger seconds = time;
+        dispatch_async(dispatch_get_main_queue(), ^{
+            [self.button setTitle:[NSString stringWithFormat:@"重新发送(%.2ld)", seconds] forState:UIControlStateNormal];
+            [self.button setTitleColor:[UIColor lightGrayColor] forState:UIControlStateNormal];
+            [self.button setUserInteractionEnabled:NO];
+        });
+        time--;
+    }
+});
+

启动定时器(默认是停止的)

dispatch_resume(timer);
+
+ +
+ +
+ +
+ + + + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/page/8/index.html b/page/8/index.html new file mode 100644 index 0000000..7be7f4b --- /dev/null +++ b/page/8/index.html @@ -0,0 +1,1198 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ +
+ + +
+ + +

+ Quartz2D绘制曲线 +

+ + +
+ +
+ +

使用函数绘制曲线

+

Quartz 2D提供了CGContextAddCurveToPoint() 函数和CGContextAddQuadCurveToPoint()两个函数来向当前上下文添加曲线,前者用于添加贝塞尔曲线,后者用于添加二次曲线。
确定一条贝塞尔曲线需要4个点:开始点、第一个控制点、第二个控制点和结束点。

+

确定一条二次曲线需要三个点:开始点、控制点和结束点。

+

添加贝塞尔曲线

+
CGContextAddCurveToPoint()
+
+CGContextRef ctx = UIGraphicsGetCurrentContext();
+
+// 添加曲线路径
+// 设置起点
+CGContextMoveToPoint(ctx, 0, 0);
+
    +
  • 参数1: 上下文对象
  • +
  • 参数2: 控制点1X坐标
  • +
  • 参数3: 控制点1Y坐标
  • +
  • 参数4: 控制点2X坐标
  • +
  • 参数5: 控制点2Y坐标
  • +
  • 参数6: 终点x坐标
  • +
  • 参数7: 终点Y坐标
  • +
+

code:

+
CGContextAddCurveToPoint(ctx, 30, 200, 300, 20, 300, 300);
+
+// 设置颜色
+[[UIColor magentaColor] setStroke];
+
+// 渲染
+CGContextStrokePath(ctx);
+
+// 关闭图形上下文
+CGContextClosePath(ctx);
+添加二次曲线
+CGContextAddQuadCurveToPoint()
+
+// 获取图形上下文
+CGContextRef ctx = UIGraphicsGetCurrentContext();
+
+// 起始点
+CGContextMoveToPoint(ctx, 20, 10);
+
    +
  • 参数: 图形上下文
  • +
  • 参数: 控制点坐标
  • +
  • 参数: 结束点坐标
  • +
+

添加二次曲线

+
CGContextAddQuadCurveToPoint(ctx, 30, 200, 200, 40);
+
+[[UIColor cyanColor] setStroke];
+CGContextStrokePath(ctx);
+// 关闭图形上下文
+CGContextClosePath(ctx);
+

绘制形状

+

使用绘制二次曲线的函数绘制花瓣

+

程序中CGContextAddFlower代码分别添加5瓣花朵路径、6瓣花朵路径、7瓣花朵路径,然后使用不同的颜色来填充这些路径。注意到上面的程序并未在每次添加花朵路径后立即关闭,这也是允许的,而且每次填充路径时并不会再次填充前一次已经填充过的路径。这是因为只用程序绘制了CGContextRef当前所包含的路径,系统会自动清除已经绘制的路径。

+

注意:每次绘制完成后,CGContextRef会自动清除已经绘制完成的路径

+

该方法负责绘制花朵。
n:该参数控制花朵的花瓣数;dx、dy:控制花朵的位置;size:控制花朵的大小;
length:控制花瓣的长度

+
void CGContextAddFlower(CGContextRef c , NSInteger n
+                        , CGFloat dx , CGFloat dy , CGFloat size , CGFloat length)
+{
+    CGContextMoveToPoint(c , dx , dy + size);  // 移动到指定点
+    CGFloat dig = 2 * M_PI / n;
+    // 采用循环添加n段二次曲线路径
+    for(int i = 1; i < n + 1 ; i++)
+    {
+        // 计算控制点坐标
+        CGFloat ctrlX = sin((i - 0.5) * dig) * length + dx;
+        CGFloat ctrlY= cos((i - 0.5 ) * dig) * length + dy;
+        // 计算结束点的坐标
+        CGFloat x = sin(i * dig) * size + dx;
+        CGFloat y =cos(i * dig) * size + dy;
+        // 添加二次曲线路径
+        CGContextAddQuadCurveToPoint(c, ctrlX , ctrlY , x , y);
+    }
+}
+

绘制花瓣

+
- (void)drawStar
+{
+    CGContextRef ctx = UIGraphicsGetCurrentContext();  // 获取绘图的CGContextRef
+    CGContextBeginPath(ctx);  // 开始添加路径
+    CGContextAddFlower(ctx , 5 , 50 , 100 , 30 , 80);  // 添加5瓣花朵的路径
+    CGContextSetRGBFillColor(ctx, 1, 0, 0, 1);  // 设置填充颜色
+    CGContextFillPath(ctx);
+    CGContextAddFlower(ctx , 6 , 160 , 100 , 30 , 80);  // 添加6瓣花朵的路径
+    CGContextSetRGBFillColor(ctx, 1, 1, 0, 1);  // 设置填充颜色
+    CGContextFillPath(ctx);
+    CGContextAddFlower(ctx , 7 , 270 , 100 , 30 , 80);  // 添加7瓣花朵的路径
+    CGContextSetRGBFillColor(ctx, 1, 0, 1, 1);  // 设置填充颜色
+    CGContextFillPath(ctx);
+    CGContextClosePath(ctx);  // 关闭路径
+}
+
+ +
+ +
+ +
+ + + +
+ +
+ + +
+ + +

+ Quartz2D 初识 +

+ + +
+ +
+ +

Quartz2D 二维绘图引擎,支持iOS和Mac系统么,可以实现的功能有

+
    +
  • 绘制图形: 线条,三角形,矩形,圆,弧等
  • +
  • 绘制文字
  • +
  • 绘制图像或者图片
  • +
  • 读取生成PDF
  • +
  • 截图/裁剪图片
  • +
  • 自定义UI控件
  • +
  • 手势解锁
  • +
  • 图形上下文
  • +
+

保存绘图信息和状态

确定输出目标(PDF,Bitmap或者显示器)

+
Bitmap Graphics Context
+PDF Granhics Context
+Window Graphics Context
+Layer Graphics Context
+

绘制线段

+
- (void)drawLine {
+    // 获取图形上下文
+    // 所用的是UIGraphics上下文
+    CGContextRef ctx = UIGraphicsGetCurrentContext();
+
+    // 创建描述路径
+    CGMutablePathRef path = CGPathCreateMutable();
+    // 设置起点
+    // path : 表示给那个路径设置起点
+    CGPathMoveToPoint(path, NULL, 10, 10);
+    CGPathAddLineToPoint(path, NULL, 80, 80);
+
+    // 添加路径到上下文
+    CGContextAddPath(ctx, path);
+
+    // 渲染
+    CGContextStrokePath(ctx);
+}
+

系统底层自动将路径添加到图形上下文

+
- (void)drawLine2 {
+
+    CGContextRef ctx = UIGraphicsGetCurrentContext();
+
+    // 描述路径
+    // 此方法底层会自动将路径添加到图形上下文
+    CGContextMoveToPoint(ctx, 50, 50);
+    CGContextAddLineToPoint(ctx, 100, 100);
+
+    CGContextSetLineWidth(ctx, 5);
+
+    [[UIColor redColor] set];
+    // 渲染
+    CGContextStrokePath(ctx);
+}
+

使用C语言函数的封装

+
- (void)drawLine3 {
+    // 使用UIKit 已经封装的功能,面向对象
+    // 贝塞尔路径
+
+    UIBezierPath *path = [UIBezierPath bezierPath];
+    [path moveToPoint:CGPointMake(10, 10)];
+    [path addLineToPoint:CGPointMake(50, 50)];
+
+    // 绘制路径
+    [path stroke];
+}
+

绘制多条线段

+
    +
  • 绘制多条线,状态不好管理
  • +
  • 默认下一条线的起点是上一条线的终点
  • +
  • 绘制多天不连接的线时需要重新设置起点
  • +
  • 为了方便管理,应该让每一条线对应一条路径
  • +
  • 使用UIBeizerPath
  • +
+

code:

+
- (void)drawMoreLine {
+    CGContextRef ctx = UIGraphicsGetCurrentContext();
+
+    // 描述路径
+    CGContextMoveToPoint(ctx, 20, 20);
+    CGContextAddLineToPoint(ctx, 100, 100);
+
+    // 设置第二条线的起点
+    CGContextMoveToPoint(ctx, 20, 30);
+    CGContextAddLineToPoint(ctx, 150, 100);
+
+    // 设置绘图状态要在渲染之前,都是给上下文设置
+    // 描边颜色
+    [[UIColor greenColor] setStroke];
+    // 线宽
+    CGContextSetLineWidth(ctx, 10);
+    // 连接样式
+    // kCGLineJoinMiter,    斜接
+    // kCGLineJoinRound,    圆角
+    // kCGLineJoinBevel     平切
+    CGContextSetLineJoin(ctx, kCGLineJoinBevel);
+    // 顶角样式,线的顶端效果
+    CGContextSetLineCap(ctx, kCGLineCapRound);
+
+    // 渲染
+    CGContextStrokePath(ctx);
+}
+

使用UIBezierPath 绘制多条直线并设置不同状态

+
- (void)drawUIBezierPahtState {
+
+    // 第一条线
+    UIBezierPath *path = [UIBezierPath bezierPath];
+    [path moveToPoint:CGPointMake(10, 10)];
+    [path addLineToPoint:CGPointMake(80, 80)];
+
+    path.lineWidth = 20;
+    path.lineJoinStyle = kCGLineJoinRound;
+    path.lineCapStyle = kCGLineCapRound;
+    [[UIColor redColor] setStroke];
+
+    [path stroke];
+
+    // 第二条线
+    {
+        UIBezierPath *path = [UIBezierPath bezierPath];
+        [path moveToPoint:CGPointMake(40, 70)];
+        [path addLineToPoint:CGPointMake(150, 40)];
+
+        path.lineWidth = 20;
+        path.lineJoinStyle = kCGLineJoinRound;
+        path.lineCapStyle = kCGLineCapRound;
+        [[UIColor greenColor] setStroke];
+
+        [path stroke];
+    }
+}
+
+ +
+ +
+ +
+ + + +
+ +
+ + +
+ + +

+ OC中运行时 +

+ + +
+ +
+ +

RunTime
对于C语言,函数的调用在编译的时候会决定调用哪个函数,而对于面向对象的语言对于[调用某个对象的方法或函数]就叫「消息传递」。所以在 Java,C++ 这些语言里[调用某个对象的方法或函数]和 Objective-C 里[向某个对象发送消息]在概念上就是一样的事情,

+

RunTime 简介

    +
  • RunTime 简称运行时,OC中通常叫运行时机制,主要的是消息机制
  • +
  • 消息机制:在OC中,属于动态调用,在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用
  • +
+

RunTime 作用

    +
  • 发送消息:让对象发送消息
  • +
  • 交换方法
  • +
  • 动态添加方法
  • +
  • 动态获取属性和方法
  • +
  • 字典转模型
  • +
  • 给分类添加属性
  • +
+

原理:给分类添加一个属性,重写了setter、getter方法,但是没有生成 _成员变量,需要通过运行时关联到分类中, 其本质上就是给这个类添加关联,存值和取值
code

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
self.oldurlPath = urlPath;

- (void)setOldurlPath:(NSString *)oldurlPath {

// RunTime 关联
// 第一个参数:给哪个对象添加关联
// 第二个参数:关联的key,通过这个key获取
// 第三个参数:关联的value
// 第四个参数:关联的策略
objc_setAssociatedObject(self, key, oldurlPath, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (NSString *)oldurlPath {
return objc_getAssociatedObject(self, key);
}
+ +
+ +
+ +
+ + + +
+ +
+ + +
+ + +

+ WKWebiView 使用 +

+ + +
+ +
+ +

WKWebiView iOS8.0 出现,性能要远远好于 iOS 2.0时出现的 UIWebView,主要记录WKWebView 的创建和代理方法

创建WKWebView

WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
+self.webView = webView;
+[self.view addSubview:webView];
+webView.UIDelegate = self;
+webView.navigationDelegate = self;
+[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.baidu.com"]]];
+

遵守协议 WKNavgationDelegate WKUIDelegate

+

代理方法

    +
  • WKNavgationDelegate 方法

    +
      +
    • 发送请求之前决定是否跳转

      +
      1
      2
      3
      4
      5
      6
      7
      - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {

      decisionHandler(WKNavigationActionPolicyAllow);
      // 不允许跳转
      decisionHandler(WKNavigationActionPolicyCancel);
      NSLog(@"decidePolicyForNavigationAction");
      }
      +
    • +
    • 收到相应之后决定是否跳转

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {

      decisionHandler(WKNavigationResponsePolicyAllow);

      decisionHandler(WKNavigationResponsePolicyCancel);

      NSLog(@"decidePolicyForNavigationResponse");

      }
      +
    • +
    • 开始加载时调用

      +
      1
      2
      3
      4
      - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation {

      NSLog(@"didStartProvisionalNavigation");
      }
      +
    • +
    • 接受到服务器的跳转请求时调用

      +
      1
      2
      3
      - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
      NSLog(@"didReceiveServerRedirectForProvisionalNavigation");
      }
      +
    • +
    +
  • +
+
- 页面加载失败时调用 **存在缓存问题**
+
+
1
2
3
4
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

NSLog(@"error= %@", error);
}
+ + +- 内容开始返回时调用 + +
1
2
3
4
- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation {

NSLog(@"didCommitNavigation");
}
+
    +
  • 内容返回成功后调用

    +
    1
    2
    3
    4
    - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {

    NSLog(@"didFinishNavigation");
    }
    +
  • +
  • 开始返回错误时调用

    +
    1
    2
    3
    4
    - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

    NSLog(@"didFailNavigation");
    }
    +
  • +
+
    +
  • 目前不知道什么时候调用

    +
    1
    2
    3
    - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler {
    NSLog(@"didReceiveAuthenticationChallenge");
    }
    +
      +
    • 目前不知道什么时候调用

      +
      1
      2
      3
      4
      - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView API_AVAILABLE(macosx(10.11), ios(9.0)) {

      NSLog(@"webViewWebContentProcessDidTerminate");
      }
      +
    • +
    +
  • +
+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ + +

+ WKWebiView 使用 +

+ + +
+ +
+ +

WKWebiView iOS8.0 出现,性能要远远好于 iOS 2.0时出现的 UIWebView,主要记录WKWebView 的创建和代理方法

创建WKWebView

WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
+self.webView = webView;
+[self.view addSubview:webView];
+webView.UIDelegate = self;
+webView.navigationDelegate = self;
+[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.baidu.com"]]];
+

遵守协议 WKNavgationDelegate WKUIDelegate

+

代理方法

    +
  • WKNavgationDelegate 方法

    +
      +
    • 发送请求之前决定是否跳转

      +
      1
      2
      3
      4
      5
      6
      7
      - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {

      decisionHandler(WKNavigationActionPolicyAllow);
      // 不允许跳转
      decisionHandler(WKNavigationActionPolicyCancel);
      NSLog(@"decidePolicyForNavigationAction");
      }
      +
    • +
    • 收到相应之后决定是否跳转

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {

      decisionHandler(WKNavigationResponsePolicyAllow);

      decisionHandler(WKNavigationResponsePolicyCancel);

      NSLog(@"decidePolicyForNavigationResponse");

      }
      +
    • +
    • 开始加载时调用

      +
      1
      2
      3
      4
      - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation {

      NSLog(@"didStartProvisionalNavigation");
      }
      +
    • +
    • 接受到服务器的跳转请求时调用

      +
      1
      2
      3
      - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
      NSLog(@"didReceiveServerRedirectForProvisionalNavigation");
      }
      +
    • +
    +
  • +
+
- 页面加载失败时调用 **存在缓存问题**
+
+
1
2
3
4
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

NSLog(@"error= %@", error);
}
+ + +- 内容开始返回时调用 + +
1
2
3
4
- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation {

NSLog(@"didCommitNavigation");
}
+
    +
  • 内容返回成功后调用

    +
    1
    2
    3
    4
    - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {

    NSLog(@"didFinishNavigation");
    }
    +
  • +
  • 开始返回错误时调用

    +
    1
    2
    3
    4
    - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

    NSLog(@"didFailNavigation");
    }
    +
  • +
+
    +
  • 目前不知道什么时候调用

    +
    1
    2
    3
    - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler {
    NSLog(@"didReceiveAuthenticationChallenge");
    }
    +
      +
    • 目前不知道什么时候调用

      +
      1
      2
      3
      4
      - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView API_AVAILABLE(macosx(10.11), ios(9.0)) {

      NSLog(@"webViewWebContentProcessDidTerminate");
      }
      +
    • +
    +
  • +
+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ + +

+ WKWebiView 使用 +

+ + +
+ +
+ +

WKWebiView iOS8.0 出现,性能要远远好于 iOS 2.0时出现的 UIWebView,主要记录WKWebView 的创建和代理方法

创建WKWebView

WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
+self.webView = webView;
+[self.view addSubview:webView];
+webView.UIDelegate = self;
+webView.navigationDelegate = self;
+[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.baidu.com"]]];
+

遵守协议 WKNavgationDelegate WKUIDelegate

+

代理方法

    +
  • WKNavgationDelegate 方法

    +
      +
    • 发送请求之前决定是否跳转

      +
      1
      2
      3
      4
      5
      6
      7
      - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {

      decisionHandler(WKNavigationActionPolicyAllow);
      // 不允许跳转
      decisionHandler(WKNavigationActionPolicyCancel);
      NSLog(@"decidePolicyForNavigationAction");
      }
      +
    • +
    • 收到相应之后决定是否跳转

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {

      decisionHandler(WKNavigationResponsePolicyAllow);

      decisionHandler(WKNavigationResponsePolicyCancel);

      NSLog(@"decidePolicyForNavigationResponse");

      }
      +
    • +
    • 开始加载时调用

      +
      1
      2
      3
      4
      - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation {

      NSLog(@"didStartProvisionalNavigation");
      }
      +
    • +
    • 接受到服务器的跳转请求时调用

      +
      1
      2
      3
      - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
      NSLog(@"didReceiveServerRedirectForProvisionalNavigation");
      }
      +
    • +
    +
  • +
+
- 页面加载失败时调用 **存在缓存问题**
+
+
1
2
3
4
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

NSLog(@"error= %@", error);
}
+ + +- 内容开始返回时调用 + +
1
2
3
4
- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation {

NSLog(@"didCommitNavigation");
}
+
    +
  • 内容返回成功后调用

    +
    1
    2
    3
    4
    - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {

    NSLog(@"didFinishNavigation");
    }
    +
  • +
  • 开始返回错误时调用

    +
    1
    2
    3
    4
    - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

    NSLog(@"didFailNavigation");
    }
    +
  • +
+
    +
  • 目前不知道什么时候调用

    +
    1
    2
    3
    - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler {
    NSLog(@"didReceiveAuthenticationChallenge");
    }
    +
      +
    • 目前不知道什么时候调用

      +
      1
      2
      3
      4
      - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView API_AVAILABLE(macosx(10.11), ios(9.0)) {

      NSLog(@"webViewWebContentProcessDidTerminate");
      }
      +
    • +
    +
  • +
+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ + +

+ WKWebiView 使用 +

+ + +
+ +
+ +

WKWebiView iOS8.0 出现,性能要远远好于 iOS 2.0时出现的 UIWebView,主要记录WKWebView 的创建和代理方法

创建WKWebView

WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
+self.webView = webView;
+[self.view addSubview:webView];
+webView.UIDelegate = self;
+webView.navigationDelegate = self;
+[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.baidu.com"]]];
+

遵守协议 WKNavgationDelegate WKUIDelegate

+

代理方法

    +
  • WKNavgationDelegate 方法

    +
      +
    • 发送请求之前决定是否跳转

      +
      1
      2
      3
      4
      5
      6
      7
      - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {

      decisionHandler(WKNavigationActionPolicyAllow);
      // 不允许跳转
      decisionHandler(WKNavigationActionPolicyCancel);
      NSLog(@"decidePolicyForNavigationAction");
      }
      +
    • +
    • 收到相应之后决定是否跳转

      +
      1
      2
      3
      4
      5
      6
      7
      8
      9
      - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {

      decisionHandler(WKNavigationResponsePolicyAllow);

      decisionHandler(WKNavigationResponsePolicyCancel);

      NSLog(@"decidePolicyForNavigationResponse");

      }
      +
    • +
    • 开始加载时调用

      +
      1
      2
      3
      4
      - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation {

      NSLog(@"didStartProvisionalNavigation");
      }
      +
    • +
    • 接受到服务器的跳转请求时调用

      +
      1
      2
      3
      - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
      NSLog(@"didReceiveServerRedirectForProvisionalNavigation");
      }
      +
    • +
    +
  • +
+
- 页面加载失败时调用 **存在缓存问题**
+
+
1
2
3
4
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

NSLog(@"error= %@", error);
}
+ + +- 内容开始返回时调用 + +
1
2
3
4
- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation {

NSLog(@"didCommitNavigation");
}
+
    +
  • 内容返回成功后调用

    +
    1
    2
    3
    4
    - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {

    NSLog(@"didFinishNavigation");
    }
    +
  • +
  • 开始返回错误时调用

    +
    1
    2
    3
    4
    - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

    NSLog(@"didFailNavigation");
    }
    +
  • +
+
    +
  • 目前不知道什么时候调用

    +
    1
    2
    3
    - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler {
    NSLog(@"didReceiveAuthenticationChallenge");
    }
    +
      +
    • 目前不知道什么时候调用

      +
      1
      2
      3
      4
      - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView API_AVAILABLE(macosx(10.11), ios(9.0)) {

      NSLog(@"webViewWebContentProcessDidTerminate");
      }
      +
    • +
    +
  • +
+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ + +

+ git 使用 +

+ + +
+ +
+ +

使用 git 提交代码

    +
  • 初始化工作目录

    +
    1
    git init
    +
  • +
  • 配置用户名和邮箱(当前仓库)

    +
    1
    2
    git config user.name "aTreey"
    git config user.email "480814177@qq.com"
    +
  • +
+
    +
  • 配置全局的用户名和密码

    +
    1
    2
    git config --global user.name "aTreey"
    git config --global user.email "480814177@qq.com"
    +
  • +
  • 创建文件

    +
    1
    touch main.m
    +
  • +
  • 添加文件到暂缓区

    +
    1
    git add main.m
    +
  • +
  • 查看文件状态

    +
    1
    git status
    +
  • +
  • 新添加的文件或者新修改的文件在工作区中,没有添加到暂缓区中

    +
  • +
+
1
Untracked files: (红色)
+
    +
  • 需要添加到暂缓区中
  • +
+
1
git add main.m
+
    +
  • 出现一下提示方可提交
  • +
+
1
Changes to be committed: (绿色)
+
    +
  • 给git起别名

    +
    1
    2
    git config alias.st "status"
    git config alias.ci "commit -m"
    +
  • +
  • 查看所有版本库日志(只能查看当前版本的以前日志)

    +
    1
    git log
    +
  • +
  • 查看指定文件的版本库日志

    +
    1
    git log 文件名
    +
  • +
  • 查看所有的操作的日志

    +
    1
    git reflog
    +
  • +
+

版本会退

    +
  • 强制回退到当前版本

    +
    1
    git reset --hard HEAD
    +
  • +
  • 强制回退到当前版本的上一个版本

    +
    1
    git reset --hard HEAD^
    +
  • +
  • 强制回退到当前版本的上上个版本

    +
    1
    git reset --hard HEAD^^
    +
  • +
  • 强制回退到当前版本的前100个版本

    +
    1
    git reset --hard HEAD~100
    +
  • +
  • 强制回退指定版本

    +
    1
    git reset --hard HEAD 版本号前七位
  • +
+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ + +

+ TableView 使用 +

+ + +
+ +
+ +

tableView 使用总结

    +
  • 字典的 key enumerator 的使用

    +
  • +
  • tableViewCell 的 selected 方法可以监听cell 的选中, 使用selected 参数

    +
  • +
  • 网络慢时点击左边cell右边数据不能及时刷新, 用户看到的还是上一个cell显示的数据

    +
      +
    • 解决办法: 在网络加载之前就reload数据,让显示空白
    • +
    +
  • +
  • 点击左边的cell时 上拉加载更多控件的显示和隐藏逻辑

    +
      +
    • 解决办法: 在每次刷新右边数据时,控制footer的显示
    • +
    +
  • +
+
    +
  • 在numberOfRowsInSection方法中控制

    +
  • +
  • 每次点击cell之前要结束上一个没有刷新完的,防止奔溃

    +
  • +
  • 加载更多时

    +

    在加载第一页时设置currentPage为1,在加载更多的逻辑中 ++currentPage
    点击cell加载时上一个加载更多的网络数据还没有回来,在当前请求之前结束掉所有的网络请求
    停止之后的刷新控件状态会有错误,没有更多还是还可以加载状态不确定
    在点击左边cell后刷新右边的之前需要检查右边tableView 的footer的状态
    下拉刷新

    +
  • +
  • 第一次下拉刷新之后,第二次下拉刷新,将数据添加到数组中会导致两次数据重复

    +
      +
    • 解决办法:清除以前的所有数据
    • +
    +
  • +
  • 下拉刷新完毕后结束刷新后需要检查footer的状态

    +
  • +
  • 网络慢时连续点击cell,连发多个请求成功回调时数据显示错乱,或者是数据已经回来了但是还是显示上一个请求正在请求。。。

    +
      +
    • 解决办法:点击左边某个类别时,结束所有的刷新(上拉和下拉)
    • +
    +
  • +
  • 连续点击发送请求

    +
      +
    • 解决办法:保存请求参数, 比较上一次参数和当前参数, 如果 != 直接return,不让进入成功回调解析数据, 失败的时候也是同样 return,不提示用户
    • +
    +
  • +
  • 控制器正在请求数据时控制器销毁, 导致奔溃

    +
      +
    • 解决办法:在dealloc 中停止所有的操作
    • +
    +
  • +
+

cell 的frame 设置

    +
  • cell 之间的间隙以及cell的边距 重写frame 在super 之前设置

    +
      +
    • 设置frame 的x ,width, height 等属性
    • +
    +
  • +
  • 分页的2种做法

    +
      +
    • 下拉刷新

      +

      page 加载最新的 加载第一页 浪费用户的流量
      加载比当前更新的数据, 需要服务器支持,将当前最新的数据id发送给服务器,服务器加载比当前id还大的数据插入到最前面

      +
    • +
    • 上拉加载

      +

      将页码发给服务器

      +

      将最后面的数据的id发给服务器,

      +

      加载比当前id更小的数据

      +
    • +
    +
  • +
+ + +
+ +
+ +
+ + + +
+ +
+ + +
+ + +

+ Hexo个人博客搭建 +

+ + +
+ +
+ +

安装Homebrew

+
1
/usr/bin/ruby -e " (curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
+

安装git

1
sudo brew install git
+

安装node.js

+

安装hexo

    +
  • 需要输入密码: sudo 管理员权限 -g 全局安装
  • +
+
1
sudo npm install -g hexo
+

初始化 执行 hexo init 命令

    +
  • 指定安装文件 blog
  • +
+
1
hexo init blog
+
    +
  • cd 到 文件夹 blog 下,安装 npm
  • +
+
1
npm install
+
    +
  • 开启服务
  • +
+
1
hexo s
+ +

关联 github

    +
  • 添加 ssh key 到github

    +
  • +
  • 设置 Mac 显示隐藏文件

    +
      +
    • 在终端执行一下命令后,重启Finder

      +
      1
      defaults write com.apple.finder AppleShowAllFiles -bool true
      +
    • +
    +
  • +
  • 检查 SSH keys 是否存在 github

    +
      +
    • 打开 Finder 前往(command + shit + G)

      +
      1
      ~/.ssh/id_rsa.pub
      +
    • +
    +
  • +
  • 如果有文件说明存在 SSH key, 打开 id_rsa.pub 文件,复制里面的信息

    +
  • +
  • 进入 Github –> Settings –> SSH keys –> add SSH key
    任意添加一个title 粘贴内容到key 里,点击Add key 绿色按钮即可。
  • +
+

创建名字为 用户名.github.io 的仓库

自行百度。。。。

+

配置 hexo

    +
  • vim 打开 _config.yml修改 deploy 模块
  • +
+
1
vim _config.yml
+
1
2
3
4
deploy:
type: git
repository: https://github.com/用户名/用户.github.io.git
branch: master
+

冒号后需要加空格

+

生成 hexo 的静态页面

1
hexo generate 后者 hexo g
+
    +
  • 报以下错
  • +
+
1
2
ERROR Local hexo not found in ~/blog
ERROR Try runing: 'npm install hexo --save'
+
    +
  • 执行下面代码
  • +
+
1
npm install hexo --save
+

部署 hexo

1
hexo deploy 或者 hexo d
+
    +
  • 报以下错误
  • +
+
1
ERROR Deployer not found: git
+

解决方法:

+
1
2
npm install hexo-deployer-git --save
hexo d
+
    +
  • 配置 Deloyment 前,需要配置自己的身份信息
  • +
+
1
2
git config --global user.name "yourname"
git config --global user.email "youremail"
+

如果执行完以上命令还是不行,删除blog文件下的 .deploy_git 文件重新 hexo deploy

+ + +
+ +
+ +
+ + + + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/page/9/index.html b/page/9/index.html new file mode 100644 index 0000000..88ea010 --- /dev/null +++ b/page/9/index.html @@ -0,0 +1,406 @@ + + + + + + + + aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+ +
+ + +
+ + +

+ iOS 弹框视图 +

+ + +
+ +
+ +

UIActionSheet UIAlertView UIAlertViewController 的区别

+

UIActionSheet

    +
  • iOS 8.3 之后过期
  • +
  • 模态弹出,显示在试图的底部 类似菜单提供选择
  • +
+
1
2
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"title" delegate:self cancelButtonTitle:@"取消" destructiveButtonTitle:nil otherButtonTitles:@"按钮1", @"按钮2", nil];
[actionSheet showInView:self.view];
+

UIAlertView

    +
  • iOS 9.0 之后过期
  • +
  • 模态弹出,显示在试图的中间位置 类似一个对话框提供选择
  • +
+
1
2
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"考勤方式" message:nil delegate:self cancelButtonTitle:@"取消" otherButtonTitles:faceBtn, fingerPrintBtn, nil];
[alertView show];
+

UIAlertViewController

    +
  • iOS 8.0 起可以使用
  • +
  • style : 默认样式, 类似 UIActionSheet 列表形式
  • +
  • style 为:UIAlertControllerStyleAlert 对话框形式
  • +
  • 设置按钮的样式
  • +
+

如果修改 UIAlertAction 中stye 为:

+

UIAlertActionStyleCancel
取消按钮会和其他按钮分开

+

如果修改 UIAlertAction 中stye 为:

+

UIAlertActionStyleDestructive
取消按钮会以红色警告的方式显示, 但是按钮之间不会分开

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"考勤方式" message:nil preferredStyle:UIAlertControllerStyleActionSheet];
UIAlertAction *faceBtn = [UIAlertAction actionWithTitle:@"人脸识别" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { }];

UIAlertAction *fingerPrintBtn = [UIAlertAction actionWithTitle:@"指纹认证" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {

}];

UIAlertAction *cancelBtn = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {

}];

[alert addAction:faceBtn];
[alert addAction:fingerPrintBtn];
[alert addAction:cancelBtn];
[self presentViewController:alert animated:YES completion:nil];
+ +
+ +
+ +
+ + + +
+ +
+ + +
+ + +

+ 本地缓存 +

+ + +
+ +
+ +

保存图片到沙盒

1
2
3
4
5
6
- (void)saveImage:(UIImage *)image InSanBoxWithIndexPath:(NSInteger)indexPath {

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *filePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:@"pic_%ld.png", indexPath]]; // 保存文件的名称
[UIImagePNGRepresentation(image)writeToFile:filePath atomically:YES];
}
+

获取沙盒中的图片

1
2
3
4
5
6
7
- (UIImage *)fetchImageFromSanBoxWithIndexPath:(NSInteger)indexPath
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *filePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:@"pic_%ld.png", indexPath]]; // 保存文件的名称
UIImage *img = [UIImage imageWithContentsOfFile:filePath];
return img;
}
+

删除沙盒中的图片

1
2
3
4
5
6
7
8
9
10
11
12
- (BOOL)deleteImageAtIndexPath:(NSInteger)index;
{

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *filePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:@"pic_%ld.png", index]];

if([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
return [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
}

return NO;
}
+

修改图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
- (UIImage *)fixOrientation:(UIImage *)aImage {

// No-op if the orientation is already correct
if (aImage.imageOrientation == UIImageOrientationUp)
return aImage;

// We need to calculate the proper transformation to make the image upright.
// We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.
CGAffineTransform transform = CGAffineTransformIdentity;

switch (aImage.imageOrientation) {
case UIImageOrientationDown:
case UIImageOrientationDownMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.width, aImage.size.height);
transform = CGAffineTransformRotate(transform, M_PI);
break;

case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.width, 0);
transform = CGAffineTransformRotate(transform, M_PI_2);
break;

case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
transform = CGAffineTransformTranslate(transform, 0, aImage.size.height);
transform = CGAffineTransformRotate(transform, -M_PI_2);
break;
default:
break;
}

switch (aImage.imageOrientation) {
case UIImageOrientationUpMirrored:
case UIImageOrientationDownMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.width, 0);
transform = CGAffineTransformScale(transform, -1, 1);
break;

case UIImageOrientationLeftMirrored:
case UIImageOrientationRightMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.height, 0);
transform = CGAffineTransformScale(transform, -1, 1);
break;
default:
break;
}

// Now we draw the underlying CGImage into a new context, applying the transform
// calculated above.
CGContextRef ctx = CGBitmapContextCreate(NULL, aImage.size.width, aImage.size.height,
CGImageGetBitsPerComponent(aImage.CGImage), 0,
CGImageGetColorSpace(aImage.CGImage),
CGImageGetBitmapInfo(aImage.CGImage));
CGContextConcatCTM(ctx, transform);
switch (aImage.imageOrientation) {
case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
// Grr...
CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.height,aImage.size.width), aImage.CGImage);
break;

default:
CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.width,aImage.size.height), aImage.CGImage);
break;
}
CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
UIImage *img = [UIImage imageWithCGImage:cgimg];
CGContextRelease(ctx);
CGImageRelease(cgimg);
return img;
}
+ +
+ +
+ +
+ + + +
+ +
+ + +
+ + +

+ SQL 使用 +

+ + +
+ +
+ +

常用SQL 语句

创建一张表

+
create table if not exists t_product(productId integer, productName text, productPrice real);
+

删除一张表

+
drop table if exists t_product;
+

插入一条记录

+

如果不写某个字段和对应值就会为NUll

+
insert into t_product(productId, productName, productPrice) values (0001, 'iPhone7', 6100.8);
+

修改记录

+
update t_product set productId = 20002 where productName = 'iPhone5s';
+

删除

+
delete from t_product where productName = 'iphone5s';
+

注意:条件语句不能单独存在,只能在修改/删除/查询之后

+

简单查询

准备数据生成sql语句文件

+
NSMutableString *mutableString = [NSMutableString string];
+int productIdValue = arc4random_uniform(1000);
+NSString *productNameValue = [NSString stringWithFormat:@"iPhone_%d",1 + arc4random_uniform(8)];
+CGFloat productPriceValue = 3000 + arc4random_uniform(1000);
+
+for (NSInteger i = 0; i < 1000; i++) {
+    [mutableString appendFormat:@"insert into t_product(productId, productName, productPrice) values (%d, %@, %f); \r\n", productIdValue, productNameValue, productPriceValue];
+}
+
+// 写入文件
+[mutableString writeToFile:@"/Users/Apeng/work/Demo/Sqlite/products.sql" atomically:YES encoding:NSUTF8StringEncoding error:NULL];
+

生成的sql语句文件

+

+

SQL 查询语句

+
select *(表示字段)from t_product(表名)where(条件语句) productId > 100 (字段满足的条件) ;
+
+select * from t_product where productPrice < 5000; 
+


or: 或

+

+

and: 且

+

+

分页查询

+

limit 0,5 ( limit 索引,每页取得数据条数 )

+

第一页 limit 0,5;

+

第二页 limit 5,5;

+

第三页 limit 10,5;

+

第四页 limit 15,5;

+

第N页 limit (N - 1)* 5

+
select * from t_product where productName = 'iPhone_8' and productPrice < 3300 order by productPrice limit 5,5;
+

+

排序查询

+

order by 默认是从小到大,一般是在某一个结果之后

+

默认升序

+
// 默认升序
+select * from t_product where productName = 'iPhone_8' and productPrice < 3300 order by productPrice;
+
+// 降序
+select * from t_product where productName = 'iPhone_8' and productPrice < 3300 order by productPrice desc;
+

+

模糊查询

+

关键字搜索 ,% 是指通配符

+

SQL 顺序

+
select * from 表名 条件语句(模糊匹配)排序语句 分页语句
+
+select * from t_product where productName like '%_4' order by productPrice limit 0,5;
+

+

主键

+

数据库的约定俗称,自增长,建议创建表时增加主键,也可以之后再设计表

+
CREATE TABLE IF NOT EXISTS t_class (id integer PRIMARY KEY, className text, classNO integer); 
+

外键约束

+

当两张表有关联时,需要使用外键约束,一张表中的字段名所对应的值只能来自于另一张表中

+

+

添加外键约束之后的变化

+

+

多表查询

+

t_department(部门表)和 t_employee(员工表)两张表

+

需求:在 t_employee 表中查出部门研发部的人员

+

1 嵌套查询:

+

先查根据部门名称查出部ID,

+

+

然后再用部门ID查出员工表中的员工

+

以上两步的代码可合并

+

根据部门名称查出部门ID

+
SELECT id from t_department WHERE departmentName = '研发部';
+

根据部门id查出这个部门的人员

+
SELECT * from t_employee WHERE departmentID = 1;
+

合并后为

+
select * from t_employee where departmentID = (SELECT id from t_department WHERE departmentName = '研发部'); 
+

结果为:

+

+
    +
  1. 链接查询
  2. +
+

给表起别名,给字段起别名

+
select employee.*, depart.departmentName deptN from t_department depart, t_employee employee;
+

/*多表查询之链接查询/

+
select t_employee.*, t_department.departmentName FROM t_department, t_employee;
+

+

消除笛卡尔积

+
select employee.*, depart.departmentName deptN from t_department depart, t_employee employee where employee.departmentID = depart.id and employee.departmentID = 1;
+

+ + +
+ +
+ +
+ + + + + + + +
+ + + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/scaffolds/draft.md b/scaffolds/draft.md deleted file mode 100644 index 498e95b..0000000 --- a/scaffolds/draft.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: {{ title }} -tags: ---- diff --git a/scaffolds/page.md b/scaffolds/page.md deleted file mode 100644 index f01ba3c..0000000 --- a/scaffolds/page.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: {{ title }} -date: {{ date }} ---- diff --git a/scaffolds/post.md b/scaffolds/post.md deleted file mode 100644 index 1f9b9a4..0000000 --- a/scaffolds/post.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: {{ title }} -date: {{ date }} -tags: ---- diff --git a/search.xml b/search.xml new file mode 100644 index 0000000..64eee28 --- /dev/null +++ b/search.xml @@ -0,0 +1,1242 @@ + + + + + + + + + /2019/06/23/Flutter%E6%89%93%E5%8C%85/ + + Flutter 打包

app 图标替换

Android 图标更换

  • 替换图中路径的图片资源即可,
  • 注意图片名字需要是 ic_launcher
  • 每个尺寸对应不同的文件大小
  • 如果要修改启动图片名称需要先在主配置文件中修改

iOS 图标更换

修改app图标名称

  • 找到 Android 主配置文件
  • 修改 android:label=”app_flutter” 中名称

修改app 的名称

打包

生成key

]]>
+ + + +
+ + + + + + + /2019/06/23/Flutter%E4%B8%AD%E6%9C%AC%E5%9C%B0%E5%9B%BE%E7%89%87%E8%B5%84%E6%BA%90%E4%BD%BF%E7%94%A8/ + + Flutter中本地图片资源使用

新建图片文件目录

添加图片到项目

手动投入图片到新建的文件目录

声明图片资源

找到项目中 pubspec.yaml 文件,找到 assets: 选项 按照注释代码格式声明图片路径

1
2
assets:
- lib/assets/ideaOne.png

使用图片资源

1
Image.asset('lib/assets/ideaOne.png')
]]>
+ + + +
+ + + + + + + /2019/06/20/Flutter%E9%A1%B5%E9%9D%A2%E5%AF%BC%E8%88%AA/ + + Flutter页面导航

导航栏的使用

程序主入口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class MyApp extends StatelessWidget {

// 声明参数
final List<String> items;

// 构造函数
MyApp({Key key, @required this.items}):super(key: key); // 调用父类的方法


// 重写
@override

Widget build(BuildContext context) {

// 返回一窗口
return MaterialApp(
title:'Flutter --',
// 基础组件学习
// home: MyLearningFlutterHome(),

home: FirstScreen(),
);
}
}

导航栏页面跳转参数传递

商品类

1
2
3
4
5
6
class Product {
final String title; // 定义商品标题
final String description;
// 定义构造方法
Product(this.title, this.description);
}

第一级页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class FirstScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: new Text('第一个页面'),
),

body: Center(
child: RaisedButton(
child: new Text('点击跳转详情页'),
onPressed: (){
Navigator.push(context, new MaterialPageRoute(
builder: (context) => (new SecondScreen())
// builder: (context){
// new SecondScreen();
// }
));
},
),
),
);
}
}

详情页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class SecondScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('详情页面')
),

body: Center(
child: RaisedButton(
child: Text('返回上一级页面'),

onPressed: () => (Navigator.pop(context)),

// onPressed: (){
// Navigator.pop(context);
// },
),
),
);
}
}

导航栏页面返回反向参数传递

使用异步请求和等待

商品列表页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class ProductListView extends StatelessWidget {
// 定义变量,里面是一个对象
final List<Product> products;

// 使用构造方法 函数的方式接受参数
ProductListView({Key key, @required this.products}):super(key:key); // 设置必参,调用父类方法

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('商品列表'),
),

// 使用 ListView.builder 动态构建
body: ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(products[index].title),
onLongPress: (){
print("长按事件响应");
},


// 点击时传入参数
onTap: (){
print('点击事件');
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProducterDetailView(product:products[index])
)
);
},
);
},
),
);
}
}

商品详情页面点击按钮返回上一级页面,并带有参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class ProducterDetailView extends StatelessWidget {
final Product product;

ProducterDetailView({Key key, @required this.product}):super(key:key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Product ${product.title} 详情页面')
),

body: Center(
// 有多控件
child: Column(
children: <Widget>[
Text('Product ${product.title} desc ${product.description}'),
RaisedButton(
child: Text('back Product List'),
color: Colors.blue,
onPressed: (){
Navigator.pop(context, 'selected Id = ${product.title}');
},
)
],
),
),
);
}
}

]]>
+ + + +
+ + + + + + + /2019/06/19/Flutter%E4%B8%AD%E5%B8%83%E5%B1%80/ + + Flutter中布局

线性布局

主轴、副轴

  • main轴:如果用column组件,垂直就是主轴,如果用Row组件,那水平就是主轴

  • cross轴:幅轴,是和主轴垂直的方向。用Row组件,那垂直就是幅轴,Column组件,水平方向就是副轴

Row 水平方向布局组件

  • 非自适应布局:根据元素本身大小来布局
  • 自适应布局:使用 Expanded 布局

非自适应布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class MyRowNoChangeLayout extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Container(
child: Row(
children: <Widget>[
new RaisedButton(
onPressed: (){
print('点击了---- 红色');
},

color: Colors.red,
child: new Text('按钮一红色'),

),

new RaisedButton(
onPressed: (){
print('点击了---- 黄色');
},

color: Colors.yellow,
child: new Text('按钮一黄色'),

),
new RaisedButton(
onPressed: (){
print('点击了---- 绿色');
},

color: Colors.green,
child: new Text('按钮一绿色'),

)
],
),
);
}
}

自适应布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class MyRowAutoLayout extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Container(
child: Row(
children: <Widget>[
Expanded(
child: new RaisedButton(
onPressed: (){
print('点击了---- 红色');
},

color: Colors.red,
child: new Text('按钮一红色'),
),
),

Expanded(
child: new RaisedButton(
onPressed: (){
print('点击了---- 黄色');
},

color: Colors.yellow,
child: new Text('按钮一黄色'),

)
),

Expanded(
child: new RaisedButton(
onPressed: (){
print('点击了---- 绿色');
},

color: Colors.green,
child: new Text('按钮一绿色'),

)
)
],
),
);
}
}

非自动布局和自动布局混合使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class MyRowLayout extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Container(
child: Row(
children: <Widget>[
new RaisedButton(
onPressed: (){
print('点击了---- 红色');
},

color: Colors.red,
child: new Text('按钮一红色'),
),

Expanded(
child: new RaisedButton(
onPressed: (){
print('点击了---- 黄色');
},

color: Colors.yellow,
child: new Text('按钮一黄色'),

)
),

new RaisedButton(
onPressed: (){
print('点击了---- 绿色');
},

color: Colors.green,
child: new Text('按钮一绿色'),

)
],
),
);
}
}

Column 垂直方向布局组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class MyColunmLayout extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Container(
color: Colors.cyan,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.max,
// 设置主轴的对齐方式
mainAxisAlignment: MainAxisAlignment.center,
// 设置横轴的对齐方式
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new RaisedButton (
onPressed: (){
print('点击了---- 红色');
},

color: Colors.red,
child: new Text('按钮一红色'),
),

new RaisedButton(
onPressed: (){
print('点击了---- 绿色');
},
color: Colors.green,
child: new Text('按钮一绿色'),
),

new Text('文字内容',
textAlign: TextAlign.center,
),
new Text('测试文字',
textAlign: TextAlign.right,
)
],
),
);
}
}

层叠布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// stack层叠布局

class MyStackLayout extends StatelessWidget {
@override
Widget build(BuildContext context) {
// 返回一个 Stack 布局
return new Stack(
// 设置位置
alignment: const FractionalOffset(0.5, .9),
// 圆角组件
children: <Widget>[
new CircleAvatar(
backgroundImage: new NetworkImage('https://p.ampmake.com/mall/product/26153760-b964-45d5-b811-7f2bf5844102.jpg'),
// backgroundImage: new Image.network('https://p.ampmake.com/mall/product/26153760-b964-45d5-b811-7f2bf5844102.jpg'),
radius: 100.0,
),

// 容器组件
new Container(
// 设置容器掩饰
decoration: BoxDecoration(
color: Colors.blue,
// border
border: Border.all(
color: Colors.red,
width: 3,
),
),

padding: const EdgeInsets.all(10.0),

child: new Text('stack 布局',
textAlign: TextAlign.center,
maxLines: 1,
// 文字样式
style: TextStyle(
color: Colors.yellow,
textBaseline: TextBaseline.ideographic,
// 下划线
decoration: TextDecoration.underline,
// 下划线颜色
decorationColor: Colors.orange,
// 下划线样式
decorationStyle: TextDecorationStyle.double,
),
),
)
],

);
}
}

Positioned组件

  • bottom: 距离层叠组件下边的距离
  • left:距离层叠组件左边的距离
  • top:距离层叠组件上边的距离
  • right:距离层叠组件右边的距离
  • width: 层叠定位组件的宽度
  • height: 层叠定位组件的高度
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class MyMoreStackLayout extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Stack(
children: <Widget>[
new CircleAvatar(
backgroundImage: NetworkImage('https://p.ampmake.com/mall/product/26153760-b964-45d5-b811-7f2bf5844102.jpg'),
radius: 100.0,
),

new Positioned(
top: 5,
left: 50,
// right: 5,
width: 100,
child: new RaisedButton(
onPressed: (){
print("多个组件层叠布局");
},

color: Colors.deepOrange,
child: Text('测试按钮'),
),
),

new Positioned(
bottom: 10,
right: 10,
left: 10,
child: new Text('flutter 多个试图 stack布局',
maxLines: 1,
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.lightGreen,
fontSize: 20,
fontStyle: FontStyle.italic
),
)
)
],
);
}
}

Card 布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
class MyCardLayout extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Card(
child: Column(
children: <Widget>[
new ListTile(
title: new Text('card 布局1'),
subtitle: new Text('子标题1',
textAlign: TextAlign.right,
style: TextStyle(
color: Colors.lightGreen
),
),
leading: new Icon(Icons.account_balance, color: Colors.purple)
),

new Divider(
color: Colors.cyan
),

new ListTile(
title: new Text('card 布局2'),
subtitle: new Text('子标题2',
textAlign: TextAlign.right,
style: TextStyle(
color: Colors.lightGreen
),
),
leading: new Icon(Icons.account_box, color: Colors.yellow)
),

new Divider(
color: Colors.red,
),

new ListTile(
title: new Text('card 布局3'),
subtitle: new Text('子标题3',
textAlign: TextAlign.right,
style: TextStyle(
color: Colors.lightGreen
),
),
leading: new Icon(Icons.account_circle, color: Colors.red)
),

new Divider(
color: Colors.orangeAccent,
),

new ListTile(
title: new Text('card 布局4'),
subtitle: new Text('子标题4',
textAlign: TextAlign.right,
style: TextStyle(
color: Colors.lightGreen
),
),
leading: new Icon(Icons.camera_enhance, color: Colors.brown)
)
],
),
);
}
}
]]>
+ + + +
+ + + + + GridView组件使用.md + + /2019/06/18/Flutter%E4%B8%ADGridView%E7%BB%84%E4%BB%B6/ + + GridView 组件使用
  • padding: const EdgeInsets.all(10.0) 设置边距为10
  • crossAxisCount:横轴子元素的数量crossAxisCount。
  • mainAxisSpacing:主轴方向的间距。
  • crossAxisSpacing:横轴元素的间距。
  • childAspectRatio:元素的宽高比

简单例子代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class MyGridView2 extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return Container(
child: new GridView(
// 设置边距
padding: const EdgeInsets.all(10.0),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(

// 横轴 数量
crossAxisCount: 3,
crossAxisSpacing: 10,
mainAxisSpacing: 10.0,
// 宽高比:默认是1.0
childAspectRatio: 1.5,
),

children: <Widget>[
new Image.network('https://github.com/Jinxiansen/SwiftUI/raw/master/images/example/Text.png', fit: BoxFit.cover),
new Image.network('https://p.ampmake.com/mall/product/9666f00e-f02a-46ce-9b7c-090bf6aba9aa.png', fit: BoxFit.cover),
new Image.network('https://p.ampmake.com/mall/product/d6fdc891-eec3-47ce-b837-df726c73a9b8.png', fit: BoxFit.cover),
new Image.network('https://p.ampmake.com/mall/product/0cccf224-57d1-4a3e-bf06-9df5fed0f4e0.png', fit: BoxFit.cover),
new Image.network('https://p.ampmake.com/mall/product/2bf5c0d7-dc3a-45d2-ab1b-40fe737aa79a.jpg', fit: BoxFit.cover),
new Image.network('https://p.ampmake.com/mall/product/26153760-b964-45d5-b811-7f2bf5844102.jpg', fit: BoxFit.cover),
new Image.network('https://p.ampmake.com/mall/product/a20bb53c-b710-4b39-9607-91bef06ee54e.png', fit: BoxFit.cover),
],
)
);
}
}

以前的写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class MyGridView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
child: new GridView.count(
padding: const EdgeInsets.all(15.0),
// 横轴 item 数量
crossAxisCount: 3,
// 横轴 间距
crossAxisSpacing: 10.0,
children: <Widget>[
const Text('我是第一个'),
const Text('我是第二个'),
const Text('我是第三个'),
const Text('我是第四个'),
const Text('我是第五个'),
const Text('我是第六个'),
const Text('我是第七个'),
],
),
);
}
}

]]>
+ + + + + + Flutter GridView + + + +
+ + + + + + + /2019/06/18/%E7%AC%AC%E5%8D%81%E5%9B%9B%E7%AB%A0%E7%AC%AC%E4%B8%89%E6%96%B9%E5%BA%93%E9%9D%A2%E8%AF%95/ + + 第十四章 第三方库

AFNetworking

框架图

会话NSURSession

主要类关系图

AFURLSessionManager

  • 创建和管理 NSURLSession, NSURLSessionTask
  • 实现NSURLSessionDelegate 等协议的代理方法
  • 引入AFSecurityPolicy 保证请求安全,https 请求时 证书检验,公钥验证
  • 引入AFNetworkReachabilityManager 监控网络状态

SDWebImage

架构图

加载图片流程

Reactive Cocoa

RACStream

AsyncDisplayKit

主要解决的问题

基本原理

]]>
+ + + +
+ + + + + + + /2019/06/18/%E7%AC%AC%E5%9B%9B%E7%AB%A0OC%E8%AF%AD%E8%A8%80%E7%89%B9%E6%80%A7%E9%9D%A2%E8%AF%95/ + + OC 语言特性

分类

原理

由运行时来决议的,不同分类当中含有同名方法 谁最终生效取决于谁最终参与编译,最后参编译的同名分类方法会最终生效,假如分类方法中添加的方法和宿主类中的某一个方法名相同,分类中的方法会覆盖宿主类中的同名方法,覆盖是指在消息传递过程中优先查找数组靠前的元素,如果查找到了同名方法就直接调用,实际上宿主类的同名方法实现仍然是存在的,我们可可以通过一些手段调用到原有类的同名方法的实现

使用场景

  • 申明私有方法,分类的 .m 文件中申明私有方法啊,对外不暴露
  • 对类中的代码进行抽取分解
  • 把Framework的私有方法公开化

特点

  • 运行时决议,运行时通过runtime 才把分类中的方法添加到了宿主类上
  • 可以为系统类添加方法
  • 有多个分类中存在同名方法时,最后编译的方法会最先生效
  • 分类中有和宿主类同名方法时,底层中通过内存copy将分类方法“覆盖”宿主类方法
  • 名字相同的分类编译会报错

分类中可以添加的内容

  • 实例方法
  • 类方法
  • 协议
  • 属性,实际上只是声明了 getter / setter 方法,并没有添加成员变量

关联对象技术

关联对象由 AssocicationsManager 管理并在 AssociationsHashMap 上存储
所有对象的关联内容都在同一个全局容器中

  • 给分类添加成员变量,通过关联对象的方式

扩展

使用场景

  • 声明私有属性
  • 声明私有方法
  • 声明私有成员变量

特点

  • 编译时决议
  • 只以声明的形式存在,多数情况下寄生在宿主类的.m中
  • 不能为系统添加扩展

代理

  • 代理设计模式,传递方式是一对一
  • 使用weak 避免循环引用

通知

使用观察者模式来实现的用于跨层传递消息的机制

KVO

KVO 是系统对观察者模式的又一实现,使用isa 混写技术(isa - swizzling)来动态运行时为某一个类添加一个子类重写了它的setter 方法,同时将原有类的isa指针指向了新创建的类上

  • KVO 是OC对观察者模式的又一实现
  • apple 使用isa 混写技术(isa - swizzling)来实现KVO
    • 给我A类注册一个观察者时本质上是调用系统的 Observer for keyPath 方法,系统会创建一个NSKVONotifiying_A 的类
  • 使用 setter 方法设置值KVO才能生效
  • 使用setValue:forKey: 改变值KVO才能生效
  • 使用下划线的成员变量直接修改值需要手动添加KVO才能生效(增加 willChangeValueForKey 和 didChangeValueForKey 两个方法)

KVC

apple提供的键值编码技术

  • (nullable id)valueForKey:(NSString *)key;
  • (void)setValue:(nullable id)value forKey:(NSString *)key;

    以上两个方法中的key是没有任何限制的,只要知道对应的成员变量名称,就可以对私有的成员变量设置和操作,这违背了面向对象的编程思想

(void)setValue:(nullable id)value forKey:(NSString *)key; 方法调用流程

  • 先判断是否有跟key 相关的setter 方法,如果有就直接调用,结束调用
  • 如果没有再判断是否存在实例变量,如果存在直接给赋值,结束调用
  • 如果说实例变量不存在,会去调用 - (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key; 抛出异常

属性关键字

读写权限

  • readonly

  • readwrite 系统默认

原子性

  • atomic (系统默认)保证赋值和获取(对成员属性的直接获取和赋值)时线程安全,并不代表操作和访问,如果atomic修饰的一个数组,只能保证独具改数组的读取和赋值时线程安全,并不能保证对数据操作(增加/删除数组元素)时是安全的
  • nonatomic

引用技术

  • assign
    • 修饰基本数据类型,
    • 修饰对象时不改变其引用计数,
    • 会产生悬垂指针
  • weak
    • 不改变被修饰对象的引用计数,
    • 所指对象被释放之后指针自动置为nil

两者区别:

  • weak可以修饰对象,而assign 既可以修饰对象也可以修饰基本数据类型
  • assign 修饰的对象,对象释放后指针仍然指向原对象的内存地址,而weak 修饰的对象,释放后会自动置为nil

问题1

weak 修饰的对象为什么释放后会自动置为nil

问题2

@property (copy) NSMutableArray *array; 有什么问题

  • 被copy修饰后,如果赋值过来的是NSMutableArray,copy之后就是NSArray,如果赋值过来的是NSAarray,copy 之后仍然是NSArray,有可能会调用array的添加元素方法会导致crash

copy 关键词

  • 浅拷贝:内存地址的复制,让目标对象指针和源对象指向同一块内存空间,
    • 会增加对象的引用计数
    • 并没有一个新的内存分配
  • 深拷贝:目标对象指针和源对象指针分别指向两块内容相同的内存空间
    • 不会增加源对象的引用计数
    • 有新对象的内存分配

MRC 重写 retain修饰的变量的setter 方法

1
2
3
4
5
6
7
8

- (void)setObj:(id)obj {
if (_obj != obj) {
[_obj release];
}
_obj = [obj retain];

}

判断是为了防止异常处理,如果不做 if 判断,当传入的 obj 对象正好是原来的_obj 对象,对原对象尽行releas 操作,实际上也会对传入的对象进行releas 操作进行释放,此时如果再通过obj指针访问废弃的对象时就会导致carsh

]]>
+ + + +
+ + + + + + + /2019/06/18/%E7%AC%AC%E5%8D%81%E7%AB%A0%E7%BD%91%E7%BB%9C%E7%9B%B8%E5%85%B3/ + + 第十章 网络请求相关

HTTP协议

超文本传输协议

请求/响应报文

请求报文:

  • 请求行
    方法 URL 协议版本(1.1版本) CRLF(回车)

  • 首部字段区域
    首部字段名(key) :值(value )CRLF
    首部字段名(key) :值(value )CRLF

  • 实体主体
    get 请求没有
    post 有实体主体

响应报文:

  • 响应行
    版本 状态码 状态码描述(短语) CRLF

  • 首部字段区

  • 响应实体主体

连接建立流程

三次握手和四次挥手

三次握手

  • 客户端 发送 syn 同步报文到Server,
  • server 端收到syn报文后会,链接建立,server 端会返回一个 syn,ack 报文给客户端,
  • 客户端收到 server 端的ask 报文后,会再次回应一个确认ack 的报文

数据请求

  • 接下来在已经建立的tcp 通道上进行http 的请求报文
  • server 端回复给客户端一个http响应报文

四次挥手

  • 客户端发送 fin 终止报文到server 端
  • server 端收到报文后会回回复一个ack 报文到客户端
  • 客户端到server 端的tcp的链接已经断开,但是 server 端到客户端的链接还会传输数据
  • server 端向客户端发送一个fin ack 报文 ,然后客户端回应给server 一个ack 报文,释放链接

HTTP 的特点

  • 无连接
    非持久链接 需要多次断开/建立链接,就会经历 三次握手,四次挥手

    HTTP 的持久链接: 多个http请求在同一个tcp 通道上, 一定时间内不会断开tcp通道,提高了网络请求的效率

    头部字段:

    coonection : keep-alive 客户端是否采用持久链接time :20 多长时间有效max:10 最多发生多少个http请求和响应对
  • 无状态
    同一个用户多次发送http请求,server 端是不知道是同一个用户的,server 端通过 Cookie/Session 规避这个问题

    • cookie
      cookie 主要是用来记录用户状态,区分用户,cookie由server端返回给客户端,cookie 状态保存在客户端

      保证cookie 的安全

      • cookie 加密处理
      • 只在https 上携带Cookie
      • 设置Cookie为 httpOnly,防止跨站脚本攻击
    • session 用来记录用户的状态,区分用户,状态存放在服务端

HTTPS 与网络安全

HTTPS 都使用了那些加密手段,为什么?

  • 建立链接过程使用非对称加密,因为耗时
  • 后续通信数据传输过程中使用对称性加密

对称性加密

TCP: 传输控制协议

  • 面向链接
    TCP 是全双工的所以数据传输之前需要三次握手,传输完毕需要四次挥手,
  • 可靠传输

    • 无差错
    • 按序到达
  • 面向字节流

    发送方发送数据时不管一次性提交给TCP的缓冲是多大的数据,TCP本身会根据实际情况来划分数据包再发送给接受数据方

  • 流量控制

    滑动窗口协议

UDP : 用户数据协议

  • 无连接
    发送数据时不需要建立链接,传输完毕也不需要释放链接

  • 尽最大努力交付
    是不保证可靠传输的,

  • 面向报文
    既不合并,也不拆分

问题

GET 和post 请求的区别

  • GET 获取资源的

    • 安全的: 不因该引起Server 端的任何状态变化的
    • 幂等的:同一个请求方法执行多次和执行一次的效果完全相同
    • 可缓存的:请求是可以缓存的,代理服务器可以缓存
    • 请求参数以? 分割拼接到URL 后面
  • post 请求处理资源

    • 不安全的:
    • 非幂等的
    • 不可缓存的

GET 请求参数有长度限制,是2048 个字符,post 一般没有限制

HTTPS 链接建立流程

客户端会发送服务端一个支持的加密算法列表
包括 TLS 的版本号 + 随机数C,
服务端再回给客户端一个证书
包括 商定的加密算法 后续首先通过非对称加密进行对称性加密的密钥传输,http的网络就通过被非对称密钥保护的对称密钥访问和传输数据

TCP 和 UDP的区别

tcp是面向链接的,并且支持可靠的传输,支持面向字节流, tcp 提供了流浪上的控制和拥塞的控制
UDP 提供了简单的复用/分用及差错检测的传输层的功能,

状态吗有哪些

  • 1XX
  • 2XX:成功
  • 3XX:重定向
  • 4XX:客户端请求错误
  • 5XX: 服务器端请求错误

为什么需要三次握手,两次行不行?

解决同步请求链接建立时超时的情况,如果只有两次握手,

持久链接怎么判断一个请求是否结束

  • 请求/响应报文的头部字段

响应报文 content-length: 1024 头部字段是由server 端返回数据大小,客户端根据所接收的数据的字节数是否达到了头部字段 cotent-length 的大小判断

  • 通过post请求时server 端返回数据有可能需要多次响应才能返回,此时需要根据chunked 字段名 是否为空来判断

Charels 抓包原理是什么?

中间人攻击

正常步骤是 客户端 —- server

抓包:发送请求和响应请求都可以通过中间人修改再返回给某一端

客户端 —中间人—- server

DNS 解析

域名到IP地址的映射,DNS解析请求采用UDP数据报,且明文

NDS 解析存在的常见问题

  • DNS 劫持问题
    钓鱼网站
  • DNS 解析转发问题

DNS 劫持和HTTP 的关系时怎么样的

没有关系,

  • DNS 解析发在HTTP 建立连接之前
  • DNS 解析请求使用UDP数据报, 端口是53,跟HTTP没有任何关系

解决DNS 劫持问题

  • httpDNS
  • 长连接
]]>
+ + + +
+ + + + + + + /2019/06/18/%E7%AC%AC%E5%8D%81%E4%B8%80%E7%AB%A0%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E9%9D%A2%E8%AF%95/ + + 第十一章 设计模式

六大设计原则

单一职责原则:一个类只负责一件事

开闭原则: 对修改关闭,对扩展开放

接口隔离原则:使用多个专门的协议,而不是一个庞大臃肿的协议,iOS系统的UITableView的数据和代理协议分开

依赖倒置原则:抽象不应该依赖于具体实现,具体实现可以依赖于抽象

里氏替换原则:父类可以被子类无缝替换,且原有功能不受任何影响,iOS系统 的 KVO 机制遵守了这一原则,当调用 addObserver 方法时,因为系统在动态运行时为我们创建了一个子类,直观感受是仍然使用的是父类,实际上系统已经替换为创建的子类,所以KVO 机制 不但使用了管擦这模式,并且遵从了 里氏替换原则

迪米特法则: 一个对象应对其他对象有尽可能少的了解,平时所说的高内聚,低耦合

常用设计模式

责任链

有三个业务分别为A/B/C,以前的调用顺序为 A -> B -> C ,现在顺序有调整,

定义某一个类,有一个成员变量他的类型跟原有类类型一致,组成一个责任链,让处理具体业务的责任者去处理

桥接模式

业务解耦的问题:同一个列表和多套数据显示,多套数据通过后端来控制是共存,

类构成

抽象类A ——> class B

适配器模式

一个现有类需要适应变化的问题, 年代比较久远的类或者对象

类构成

  • 对象适配
  • 类适配

单例模式

命令模式

行为参数化

降低代码重合度

]]>
+ + + +
+ + + + + + + /2019/06/18/%E7%AC%AC%E5%8D%81%E4%BA%8C%E7%AB%A0%E6%9E%B6%E6%9E%84%E5%92%8C%E6%A1%86%E6%9E%B6%E9%9D%A2%E8%AF%95/ + + 第十二章 架构和框架

目的

  • 实现模块化
  • 分层
  • 解耦

图片缓存

图片缓存框架设计方案?

图片缓存时内存设计上需要考虑的问题

  • 内存存储的size

    • 10kb的图片开辟的内存空间 50 张
    • 100kb图片的 20 张
    • 100kb 以上的 10 张
      通过队列的方式存储,先进先出的方式淘汰
  • 淘汰策略

    • 以队列先进先出的方式淘汰
    • 已LRU 算法 (如 30 min 之内是否使用过)

磁盘设计考虑的问题

  • 存储方式
  • 大小限制
  • 淘汰策略(超过 7 天)

网络部分的设计需要考虑的问题

  • 图片请求最大并发量
  • 请求超时策略,一旦超时了,可以重试,如果重试2次失败,是否还下载
  • 请求优先级,下载或者缓存的图片是否紧急需要

图片通过什么方式进行读写,过程是怎样的?

  • 以图片的url 的单向哈希值作为key 存储
  • 读取过程

图片解码

  • 对于不同格式的图片,解码采用什么方式来做?

    • 应用策略模式对于不同图片格式进行解码
  • 在那个阶段做图片解码处理?

    • 再磁盘读取后未解码或者网络请求返回后,解码后再放到内存中,可以减轻主线程的压力,解码在主线程中

线程处理

阅读时长统计

怎样设计一个时长统计框架?

为何要有不同类型的记录器,处于什么考虑?

基于不同分类场景提供的关于记录的封装、适配

记录的数据由于程序杀死或者断电,关机等丢失,你是怎样处理的?

  • 定时写磁盘
  • 限定内存缓存条数,超过该条数就写如磁盘

记录器上传 中延时上传的具体场景有哪些?上传时机如何把握?

上传时机

  • 立刻上传
  • 延时上传
  • 定时上传

延时上传场景

统计结果立即上传会重复调用接口导致资源浪费

  • 前后台切换
  • 从无网到有网的变化时
  • 通过其他的接口捎带着上传

复杂页面的架构

MVVM 框架思想

RN 的数据流思想

任何一个子节点是没有权力做自己的变化更新的,它必须把这个消息传递给根结点,根结点通过自顶向下的遍历查找需要更新的节点

系统UIView 更新机制的思想

AsyncDisplayKit 关于预排版的设计思想

客户端整体架构

需要独立于APP 的通用层
通用的业务层
中间层
业务层

业务之间的解耦通信方式

  • OpenURL
  • 依赖注入方式, 在中间层通过代理 的方式实现业务A 和业务B之间的通信
]]>
+ + + +
+ + + + + + + /2019/06/18/%E7%AC%AC%E5%8D%81%E4%B8%89%E7%AB%A0%E7%AE%97%E6%B3%95%E9%9D%A2%E8%AF%95/ + + 第十三章算法

]]>
+ + + +
+ + + + + + + /2019/06/18/%E7%AC%AC%E5%85%AB%E7%AB%A0%E5%A4%9A%E7%BA%BF%E7%A8%8B%E9%9D%A2%E8%AF%95/ + + 第三章多线程面试

]]>
+ + + +
+ + + + + + + /2019/06/18/%E7%AC%AC%E5%85%AD%E7%AB%A0%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86%E9%9D%A2%E8%AF%95/ + + 内存管理

内存布局

  • stack 栈是从高地址向低地址扩展,所以栈是向下增长的,对象和block copy 后都会放在堆上
  • heap 堆区向上增长
  • 散列表方式

    • 自旋锁
      • 自旋锁是“忙等” 的锁
    • 引用计数表
    • 弱应用表

内存管理方案

  • 小对象使用 TaggedPointer
  • 64 位架构下面使用的是 NONPONINT_ISA 内存管理方案,非指针型的 isa,内部存储了一些
  • 散列表结构 (Side Tables() 结构)
    • 自旋锁
    • 引用计数表
    • 弱引用表
    • Side Tables() 结构

为什么不是一个SideTable,而是多个组成了Side Tables

如果所有对象的存储都放在一张大表当中,因为每个对象是在不同的线程中创建的,要操作其中一个对象时,需要将表加锁处理保证数据安全,要等锁释放之后才能操作下一个对象,此时存在效率问题,引入分离锁技术方案

  • 分离锁:把引用计数表分成多张表进行,
  • Side Tables 本质是一张Hash 表

自旋锁

是“忙等”的锁,如果当前锁已被其他线程获取,当前线程会不断探测是否被释放,会第一时间获取,而其他锁比如信号量当它获取不到锁时,会把自己的线程阻塞休眠,等到其他线程释放这个锁时再唤醒这个锁

  • 适用于轻量访问

引用计数表

使用过Hash表来实现,hash查找提高了查找高效率,插入和获取是通过Hash 算法来是实现的,避免了for循环

alloc 实现

经过一系列调用,最终调用了C函数 calloc, 并没有引用计数+1

retain 实现

底层经过了2次Hash表查找

release 实现

和 retain 实现相反

dealloc 实现原理

objc_destructInstance() 实现

clearDeallocating() 实现

MRC

手动引用计数

ARC

  • 编译器在对应位置自动插入retain 和 release 操作,并和runtime协作达到自动引用计数管理内存的效果

  • ARC 中禁止手动调用 retain/release

  • ARC 中新增 weeak、strong 等关键字

弱引用管理

被声明为__weak 的对象指针经过编译后会进过调用以下方法,在最终的weak_register_no_lock() 方法中进行弱引用变量的添加。添加的位置是通过Hash算法查找的

weak 修饰的对象,释放时置为nil 如何实现的

当一个对象被 dealloc 之后,在dealloc内部实现当中会调用弱引用清除的相关函数,在相关函数当中会根据当前对象指针查找弱引用表,把当前对象下对应的弱应用都取出,遍历弱应用指针并置为nil。

自动释放池

是以栈为结点通过双向链表的形式组合而成,和线程一一对应

Autoreleasepool

循环引用

自循环引用

一个对象中有拥有一个 强持有他的 obj,如果给 obj 赋值为原对象就会造成自循环引用

相互循环引用

  • 代理
  • Block
  • NSTimer
  • 大环多循环

多循环引用

解决循环引用的方案

__weak

解决相互循环引用

__block

__unsafe_unretained

一般不建议使用

]]>
+ + + +
+ + + + + + + /2019/06/18/%E7%AC%AC%E4%BA%94%E7%AB%A0Runtime%E7%9B%B8%E5%85%B3%E9%9D%A2%E8%AF%95/ + + Runtime 相关问题

数据结构

objc_object 结构体

objc_class

对象、类对象、元类对象的区别

  • 类对象存储实例方法列表等信息
  • 元类对象存锤类方法列表等信息

消息传递

void objc_msgSend(void /* id self, SEL op, ... */)

经过编译器转变后
[self class] <==> objc_msgSend(self, @selector(class))
第一个参数是消息传递的接收者self, 第二个参数是传递的消息名称(选择器)

void objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */)
第一个参数是一个结构体指针

//
struuch objc_super {
__unsafe_unretained id receiver; // 就是当前的对象
}

[superclass] <==> objc_msgSendSuper(super, @selector(class))

无论调用[super class] 还是 [self class] 最终这条消息的接收者都是当前对象

图1

消息传递过程

图2

问题1

打印结果都是 Phone

原因:

  • 无论调用[super class] 还是 [self class] 最终这条消息的接收者都是当前对象
  • [self class] 在Phone的类对象中查找class 方法,没有去父类查找,父类也没有,如图1顺次往上查找到根类,调用根类中class 的具体实现
  • [super class]只是跨越了当前类,直接从当前类的父类中查找 class 方法,父类中没有class 方法,如图1所示只能继续往上查找到根类对象,在NSObject 中有class 方法实现,所以接受者任然是当前的对象

缓存查找

根据给定的SEL(方法选择器)通过一个函数来映射出bucket_t 在数组当中的位置,本质上是哈希查找

当前类中查找

当前类中存在着对应的方法列表

  • 对于已经排序好的列表,采用二分查找算法查找对应的执行函数
  • 对于没有排序的列表,采用一般遍历查找方法查找对应的执行函数

父类逐级查找

图3

  • 最关键是通过当前类的 superclass 成员变量去查找父类,或者访问父类
  • curClass 的父类是否为 nil?如果是NSObject 类,他的父类为空,所以会结束查找
  • 如果当前类有父类就要去该父类的缓存中查找,如果在缓存中命中,那么就结束查找,如果缓存中没有,就需要遍历当前类的父类的方法列表,如果没有就遍历父类的父类查找,沿着superclass指针逐级向上查找,直到查找到NSObject,再去Superclass。如果还是没有为nil时就结束父类逐级查找

消息转发

resolveInstanceMethod:

  • 该方法是类方法不是实例方法,参数是SEL 方法选择器类型
  • 返回值是一个BOOL值
  • 告诉系统是否要解决当前实例方法的实现
  • 如果返回YES,结束消息转发
  • 如果返回NO,系统会给第二次机会处理这个消息,会回掉forwardingTargetForSelector:

forwardingTargetForSelector:

  • 参数是 SEL 方法选择器
  • 返回值是 id 类型,说明有哪个对象来处理,转发的对象是谁,如果返回了转发目标就会结束当前调用
  • 如果返回为nil,系统会调用 methodSignatureForSelector:方法,最后一次机会处理这个消息

methodSignatureForSelector:

  • 参数是 SEL 方法选择器
  • 返回值是一个对象,实际上这个对象是对methodSignatureForSelector 方法的参数,参数个数,参数类型和返回值的包装
  • 如果返回一个方法签名的话,就会调用 forwardInvocation: 方法
  • 如果返回为nil,标记为消息无法处理,程序crash

forwardInvocation:

  • 如果此方法不能处理消息,程序crash

代码示例

.h 文件

1
2
3
4
@interface RunTimeObject : NSObject
// 只声明,不实现,验证消息转发机制
- (void)test;
@end

.m 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
@implementation RunTimeObject

// 实现消息转发流程 中的方法
/**
是否要解决当前实例方法的实现
*/
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(test)) {
NSLog(@"resolveInstanceMethod:");
// 如果返回YES,消息转发就会结束
// return YES;

return NO;
} else {

NSLog(@"super resolveInstanceMethod:");
// 返回父类的默认调用
return [super resolveInstanceMethod:sel];
}
}


/**
- 参数是 SEL 方法选择器
- 返回值是 id 类型,说明有哪个对象来处理,转发的对象是谁,如果返回了转发目标就会结束当前调用
- 如果返回为nil,系统会调用 methodSignatureForSelector:方法,最后一次机会处理这个消息
*/
- (id)forwardingTargetForSelector:(SEL)aSelector {
NSLog(@" forwardingTargetForSelector: ");
return nil;
}

/**
- 参数是 SEL 方法选择器
- 返回值是一个对象,实际上这个对象是对`methodSignatureForSelector` 方法的参数,参数个数,参数类型和返回值的包装
- 如果返回一个方法签名的话,就会调用 forwardInvocation: 方法
- 如果返回为nil,标记为消息无法处理,程序crash

*/
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if (aSelector == @selector(test)) {
NSLog(@"methodSignatureForSelector:");
/**
v: 表示这个方法返回值是 void

固定参数@: 表示参数类型是 id, 即self
固定参数`:` : 表示参数类型是选择器类型,即 @selector(test)
*/
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
} else {

NSLog(@"super methodSignatureForSelector:");
// 返回父类的默认调用
return [super methodSignatureForSelector:aSelector];
}
}


- (void)forwardInvocation:(NSInvocation *)anInvocation {
NSLog(@"forwardInvocation:");
}

@end

调用

1
2
3
4
- (void)runTiemTest {
RunTimeObject *obj = [[RunTimeObject alloc] init];
[obj test];
}

结果:

1
2
3
4
5
resolveInstanceMethod:
forwardingTargetForSelector:
methodSignatureForSelector:
super resolveInstanceMethod:
forwardInvocation:

为类动态添加方法

methond Swizzling

使用场景

  • 页面中的进出添加统计信息,使用 methond Swizzling 替换viewillAppear 方法

动态添加方法

  • perforSelector: 实际上是考察class_addMethod 的方法使用

动态方法解析

  • @dynamic关键字
  • 动态运行时语言将函数决议推迟到运行时
  • 编译时不生成setter/getter 方法,而是在运行时才添加具体的执行函数

问题

[obj foo] 和 obj_msgSend()函数之间有什么关系?

  • [obj foo]经过编译器处理过后会变成 obj_msgSend()有两个参数,一个是obj,第二个参数是foo 选择器

runtime 如何通过Selector找到对应的IMP地址的?

消息传递机制

能否向编译后的类增加实例变量

编译之前就完成了实例变量的布局,
runtime_r_t 表示是readonly 的,编译后的类是不能添加实例变量的,可以向动态添加的类中添加实例变量

]]>
+ + + +
+ + + + + + + /2019/06/18/%E7%AC%AC%E4%B9%9D%E7%AB%A0Runloop%E9%9D%A2%E8%AF%95/ + + RunLop

]]>
+ + + +
+ + + + + + + /2019/06/18/%E7%AC%AC%E4%B8%83%E7%AB%A0Blocks%E9%9D%A2%E8%AF%95/ + + Blocks

Block 是什么?

Blocks 是 C 语言的扩充功能,将函数及其执行上下文封装起来的对象,带有自动变量(局部变量)的匿名函数

  • 因为block 底层C++ 结构体中存在着 isa 指针,所以是一个对象
  • 因为block 底层C++ 结构体中存在着一个函数指针 FuncPtr,所以是一个函数

什么是Block 调用

Block 的调用就是函数的调用
在终端使用 clang 编译之后查看文件内容可得出以下结论:

  • 对其进行一个强制转换,转换之后取出成员变量 FuncPtr

截获变量

以下代码运行结果是 12

原因:block 对基本数据类型的局部变量会截获其值,在定义 block 时以值的方式传到了block 对应的结构体当中,而调用block 时直接使用的是block对应结构体当中已经传过来的那个值,所以值为12
  • 局部变量截获

    • 基本数据类型的局部变量截获其值
    • 对象类型的局部变量连同所有权修饰符一起截获
  • 局部静态变量: 已指针形式截获

  • 全局变量:不截获

  • 静态全局变量: 不截获

以下代码运行结果是 8

原因:block 对局部静态变量会以指针形式截获,所以值为8

__block 修饰符

  • __block 修饰后的变量最后变成了对象,底层c++ 结构体中存在isa指针
  • 一般情况下,对被截获变量进行赋值操作需要添加block 修饰符,而操作是不需要添加block 修饰符的
  • 不需要__block 修饰符,全局变量和静态全局变量 block 是不截获的,所以不需要修饰,静态局部变量是通过指针截获的,修改外部的变量,也不需要添加
    • 静态局部变量
    • 全局变量
    • 静态全局变量

不需要添加__Block 修饰符

1
2
3
4
5
6
7
8
- (void)test {
NSMutableArray *array = [NSMutableArray array];
void(^block)(void) = ^{
[array addObject:@1234];
};

block();
}

需要添加__Block 修饰符

1
2
3
4
5
6
7
8
- (void)test {
__block NSMutableArray *tempArray = nil;
void(^block)(void) = ^{
tempArray = [NSMutableArray array];
};

block();
}

以下代码运行结果是 8

1
2
3
4
5
6
7
8
- (void)test__Block3 {
__block int multiplier = 6;
int(^Block)(int) = ^int(int num){
return multiplier * num;
};
multiplier = 4;
NSLog(@"__block--test__Block3---result is %d", Block(2));
}

原因

栈上Block 的block 变量中的 forwarding 指针指向的是自身

Block 的内存管理

Block 的 内存分布

Block 的 Copy 操作

  • 全局block
  • 堆block
  • 栈block

Block 循环引用

  • 如果当前block对当前对象的某一变量进行截获的话,block 会对对应变量有一个强引用,当前block因为当前的对象对其有个强引用,产生了自循环引用,通过声明为__weak变量来解决
  • 如果定义了__block 修饰符的话也会产生循环引用,在ARC 下会产生循环引用,而在MRC 下不会,在ARC 下通过打破环来消除,但是存在弊端时当block 没有机会执行时,

循环引用1

解决方法:

大环循环引用2

以上代码,在MRC 下,不会产生循环引用,在ARC 下,会产生循环应用,引起内存泄露

解决方法

如果block 没有机会执行,那么循环引用将不会被打破

]]>
+ + + +
+ + + + + + + /2019/06/18/%E7%AC%AC%E4%B8%89%E7%AB%A0UI%E8%A7%86%E5%9B%BE%E5%93%8D%E5%BA%94%E9%93%BE%E7%A6%BB%E5%B1%8F%E6%B8%B2%E6%9F%93%E9%9D%A2%E8%AF%95/ + + UITableView 相关
  • 重用机制
  • 并发访问,数据拷贝问题:主线程删除了一条数据,子线程数据由删除之前的数据源copy而来,子线程网络请求,数据解析,预排版等操作后,回到主线程刷新时已经删除的数据又会重现

    • 主线程数据源删除时对数据进行记录,子线程的数据回来后再进行一次同步删除操作,然后再刷新数据

    • 使用GCD中的串行队列,将数据删除和数据解析在此队列中进行

UIView 和CALayer 的关系

  • UIView 为 CALayer 提供内容,负责处理触摸事件,参与响应链
  • CALayer 负责显示内容,分工明确,UIView 的Layer 实际上就是CALayer, backgroundColor 等属性本质是对CALayer 做了一层包装

事件传递

  • hitTest 方法是返回响应时间的试图
  • pointInside withEvent 方法判断点击位置是否在当前视图范围内
  • 倒序遍历最终响应事件的试图,最后添加的试图会最优先被遍历到

需求案例

只让正方形的view中圆形区域内可以响应事件,四个角的位置不响应事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class CircularAreaResponseButton: UIButton {

override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if self.isHidden || !self.isUserInteractionEnabled || self.alpha <= 0.01 {
return nil
}

var hitView: UIView?
if self.point(inside: point, with: event) {
// 遍历当前视图的子试图
for subView in subviews {
// 坐标转换
let convertPoint = self.convert(point, to: subView)
if let hitTestView = subView.hitTest(convertPoint, with: event) {
// 找到了最终响应事件的试图
hitView = hitTestView
break;
}
}

if hitView != nil {
return hitView
}
return self
}
return nil
}


override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
let x1 = point.x
let y1 = point.y

let x2 = self.frame.width / 2.0
let y2 = self.frame.height / 2.0
// 使用平面内两点见距离公式计算距离, 并且判断是否 <= 圆的半径
return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)) <= x2
}
}

视图响应

UIView 都继承于 UIResponder,都有以下方法

1
2
3
4
func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?)
func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?)
func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?)

如果事件沿着视图响应链一直传递到 UIApplicationDelegate 仍然没有任何一个视图处理事件,程序将会发生什么?

  • 忽略掉了这个事件当作什么都没有发生

图像显示原理

CALayer 的 contents 是位图

  • CPU的工作

    Layout | Display | Prepare | Commite |
    ———|———–|————|———|
    UI布局 | 绘制 | 图片编解码 | 提交位图 |
    文本计算 |

  • GPU渲染管线

    定点着色 | 图元转配 | 光栅化 | 片段着色 | 片段处理 |
    ——–|——–|——-|———|———|

提交到帧缓冲区(frameBuffer)

UI卡顿掉帧的原因

1/60 ms 时间内, 由cpu 和GPU 协同产生最终的数据,当 CPU UI布局,文本计算所用的时间较长时,留给GPU渲染,提交到帧缓冲区的时间就会变短,这样以来就会发生掉帧情况

UIView 的绘制原理

CPU优化方案

  • 对象的创建、调整、销毁放在子线程,节省cpu的一部分时间
  • 预排版(布局计算,文本计算)放在子线程中
  • 预渲染(文本等异步绘制,图片编解码)等

GPU优化方案

在屏渲染

指GPU的渲染操作是在当前用于显示的屏幕缓冲区域中进行

离屏渲染

指GPU在当前屏幕缓冲区域外新开劈了一个缓冲区域进行渲染操作
指定了UI视图图层的某些属性,标记为视图在未预合成之前不能用于在当前屏幕上直接显示的时候就会发生离屏渲染,例如图层的圆角设置和蒙层设置

  • 离屏渲染何时触发

    • 设置layer 的圆角并且和 maskToBounds 一起使用时
    • 图层蒙版
    • 阴影
    • 光栅化
  • 为何要避免离屏渲染

    • 离屏渲染,会增加 GPU 的工作量,GPU工作量的增加就很有可能导致CPU和GPU的总耗时超过1/60s,导致UI卡顿和掉帧
  • 避免离屏渲染

###异步绘制

]]>
+ + + +
+ + + + + + + /2019/06/18/%E4%B8%80%E8%87%AA%E5%8A%A8%E5%BC%95%E7%94%A8%E8%AE%A1%E6%95%B0/ + + 自动引用计数

什么是自动引用计数

内存管理中对引用采取自动计数的技术

内存管理

  • 自己生成的对象,自己所持有
    • alloc
    • new
    • copy
    • mutableCopy
  • 非自己生成的对象,自己也能持有
    • retain
  • 不再需要自己持有的对象时释放
    • release
  • 非自己持有的对象无法释放

  • 废弃对象

    • dealloc

__strong

__strong 修饰符表示对对象的“强引用”

__unsafe_unretained 修饰符

__unsafe_unretained 是不安全的所有权修饰符, 被它修饰的变量不属于编译器的内存管理对象

1
id __unsafe_unretained objc = [[NSObject alloc] init];

Xcode 提示:Assigning retained object to unsafe_unretained variable; object will be released after assignment

将保留对象赋给unsafe_unretain变量;对象将在赋值后释放

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
id __unsafe_unretained obj1 = nil;

{
id __unsafe_unretained obj = [[NSObject alloc] init];

// 自己生成并持有对象
// 因为obj0 变量是__strong,强引用,所以自己持有对象
id __strong obj0 = [[NSObject alloc] init];

// obj0 变量赋值给你obj1
// ojb1 变量不持有对象的强应用,也不持有弱引用
obj1 = obj0;


// 输出 obj1 变量表示的对象
NSLog(@"A: %@", obj1);
} // obj0变量 超出了其作用域,强引用失效自动释放自己持有的对象,除此之外没有其他持有者,所以废弃该对象




// 输出 obj1 变量表示的对象
// obj1 变量表示的对象因为无持有者已经销毁,坏的内存访问
NSLog(@"B: %@", obj1);
]]>
+ + + +
+ + + + + + + /2019/06/18/is%20%E5%85%B3%E9%94%AE%E5%AD%97%E7%B1%BB%E5%9E%8B%E6%A3%80%E6%9F%A5/ + + swift类型检查和类型转换

is 关键字类型检查

判断父类和子类之间的关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func typeCheck() {
let programmer = Programmer(name: "老张-php")
if dev is iOSDeveloper {
print("iOS 开发者")
} else {
print("其他开发者")
}

if programmer is Coder {
print("老张-php是一农个码农")
} else {
print("")
}
}

as 关键字类型转换

1
2
let ios = iosDev as? Programmer
let ios2 = iosDev as! Coder

判断是否遵守了协议

swift 中协议可以当作是一个类型使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
et ios = iosDev as? Programmer
let ios2 = iosDev as Coder

// 是否遵守了 Person 协议
if iosDev is Person {
print("遵守了Person协议")
}

let devs = [dev, iosDev, programmer] as [Any]

for dev in devs {
if let dev = dev as? Person {
print(dev)
print("遵守Person协议")
} else {
print("未遵守Person协议")
}
}

AnyObject Any

1
2
3
4
var anyObjectArray: [AnyObject] = [CGFloat(0.5) as AnyObject,
1 as AnyObject,
"string" as AnyObject,
iOSODev]

swift是面向函数编程, 函数是一等公民,数组中可以存入一个函数,函数表达的是一个过程,不是一个名词或者物体,所以函数不是一个对象,需要使用 any 关键字

1
2
3
4
var anyArray: [Any] = [CGFloat(0.5),
1,
"string",
iOSODev]

放入函数

1
anyArray.append({ (a: Int) -> (Int) in return a * a })
]]>
+ + + +
+ + + + + + + /2019/06/18/npm%E5%92%8Ccnpm/ + + ReactNative配置.npm 和cnpm 相关

npm

Nodejs的包管理工具

npm 常用命令

  • 更新或卸载

    1
    npm update/uninstall moduleName
  • 查看当前目录下已安装的包

    1
    npm list
  • 查看全局安装的包的路径

    1
    npm root -g
  • 全部命令

    1
    npm help

cnpm

因为npm安装插件是从国外服务器下载,受网络影响大,可能出现异常,cnpm 淘宝镜像

安装

1
npm install -g cnpm --registry=https://registry.npm.taobao.org

设置镜像源

1
2
cnpm set registry http://registry.cnpm.taobao.com
npm set registry http://registry.cnpm.taobao.com

配置 .npmrc 文件

前端项目开发离不开安装各种npm依赖包,可以选择远程的仓库也可以选择本地的仓库,但更改仓库地址需要在安装时控制台打命令,比较麻烦, 而npmrc可以很方便地解决上面问题

.npmrc 原理

当安装项目的依赖包时,会查找并读取项目根目录下的.npmrc文件的配置,自动指定仓库地址

  • 打开 .npmrc

    1
    open .npmrc
  • 编辑文件, 指定特定的库的镜像源, 比如以@test开头的库镜像为:http://registry.cnpm.test.com/ 而其他的正常

    1
    2
    @test:registry=http://registry.cnpm.lietou.com/
    registry=https://registry.npm.taobao.org/
]]>
+ + + +
+ + + + + + + /2019/06/18/swift%E4%B8%AD%E5%BC%BA%E5%BC%95%E7%94%A8%E5%BE%AA%E7%8E%AF%E5%92%8Cweak%E5%85%B3%E9%94%AE%E5%AD%97/ + + Swift中内存管理

强引用循环和 weak 关键字

房东

1
2
3
4
5
6
7
8
9
10
11
12
class Landlord {
var name: String
var house: House?

init(name: String) {
self.name = name
}

deinit {
print("Landlord内存释放")
}
}

房子

1
2
3
4
5
6
7
8
9
10
11
12
class House {
var name: String
var landlord: Landlord?

init(person: String) {
self.name = person
}

deinit {
print("House内存释放")
}
}

循环引用

1
2
3
4
5
6
7
8
9
10
11
func strongRetain() {
var landlord: Landlord? = Landlord(name: "老李")
var house: House? = House(person: "望京府邸")
// 房东有个房子
landlord?.house = house
// 房子有个主人
house?.landlord = landlord

landlord = nil
house = nil
}
  • 虽然 landlord = nil 和 house = nil,但是 Landlord 和 House 内存空间并没有释放 (并未执行 deinit 函数)

  • 原因:因为两个内存空间相互引用,引用计数不为0,形成了强循环引用

  • 解决办法:使用weak 关键字来修饰两者中其中一个变量

    • 使用weak修饰的变量必须是可选类型,可以赋值为nil
    • 使用weak修饰必须是var 类型变量
1
2
3
4
5
6
7
8
9
10
11
12
class House {
var name: String
weak var landlord: Landlord?

init(person: String) {
self.name = person
}

deinit {
print("House内存释放")
}
}

unowned 关键字的使用

相互循环引用的两个变量有一个是可选的

有个身份证的类,每个身份证肯定对应一个人

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class ID {
var number: String
let person: Landlord

init(person: Landlord, number: String) {
self.person = person
self.number = number
}

deinit {
print("ID内存释放")
}
}

```

房东

class Landlord {
var name: String
var house: House?
/// 身份证可以为nil,
var id: ID?

init(name: String) {    self.name = name}deinit {    print("Landlord内存释放")}

}

1
2

循环引用

func unownedTest() {
var person: Landlord? = Landlord(name: “老王”)
var id: ID? = ID(person: person!, number: “10010”)

person?.id = idperson = nilid = nil

}

1
2
3
4
5
6
7
8

- person 和 id 之间有强引用关系
- 每个id必须有一个对应的人并且是固定的只能使用let 修饰,不能为nil, 而id可以为空
- unowned和 weak 作用相同都是弱引用,只是unowned 修改的变量不能是nil (可选型的)
- unowned只能使用在 class 类型上,不能修饰函数类型,例如闭包存在循环引用时不能使用unowned修饰
- 解决方法:
- 此时可以通过使用 weak 修饰 id 的方式,因为 id 可以为nil
- let 修饰并且不能为nil 这种情况可以使用 unowned 修饰解决
class ID {    var number: String    unowned let person: Landlord    init(person: Landlord, number: String) {        self.person = person        self.number = number    }    deinit {        print("ID内存释放")    }}
1
2
3
4

**unowned有一定的危险性**

因为unowned 修饰的对象不能赋值为nil,所以执行下面的代码会crash,

func unownedTest() {
var person: Landlord? = Landlord(name: “老王”)
var id: ID? = ID(person: person!, number: “10010”)

person?.id = id// 提前释放 person 内存person = nil// 获取身份证对应的人print(id?.person) // 此处奔溃,let owner = id?.personid = nil

}

1
2

正确的写法因该是先将🆔设置为nil,再将 Person 设置为nil,再调用 print(id?.person) 就不会再报错

func unownedTest() {
var person: Landlord? = Landlord(name: “老王”)
var id: ID? = ID(person: person!, number: “10010”)

person?.id = idid = nil// 提前释放 person 内存person = nil// 获取身份证对应的人print(id?.person) // 此处奔溃,let owner = id?.person

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

#### 相互循环引用的两个变量都不是可选的

使用隐式可选类型

### 闭包中的强引用循环

- 并不是所有的闭包都会产生循环引用,只有相互引用的两个对象会产生循环引用

- 使用swift闭包捕获列表,在闭包的声明参数之前使用 [unowned self], 不需要解包

- 使用 unowned表示这个对象不能为空,可能存在风险,如果对象为空就会crash


使用 unowned 对象不能为空,可能存在风险

class Closure {
var closure: ((Int) -> Void)?

init() {    closure = { [unowned self] index in        print(self)        print(index)    }}

}

1
2

使用 weak, 此时需要解包

closure = { [weak self] index in
// 解包
if let self = self {
print(self)
print(index)
}
}

1
2

强制解包

closure = { [weak self] index in
// 强制解包
print(self!)
print(index)
}
`

]]>
+ + + +
+ + + + + + + /2019/06/18/typescripe%20%E5%AE%89%E8%A3%85%E5%8F%8A%E8%AF%AD%E6%B3%95/ + + ReactNative 中使用 TypeScript

从RN0.57版本开始已经可以直接支持typescript,无需任何配置。在保留根入口文件index.js的前提下,其他文件都可以直接使用.ts或.tsx后缀。但这一由babel进行的转码过程并不进行实际的类型检查,因此仍然需要编辑器和插件来共同进行类型检查

TypeScript和JavaScript对比

两者深度对比

最大的区别:

  • TypeScript是强类型语⾔和静态类型语⾔(需要指定类型)
  • JavaScript是弱类型语⾔和动态类型语⾔(不需要指定类型)

TypeScript

由微软开发的自由和开源的编程语言,是JavaScript的一个严格超集,并添加了可选的静态类型和基于类的面向对象编程

TypeScript定义

安装及语法

教程

安装
1
npm install -g typescript
安装 typescript 依赖管理器 typings
1
npm install -g typings
安装 typings 依赖
1
2
typings install npm~react --save
typings install dt~react-native --globals --save
使用 yarn 安装

如果已经安装了 yarn 安装如下:

1
2
yarn add tslib @types/react @types/react-native
yarn add --dev react-native-typescript-transformer typescript
创建 tsconfig.json 配置文件
  • cd 到 ReactNative 项目根文件夹下

    1
    tsc --init
  • 修改 tsconfig.json 文件

    如果一个目录下存在一个tsconfig.json文件,那么它意味着这个目录是TypeScript项目的根目录。 tsconfig.json文件中指定了用来编译这个项目的根文件和编译选项

    tsconfig.json文件配置详解

    TypeScript配置文件tsconfig简析

    务必设置”jsx”:”react”
    注意多余的注释可能会不兼容,需要移除

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    {
    "compilerOptions": {
    "target": "es6",
    "allowJs": true,
    "jsx": "react",
    "outDir": "./dist",
    "sourceMap": true,
    "noImplicitAny": false
    },

    // "files"属性 :指定一个包含相对或绝对文件路径的列表。 "include"和"exclude"属性指定一个文件glob匹配模式列表

    "include": [
    "typings/**/*.d.ts",
    "src/**/*.ts",
    "src/**/*.tsx"
    ],
    "exclude": [
    "node_modules"
    ]
    }
  • cd 到项目根目录中新建存放 typescripe 源代码的文件夹

建立 rn-cli.config.js 文件,使用支持typescript的transfomer
1
2
3
4
5
6
7
8
module.exports = {
getTransformModulePath() {
return require.resolve('react-native-typescript-transformer');
},
getSourceExts() {
return ['ts', 'tsx'];
}
}
修改 .babelrc 文件
1
2
3
4
{
"presets": ["react-native"],
"plugins": ["transform-decorators-legacy"]
}
修改项目中文件导入
  • 入口index.jsApp.js的文件名请不要修改,项目根目录下新建src文件夹
  • src文件夹下新建 .tsx 后缀的文件
  • .tsx 后缀的文件引用react的写法有所区别

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import * as React from 'react'
    import { Text, View } from 'react-native';
    import { Component } from 'react';

    export default class HelloWorld extends Component{
    render() {
    return (
    <View>
    <Text>Hello!</Text>
    <Text>Typescript</Text>
    </View>
    );
    }
    }
  • App.js中使用

    • App.js 导入方式修改为如下方式

      1
      2
      3
      4
      import './src';
      ```

      - 导入 .tsx 类型的组建

      import HelloWorld from “./src/HelloWorld”;

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11

      ### 配置jsconfig

      `jsconfig.json`目录中存在文件表明该目录是JavaScript项目的根目录。该jsconfig.json文件指定根文件和JavaScript语言服务提供的功能选项

      [jsconfig详解](https://code.visualstudio.com/docs/languages/jsconfig)

      [jsconfig.json](https://jeasonstudio.gitbooks.io/vscode-cn-doc/content/md/%E8%AF%AD%E8%A8%80/javascript.html?q=)


      ##### 新建 `jsconfig.json` 文件

touch jsconfig.json

1
2

##### 编辑 `jsconfig.json` 文件

{
“compilerOptions”: {
“allowJs”: true,
“allowSyntheticDefaultImports”: true,
“emitDecoratorMetadata”: true
},
“exclude”: [
“node_modules”,
“rnApp/Tools” // 项目根目录下新建的存放RN中所用的一些工具类
]
}

1
2
3


### package.json 文件配置

{
“name”: “RNProject”,
“version”: “0.0.1”,
“private”: true,
“scripts”: { // 通过设置这个可以使npm调用一些命令脚本,封装一些功能
“start”: “node node_modules/react-native/local-cli/cli.js start”,
“test”: “jest”
},
“dependencies”: { // 发布后依赖的包
“@types/react”: “^16.4.16”,
“@types/react-native”: “^0.57.4”,
“react”: “16.5.0”,
“react-native”: “0.57.2”,
“react-transform-hmr”: “^1.0.4”,
“tslib”: “^1.9.3”
},

“devDependencies”: { // 开发中依赖的其它包
“babel-jest”: “23.6.0”,
“jest”: “23.6.0”,
“metro-react-native-babel-preset”: “0.48.0”,
“react-native-typescript-transformer”: “^1.2.10”,
“react-test-renderer”: “16.5.0”,
“typescript”: “^3.1.2”
},

“jest”: { // JavaScript 的单元测试框架
“preset”: “react-native”
}
}

1
2
3
4
5
6
7
8
9

##### `–save` 和 `-–save-dev` 区别

- --save参数表示将该模块写入dependencies属性,
- --save-dev表示将该模块写入devDependencies属性。

##### 添加开发依赖库

- 类型检查

npm install @types/jest --save-devnpm install @types/react --save-devnpm install @types/react-native --save-dev 
1
- 装饰器转化核心插件
npm install babel-plugin-transform-decorators-legacy --save-dev
1
2

- 测试框架
npm install react-addons-test-utils --save-devnpm install react-native-mock --save-dev
1
2

##### 删除依赖库

npm uninstall @types/jest –save

1
2


npm uninstall @types/jest –save-dev

1
2

##### 彻底删除,-d 表示 devDependencies, -O 表示 optionalDependencies 中

npm uninstall -s -D -O @types/jest

1
2
3
4
5
6
7
8
9
10

### 报错汇总

##### 错误1 `Cannot find module 'react-transform-hmr/lib/index.js'`

[解决1](https://stackoverflow.com/questions/50142561/cannot-find-module-react-transform-hmr-lib-index-js?noredirect=1&lq=1)

[解决2](https://github.com/facebook/react-native/issues/21530)

- 安装 `react-transform-hmr`
npm install react-transform-hmr --save
1
2

- 清除缓存重新启动服务
npm start --reset-cache```
]]>
+ + + +
+ + + + + + + /2019/06/18/%E3%80%8A52%E4%B8%AA%E6%96%B9%E6%B3%95%E7%AC%AC%E4%B8%80%E7%AB%A0%E3%80%8B/ + + 《52个方法-第一章》

一、OC起源

  • OC使用的是“消息结构”而不是“函数调用”
  • 两者区别:
    • 消息结构:在运行时所执行的代码由运行环境来决定,无论是否多态总会在运行时才会去查找所要执行的方法
      1
      2
      NSObject *objc = [NSObject new];
      [objc performWith: parameter1 and: parameter2];
- 函数调用:由编译器决定,如果函数多态,编译时就要查处到底应该执行那个函数实现
1
2
Object *objc = new Object;
objc -> perform(parameter1, parameter2);
  • 消息结构中编译器不会关心接收消息的对象是何种类型,接收消息的对象问题也要在运行时处理,此过程叫做“动态绑定”

要点:

  1. OC语言使用的动态绑定的消息结构,在运行时所执行的代码由运行环境来决定,无论是否多态总会在运行时才会去查找所要执行的方法,
  2. OC 中对象总是分配在“堆空间”,而绝不会分配在”栈“上,不带 “*”的变量或者结构体等可能会使用“栈空间”

二、类头文件尽量少导入其他头文件

要点:

  1. 除非必要,否则不要引入头文件,使用向前声明降低类之间的耦合
  2. 有时无法使用向前声明,应该把该类遵守协议的声明放到“class-continuation分类”(匿名分类)中。
  3. 如果“class-continuation分类”也不行的话,就把协议单独放在一个文件中,再将其引入### 三、多用字面量语法,少用与之等价的方法

三、多用字面量语法,少用与之等价的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
- (void)tempMethond {
// OC1.0 起,支持简单方式创建基本类型对象

// - 使用字符串字面量:简介易读
NSString *someString = @"Effective Objective-C 2.0";
// 传统方式
NSString *someStrongOld = [NSString stringWithFormat:@"Effective Objective-C 2.0"];


// - 字面数值
NSNumber *someNumber = @1;
NSNumber *floatNumber = @2.5f;
NSNumber *doubleNumber = @3.1415926;
NSNumber *boolNumber = @YES;
NSNumber *charNumber = @'a';
// 字面量语法用于表达式
int x = 5;
float y = 6.23f;
NSNumber *expressionNumber = @(x * y);


// - 字面量数组
NSArray *animals = @[@"cat", @"dog", @"mouse", @"badger"];
NSString *dog = animals[1];

// arrayWithObjects 和 字面量 的区别:
// arrayWithObjects 方法依次处理各个参数,直到发现nil为止
// 字面量数组遇到nil直接抛出异常
NSArray *animalsOld = [NSArray arrayWithObjects:@"cat", @"dog", @"mouse", @"badger", nil];
NSString *dogOld = [animalsOld objectAtIndex:1];


// - 字面量字典
// 如果有nil直接抛出异常
NSDictionary *personData = @{@"firtName": @"Matt",
@"lastName": @"Galloway",
@"age": @28};
NSString *lastName = personData[@"lastName"];

// dictionaryWithObjectsAndKeys 方法依次处理各个参数,直到发现nil为止
NSDictionary *personDataOld = [NSDictionary dictionaryWithObjectsAndKeys:@"Matt",@"firtName",
@"Galloway", @"lastName",
[NSNumber numberWithInteger:28], @"age", nil];

NSString *lastNameOld = [personData objectForKey:@"lastName"];


// - 可变数组和字典
NSMutableArray *mutableArray = animals.mutableCopy;
mutableArray[1] = @"dog";

NSMutableDictionary *mutableDict = [personData mutableCopy];
mutableDict[@"lastName"] = @"Galloway";
}

要点:

  1. 使用字面量语法创建字符串,数值,数组,字典简明扼要
  2. 通过下标操作获取数组,字典对应的元素
  3. 使用字面量创建数组,字典时若有nil ,会抛出异常,确保值里不含nil### 五、用枚举表示状态、选项、状态码

四、多用类型常量,少用#define预处理指令

宏定义
  • 缺点:
    • 可以把所有相同的字符串值都替换为定义的值,
      • 定义出来的常量没有具体的类型信息
      • 如果有人重新定义了宏定义,不会有警告或者提醒,导致程序中值不一样
类型常量
  • 命名规则:

    • 如果只是在实现文件.m 中使用,则在前面加字母k
    • 如果此常量在类之外也可见,则以类名为前缀,(第19条详细了解命名习惯)
  • 声明位置:

    • 总是喜欢在头文件中声明宏定义,可能和常量名冲突,
    • 所有引入这个头文件的类都会出现宏定义或者常量,相当于声明了一个名叫kAnimationDuration的全局变量
    • 应该加上前缀,表明其所属的具体类,例如:EOCViewClassAnimationDuration
  • 常量从右往左解读依次是: 具体类型,不可修改

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // const 修饰的是常量不希望别人修改, NSString * 指向NSString对象
    extern NSString *const EOCStringConstant;


    //两者写法都可以
    extern const NSTimeInterval EOCAnimatedViewAnimationDuration;
    extern NSTimeInterval const EOCAnimatedViewAnimationDuration1;
    ```

    - 如果不打算公开常量,可以定义在 .m 实现文件里

    static const NSTimeInterval kAnimationDuration = 0.3;

    1
    2
    3

    - static关键字
    - static 修饰意味着该变量仅在定义此变量的编译单元(实现文件.m)中可见,作用域就是当前.m文件
    static const NSTimeInterval kAnimationDuration = 0.3;
    1
    2
    3
    - 如果需要声明一个外部可见的常值变量,需要在头文件中声明并使用 extern 关键词修饰 "extern 

    在 .h 文件中
    extern NSString *const EOCStringConstant;// 两者写法都可以extern const NSTimeInterval EOCAnimatedViewAnimationDuration;extern NSTimeInterval const EOCAnimatedViewAnimationDuration1;
    1
    2

    在 .m 文件中
    NSString *const EOCStringConstant = @"VALUE";const NSTimeInterval EOCAnimatedViewAnimationDuration = 0.3;NSTimeInterval const EOCAnimatedViewAnimationDuration1 = 0.3;
    1
    2
    3
    4
    5
    6
    7
    8
    9

    **要点:**

    1. 定义常量时static const定义编译单元(实现文件.m)中可见的常量,无需为其名称加前缀
    2. 在头文件中使用extern 声明全局常量,并在实现文件中定义其值,这种常量要出现在全局符号表中,所以加相关类作为前缀

    ### 五、用枚举表示状态、选项、状态码

    - 普通枚举

    enum EOCConnectionState {

    EOCConnectionStateDisconnected, // 断开连接EOCConnectionStateConnecting,   // 连接中...EOCConnectionStateConnected,    // 已连接

    };

    1
    普通枚举如果要使用如下:

    enum EOCConnectionState status = EOCConnectionStateConnected;

    1
    2

    如果每次都不需要输入enum关键字,只需要typedef关键字重新定义枚举类型

    EOCConnectionState status1 = EOCConnectionStateConnecting;

    1
    2

    - 设置枚举的初始值

    enum EOCConnectionStateConnectionState {

    EOCConnectionStateConnectionStateDisconnected = 1, // 断开连接EOCConnectionStateConnectionStateConnecting,   // 连接中...EOCConnectionStateConnectionStateConnected,    // 已连接

    };

    1
    2
    3
    4
    5

    - 可选枚举

    - << 位操作符(左移), 表示往左移动N位,使用 位运算符 | (或)来组合使用
    - 使用位运算符 &(与) 判断是否开启了某个选项

    enum UIViewAutoresizing {

    UIViewAutoresizingNone                 = 0,UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,UIViewAutoresizingFlexibleWidth        = 1 << 1,UIViewAutoresizingFlexibleRightMargin  = 1 << 2,UIViewAutoresizingFlexibleTopMargin    = 1 << 3,UIViewAutoresizingFlexibleHeight       = 1 << 4,UIViewAutoresizingFlexibleBottomMargin = 1 << 5

    }

    1
    2
    3
    4

    - 使用宏定义枚举

    普通枚举类型(老式语法定义枚举)

    typedef enum : NSUInteger {

    EOCConnectionState2DisConnected,EOCConnectionState2DisConnecting,EOCConnectionState2Connected,

    } EOCConnectionState2;

    1
    2

    NS_ENUM 宏定义的枚举展开后:

    typedef enum EOCConnectionState_3 : NSUInteger EOCConnectionState_3;
    enum EOCConnectionState_3 : NSUInteger {

    EOCConnectionState_3DisConnected,EOCConnectionState_3DisConnecting,EOCConnectionState_3Connected,

    };

    1
    2

    - 定义新特性枚举

    typedef NS_ENUM(NSUInteger, EOCConnectionState3) {

    EOCConnectionState3DisConnected,EOCConnectionState3DisConnecting,EOCConnectionState3Connected,

    };

    1
    2
    3
    4

    - 枚举使用

    .h文件

    #import <UIKit/UIKit.h>

    typedef enum EOCConnectionState EOCConnectionState;

    typedef NS_ENUM(NSUInteger, EOCConnectionState3) {

    EOCConnectionState3DisConnected,EOCConnectionState3DisConnecting,EOCConnectionState3Connected,

    };

@interface EOCEnum : NSObject@property (nonatomic, assign) enum EOCConnectionState state;@property (nonatomic, assign) EOCConnectionState3 state3;@end
1
2

.m 文件中
#import "EOCEnum.h"@implementation EOCEnum- (void)testEnum { // 未使用typedef 关键字 enum EOCConnectionState status = EOCConnectionStateConnected; NSLog(@"%u", status); // 使用typedef 关键字 EOCConnectionState status1 = EOCConnectionStateConnecting; NSLog(@"%u", status1); UIViewAutoresizing resizing = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; if (resizing & UIViewAutoresizingFlexibleWidth) { // 设置了 UIViewAutoresizingFlexibleWidth 约束 }}// UIKit 试图所支持的设置显示方向- (UIInterfaceOrientationMask)supportedInterfaceOrientations { return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft;}// 枚举使用- (void)userEnum { switch (_state3) { case EOCConnectionState3DisConnected: // ... break; case EOCConnectionState3DisConnecting: // ... break; case EOCConnectionState3Connected: // ... break; default: // 最好不用,防止以后增加一种状态机之后,编译器不发出警告 break; }}@end```

要点:

  1. 使用枚举表示状态机的状态、传递给方法的选项以及状态码等值,给枚举值起名时要注重易懂
  2. 如果一个类型可以使用状态机的状态来表示,并且多个选项可同时使用,那么就定义为可选的枚举类型,枚举各值定义为2的幂,以便通过“按位或”操作组合
  3. 使用NS_ENUM 和 NS_OPTIONS 宏来定义枚举,并指明底层的数据类型。这样可以确保枚举是用开发者所选的底层数据类型实现出来的,而不会采用编译器所选的类型
  4. switch语句处理枚举类型时不要实现default 分支,如果以后加入枚举的新状态之后,编译器会发出警告
]]>
+ + + +
+ + + + + + + /2019/06/18/iOS%E9%80%9A%E8%BF%87URL%E8%8E%B7%E5%8F%96HTML%E4%B8%AD%E6%A0%87%E9%A2%98/ + + iOS通过URL获取HTML中标题

获取标题

String扩展一个方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private let patternTitle = "(?<=title>).*(?=</title)"
extension String {
static func parseHTMLTitle(_ urlString: String) -> String? {
let url = URL(string: urlString)!
do {
let html = try String(contentsOf: url, encoding: .utf8)
let range = html.range(of: patternTitle, options: String.CompareOptions.regularExpression, range: html.startIndex..<html.endIndex, locale: nil)
guard let tempRange = range else { return "" }
let titleStr = html[tempRange]
return String(titleStr)
} catch {
return ""
}
}
}
iOS 中简单正则表达式

iOS 中 正则表达式中的\ 需要使用 \\ 来表示

// 获取let testStr = "我到底是[iOS]开发还是[产品]经理"// 获取 "[" 和 "]" 之间的所有内容, 左边[ 起一直到 右边 ] 结束的所有内容 let pattern1 = "\\[.*\\]" // [iOS]开发还是[产品]// 获取 "[" 和 "]" 之间的所有内容,包括[]let pattern2 = "\\[.*?\\]"    // [iOS]// 获取第一个 "[" 和 "]" 之间的所有内容 不包括[] let pattern3 = "(?<=\\[).*?(?=\\])" // iOS// 获取html中 title 标签中内容let pattern5 = "(?<=title>).*(?=</title)" 
获取范围截取子串
let testRange1 = testStr.range(of: pattern1, options: String.CompareOptions.regularExpression, range: testStr.startIndex..<testStr.endIndex, locale: nil)    let result1 = String(testStr[testRange1!])let testRange2 = testStr.range(of: pattern2, options: String.CompareOptions.regularExpression, range: testStr.startIndex..<testStr.endIndex, locale: nil)let result2 = String(testStr[testRange2!])let testRange3 = testStr.range(of: pattern3, options: String.CompareOptions.regularExpression, range: testStr.startIndex..<testStr.endIndex, locale: nil)let result3 = String(testStr[testRange3!])print(result1) print(result2) print(result3) 
正则表达式

通过URL解析html中的标题

  • 获取html字符串
  • 正则匹配
  • 生成字符串Range
  • 截取字符串

匹配一次直接返回结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 生成表达式
let regular = try NSRegularExpression(pattern: pattern5, options: .caseInsensitive)


// 开始匹配
let matchResult1 = regular.firstMatch(in: html, options: NSRegularExpression.MatchingOptions.reportProgress, range: NSMakeRange(0, html.count))

// 获取匹配结果范围
let matchRange = matchResult1?.range
let range = Range(matchRange!)!

// 生成String.Index 类型范围
let startIndex = html.index(html.startIndex, offsetBy: range.lowerBound)
let endIndex = html.index(html.startIndex, offsetBy: range.upperBound)
let range3 = Range(uncheckedBounds: (lower: startIndex, upper: endIndex))

// 截取字符串
let result1 = String(html[range3])
print(result1)

全部匹配后返回结果时一个集合

1
2
3
4
5
6
7
8
9
10
let matchResult2 = regular.matches(in: html, options: .reportProgress, range: NSMakeRange(0, html.count))
matchResult2.forEach { (result) in
let range = Range(result.range)!

let startIndex = html.index(html.startIndex, offsetBy: range.lowerBound)
let endIndex = html.index(html.startIndex, offsetBy: range.upperBound)

let subStr2 = html[Range(uncheckedBounds: (startIndex, endIndex))]
print(String(subStr2))
}

将匹配到的内容提还

1
let str3 = regular.stringByReplacingMatches(in: html, options: .reportProgress, range: NSMakeRange(0, html.count), withTemplate: "====")
]]>
+ + + +
+ + + + + + + /2019/06/18/iOS%E9%9D%A2%E8%AF%95OC%E8%AF%AD%E8%A8%80%E7%89%B9%E6%80%A7/ + + OC 语言特性

分类

原理

由运行时来决议的,不同分类当中含有同名方法 谁最终生效取决于谁最终参与编译,最后参编译的同名分类方法会最终生效,假如分类方法中添加的方法和宿主类中的某一个方法名相同,分类中的方法会覆盖宿主类中的同名方法,覆盖是指在消息传递过程中优先查找数组靠前的元素,如果查找到了同名方法就直接调用,实际上宿主类的同名方法实现仍然是存在的,我们可可以通过一些手段调用到原有类的同名方法的实现

使用场景

  • 申明私有方法,分类的 .m 文件中申明私有方法啊,对外不暴露
  • 对类中的代码进行抽取分解
  • 把Framework的私有方法公开化

特点

  • 运行时决议,运行时通过runtime 才把分类中的方法添加到了宿主类上
  • 可以为系统类添加方法
  • 有多个分类中存在同名方法时,最后编译的方法会最先生效
  • 分类中有和宿主类同名方法时,底层中通过内存copy将分类方法“覆盖”宿主类方法
  • 名字相同的分类编译会报错

分类中可以添加的内容

  • 实例方法
  • 类方法
  • 协议
  • 属性,实际上只是声明了 getter / setter 方法,并没有添加成员变量

关联对象技术

关联对象由 AssocicationsManager 管理并在 AssociationsHashMap 上存储
所有对象的关联内容都在同一个全局容器中

  • 给分类添加成员变量,通过关联对象的方式

扩展

使用场景

  • 声明私有属性
  • 声明私有方法
  • 声明私有成员变量

特点

  • 编译时决议
  • 只以声明的形式存在,多数情况下寄生在宿主类的.m中
  • 不能为系统添加扩展

代理

  • 代理设计模式,传递方式是一对一
  • 使用weak 避免循环引用

通知

使用观察者模式来实现的用于跨层传递消息的机制

KVO

KVO 是系统对观察者模式的又一实现,使用isa 混写技术(isa - swizzling)来动态运行时为某一个类添加一个子类重写了它的setter 方法,同时将原有类的isa指针指向了新创建的类上

  • KVO 是OC对观察者模式的又一实现
  • apple 使用isa 混写技术(isa - swizzling)来实现KVO
    • 给我A类注册一个观察者时本质上是调用系统的 Observer for keyPath 方法,系统会创建一个NSKVONotifiying_A 的类
  • 使用 setter 方法设置值KVO才能生效
  • 使用setValue:forKey: 改变值KVO才能生效
  • 使用下划线的成员变量直接修改值需要手动添加KVO才能生效(增加 willChangeValueForKey 和 didChangeValueForKey 两个方法)

KVC

apple提供的键值编码技术

  • (nullable id)valueForKey:(NSString *)key;
  • (void)setValue:(nullable id)value forKey:(NSString *)key;

    以上两个方法中的key是没有任何限制的,只要知道对应的成员变量名称,就可以对私有的成员变量设置和操作,这违背了面向对象的编程思想

(void)setValue:(nullable id)value forKey:(NSString *)key; 方法调用流程

  • 先判断是否有跟key 相关的setter 方法,如果有就直接调用,结束调用
  • 如果没有再判断是否存在实例变量,如果存在直接给赋值,结束调用
  • 如果说实例变量不存在,会去调用 - (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key; 抛出异常

属性关键字

读写权限

  • readonly

  • readwrite 系统默认

原子性

  • atomic (系统默认)保证赋值和获取(对成员属性的直接获取和赋值)时线程安全,并不代表操作和访问,如果atomic修饰的一个数组,只能保证独具改数组的读取和赋值时线程安全,并不能保证对数据操作(增加/删除数组元素)时是安全的
  • nonatomic

引用技术

  • assign
    • 修饰基本数据类型,
    • 修饰对象时不改变其引用计数,
    • 会产生悬垂指针
  • weak
    • 不改变被修饰对象的引用计数,
    • 所指对象被释放之后指针自动置为nil

两者区别:

  • weak可以修饰对象,而assign 既可以修饰对象也可以修饰基本数据类型
  • assign 修饰的对象,对象释放后指针仍然指向原对象的内存地址,而weak 修饰的对象,释放后会自动置为nil

问题1

weak 修饰的对象为什么释放后会自动置为nil

问题2

@property (copy) NSMutableArray *array; 有什么问题

  • 被copy修饰后,如果赋值过来的是NSMutableArray,copy之后就是NSArray,如果赋值过来的是NSAarray,copy 之后仍然是NSArray,有可能会调用array的添加元素方法会导致crash

copy 关键词

  • 浅拷贝:内存地址的复制,让目标对象指针和源对象指向同一块内存空间,
    • 会增加对象的引用计数
    • 并没有一个新的内存分配
  • 深拷贝:目标对象指针和源对象指针分别指向两块内容相同的内存空间
    • 不会增加源对象的引用计数
    • 有新对象的内存分配

MRC 重写 retain修饰的变量的setter 方法

1
2
3
4
5
6
7
8

- (void)setObj:(id)obj {
if (_obj != obj) {
[_obj release];
}
_obj = [obj retain];

}

判断是为了防止异常处理,如果不做 if 判断,当传入的 obj 对象正好是原来的_obj 对象,对原对象尽行releas 操作,实际上也会对传入的对象进行releas 操作进行释放,此时如果再通过obj指针访问废弃的对象时就会导致carsh

]]>
+ + + +
+ + + + + + + /2019/06/18/iOS%20%E5%8A%A8%E6%80%81%E8%AE%BE%E7%BD%AETableHeaderView%E9%AB%98%E5%BA%A6/ + + iOS动态设置tableHeaderView高度

需求

动态的设置tableHeaderView的高度

遇到的问题:

  • 设置tableHeaderViewframe无法实现,有时候会出现tableView 中间的 cell 不显示
  • 有时候会导致headerViewcell内容重叠

  • 通过计算控件高度设置frame方式麻烦

正确的步骤

  • 创建HeaderView
  • 取出UITableViewtableHeaderView
  • 设置frame大小,调整headerView布局
  • 重新给UITableViewtableHeaderView

使用AutoLayout

UITableView增加分类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
extension UITableView {
/// set tableHeaderView
func setTableHeaderView(_ headerView: UIView) {
headerView.translatesAutoresizingMaskIntoConstraints = false
self.tableHeaderView = headerView
// autolayout
NSLayoutConstraint(item: headerView, attribute: .centerX, relatedBy: .equal, toItem: self, attribute: .centerX, multiplier: 1.0, constant: 0).isActive = true
NSLayoutConstraint(item: headerView, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1.0, constant: 0).isActive = true
NSLayoutConstraint(item: headerView, attribute: .width, relatedBy: .equal, toItem: self, attribute: .width, multiplier: 1.0, constant: 0).isActive = true
}
/// update
func updateHeaderView() {
guard let headerView = self.tableHeaderView else { return }
headerView.layoutIfNeeded()
let header = self.tableHeaderView
self.tableHeaderView = header
}
}
]]>
+ + + +
+ + + + + + + /2019/06/18/iOS%E5%BC%80%E5%8F%91_%E4%BA%8E%E9%B8%BF%E9%B9%8F/ + + 个人信息

工作经历

猎聘网 (2018.2 ~ 至今)

Swift重构猎聘App中应聘记录模块,推进项目组使用AutoLayout技术,后期负责开发迭代乐班班项目,参与每版需求分析和主要代码编写,解决复杂功能页面卡顿,内存优化,BUG修复及iOS新特性适配等。

北京万朝科技有限公司(2015.5 ~ 2018.2)

在该项目中担任iOS开发⼯作,参与项目需求分析、核⼼代码编写以及常用工具类的封装,BUG修复,版本迭代更新,推进组内swift的使用。

中盎炬华科技发展有限公司(2014.4月 ~ 2015.5)

参与iOS客户端的开发维护完成开发工作,实现一些交互动画和效果;研究最新技术,优化程序代码,集成第三方SDK。

项⽬经历

1. 猎聘APP (2018.2 ~ 至今)

项⽬目描述: 为中高端人才和经理人提供招聘的平台

  • 使用 swift 重构应聘记录模块中相关页面及功能
  • 推进整个项目组使用 AutoLayout 技术

2. 乐班班

项⽬目描述: 音/视频播放、直播,内购,即时通信等功能,由OC、Swift,RN 技术混编,项⽬应⽤技术

  • 使用ReactNative技术重构和开发部分页面
  • 使用Xcode、Instruments工具查找、分析、解决项目内存泄露,修复BUG
  • Swift重构通信模块,用collectionView构建消息页面,自定义emoj Keyboard,封装常用UI控件,自定义控制器转场动画
  • 使用AVFoundation 实现视频录制,上传,播放功能
  • 通过GCD dispatch semaphors 解决并发网络请求问题
  • 使用collectionView构建复杂的视频播放页面,企业大学首页

3. e蜂通信 (2015.5 ~ 2018.2)

项⽬目描述:整合了办公OA、CRM系统,新增即时通信、分享、远程监控、解密外发、红包、私聊等功能;由OC和swift混编, 项⽬目应⽤用技术:

  • 使⽤CocoaAsyncSocket自定义通信协议,实现聊天,特定业务消息触达功能
  • 使⽤cocoaPod实现组件化,
  • 封装swift基础库,完善swift和Objective-C混编
  • 实现分享⼩视频、图片、点赞、评论功能
  • ⾃定义相机实现人脸识别打卡、扫描二维码功能
  • 封装常⽤UI控件,使用Core Animation实现动画效果,集成百度地图SDK
  • 利⽤JavaScriptCore完成H5和Native之间的交互

4. 四万公里 (2014.4 ~ 2015.5)

项目描述:针对境外旅游群体提供便捷服务,项⽬目应⽤用技术:

  • 封装 TableViewController基类,对接服务端接口等
  • 封装基础UI控件
  • 集成科大讯飞SDK, 实现了实时翻译功能
  • 集成滴滴打车SDK, 实现用户可在国外打车功能

专业技能

  1. 熟练使用Objective-C、Swift,及Swift与Objective-C混编,有良好的编程习惯
  2. 熟练掌握iOS内存管理机制以及ARC技术,结合Instruments对项目进行内存优化
  3. 熟练掌握NSThread/GCD/NSOperation等并发编程技术
  4. 熟练掌握iOS中事件链和响应链的传递方式
  5. 熟练使⽤AFNetworking,Moya,FMDB,SDWebImage,Masonry等第三⽅开源框架
  6. 熟练掌握各种UI控件封装,熟悉多视图开发,能实现复杂的界⾯面交互
  7. 掌握KVO原理理、KVC原理理、SDWebImage内部实现过程、RunLoop的应⽤
  8. 有团队开发经验,同时能够独立完成APP开发,代码编写,调试和发布

自我介绍

工作之余了解使⽤ JavaScript,RxSwift 函数响应式编程,小程序开发等知识,经常关注iOS博客和Github 上优秀代码,以及在raywenderlich上学习新知识,在个人博客做些技术积累。 计划在iOS领域深⼊同时也扩展⾃己的知识⾯,了解使⽤后端和前端技术。

]]>
+ + + +
+ + + + + + + /2019/06/18/Xcode%E4%B8%AD%E4%BD%BF%E7%94%A8Debug%20Memory%20Graph%E6%A3%80%E6%9F%A5%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F/ + + Xcode中使用Debug Memory Graph检查内存泄漏

勾选如下图所示选项

运行程序后点击

内存泄露定位

]]>
+ + + +
+ + + + + + + /2019/06/18/flutter%E5%AE%89%E8%A3%85/ + + flutter安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 导入头文件
import 'package:flutter/material.dart';

void main() => (MyApp());

// 定义类, 继承

class MyApp extends StatelessWidget {
// 重写
@override

Widget build(BuildContext context) {

// 返回一窗口
return MaterialApp(
title:'Flutter',
home: Scaffold(
appBar: AppBar(
title: Text('flutter 初体验')
),

body: Center(
child: Text('flutter 学习之路'),
),
),
);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}

class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);

// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.

// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".

final String title;

@override
_MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;

void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}

@override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Invoke "debug painting" (press "p" in the console, choose the
// "Toggle Debug Paint" action from the Flutter Inspector in Android
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
// to see the wireframe for each widget.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'你可以点击按钮来修改文字,我是热重载的结果',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
]]>
+ + + +
+ + + + + + + /2019/06/18/Swift%E9%A1%B9%E7%9B%AE%E4%B8%AD%E9%9B%86%E6%88%90RN/ + + Swift已有项目中集成ReactNative

本教程针对于已经有的Swift项目集成ReactNative

创建新的空目录

为了不影响原有的swift项目目录结构,建议先创建一个空的文件夹,例如SwiftRN

创建package.json文件

终端进入创建的SwiftRN文件目录下,创建package.json文件

1
touch package.json
修改package.json文件,指定各个版本
1
2
3
4
5
6
7
8
9
10
11
12
13
{
"name": "SwiftRN",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start",
"test": "jest"
},
"dependencies": {
"react": "16.3.1",
"react-native": "^0.56.0"
}
}
执行npm install

在创建package.json所在的文件目录下执行,在此过程中可以遇到的错误可能较多

1
npm install
添加index.js文件
1
touch index.js
修改文件内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import {
AppRegistry,
Text,
View,
} from 'react-native';

import React, { Component } from 'react';

// 用于最后在项目中测试
class SwiftRNHomeView extends Component {
render() {
return(
<View style={{backgroundColor:'white'}}>
<Text style={{fontSize: 20, marginTop: 200}}>Hellow world</Text>
</View>
);
}
}


class MyApp extends Component {
render() {
return <SwiftRNHomeView></SwiftRNHomeView>;
}
}

AppRegistry.registerComponent('SwiftRN', () => MyApp);

配置swift项目

将新创建的Swift文件目录下的所有文件拷贝到Swift项目所在的根目录下

使用Pod集成RN

swift项目采用了pod管理第三方库,在Podfile文件中加入下面代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
pod 'React', :path => './node_modules/react-native', :subspecs => [
'Core',
'RCTText',
'RCTNetwork',
'RCTWebSocket', # 这个模块是用于调试功能的
'CxxBridge', # Include this for RN >= 0.47,不引入的话会报错误或者警告
'DevSupport', # Include this to enable In-App Devmenu if RN >= 0.43,不引入的话会报错误或者警告
'RCTAnimation', 不引入的话会报错误或者警告
]


# 如果你的RN版本 >= 0.42.0,请加入下面这行,不引入的话会报错误或者警告
pod "yoga", :path => "./node_modules/react-native/ReactCommon/yoga"
pod 'DoubleConversion', :podspec => './node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
pod 'glog', :podspec => './node_modules/react-native/third-party-podspecs/glog.podspec'
pod 'Folly', :podspec => './node_modules/react-native/third-party-podspecs/Folly.podspec'

创建桥接文件

  • 如果原项目中有SwiftDemo-Bridging-Header.h类似这个文件可以跳过。
  • 如果从来没有创建过桥接文件,创建一个OC的类会自动弹出是否需要创建桥接文件,直接创建即可,最后把无用的OC文件删掉

引用RN头文件

在桥接文件中导入头文件

1
2
#import <React/RCTRootView.h>
#import <React/RCTBundleURLProvider.h>

使用RN页面

在模拟器中使用RN的页面, 确保在 info.plist 文件中添加了Allow Arbitrary Loads=YES

  • moduleName 对应的名字和index.js中 AppRegistry.registerComponent(‘SwiftRN’, () => MyApp)注册的名字必须相同
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import UIKit

class RNTestController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "http://localhost:8081/index.bundle?platform=ios")!
let rootView = RCTRootView(
bundleURL: url,
moduleName: "SwiftRN", //这里的名字必须和AppRegistry.registerComponent('MyApp', () => MyApp)中注册的名字一样
initialProperties: nil,
launchOptions: nil
)
view = rootView
}
}
]]>
+ + + +
+ + + + + + + /2019/06/18/Swift%E9%94%99%E8%AF%AF%E5%A4%84%E7%90%86/ + + Swift 中错误处理

assert 断言

只在debug 模式下程序会自动退出

1
public func assert(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)

assertionFailure 断言错误

只在debug 模式下程序会自动退出, 没有条件

1
public func assertionFailure(_ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)

precondition

releas 模式程序也会退出

1
public func precondition(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)

throw

]]>
+ + + +
+ + + + + + + /2019/06/18/Swift%E6%B3%9B%E5%9E%8B/ + + Swift 泛型

泛型定义–wikipedia

  1. 在程序编码中一些包含类型参数的类型,也就是说泛型的参数只可以代表类,不能代表个别对象。(这是当今较常见的定义)
  2. 在程序编码中一些包含参数的类。其参数可以代表类或对象等等。(现在人们大多把这称作模板)

泛型的用处

泛型可以实现传入一个指定类型后做一些操作然后返回后还是原来指定的类型,你可以指定特定的类型(特定是指约束)

泛型和 Any 的区别

区别:Any能代表任何类型,但是不能返回某个具体的类型,都需要使用 as 去转化,而泛型正好相反

  • Any 可以代表任何类型,除了class之外还可以代表struct,enum,
  • AnyObject 可以代码任何 class 的类型, swift 中基本类型, ArrayDictionary 都是 struct 类型,都不能使用 AnyObject 来接受

Swift中Any 和 AnyObject用法

泛型函数和泛型参数

swapTwoValues是一个泛型函数,可以接收或者返回任何类型

1
2
3
4
5
func swapTwoValues<T>(inout a: T, inout b: T) {
let temporaryA = a
a = b
b = temporaryA
}
  • swapTwoValues 函数名后面的 <T> 表示这个 T 类型是函数所定义的一个类型, T 只是一个类型的占位符,不用关心具体类型

  • (inout a: T, inout b: T) 中a和b两个参数类型T是泛型参数

类型约束

两个泛型参数的泛型函数

1
2
3
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
// function body goes here
}
  • 泛型参数T约束条件:必须是SomeClass类的子类
  • 泛型参数U约束条件:必须遵守SomeProtocol协议
  • 使用where指定约束
]]>
+ + + +
+ + + + + + + /2019/06/18/Swift%E5%8D%8F%E8%AE%AE%E5%88%AB%E5%90%8D/ + + Swift协议和别名

协议

某些属性的集合,可以有多个协议组成,具有的特点

  1. 可以声明函数,但是不能实现,由实现这协议的具体结构或者类来实现
  2. 声明的函数如果有参数也不能设置参数的默认值, 默认参数被认为也是函的一种实现
  3. 可以声明属性,必须是计算型属性 只能使用var 修饰,不能是存储型属,必须在 {} 中明确指出属性的读取类型 例如 {get set}
  4. 遵守协议的类可选择存储型属性或者计算型属性来实现协议中定义的属性
  5. protocol Person: class { } 表示此协议只能被类来遵守,结构体能遵守
  6. 协议可以看着是一种类型,如果协议中的属性是只读的,如果是协议类型只能访问get 方法, 但是遵守协议的具体类或者结构可以设置属性的读和写

  7. 协议中的构造函数

    - 如果父类中遵守了协议中的构造函数,子类中必须重写父类中的构造函数并且使用require修饰构造函数,表示继承此类的子类也必须实现该构造函数- 如果class 使用了final 修饰,表示不能被继承,此时不需要 require 关键词修饰

类型别名

  • 定义别名 typealias, 供扩展使用

    1
    2
    3
    4
    5
    6
    7
    8
    typealias Length = Double
    extension Double {
    var km: Length {return self * 1_000.0}
    var m: Length {return self * 100.0}
    var cm: Length {return self / 10.0}
    /// 英尺
    var ft: Length { return self / 3.28084}
    }
使用
1
let distance: Length = 10.5.km
  • 定义别名防止硬编码

    1
    2
    // 音频采样率
    typealias AudioSample = UInt64

协议中 typealias

协议中定义一个属性,该属性的类型根据不同的情况返回不同的类型(我理解为类似泛型),具体的类型由遵守此协议的类或者结构指定

  • 定义协议中别名
1
2
3
4
5
protocol WeightCalculable {
// 属性重量根据不同体积返回的可能是Int,可以可能是double类型,所以使用别名, 协议中使用associatedtype关键词,
associatedtype WeightType
var weight: WeightType { get }
}

扩展 Int 中的吨单位

1
2
3
4
extension Int {
typealias Weight = Int
var t: Weight {return self * 1_000}
}
  • 使用协议中别名
  • 一部手机的时使用该协议WeightCalculable
1
2
3
4
5
6
class iPhone: WeightCalculable {
typealias WeightType = Double
var weight: WeightType {
return 0.5
}
}
  • 一辆大卡时使用该协议WeightCalculable
1
2
3
4
5
6
7
8
class Car: WeightCalculable {
typealias WeightType = Int
let weight: WeightType

init(weight: Int) {
self.weight = weight
}
}
1
let bigCar = Car(weight: 8_000.t) // 8 吨

面向协议编程

  • 定义比赛协议,具有 赢/输/总场次/胜率 等特性
1
2
3
4
5
6
protocol Recordable: CustomStringConvertible {
var wins: Int { get }
var losses: Int { get }

func winningPerent() -> Double
}
  • 协议中方法或者属性的默认实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
extension Recordable {
// CustomStringConvertible 协议默认实现
var description: String {
return String(format: "WINS: %d---LOSSES: %d", [wins, losses])
}

// 默认实现总场数计算型属性
var totalGames: Int {
return wins + losses
}

// 默认实现的方法
func shoutWins() {
print("come on", wins, "times!")
}
}

篮球比赛:

  • Equatable 协议 - 相等比较 需要重载 ==
  • Comparable 协议 - 是否可比较 ,重载 < 可直接调用sort()函数
  • CustomStringConvertible 协议 - 打印信息,需重写 description
  • Recordable协议 - 比赛协议,没有平局
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
struct BasketballRecord:Equatable, Comparable, CustomStringConvertible, Recordable {
var wins: Int
var losses: Int


/// 在具体定义的类型中实现 协议中的属性或者方法就会覆盖协议中的
var totalGames: Int = 200

var description: String {
return "本次比赛胜: " + String(wins) + "负" + "\(losses)"
}

// 胜率
func winningPerent() -> Double {
return (Double(wins) / Double(totalGames))
}

// swift4.1 后,不需要再重写此方法,swift 会自动合成,如果不想让某一个属性参与比较,就需要重写该方法
static func == (lhs: BasketballRecord, rhs: BasketballRecord) -> Bool {
return lhs.wins == rhs.wins && lhs.losses == rhs.losses
}

// Comparable
static func < (lhs: BasketballRecord, rhs: BasketballRecord) -> Bool {
if lhs.wins != rhs.wins {
return lhs.wins < rhs.wins
}
return lhs.losses > rhs.losses
}
}
```

#### 足球比赛:

有平局的情况,原来的协议不能满足,需要增加一个平局的协议

- 定义平局协议

protocol Tieable {
/// 平局,可读写
var ties: Int { get set }

}

1
2
3
4

- 足球比赛需要遵守的协议 `Recordable` 和 `Tieable`

- 总场数需要加上平局的场数,需要实现totalGames的get方法

struct FootableRecord: Recordable, Tieable {
var wins: Int
var losses: Int
// 平局数
var ties: Int

// 总场数var totalGames: Int {    return wins + losses + ties}var description: String {    return "本次比赛胜: " + String(wins) + "负" + "\(losses)"}func winningPerent() -> Double {    return (Double(wins) / Double(totalGames))}

}

1
2
3
4
5
6
7
8
9
10
11
12

- 足球比赛中以上写法存在的问题

- 所有有平局的比赛计算总场次的逻辑都是一样,每次都要写一次实现
- 单一修改 Recordable 协议不能解决问题,
- 存在平局和没有平局两种情况 totalGames 的计算逻辑不相同

- 解决办法:
- 扩展遵守了`Recordable`协议的类型,前提条件是:这个类型遵守了 Tieable 同时也遵守了 `Recordable ` 协议,可以理解为如果该类型遵守了 `Tieable `协议之后,`Recordable` 协议中的 totalGames 属性实现是另一种方式了,不在是以前的 `totalGames` 中直接返回 `wins + losses` 的值


定义协议

protocol Recordable: CustomStringConvertible {
var wins: Int { get }
var losses: Int { get }

func winningPerent() -> Double

}

1
2

协议默认实现

extension Recordable {
var description: String {
return String(format: “WINS: %d—LOSSES: %d”, [wins, losses])
}

// 默认实现总场数计算型属性var totalGames: Int {    return wins + losses}// 默认实现胜率func winningPerent() -> Double {    return (Double(wins) / Double(totalGames))}// 默认实现的方法func shoutWins() {    print("come on", wins, "times!")}

}

1
2

扩展遵守了Tieable 的 类型的 Recordable 协议

extension Recordable where Self: Tieable {
var totalGames: Int {
return wins + losses + ties
}
}

1
2
3
4
5
6

### 协议聚合

如果对某一类型需要限制他必须遵守某些协议,可以使用协议聚合来定义

比如: 有个奖赏协议

protocol Prizable {
func isPrizable() -> Bool
}

1
2

篮球比赛遵守此协议

// 篮球比赛奖赏
extension BasketballRecord: Prizable {
func isPrizable() -> Bool {
return totalGames > 10 && winningPerent() > 0.5
}
}

1
2

足球比赛奖赏遵守此协议

extension FootableRecord: Prizable {
func isPrizable() -> Bool {
return wins > 1
}
}

1
2

现在有某一个学生也遵守了此协议

struct Student: Prizable {
var name: String
var score: Int

func isPrizable() -> Bool {    return score >= 60}

}

1
2

定义奖赏的方法,参数类型必须是遵守了此协议的结构或者类型

private func award(_ one: Prizable) {
if one.isPrizable() {
print(one)
print(“恭喜获得奖励”)
} else {
print(one)
print(“很遗憾”)
}
}

1
2

如果说 BasketballRecord 这个类还遵守了其他的协议,例如遵守了 `Recordable` 协议, 并且这个协议也遵守了 `CustomStringConvertible` 并且默认实现了`description` 的`get` 方法

// MARK: - 比赛协议,具有 赢。输。总场次。胜率 特性
protocol Recordable: CustomStringConvertible {
var wins: Int { get }
var losses: Int { get }

func winningPerent() -> Double

}

1
2

`Recordable` 默认实现

extension Recordable {
var description: String {
return String(format: “WINS: %d—LOSSES: %d”, [wins, losses])
}

// 默认实现总场数计算型属性var totalGames: Int {    return wins + losses}// 默认实现胜率func winningPerent() -> Double {    return (Double(wins) / Double(totalGames))}// 默认实现的方法func shoutWins() {    print("come on", wins, "times!")}

}

1
2
3
4
5

此时如果 `Student` 类还是调用 `award` 方法的话,print(one) 打印的信息将是`Recordable `中默认实现的内容,因此需要约束`award`函数的参数必须遵守两个协议让`Student` 也重写自己的`description `属性的`get`方法,不能再让 `Prizable` 扩展 默认实现

- swift 3 写法: protocol<A, B>
- swift 4 写法: A & B

private func award2(_ one: Prizable & CustomStringConvertible) {
if one.isPrizable() {
print(one)
print(“恭喜获得奖励”)
} else {
print(one)
print(“很遗憾”)
}
}

1
2
3
4
5
6
7
8
9
10

### 泛型约束


- 定义一个函数,找出一个学生数组中分数最大的
- 参数:一个学生数组,都遵守了 Comparable 的类型
- 返回值:某个遵守了 Comparable 的类型实例
- 此时函数报错 `Protocol 'Comparable' can only be used as a generic constraint because it has Self or associated type requirements`,

因为 Comparable 协议中定义的方法 public static func < (lhs: Self, rhs: Self) -> Bool 的参数类型是Self,是具体的某个类型

func maxScore(seq: [Comparable]) -> Comparable { }

1
2
3
4
5


- 如果需要定义一个函数实现在一个数组中找出需要奖励的人的名字该如何实现呢
- 参数:遵守两个协议 Comparable 和 Prizable 协议, 并且使用泛型
- 返回值:返回值是可选值,有可能没有任何奖励的对象

func topPrizable<T: Comparable & Prizable>(seq: [T]) -> T? {
return seq.reduce(nil) { (tempTop: T?, condender: T) in
guard condender.isPrizable() else { return tempTop }
// 解包 condender 失败, 上一层验证了他必须是奖励的那个
guard let tempTop = tempTop else { return condender }
return max(tempTop, condender)
}
}

`

]]>
+ + + +
+ + + + + + + /2019/06/18/Swift%E5%8D%8F%E8%AE%AE%E6%89%A9%E5%B1%95/ + + Swift协议扩展

定义结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct Point {
var x = 0.0
var y = 0.0

}

struct Size {
var height = 0.0
var width = 0.0
}

struct Rectangle {
var origin = Point()
var size = Size()

init(origin: Point, size: Size) {
self.origin = origin
self.size = size
}
}

扩展

  • 扩展方法
1
2
3
4
5
6
extension Rectangle {
mutating func translate(x: Double, y: Double) {
self.origin.x += x
self.origin.y += y
}
}
  • 只能扩展计算型的属性,不能扩展存储型属性, 存储型属性需要在定义类或者结构体时声明
1
2
3
4
5
6
7
8
9
10
11
12
13
14
extension Rectangle {
var center: Point {
get {
let center_x = origin.x + size.width / 2.0
let center_y = origin.y + size.height / 2.0
return Point(x: center_x, y: center_y)
}

set {
origin.x = newValue.x - size.width / 2.0
origin.y = newValue.y - size.height / 2.0
}
}
}
  • 扩展构造方法

    • 类中不能扩展指定构造方法,只能在结构体中扩展
    • 结构体中不能扩展便利构造方法m,只能在类中扩展
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

extension Rectangle {
init(center: Point, size: Size) {
let origin_x = center.x - size.width / 2.0
let origin_y = center.y - size.height / 2.0
self.origin = Point(x: origin_x, y: origin_y)
self.size = size
}

// 结构体中不能扩展便利构造函数 Delegating initializers in structs are not marked with 'convenience'
// convenience init(center: Point, size: Size) {
// let origin_x = center.x - size.width / 2.0
// let origin_y = center.y - size.height / 2.0
// self.origin = Point(x: origin_x, y: origin_y)
// self.size = size
// }
}
  • 扩展嵌套类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
extension Rectangle {
enum Vertex: Int {
case left_top
case left_bottom
case right_bottom
case right_top
}

// 获取某一个顶点坐标
func point(of vertex: Vertex) -> Point {
switch vertex {
case .left_top:
return origin
case .left_bottom:
return Point(x: origin.x, y: origin.y + size.height)
case .right_bottom:
return Point(x: origin.x + size.width, y: origin.y + size.height)
case .right_top:
return Point(x: origin.x + size.width, y: origin.y)
}
}
}
  • 扩展下标,根据传入的索引获取对应顶点坐标
1
2
3
4
5
6
extension Rectangle {
subscript(index: Int) -> Point? {
assert(0 <= index && index < 4, "传入值非法")
return point(of: Vertex(rawValue: index)!)
}
}

扩展系统方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
extension Int {
/// 平方
var square: Int {
return self * self
}

/// 立方
var cube: Int {
return self * self * self
}

/// 判断数组是否在某一个范围内
func inRange(clousedLeft left: Int, openRight right: Int) -> Bool {
return self >= left && self > right
}

/// 重复执行操作
func repeatitions(task: () -> ()) {
for _ in 0..<self {
task()
}
}

/// 变长函数
func stride(from: Int, to: Int, by: Int, task: () -> ()) {
// 系统变长函数
for i in Swift.stride(from: 0, to: 21, by: 3) {
print(i)
}

for _ in Swift.stride(from: from, to: to, by: by) {
task()
}
}
}

扩展后使用

1
2
3
4
5
6
7
8
9
10
11
12
print(2.square)
print(3.cube)
print(4.inRange(clousedLeft: 0, openRight: 4))
4.repeatitions {
print("extension")
}


// 使用变长函数
4.stride(from: 0, to: 8, by: 4) {
print("stride")
}
]]>
+ + + +
+ + + + + + + /2019/06/18/SwiftLint%E4%BD%BF%E7%94%A8/ + + SwiftLint 将 swift 强制检查代码风格

教程

]]>
+ + + +
+ + + + + + + /2019/06/18/Swift%E4%B8%8B%E6%A0%87%E5%92%8C%E8%BF%90%E7%AE%97%E7%AC%A6%E9%87%8D%E8%BD%BD/ + + Swift下标和运算符重载

字典数组下标

var array = [1,2,3,4,5,6]let temp = array[0]     // 通过下标访问let temp2 = array[2]    // 通过下标访问

结构体下标

  • 对于结构体不是直接使用下标访问,会直接报错
    直接使用下标获取属性值报错: Type 'Vector_3' has no subscript members

  • 可以通过实现 subscript(index: Int) -> Double?方法让结构体支持下标访问,可以理解为一个特殊的函数,需要传入参数和返回值

  • 可以增加第二种下标–根据坐标轴获取属性值
  • 重写 subscript 的 set 方法 可以使用下标修改属性值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
struct Vector_3 {
var x = 0.0
var y = 0.0
var z = 0.0

// 根据索引获取
subscript(index: Int) -> Double? {

// 重写了get方法 实现通过下标获取属性值
get {
switch index {
case 0: return x
case 1: return y
case 2: return z
default: return nil
}
}

// 重写 set 方法,实现g通过下标设置属性值,使用系统默认的newValue
set {
guard let newValue = newValue else {return}
switch index {
case 0: x = newValue
case 1: y = newValue
case 2: z = newValue
default: return
}
}
}

// 增加第二种下标--根据坐标轴获取属性值
subscript(axis: String) -> Double? {

get {
switch axis {
case "X", "x": return x
case "Y", "y": return y
case "Z", "z": return z
default: return nil
}
}

set {
guard let newValue = newValue else {return}
switch axis {
case "X", "x": x = newValue
case "Y", "y": y = newValue
case "Z", "z": z = newValue
default: return
}
}
}
}

```

### 下标访问

结构体使用下标获取值

var v = Vector_3(x: 1, y: 2, z: 3)
print(v[0], v[1], v[2], v[3], v[100]) // Optional(1.0) Optional(2.0) Optional(3.0) nil nil
print(v[“x”], v[“y”], v[“z”], v[“i”]) // Optional(1.0) Optional(2.0) Optional(3.0) nil

1
2

结构体使用下标修改值

v[0] = 101
v[1] = 202
v[2] = 303
v[3] = 400
v[100] = 51
print(v[0], v[1], v[2], v[3], v[100]) // Optional(101.0) Optional(202.0) Optional(303.0) nil nil

1
 

v[“x”] = 100
v[“y”] = 200
v[“z”] = 300
v[“i”] = 50
print(v[“x”], v[“y”], v[“z”], v[“i”]) // Optional(100.0) Optional(200.0) Optional(300.0) nil

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

### 多维下标

定义一个关于矩阵的结构体

struct Matrix {
var data: [[Double]]
let row: Int
let column: Int

init(row: Int, column: Int) {
self.row = row
self.column = column
data = [[Double]]()
for _ in 0..<row {
let aRow = Array(repeating: 1.0, count: column)
data.append(aRow)
}
}

// 通过下标 [row,column] 方式访问
subscript(row: Int, column: Int) -> Double {
get {
assert(row >= 0 && row < self.row && column >= 0 && column < self.column, "下标不合法")
return data[row][column]
}

set {
assert(row >= 0 && row < self.row && column >= 0 && column < self.column, "下标不合法")
data[row][column] = newValue
}
}

// 通过下标 [row][column] 方式访问
subscript(row: Int) -> [Double] {
get {
assert(row >= 0 && row < self.row, "下标不合法")
// 直接返回数组,数组本身有下标
return data[row]
}

set {
assert(newValue.count == column, "下标不合法")
data[row] = newValue
}
}
}


### 运算符重载

- 重载 + 运算符

func +(one: Vector_3, other: Vector_3) -> Vector_3 {
return Vector_3(x: one[0]! + other[0]!, y: one[1]! + other[1]!, z: one[2]! + other[2]!)

// return Vector_3(x: one.x + other.x, y: one.y + other.y, z: one.z + other.z)
}

1
2

- 两个参数时相减

func -(one: Vector_3, other: Vector_3) -> Vector_3 {
return Vector_3(x: one.x - other.x, y: one.y - other.y, z: one.z - other.z)
}

1
- 一个参数时去反, 需要 prefix 修饰

prefix func -(a: Vector_3) -> Vector_3 {
return Vector_3(x: -a.x, y: -a.y, z: -a.z)
}

1
2

- 向量相乘/向量和常量相乘

func (one: Vector_3, other: Vector_3) -> Double {
return (one.x
other.x) + (one.y other.y) + (one.z other.z)
}

1
2

- 两个参数不能交换,需要重载两次 `*`

func (one: Vector_3, a: Double) -> Vector_3 {
return Vector_3(x: a
one.x, y: a one.y, z: a one.z)
}

func (a: Double, one: Vector_3) -> Vector_3 {
return one
a

// 也可采用下面写法

// return Vector_3(x: a one.x, y: a one.y, z: a * one.z)
}

1
2

- 修改自身参数,不需要返回值

func +=(one: inout Vector_3, other: Vector_3) {
// 已经重载过 + 运算符,可以直接调用
one = one + other
}

func ==(one: Vector_3, other: Vector_3) -> Bool {
return one.x == other.x &&
one.y == other.y &&
one.z == other.z
}

func !=(one: Vector_3, other: Vector_3) -> Bool {
return !(one == other)

// 也可采用下面写法return one.x != other.x ||        one.y != other.y ||        one.z != other.z

}

func <(one: Vector_3, other: Vector_3) -> Bool {
if one.x != other.x {return one.x < other.x}
if one.y != other.y {return one.y < other.y}
if one.z != other.z {return one.z < other.z}
return false
}

func <=(one: Vector_3, other: Vector_3) -> Bool {
return one < other || one == other

// 也可采用下面写法return one.x > other.x &&        one.y > other.x &&        one.z > other.z

}

func >(one: Vector_3, other: Vector_3) -> Bool {
return (one <= other)
}

1
2
3
4
5
6

### 自定义操作符

`postfix` 声明前后缀关键词, `operator ` 操作符关键词

- a+++

声明后置操作符
postfix operator +++
postfix func +++(vector: inout Vector_3) -> Vector_3 {
vector += Vector_3(x: 1.0, y: 1.0, z: 1.0)
return vector
}

1
2

- +++a

// 声明前置操作符
prefix operator +++
prefix func +++(vector: inout Vector_3) -> Vector_3 {
let temp = vector
vector += Vector_3(x: 1.0, y: 1.0, z: 1.0)
return temp
}

`

]]>
+ + + +
+ + + + + + + /2019/06/18/Swift%20%E5%8D%8F%E8%AE%AE%E4%BD%BF%E7%94%A8/ + + Swift 协议使用

对UIView 扩展

按钮/文本框抖动动画

  • 声明协议

    • 协议中定义属性:遵循该协议的类型都具有此属性

      • 必须明确规定该属性是可读的 {get} 或者可写的 {set},或是可读可写的 {get set}
      • 使用static修饰声明一个类类型属性
    • 协议中定义方法

1
2
3
4
5
6
7
8
9
10
11
12
protocol Shakable {}
extension Shakable where Self: UIView {
func shakeAnimation() {
let animation = CABasicAnimation(keyPath: "position")
animation.duration = 0.08
animation.repeatCount = 5
animation.autoreverses = true
animation.fromValue = NSValue(cgPoint: CGPoint(x: self.center.x - 4, y: self.center.y))
animation.toValue = NSValue(cgPoint: CGPoint(x: self.center.x + 4, y: self.center.y))
layer.add(animation, forKey: "position")
}
}
  • 自定义UI控件并遵守协议
1
2
3
class MyButton: UIButton, Shakable {  }
class MySwitch: UISwitch, Shakable { }
class MyTextField: UITextField, Shakable { }
  • 使用
1
2
3
4
5
6
7
8
9
10
let mySwitch = MySwitch()
mySwitch.isOn = true
self.mySwitch = mySwitch
view.addSubview(mySwitch)
mySwitch.translatesAutoresizingMaskIntoConstraints = false
mySwitch.topAnchor.constraint(equalTo: view.bottomAnchor, constant: 10).isActive = true
mySwitch.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10).isActive = true

// 调用
mySwitch.shakeAnimation()

UITableViewCell使用

定义协议

1
2
3
4
5
6
7
8
9
10
11
protocol ReusableView: class {}

extension ReusableView where Self: UIView {
static var reuseIdentifier: String {
return String(describing: self)
}
}

```

### 对tableView扩展

extension UITableView {
/// 注册cell
func register<T: UITableViewCell>(T: T.Type) where T: ReusableView {
print(T.reuseIdentifier)
self.register(T.self, forCellReuseIdentifier: T.reuseIdentifier)
}

/// 调用cellfunc dequeueReusableCell<T: UITableViewCell>(_ indexPath: IndexPath) -> T where T: ReusableView {    print(T.reuseIdentifier)    return self.dequeueReusableCell(withIdentifier: T.reuseIdentifier, for: indexPath) as! T}

}

1
2
3
4

### 用法

- 创建自定义的cell,需要遵守协议

class TableProtocolCell: UITableViewCell, ReusableView {
override var reuseIdentifier: String? {
return “cell”
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.backgroundColor = .cyan
self.textLabel?.text = “通过协议和泛型创建的cell”
self.textLabel?.textColor = .blue
}
required init?(coder aDecoder: NSCoder) {
fatalError(“init(coder:) has not been implemented”)
}
}

class TableProtocolCell2: UITableViewCell, ReusableView {
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.backgroundColor = .red
self.textLabel?.text = “cell1 - 通过协议和泛型创建的”
self.textLabel?.textAlignment = .right
}
required init?(coder aDecoder: NSCoder) {
fatalError(“init(coder:) has not been implemented”)
}
}

1
2

- 注册和使用

class TableViewProtocolController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(T: TableProtocolCell.self)
tableView.register(T: TableProtocolCell2.self)
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {    return 10}override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {    var cell = UITableViewCell()    if indexPath.row % 2 == 0 {        cell = tableView.dequeueReusableCell(indexPath) as TableProtocolCell    } else {        cell = tableView.dequeueReusableCell(indexPath) as TableProtocolCell2    }    return cell}

}

`

]]>
+ + + +
+ + + + + + + /2019/06/18/Swift%20%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/ + + Swift 学习

枚举

1. 定义 rawValue 为 Int 类型,初始值为1,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
enum Month: Int {
case January = 1
case February
case March
case April
case May
case June
case July
case August
case September
case October
case November
case December
}

2. rawValue 的整型值可以不连续

1
2
3
4
5
6
enum Season: Int {
case Spring = 1
case Summer = 5
case Autumn = 10
case Winter = 40
}

3. 枚举可以是字符串,字符串是变量名rawValue 的值相等

1
2
3
4
5
6
7
enum ProgrammerLanguae: String {
case Swift
case OC = "Objective-C"
case C = "语言"
case RN = "React-Native"
case Java
}

使用rawValue

1
2
3
func residueNewYear(month: Month) -> Int {
return 12 - month.rawValue
}

调用枚举的Moth的构造函数生成一个Month

1
2
3
4
let month = Month(rawValue: 5)
let swift = ProgrammerLanguae.Swift.rawValue
let oc = ProgrammerLanguae.OC
let RN = ProgrammerLanguae.RN.rawValue

4. 枚举关联值 associate value

1
2
3
4
5
6
7
8
9
10
11
12
enum Status {
case success(Int)
case fail(String)
case null // 未关联值
}

func associateValueTest(isSuccess: Bool) -> Status {
if isSuccess {
return .success(200)
}
return .fail("失败")
}

5. 枚举associateValue多个值

  • 本质是关联了一个元祖(value0, value1, value2…)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
enum Shape {
case Square(width: Double)
case Reactangle(width: Double, height: Double)
case Circle(x: Double, y: Double, radius: Double)
case Point
}

let square = Shape.Square(width: 20)
let reactangle = Shape.Reactangle(width: 10, height: 20)
let circle = Shape.Circle(x: 10, y: 10, radius: 20)
let point = Shape.Point

private func area(shape: Shape) -> Double {
switch shape {
case let .Square(width):
return width * width
case let .Reactangle(width, height):
return width * height
case let .Circle(_, _, radius):
return radius * radius * Double.pi
case .Point:
return 0
}
}

6. 递归枚举

  • 定义一个递归算术表达式
  • 使用 indirect 来修饰
1
2
3
4
5
6
7
8
indirect enum ArithmeticalExpression {
case Number(Int)
case Addition(ArithmeticalExpression, ArithmeticalExpression) // + 时两边也是一个表达式
case Multiplication(ArithmeticalExpression, ArithmeticalExpression) // * 时两边也是一个表达式

// indirect case Addition(ArithmeticalExpression, ArithmeticalExpression) // + 时两边也是一个表达式
// indirect case Multiplication(ArithmeticalExpression, ArithmeticalExpression) // * 时两边也是一个表达式
}
  • 递归表达式使用 (2+3) * 4
1
2
3
4
let two = ArithmeticalExpression.Number(2)
let one = ArithmeticalExpression.Number(3)
let sum = ArithmeticalExpression.Addition(two, one)
let indirectEnumResult = ArithmeticalExpression.Multiplication(sum, ArithmeticalExpression.Number(4))
  • 计算表达式值的函数
1
2
3
4
5
6
7
8
9
10
private func calculate(expression: ArithmeticalExpression) -> Int {
switch expression {
case let .Number(value):
return value
case let .Addition(left, right):
return calculate(expression: left) + calculate(expression: right)
case let .Multiplication(left, right):
return calculate(expression: left) * calculate(expression: right)
}
}

结构体

  • 结构体中的属性值没有初始化时必须使用构造函数来初始化,否则报错
1
2
3
4
5
6
7
struct Location {
var latitude: Double
var longitude: Double
var placeName: String?
}

let location1 = Location(latitude: 37.3230, longitude: -122.0322, placeName: "测试")
  • 结构体中属性如果都赋了初始值就可以直接初始化
1
2
3
4
5
6
struct Location2 {
var latitude: Double = 0
var longitude: Double = 0
}

let location2 = Location2()
  • 如果未指定初始值,swift 就不会默认初始化为(不初始化)
  • 当属性值为可选值时此时允许值为nil,那么就允许不通过构造函数来初始化赋值
  • let 只有一次赋值机会
  • 不管是类还是结构体都应该提供一个全参数的构造函数 init(latitude: Double, longitude: Double, placeName: String?)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct Location3 {
let latitude: Double
let longtitude: Double
var placeName: String?

init(coordinateString: String) {
let index = coordinateString.index(of: ",")
let index1 = coordinateString.index(after: index!)
let test_1 = coordinateString.prefix(upTo: index!)
let test_2 = coordinateString.suffix(from: index1)

latitude = Double(test_1)!
longtitude = Double(test_2)!
}

init(latitude: Double, longitude: Double, placeName: String?) {
self.latitude = latitude
self.longtitude = longitude
self.placeName = placeName
}
}

let test3 = Location3(coordinateString: "12,45")

属性和方法

计算型属性

类型属性

类型方法

属性管擦器

延迟属性

访问控制

单利模式

继承和构造函数

swift 中继承

多态性

属性和函数重载

子类两段式构造

  • 子类构造函数分为两段,第一段是构造自己,第二段是构造父类

    • 父类中构造函数
    • 子类要设置一个自己的构造函数
    • 子类构造函数中需要先初始化自己的属性
    • 然后在构造函数中调用父类初始化父类构造函数中的相关属性

      父类

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      class Father {
      var name: String
      var sex: Int = 0
      var old: Int = 32
      var desc: String {
      return "我是\(name)"
      }
      init(name: String) {
      self.name = name
      }

      func run() {
      print("Father can Running")
      }
      }
子类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Son: Father {

var isSwimming = true

var computer: String

override var desc: String {
return "Son is \(name)"
}

init(name: String, computer: String) {
self.computer = computer
super.init(name: name)
}
}
  • 子类两段构造都完成后子类的初始化才完成,可以使用self调用属性或者方法

    1
    2
    3
    4
    5
    init(name: String, computer: String) {
    self.computer = computer
    self.getToys() // 此代码报错,子类初始化未完成
    super.init(name: name)
    }

便利构造函数和指定构造函数

  • 便利构造函数只能调用自己的指定构造函数
  • 指定构造函数通过一系列的调用都会调用super.init()
  • 便利构造函数无法调用super.init()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Father {
var name: String
var sex: Int = 0
var old: Int = 32
var desc: String {
return "我是\(name)"
}
init(name: String) {
self.name = name
}

init(name: String, old: Int) {
self.name = name
self.old = old
}

func run() {
print("Father can Running")
}
}

class Son: Father {
var isSwimming = true
var computer: String

override var desc: String {
return "Son is \(name)"
}

// 子类指定构造函数,调用了父类的指定构造函数
init(name: String, computer: String) {
self.computer = computer
super.init(name: name)
}

// 子类便利构造函数,调用了指定构造函数
convenience override init(name: String) {
let computer = "iMac"
self.init(name: name, computer: computer)
}
}

构造函数继承

  • 子类有可能会继承父类的构造函数
  • 子类没有实现父类的任何指定构造函数;则自动继承父类的所有指定构造函数, 因为继承了指定构造函数所以同时便利构造函数也被继承

父类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Father {
var name: String
var sex: Int = 0
var old: Int = 32
var desc: String {
return "我是\(name)"
}

/// Father指定构造函数-1
init(name: String) {
self.name = name
}

/// Father指定构造函数-2
init(name: String, old: Int) {
self.name = name
self.old = old
}

/// Father便利构造函数
convenience init(old: Int) {
self.init(name: "Father", old: old)
}

func run() {
print("Father can Running")
}
}

子类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Son: Father {
var isSwimming = true
var computer: String
var job: String

override var desc: String {
return "Son is \(name)"
}

/// 子类重载的指定构造函数-1
convenience override init(name: String) {
self.init(name: name, computer: "Dell")
}

/// 子类重载的指定构造函数-2
override convenience init(name: String, old: Int) {
self.init(name: name, computer: "acer")
}


/// 子类自己的指定构造函数,调用了父类的指定构造函数
init(name: String, computer: String) {
self.computer = computer
self.job = "C#"
super.init(name: name)
}

// 子类便利构造函数,调用了自己指定构造函数
convenience init(computer: String) {
let name = "小张"
self.init(name: name, computer: computer)
}
}

未实现父类任何的指定构造函数

1
2
3
class Grandson: Son {
var toy: String = "dog toys"
}

子类可以调用的构造函数

1
2
3
4
5
let grandSon0 = Grandson(old: 4)
let grandSon1 = Grandson(computer: "Mi")
let grandSon2 = Grandson(name: "小王")
let grandSon3 = Grandson(name: "小虎", computer: "👽")
let grandSon4 = Grandson(name: "小李", old: 8)
  • 子类实现了父类所有的指定构造函数,则自动继承父类的所有便利构造函数
1
2
3
4
5
let son0 = Son(old: 30)
let son1 = Son(computer: "Mi")
let son2 = Son(name: "小王")
let son3 = Son(name: "小虎", computer: "👽")
let son4 = Son(name: "小李", old: 8)

required 构造函数

  • 父类中有被 required 关键词修饰的构造函数子类必须实现此构造函数
  • 子类实现的此构造函数不需要再使用 override 关键词修饰,需要 required 修饰

子类

1
2
3
4
5
6
7
8
class Father {
var name: String
/// Father指定构造函数-1
// required 修饰的指定构造函数子类中必须实现
required init(name: String) {
self.name = name
}
}

父类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Son: Father {
var isSwimming = true
var computer: String

/// 子类重载的指定构造函数-1
convenience required init(name: String) {
self.init(name: name, computer: "Dell")
}

/// 子类自己的指定构造函数,调用了父类的指定构造函数
init(name: String, computer: String) {
self.computer = computer
self.job = "C#"
super.init(name: name)
}
}
  • 子类如果实现了自己的指定构造函数,那么 required 修饰指定构造函数就初始化失败,因为自己实现了指定构造函数所以不能继承父类中的构造函数,此时可以自己实现被required 修饰的便利构造函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Son1: Son {
var sport: String

// 指定构造函数,需要调用父类的指定构造函数
init(name: String, sport: String) {
self.sport = sport
Son.init(name: name)
}

// 父类中有被 required 关键词修饰的必须实现的构造函数
convenience required init(name: String) {
// fatalError("init(name:) has not been implemented")
// 调用自己的指定构造函数,
self.init(name: name, sport: "")
}
}
]]>
+ + + +
+ + + + + + + /2019/06/18/RN%E6%8A%A5%E9%94%99/ + + RN学习中遇到的错误总结

执行 npm install 时报错

错误1. Unexpected end of JSON input while parsing near '...er":"0.4.0"},"bin":{"'

本地有package.json文件,执行 npm install 报错,或者clone 的项目执行npm install 也报同样的错误, 可能是因为缓存导致,

解决办法:

  1. 设置镜像源,以前可能大部分都是用的淘宝

    1
    npm set registry https://registry.npmjs.org/
  2. 清除缓存

    1
    npm cache clean –-force
  3. 如果提示失败,使用sudo

    1
    sudo npm cache clean --force

错误2. xcrun: error: unable to find utility "simctl", not a developer tool or in PATH

可能是因为 Xcode 版本问题引起,XCode 偏好设置中 Command line Tools 中为选择版本问题导致(我的情况是安装了Xcode 10 beta版导致的)

解决办法:

1
2
3
4
5
6
7
8
9
10
11
12
The following build commands failed:
CompileC /Users/liepin/LiePinWorkspace/RNDemo/GitHubPopular/ios/build/Build/Intermediates.noindex/RCTWebSocket.build/Debug-iphonesimulator/RCTWebSocket.build/Objects-normal/x86_64/RCTSRWebSocket.o RCTSRWebSocket.m normal x86_64 objective-c com.apple.compilers.llvm.clang.1_0.compiler
(1 failure)
Installing build/Build/Products/Debug-iphonesimulator/GitHubPopular.app
An error was encountered processing the command (domain=NSPOSIXErrorDomain, code=2):
Failed to install the requested application
An application bundle was not found at the provided path.
Provide a valid path to the desired application bundle.
Print: Entry, ":CFBundleIdentifier", Does Not Exist

Command failed: /usr/libexec/PlistBuddy -c Print:CFBundleIdentifier build/Build/Products/Debug-iphonesimulator/GitHubPopular.app/Info.plist
Print: Entry, ":CFBundleIdentifier", Does Not Exist

错误3. Error: Cannot find module '../lib/utils/unsupported.js'

安装的node 版本不是稳定的版本,需要删除后重新安装

1
2
sudo rm -rf /usr/local/lib/node_modules/npm
brew reinstall node

错误4. Couldn't find preset "module:metro-react-native-babel-preset" when running jest

解决方法

已有项目集成RN

报错1. 文件路径错误,查看你的node_modules 文件夹是在当前目录还是上级目录

[!] No podspec found for `React` in `../node_modules/react-native
  • ../ 是指父级目录

  • ./ 是指当前目录

报错 2. yoga Y 大小写为问题

[!] The name of the given podspec `yoga` doesn't match the expected one `Yoga`_modules/react-native/ReactCommon/yoga"

pod install 之后

报错 1.

解决办法: pod ‘React’, :subspecs 中加入 ‘CxxBridge’, ‘DevSupport’

pod 'React', :path => './node_modules/react-native', :subspecs => ['Core','RCTText','RCTNetwork','RCTWebSocket', # 这个模块是用于调试功能的'CxxBridge', # Include this for RN >= 0.47'DevSupport', # Include this to enable In-App Devmenu if RN >= 0.43

]

报错 2.

RCTAnimation/RCTValueAnimatedNode.h' file not found

解决方法:

1
#import <RCTAnimation/RCTValueAnimatedNode.h>

替换为

1
#import "RCTValueAnimatedNode.h"

修改头文件导入方式

报错3 版本不匹配 react native version mismatch javascript version 0.54.3 native 0.57.2

  • 关闭所有的 terminal,或者是集成开发环境的命令行窗口

    1
    2
    watchman watch-del-all
    react-native start --reset-cache
  • 重新打开一个terminal窗口

    1
    react-native run-ios

    问题描述和解决1

    问题描述和解决2

报错4 Unable to resolve module "schedule/tracking"

缺少一些开发依赖的库

npm i schedule@0.4.0 --save-dev

问题描述和解决

]]>
+ + + +
+ + + + + + + /2019/06/18/React-Native%E5%BC%80%E5%8F%91iOS%E6%89%93%E5%8C%85/ + + React-Native开发iOS打包

打jsbudnle包

创建打包后存放文件目录

  • cd 到 React-Native 项目根目录下 并且 进入 ios 文件目录下
  • 创建 bundle 文件夹

    1
    mkdir bundle

配置 React-Native 打包命令

  • 打包命令

    1
    react-native bundle
  • 在React Native项目的根目录下执行命令

    1
    react-native bundle --entry-file index.js --platform ios --dev false --bundle-output ./ios/bundle/index.jsbundle --assets-dest ./ios/bundle
- `--entry-file`: ios或者android入口的js名称,比如 `index.js`- `--platform`: 平台名称(ios或者android)- `--dev`: 设置为`false`的时候将会对`JavaScript`代码进行优化处理。- `--bundle-output`: 生成的jsbundle文件的名称和路径,比如 `./ios/bundle/index.jsbundle`- `--assets-dest`: 图片以及其他资源存放的目录

使用打包命令

  • 将打包命令添加到 packger.json

    1
    2
    3
    4
    5
    "scripts": {
    "start": "node node_modules/react-native/local-cli/cli.js start",
    "test": "jest",
    "bundle-ios": "node node_modules/react-native/local-cli/cli.js bundle --entry-file index.js --platform ios --dev false --bundle-output ./ios/bundle/index.jsbundle --assets-dest ./ios/bundle"
    },
  • 再次打包只需要 React Native项目的根目录下执行命令,不用再次输入类似上面的命令

    1
    npm run bundle-ios

集成到Xcode(iOS原生项目中)

  • 添加已经打好的.jsbundle 离线包

  • iOS 项目代码配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    private func loadReactNativeTest() {
    // debug 测试加载本地文件
    let url = URL(string: "http://localhost:8081/index.bundle?platform=ios")!

    // 加载已经加入的离线包
    let url2 = Bundle.main.url(forResource: "index", withExtension: "jsbundle")

    let rootView = RCTRootView(
    bundleURL: url,
    moduleName: "MyApp", //这里的名字要和index.js中相同
    initialProperties: nil,
    launchOptions: nil
    )
    view = rootView
    }
]]>
+ + + +
+ + + + + + + /2019/06/18/ReactNative%E4%B8%ADref%E4%BD%BF%E7%94%A8/ + + Reat Native ref 使用

State 状态的使用

在父控件中指定,整个生命周期讲不再改变
需求:一段闪烁的文字, Props 属性显示文字内容创建时初始化,State控制随时间变化是否显示,在父控件中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Blink extends Component{
// 构造
constructor(props) {
super(props);
// 初始状态
this.state = {showWithText:true};
this.state = {}
setInterval(() => {
this.setState(previousState => {
return {showWithText: !previousState.showWithText}
});
}, 100);
}
render() {
let display= this.state.showWithText ? this.props.text : ' ';
return(
<Text>{display}</Text>
);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
export default class App extends Component<Props> {
render() {
return(
<View style={styles.container}>
<Blink
text='I love to blink'
/>

<Blink
text='猜猜猜'
/>

<Blink
text='Blink is great'
/>
</View>
);
}
}

Props 属性的使用

1
2
3
4
5
6
7
8
9
// 定义一个组件
class Greeting extends Component {
render() {
return(
<Text> Hello {this.props.name}!
</Text>
);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
export default class App extends Component<Props> {
render() {
return(
<View style={styles.container}>
<Image source={{uri:'https://upload.wikimedia.org/wikipedia/commons/d/de/Bananavarieties.jpg'}}
style={{width: 190, height: 110}}
/>
<Text style={styles.instructions}>
使用Props属性
</Text>
<View>
<Greeting name='React-Native'
/>
<Greeting name='iOS'
/>
<Greeting name='Swift'
/>
</View>

</View>
);
}
}

ref的使用

  • 可以理解为组件渲染后指向组件的一个应用,可以通过ref获取到真实的组件(类似指针?)

定义

1
ref='scrollView'

获取

1
2
let scrollView = this.refs.scrollView
let scrollView = this.refs['scrollView']
  • 可以通过ref访问组件的属性和方法

ES6 定时器

  • 开启定时器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let scrollView = this.refs.scrollView;
// 2.4timer 定时器的写法
this.timer = setInterval(
()=>{
var tempPage = 0;
// 修改banner索引
if ((this.state.currentPage+1) >= ImageData.data.length) {
tempPage = 0;
} else {
tempPage = this.state.currentPage+1;
}

// 更新状态
this.setState({
currentPage: tempPage
})

let offSet_x = width * tempPage
scrollView.scrollResponderScrollTo({x:offSet_x, y:0, animated: true})
},
this.state.duration
);
  • 暂停定时器
1
this.timer && clearInterval(this.timer)
]]>
+ + + +
+ + + + + + + /2019/06/18/JavaScript%E5%9F%BA%E7%A1%80/ + + js 学习

数据类型

1
2
3
4
5
6
7
8
var fullName;
// undefined

fullName
// undefined

fullName + 2
// NaN
  • 判断类型名称

    1
    2
    3
    4
    fullName = '张'
    "张"
    typeof(fullName)
    // "string"
1
2
3
4
var weight = 100;
undefined
typeof(weight)
// "number"
1
2
3
4
var firstName = '王', lastName = '张'
undefined
firstName + lastName
// "王张"
  • 类型不相同时转换后在拼接

    1
    2
    3
    4
    var weightIncrease = '2.5斤'
    undefined
    weight + weightIncrease
    // "1002.5斤"

数组

var array = [];undefinedarray.length// 0typeof(array)// "object"
  • 增加元素

    1
    2
    3
    4
    array.push('1')
    // 4
    array
    // (4) ["ab", "cbd", "fcg", "1"]
  • 数组前面添加

1
2
3
4
array.unshift('0')
// 6
array
// (6) ["0", "ab", "cbd", "fcg", "1", Array(2)]
  • 删除最后元素

    1
    2
    3
    4
    array.pop() // 返回删除的元素
    // (2) ["2", "3"]
    array
    // (5) ["0", "ab", "cbd", "fcg", "1"]
  • 删除第一个元素

    1
    2
    3
    4
    5
    6
    array.shift()
    // "0"
    array
    // (4) ["ab", "cbd", "fcg", "1"]
    ```
    - 删除指定的值,但数组元素没有被删

    delete array[2]
    // true
    array
    // (4) [“ab”, “cbd”, empty, “1”]

    1
    2

    - 删除指定的元素

    array.splice(1)
    // (3) [“cbd”, empty, “1”]
    array
    // [“ab”]

    1
    2
    3
    4

    ## 函数

    ### 调用调用

alertMessage();

function alertMessageWithParamter(message) {
alert(message)
}

alertMessageWithParamter(‘有参数的函数’)
alertMessageWithParamter(250)
alertMessageWithParamter(
console.log(‘test’)
)

1
### 函数表达式,使用函数声明的方式

var alertMessage_expression = function expression_alert (message) {
alert(message)
}

alertMessage_expression(‘匿名函数调用’)

expression_alert(‘函数表达式’)
`

]]>
+ + + +
+ + + + + + + /2019/06/18/MasonrySnapkit%E4%BD%BF%E7%94%A8%E6%80%BB%E7%BB%93/ + + Masonry使用

基本使用官网Demo非常详细,以下总结的是常见问题和技巧

安装遇到的问题

  • pod 安装后出现⚠️,项目运行报错

    1
    [UIView mas_makeConstraints:]: unrecognized selector sent to instance

    github

    解决方法

  • 使用lastBaseline属性crash

    解决办法

使用时注意点

  • edgesForExtendedLayout 方法

    重写此方法的目的是为了确定布局位置从什么位置开始什么位置结束,

    1
    2
    3
    - (UIRectEdge)edgesForExtendedLayout {
    return UIRectEdgeNone;
    }
**重写后**![](https://ws1.sinaimg.cn/large/006tNc79ly1fsgcn3fmo0j30s61iyalk.jpg)**重写前**![](https://ws4.sinaimg.cn/large/006tNc79ly1fsgcokvpdwj30s61iy7eq.jpg)
  • translucent 设置UINavigationBar是否半透明, 如果设置了导航栏的styleUIBarStyleBlackTranslucent 就为true

    设置为false不透明效果

  • extendedLayoutIncludesOpaqueBars 导航栏不透明条件下是否可以扩展,默认是NO不可以扩展

    1
    2
    self.navigationController?.navigationBar.isTranslucent = false
    self.extendedLayoutIncludesOpaqueBars = true
**设置不透明,设置可以扩展此时从坐标的(0,0)点开始布局**![](https://ws2.sinaimg.cn/large/006tNc79ly1fsggem13mjj30ow0j83zy.jpg)    
  • inset 的使用,当约束控件的属性相同时不用再考虑正负值的问题,例如要约束一个控件左边距离另一控件的右边位置时还需要使用负值情况

    1
    2
    3
    4
    5
    6
    7
    [yellowdView makeConstraints:^(MASConstraintMaker *make) {
    make.height.equalTo(@150);
    make.top.equalTo(self).inset(220);
    make.leading.equalTo(self).inset(10);
    make.trailing.equalTo(greenView.leading).inset(10);
    make.width.equalTo(greenView);
    }];
  • edges的使用,约束边距更方便

    1
    2
    3
    [edgeView remakeConstraints:^(MASConstraintMaker *make) {
    make.edges.equalTo(lasView).insets(UIEdgeInsetsMake(10, 5, 5, 5));
    }];
  • Priority 设置约束的优先级

    • priorityHigh
    • priorityMedium
    • priorityLow
  • makeConstraintsupdateConstraintsremakeConstraints三者区别

    • makeConstraints 添加约束
    • updateConstraints 更新约束,更新之前会查找一边控件已经存在的约束,没有就添加,有就更新到最新的约束
    • remakeConstraints删除控件以前的所有约束重新添加约束
  • 设置控件或者某一约束的’key’ 方便冲突时⚠️查找

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    greenView.mas_key = @"greenView";
    [greenView makeConstraints:^(MASConstraintMaker *make) {
    make.height.equalTo(yellowdView).key(@"greenView-height");
    make.top.equalTo(yellowdView).key(@"greenView-top");
    make.width.equalTo(yellowdView).key(@"greenView-width");
    make.trailing.equalTo(self).inset(10).key(@"greenView-trailing");
    // 冲突约束
    // make.leading.equalTo(greenView.trailing).inset(10).key(@"greenView-leading");
    }];
    ```

    - 使用 `MASAttachKeys`批量给`view`设置key

    MASAttachKeys(greenView, yellowdView);

    1
    2
    3
    4

    ![](https://ws3.sinaimg.cn/large/006tNc79ly1fsgalvd4l6j31a20modnp.jpg)

    - `dividedBy`和 `multipliedBy` 约束宽高比

    [topInnerView makeConstraints:^(MASConstraintMaker *make) {

    // 设置自己的宽高比是3:1make.width.equalTo( topInnerView.height).multipliedBy(3); // 乘因数// 设置宽高并且设置他们的优先级最低make.width.height.lessThanOrEqualTo(topView);make.width.height.equalTo(topView).priorityLow();// 设置位置make.top.equalTo(topView);

    }];

    1
    - 多个控件批量约束

    NSValue sizeValue = [NSValue valueWithCGSize:CGSizeMake(100, 100)];
    [@[blueView, redView, yellowView] makeConstraints:^(MASConstraintMaker
    make) {

    make.size.equalTo(sizeValue);

    }];

    [@[blueView, redView, yellowView] mas_makeConstraints:^(MASConstraintMaker *make) {

    make.top.equalTo(self).inset(20);

    }];

    [blueView makeConstraints:^(MASConstraintMaker *make) {

    make.left.equalTo(self).inset(20);

    }];

    [redView makeConstraints:^(MASConstraintMaker *make) {

    make.left.equalTo(blueView.right).inset(10);

    }];

    [yellowView makeConstraints:^(MASConstraintMaker *make) {

    make.left.equalTo(redView.right).inset(10);

    }];

    1
    2
    3
    4

    ![批量约束](https://ws1.sinaimg.cn/large/006tNc79ly1fsgaz670hvj30s61iyn2l.jpg)

    **以上代码也可以简化为**

    [redView makeConstraints:^(MASConstraintMaker *make) {

    make.left.equalTo(blueView.right).inset(10);

    }];

    [yellowView makeConstraints:^(MASConstraintMaker *make) {

    make.left.equalTo(redView.right).inset(10);

    }];

[blueView makeConstraints:^(MASConstraintMaker *make) {    make.top.left.equalTo(self);    make.size.equalTo(@[redView, yellowView, sizeValue]);}];
1
2
3
4
5
![](https://ws2.sinaimg.cn/large/006tNc79ly1fsgbhz8lw5j30s61iyte5.jpg)

## Snapkit 使用

- Swift 中`edgesForExtendedLayout:UIRectEdge` 扩展布局的边缘是一个属性,默认是`All`
self.edgesForExtendedLayout = []
1
2
3
4

在Swift中闭包参数可以使用$0、$1、$2 一次代表第一个,第二个,第三个参数

- 使用$0 来代替闭包参数
nullDataImageView.snp.makeConstraints { $0.top.equalTo(view).offset(44) $0.centerX.equalTo(view) $0.width.height.equalTo(200)}
1
2
3
4
5
6
7

- `deactivate` 和 `activate` 使用

- `activate` 激活指定的约束
- `deactivate` 移除指定的约束

添加约束
nullDataImageView.snp.makeConstraints { nullDataViewTopConstraint = $0.top.equalTo(view).offset(80).constraint nullDataViewLeftConstraint = $0.left.equalTo(view).inset(10).constraint $0.width.height.equalTo(200)}
1
2

移除对应约束,设置约束再激活约束
nullDataViewLeftConstraint?.deactivate()nullDataViewLeftConstraint?.update(inset: 100)nullDataViewLeftConstraint?.activate()UIView.animate(withDuration: 1.5) { self.view.layoutIfNeeded()}
1
2
3
4

- 更新约束

- 添加约束
nullDataImageView.snp.makeConstraints { nullDataViewTopConstraint = $0.top.equalTo(view).offset(64).constraint $0.centerX.equalTo(view) $0.width.height.equalTo(200)}
1
2

- 更新约束
nullDataViewTopConstraint?.update(inset: 84)UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseOut, animations: { self.view.layoutIfNeeded()}) { (finished) in print("动画执行完毕")}
1
2

- 添加`debug`模式中的`key`使用`labeled()`方法
nullDataImageView.snp.makeConstraints { nullDataViewTopConstraint = $0.top.equalTo(view).offset(80).constraint nullDataViewLeftConstraint = $0.left.equalTo(view).inset(10).constraint $0.width.height.equalTo(200).labeled("width和height约束")}```
  • 其他注意点都和Masnory相同
]]>
+ + + +
+ + + + + + + /2019/06/18/ES6%E8%AF%AD%E6%B3%95/ + + JS ES6 语法

1. let var 变量,作用域

var a = 10;if (a > 10) {    let b = 5; // let 只能在代码块内被访问    console.log(b);}// 访问 b 报错console.log(b);console.log(a);

2. const 只能分配一次值

{
const const_a = 1;
console.log(const_a)

// 报错const_a = 2;// 声明一个常量数组const const_array = [];const_array.push(1);const_array.push(2);// 再次赋值时会报错const_array = [3];

}

3. 解构语法

{
// 1.解构数组
function getArray() {
return [10, 20, 30];
}

let [a,b,c] = getArray()console.log(a, b, c)// 函数返回一个对象function getObj() {    return {dessert: '面包', drink: '水', play: 'Game'};}// 2.解构对象let {des: dessert,  dri: drink, pla: play} = getObj()console.log(des, dri, pla);

}

4. 模版字符串

{
let name = ‘姓名:’, password = ‘密码:’
let str_4 = name + ‘李老师’ + ‘—-‘ + password + ‘北京太热。。。’
console.log(str_4)

// 使用 `${}` 包裹会生成字符串let str_41 = `${name}李老师----${password}北京太热`console.log(str_41)// 多行显示时直接回车换行let str_42 = `${name}李老师----${password}北京太热`console.log(str_42)

}

5. 带标签的模版字符串

{
let name = ‘姓名:’, password = ‘密码:’
// 多行显示时直接回车换行
let str_5 = strTest${name}李老师 ---- ${password}北京太热
console.log(str_5)

// strings 字符中包含的被插值分割的子字符串已数组形式展现// values 字符串中的插值数组function strTest(strings, ...values) {    console.log(strings)    console.log(values)}function strTest1(strings, ...values) {    let result = '';    for (let i = 0; i < values.length; i++) {        result += strings[i]        result += values[i]    }    return result}

}

6. 判断字符串是否包含其他字符

{
let name = ‘姓名:’, password = ‘密码:’
let str_6 = ${name}李老师---${password}北京真热
console.log(str_6.startsWith(${name}))
console.log(str_6.endsWith(${name}))
console.log(str_6.includes(‘李老师’))
}

7. 函数默认参数

{
function funcDefaultParame(param1 = ‘a’, param2 = ‘b’, param3 = ‘c’) {
return ${param1}--${param2}--${param3}
}
console.log(funcDefaultParame(1, 2,3))
}

8. “…”展开操作符

{
let a = [1,2,3];
console.log(a);
console.log(…a)

let b = [...a, 4, 5];console.log(a);console.log(b)

}

9. ‘… ‘剩余操作符, 一般用在函数参数, 可以看作是一个可以展开的参数

//  ... param3表示函数可以接收除了param1, param2,两个参数外,多个参数,都会放在param3中function operator(param1, param2, ...param3) {    console.log(param1, param2, param3)}operator('a','b','c','d','e','f','g')

10. 解构对象解构函数

// 当函数参数是对象是可以按照对象解构function getObjcValue(param1, param2, {value1, value2} = {}) {    console.log(param1, param2, value1, value2)}getObjcValue(1,2,{value1: -3,value2: -4})

11. 获取函数名称

function getObjcValue(param1, param2, {value1, value2} = {}) {    console.log(param1, param2, value1, value2)}console.log(getObjcValue.name)let funName = function getfunctionName() {  }console.log(funName.name)

12. 箭头函数

// 原来普通写法var funType = function arrowfunction(param) {    return param}// 函数参数 => 函数返回值// '=>' 的左边表示箭头函数的参数,多个时表示为 (param1, param2, param3)// '=>' 的右边表示箭头函数的返回值,如果返回值是一个对象时用 { } 表示let arrowFunction = (param1, param2) => {    return `${param1}${param2}`

}

13

]]>
+ + + +
+ + + + + + + /2019/06/18/Flutter%E5%B8%B8%E7%94%A8%E7%BB%84%E4%BB%B6/ + + flutter 常用组件

组件

  • StatefulWidget
    • 具有可变状态的窗口部件,使用应用时可随时变化,例如进度条
  • StatelessWidget
    • 不可变窗口部件

Text 组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//  Text 组件
class MyText extends StatelessWidget{
@override
Widget build(BuildContext context){
return Text('reacctNative、 flutter 学习之路是非常坚信不疑的,作为一个程序员,因该对于技术有着追求,不断的提高自己才是王道,可是道理都懂依然过不好这一生',
maxLines: 2,
overflow: TextOverflow.fade,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20.0,
color: Colors.deepOrange,
decoration: TextDecoration.underline,
decorationColor: Colors.redAccent,
decorationStyle: TextDecorationStyle.dotted
),
);
}
}

COntainer 组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

// Container容器组件
class MyContainer extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
child: Text('Container 容器中有文字',
style: TextStyle(
backgroundColor: Colors.cyan,
),
),
alignment: Alignment.center, // 容器中内容对其方式
width: 300.0,
height: 200.0,
padding: const EdgeInsets.all(10), // 容器内边距
margin: const EdgeInsets.fromLTRB(10, 5, 10, 5), // container 和外部控件的边距
decoration: new BoxDecoration(
gradient: const LinearGradient(
colors: [Colors.lightBlue, Colors.greenAccent, Colors.yellow]
),

// 设置 border 的宽度
border: Border.all(width: 5, color: Colors.black)

),
);
}
}

Image 组件

fit属性

  • BoxFit.fill:全图显示,图片会被拉伸,并充满父容器。
  • BoxFit.contain:全图显示,显示原比例,可能会有空隙。
  • BoxFit.cover:显示可能拉伸,可能裁切,充满(图片要充满整个容器,还不变形)。
  • BoxFit.fitWidth:宽度充满(横向充满),显示可能拉伸,可能裁切。
  • BoxFit.fitHeight :高度充满(竖向充满),显示可能拉伸,可能裁切。
  • BoxFit.scaleDown:效果和contain差不多,但是此属性不允许显示超过源图片大小,可小不可大。

图片的混合模式

  • color:是要混合的颜色,如果你只设置color是没有意义的。
  • colorBlendMode: 是混合模式。

repeat 图片填充模式

  • ImageRepeat.repeat : 横向和纵向都进行重复,直到铺满整个画布。
  • ImageRepeat.repeatX: 横向重复,纵向不重复。
  • ImageRepeat.repeatY:纵向重复,横向不重复
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class MyImage extends StatelessWidget {
@override
Widget build(BuildContext context){
return Container(
child: new Image.network(
// 'https://images.xiaozhuanlan.com/photo/2019/28ed76123023e46483fcf0ae5c2ba11b.png',
'https://p.ampmake.com/mall/product/9666f00e-f02a-46ce-9b7c-090bf6aba9aa.png',

// 1. 设置填充模式
// fit: BoxFit.scaleDown,
// fit: BoxFit.none,
// fit: BoxFit.fill,
// fit: BoxFit.fitHeight,
// fit: BoxFit.fitWidth,

// 保持原图比例
// fit: BoxFit.contain,

// 充满整个容器,图片被裁切
// fit: BoxFit.cover,
// scale: 1.0,

// 2. 图片混合
color: Colors.red,
colorBlendMode: BlendMode.overlay,

// 3. reap
repeat: ImageRepeat.repeat,
),
width: 300.0,
height: 200.0,
color: Colors.lightBlue,
);
}
}

ListView 列表组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class MyListView extends StatelessWidget{
@override
Widget build(BuildContext contex){

return ListView(
children: <Widget>[
new ListTile(
leading: new Icon(Icons.camera),
title: Text('相册'),
),

new ListTile(
leading: new Icon(Icons.camera_alt),
title: Text('相机'),
),

new ListTile(
leading: new Icon(Icons.camera_enhance),
title: Text('相机1'),
),

new ListTile(
leading: new Icon(Icons.camera_front),
title: Text('相机2'),
),

new ListTile(
leading: new Icon(Icons.camera_rear),
title: Text('相机3'),
),
],
);
}
}

ListView.builder 适合列表项比较多(或者无限)的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
class MyListViewBuilder extends StatelessWidget {

@override
Widget build(BuildContext context){
return ListView.builder(
itemCount: 100, // 数量
itemExtent: 40, // Tile 高度
itemBuilder: (BuildContext context, int index){
return ListTile(title: Text("$index"));
}
);
}
}

横向列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class MyHorizontalListView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
height: 100,
child: ListView(
// 设置滚动方式
scrollDirection: Axis.horizontal,
children: <Widget>[
new Container(
width: 180.0,
color: Colors.green,
),
new Container(
width: 180.0,
color: Colors.yellow,
),
new Container(
width: 180.0,
color: Colors.blue,
),
new Container(
width: 180.0,
color: Colors.cyan,
),
new Container(
width: 180.0,
color: Colors.purple,
),
],
),
);
}
}

动态列表和构造方法的创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class MyDynamicListView extends StatelessWidget {
// 声明参数
final List<String> items;
// 构造方法
MyDynamicListView({Key key, @required this.items}):super(key:key);
@override
Widget build(BuildContext context) {
return Container(
child: new ListView.builder(
itemCount: items.length,
itemBuilder: (context, index){
return ListTile(
title: new Text('通过构造方法创建的动态列表${items[index]}'),
);
},
),
);
}
}

GridView 网格列表的使用

]]>
+ + + +
+ + + + + + + /2019/06/18/GCD%20%E4%BF%A1%E5%8F%B7%E9%87%8F/ + + GCD信号量用法

对于异步网络请求相互依赖问题一般用三种方式解决:

  • 网络请求嵌套
  • 使用 BlockOperation
  • GCD DispatchGroup
  • GCD semaphore(信号量)

GCD 信号量

  • semaphore 值 <= 0 阻塞
  • semaphore semaphore.signal() 使信号量值增加
  • semaphore.wait() 等待,可设置时长

request – 3 依赖 request – 1 和 request – 2,1 和 2 无需关注顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private func gcdSemaphore() {
let semaphore = DispatchSemaphore(value: 0)
DispatchQueue.global().async {
for i in 0...300 {
print("request -- 1")
}
semaphore.signal()
}

DispatchQueue.global().async {
for i in 0...200 {
print("request -- 2")
}
semaphore.signal()
}


DispatchQueue.global().async {
semaphore.wait()
semaphore.wait()
for i in 0...160 {
print("request -- 3")
}
}
}

request – 3 ,request – 2 和 request – 1,依次相互依赖

private func gcdSemaphore() {        let semaphore = DispatchSemaphore(value: 0)        DispatchQueue.global().async {            for i in 0...300 {                print("request -- 1")            }            semaphore.signal()        }        DispatchQueue.global().async {            semaphore.wait()            for i in 0...200 {                print("request -- 2")            }            semaphore.signal()        }        DispatchQueue.global().async {            semaphore.wait()            for i in 0...160 {                print("request -- 3")            }        }    }
]]>
+ + + +
+ + + + + + + /2019/06/18/Charles%20%E6%8A%93%E5%8C%85/ + + Charles 抓包

原理

简单理解如下:

  • 正常请求

    客户端 ——> 服务器

  • Charles 抓包

    客户端 ——> 代理 (Charles)——> 服务器

安装

Charles官网

破解版本请自行百度

重要说明安装之前请暂时关闭电脑的VPN

重要说明安装之前请暂时关闭电脑的VPN

重要说明安装之前请暂时关闭电脑的VPN

启动Charles

安装Charles证书

如果安装失败,请检查以前是否已经安装过,如果已经安装过并且已经失效,请删除失效证书后再安装

打开钥匙串–> 选择Charles CA -> 双击证书 –> 信任证书

设置 https

- Proxy -> SSL Proxying Settings -> SSL Proxying -> Add- Host: * 为需要过滤的域名地址,- *: 表示不过滤Port, 固定为443 `*`表示任意端口

Mac 抓包

  • Proxy -> macOS Proxy (☑️)

真机抓包

此时可以将 macOS Proxy 选项中的 ✅ 去掉,

Mac与iPhone连接必须同一网络

设置代理端口号

手机安装证书

  • 安装手机证书

  • 安装完成后弹窗

  • 查看mac 地址,为手机设置代理

    1. 根据安装手机证书后的弹窗显示IP配置

    2. Charles –> Help –> Loca IP Address

    3. 打开电脑网络设置查看

  • 设置代理

    第一步

    第二步

    第三步

  • 根据弹窗提示,在手机浏览器下载并安装证书

  • 手机信任证书

    • 设置 –> 通用 –> 关于本机 –> 证书信任设置

模拟器抓包

  • 安装模拟器证书

  • 勾选 macOS Proxy 选项

如果失败,请先关闭模拟器,重新启动Charles 再打开模拟器

使用浏览器打开网页提示不是私密链接

解决办法

]]>
+ + + +
+ + + + + Flutter 入门.md + + /2019/06/18/Flutterr%E5%85%A5%E9%97%A8/ + + flutter安装

flutter入门

flutter 安装

flutter 项目代码

fluter 中一切皆是组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 导入头文件
import 'package:flutter/material.dart';

// 程序的入口,main 函数
void main() => (MyApp());

// 定义类, 继承

class MyApp extends StatelessWidget {
// 重写
@override

Widget build(BuildContext context) {

// 返回一窗口
return MaterialApp(
title:'Flutter',
home: Scaffold(
appBar: AppBar(
title: Text('flutter 初体验')
),

body: Center(
child: Text('flutter 学习之路'),
),
),
);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}

class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);

// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.

// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".

final String title;

@override
_MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;

void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}

@override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Invoke "debug painting" (press "p" in the console, choose the
// "Toggle Debug Paint" action from the Flutter Inspector in Android
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
// to see the wireframe for each widget.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'你可以点击按钮来修改文字,我是热重载的结果',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
]]>
+ + + + + + Flutter + + + +
+ + + + + + + /2019/06/18/docs/15608702427054/ + + #

]]>
+ + + +
+ + + + + Mac安装VirtualBox连接U盘 + + /2018/02/03/Mac%E5%AE%89%E8%A3%85VirtualBox%E8%BF%9E%E6%8E%A5U%E7%9B%98/ + + 最近因为某些原因需要再Mac上安装虚拟机,所以选择了VirtualBox安装了
win10系统,但是需要再虚拟机的win10中访问U盘数据,遇到的问题是不能连接并且读取到U盘,错误提示不能分配USB内存。。。

网上找了好多办法都胡扯,根本没有解决…

解决方法:
1.升级VirtualBox到最新版
2.关闭当前的虚拟机中的操作系统
3.安装增强工具
4.重启VirtualBox,回到Mac桌面推出U盘即可

最重要的就是在第四步

]]>
+ + + +
+ + + + + Block学习探究 + + /2018/01/19/Block%E5%AD%A6%E4%B9%A0%E6%8E%A2%E7%A9%B6/ + + 代码及结果
  • 普通局部变量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    - (void)block1 {

    NSInteger num = 100;

    // 定义一个block
    dispatch_block_t block = ^ {
    NSLog(@"block1 -- numer = %zd", num);
    };

    num = 200;

    block();
    }
  • 使用__block修饰变量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    - (void)block2 {

    __block NSInteger num = 100;

    // 定义一个block
    dispatch_block_t block = ^ {
    NSLog(@"block2 -- numer = %zd", num);
    };

    num = 200;

    block();
    }
  • 全局变量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    - (void)block3 {


    // 定义一个block
    dispatch_block_t block = ^ {
    NSLog(@"block3 -- numer = %zd", blockNum);
    };

    blockNum = 200;

    block();
    }
  • 静态常量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    - (void)block4 {

    static NSInteger num = 100;

    // 定义一个block
    dispatch_block_t block = ^ {
    NSLog(@"block4 -- numer = %zd", num);
    };

    num = 200;

    block();
    }

运行结果

2018-01-19 00:04:13.759416+0800 CycleRetain[91251:2382380] block1 -- numer = 1002018-01-19 00:04:13.760206+0800 CycleRetain[91251:2382380] block2 -- numer = 2002018-01-19 00:04:13.760473+0800 CycleRetain[91251:2382380] block3 -- numer = 2002018-01-19 00:04:13.760603+0800 CycleRetain[91251:2382380] block4 -- numer = 200

原理及本质

block 根据创建位置不同,共有三种:栈block,堆block,全局block

使用block本质就是为了保证栈上和堆上block内访问和修改的是同一个变量,具体的实现是将block 修饰的变动自动封装成一个结构体,让他在堆上创建
基于OC 底层的runtime 机制
block 在内部会有一个指向结构体的指针,当调用block的时候其实就是让block找出对应的指针所指的函数地址进行调用。并传入了block自己本身

]]>
+ + + +
+ + + + + 面试中常见问题总结(二) + + /2018/01/18/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98%E6%80%BB%E7%BB%93-%E4%BA%8C/ + + 非正式协议和正式协议

非正式协议:凡是给NSObject或者其子类添加的类别都是非正式协议
正式协议:使用 @protocol 方式声明的方法列表,有 @require 和 @optional 两种类型

KVC 键值编码

之所以可以使用就是因为给 NObject 增加了 NSKeyValueCoding 非正式协议,NSObject 实现了协议,OC中几乎所有的对象都支持KVC,获取值的方法是不通过属性的setter、getter 方法,而是通过属性名称的key间接访问了实例变量,可以访问私有变量

1
2
3
4
 // 官方注释
/* Send -setObject:forKey: to the receiver, unless the value is nil, in which case send -removeObjectForKey:.
*/
- (void)setValue:(nullable ObjectType)value forKey:(NSString *)key;

如果为nil, 就会自动过滤掉, 此方法常用在字典中过滤空值

// forKeyPath: 用于符合属性,能够通过层层访问内部属性,相当于调用属性点语法, 拥有forKey 的所有功能

1
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;
  • 实现原理
    • 利用 setValue: forKeyPath: 赋值
    1. 底层实现还是使用 runtime 给对象发消息,先去查找属性是否有setter方法,存在setter 就会通过 setter 方法复制,
    2. 如果没有setter方法,接着查找下是否划线的成员变量,如果有直接给赋值
    3. 如果没有下划线的成员变量,查找是否存在和key值相同的属性,如果有就直接给属性赋值
    4. 如果没有和key值相同的属性,就会执行 setValue: forUndefinedKey: 抛出一个异常程序奔溃,这也是字典转模型中需要实现这个方法的原因

KVO 键值监听

利用一个key值来找到某个属性并监听某个属性的值的变化发送给观察者,可以理解为简单的观察者模式

  • KVO 使用

    1. 为目标对象的属性添加观察者

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      - (void)testKVO {
      Person *p = [[Person alloc] init];
      self.person = p;
      p.name = @"哈哈";

      [p addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];

      p.name = @"我不是原来的我!你能监听到我 ???";
      p.name = @"看我七十二变";
      }
2. 实现监听的方法
1
2
3
4
5
#pragma mark - kvo 回调方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {

NSLog(@"KVO监听到的对象是 = %@, 值 = %@ , change = %@", [object class], keyPath, change);
}
3. 移除观察者
1
2
3
4
5
- (void)dealloc {

[self removeObserver:self forKeyPath:@"name"];
NSLog(@"-----控制器销毁-----");
}
  • KVO原理

    类的属性被添加了观察者后,OC底层就会通过Runtime 动态的创建一个这个类的派生类,当前类内部的isa 指针指向了这个派生类,在派生类中重写基类被观察属性的 setter 方法,调用前后会调用 willChangeValueForKey, setValueForKey, didChangeValue 方法, 从而达到监听属性值的方法

]]>
+ + + +
+ + + + + 面试中常见问题总结 + + /2018/01/17/%E9%9D%A2%E8%AF%95%E4%B8%AD%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98%E6%80%BB%E7%BB%93/ + + OC 的动态性

OC是基于C语言的,C语言在编译阶段就会确定了调用具体的那个函数,OC它的底层是通过runtime 运行时机制来调用函数,在OC中成为消息发送,具体来说就是在编译阶段OC可以调用任何函数,即时这个函数是没有实现的,只有在运行的时候才会根据isa指针和已经注册的方法列表中找到 IMP(函数指针)真正要调用的那个函数,我们平时写的OC代码,在运行的时候也都是转换成了runtime 的方式进行的,不管是什么方法本质都是发送一个消息

runtime

  • 消息机制原理: 根据方法编号列表 SEL 去映射表中查找对应方法的实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Person *p = [Person allo] init];

// 底层的写法
// alloc
Person *p = objc_msgSend(objc_getClass("Person"), sel_registerName("alloc"),);

// init
p = objc_msgSend(p, sel-registerName("init"));

// 调用对象方法本质就是:让对象发消息
objc_msgSend(p, @selector(eat));

// 调用类方法本质: 让类发消息
objc_msgSend([Person class], @selector(run:), 20);

// alloc init 也可以理解为:

id objc = objc_msgSend([NSObject class], @selector(alloc));

objc = objc_msgSend(objc, @selector(alloc));
  • runtime 消息机制调用流程

    每个对象内部都有一个isa指针,指向他自己真实的类来确定调用的是那个类的方法; 每个方法sel都有一个IMP指针(指向现实函数的指针),不管是对象方法还是类方法,都会有一个方法列表

    1. 发消息时根据对象内部的isa 指针去该类所对应的方法类表中查找方法,如果没有去父类中查找
    2. 注册每个方法编号,为了以后快速查找调用
    3. 根据方法编号查找对应方法
    4. 通过isa指针和IMP指针确定最终调用的函数
  • runtime 常见使用

    1. 运行是动态添加方法
    2. 给分类添加属性,(关联属性)
    3. 字典转模型中从先遍历模型属性再从字典中查找相关值实现模型属性赋值
    4. NSCoding 自动归档解档时对象属性过多时 encodeWithCoder:initWithCoder 两个方法实现

Runloop

  • 概念

    NSRunloop 是封装 CFRunloopRef 一套纯C的函数,

  • 作用

    1. 保持程序持续运行
    2. 处理App中各种事件,具体的有:toches事件, NSTimer 事件, Selector 事件, PerformSelector 事件,source 源等等
    3. 有事件触发时就运行,没有就休眠
    4. 渲染app 中唯一一个主线程上所有UI界面的更新
  • 使用和要点

    1. Runloop 在程序入口 main 函数中就会开启,并且开启的是主线程不会销毁除非退出程序,保证了程序一直运行并能响应事件和交互
    2. 每一条线程都是唯一对应一个Runloop
    3. 主线程不需要创建,因为在程序启动的时候就已经创建了,而子线程的需要手动创建 直接获取currentRunloop 方式(本质是使用了懒加载)
    4. Runloop 对象是利用字典进行存储,因为一一对应,所有key 和value 就是线程和 他所对应的runloop

    5. Runloop 中 NSDefaultRunLoopMode 一般情况下主线程运行的 Mode

    6. UIInitializationRunLoopMode 初始化mode,通常不用
    7. UITrackingRunLoopMode 界面跟踪, 用于scrollView 追踪触摸滑动,保证界面不受其他mode影响
    8. GSEventReceiveRunLoopMode 用于接收系统事件内部 mode, 通常不用

    9. NSRunLoopCommonModes, 并不是某种具体的 mode ,开发中使用最多,他是一个组合 mode,组合了 NSDefaultRunLoopMode和 NSRunLoopCommonModes

  • Runloop 相关类

    1. Source 事件源/输入源可以理解为 激发源
    2. Timer 基于事件触发,CADisplayLink,NSTimer 都是加到了 Runloop,受 Mode 的影响,GCD 定时器不受Runloop 影响
    3. Observer 相当于runloop 循环中的监听器,时刻监听当前Runloop的运行状态
    4. 休眠没事情做的时候可以停下来,处于休眠状态
  • 使用场景

    1. 创建NSTimer
    2. PersforSelector方法
    3. 常驻线程:(创建后处于等待状态,有事件时响应事件)
      • 实现后台收集用户停留时间或者是某个按钮的点击次数,如果使用主线程会不方便,使用runloop解决
    4. AutoreleasePool 自动释放池
    5. UI更新
]]>
+ + + +
+ + + + + iOS即时通讯实现二 + + /2018/01/12/iOS%E5%8D%B3%E6%97%B6%E9%80%9A%E8%AE%AF%E5%AE%9E%E7%8E%B0%E4%BA%8C/ + + 实现IM通信所用协议

几个协议理解

xmpp 是基于xml协议主要是易于扩展,早起用于PC时代的产品使用,流量大,耗电大交互复杂其实并不适合移动互联网时代的IM产品

MQTT 协议 适配多平台,需自己扩展好友,群组等功能

protobut 协议

封装socket,自己实现

所要考虑的问题

  • 传输协议选择

    • TCP ? 一般公司都选用(技术不是很成熟)
    • UDP ?QQ 腾讯增加自己的私有协议,保证数据传递的可靠性,解决了丢包,乱序的问题
  • 聊天协议选择及代表框架

    • Socket ? CocoaAsyncSocket
    • WebSockt ?是传输通讯协议,基于socket封装的一个协议 SocketRocket
    • MQTT ?MQTTKit 聊天协议为上层协议
    • protobut 协议 ?
    • XMPP ?XMPPFramework 聊天协议为上层协议
    • 自定义
  • 传输数据格式

    • Json?
    • XML?
    • ProtocolBuffer?
  • 细节相关

    • TCP 长连接如何保持,
    • 心跳机制
    • 重连机制
    • 数据安全机制

CocoaAsyncSocket

  • CocoaAsyncSocket 的 delegate 用来设置代理处理各个回调
  • delegateQueue
  • socketQueue 串行队列,贯穿全类并没有任何加锁,确保了socket 操作中每一步都是线程安全的
  • 创建了两个读写队列(本质是数组)

    • NSMutableArray *readQueue;
    • NSMutableArray *writeQueue;
  • 全局数据缓冲: 当前socket未获取完的数据大小

    • GCDAsyncSocketPreBuffer *preBuffer;
  • 交替延时变量: 用于进行另一服务端地址请求的延时

    • alternateAddressDelay
  • connect

  • disconnect

数据粘包,断包

  • 粘包: 如果客户端同一时间发送几条数据,而服务器只收到一大条数据

    • 原因:
    • 由于传输的是数据流,经过TCP传输后,TCP使用了优化算法将多次间隔较小且数据量小的数据合并成一个大的数据块,因此三条数据合并成了一条

      TCP,UDP都可能造成粘包的原因

    • 发送端需要等缓冲区满了才发送出去,做成粘包

    • 接收方不及时接收缓冲区的包,造成多个包接收
  • 断包: 因为一次发送很大的数据包,缓冲区有限,会分段发送或读取数据

CocoaAsyncSocket的封包,拆包处理

  • 读取数据方法:

    // 有数据时调用,读取当前消息队列中的未读消息- (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag
**每次读取数据,每次都必须手动调用上述 readData 方法超时设置为不超时才能触发消息回调的代理****因此在第一连接时调用,再在接到消息时调用,上述方法就可以达到每次收到消息都会触发读取消息的代理**以上做法存在的问题就是没有考虑数据的拆包会有粘包情况,这时候需要用下面两个`read`方法,1. 读取指定长度、2.读取指定边界    // 读取特定长度数据时调用    - (void)readDataToLength:(NSUInteger)length withTimeout:(NSTimeInterval)timeout tag:(long)tag    // 读到特定的 data 边界时调用    - (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag**解决办法及具体思路**- 封包:给每个数据包增加一个长度或者加一个开始结束标记,表明数据的长度和类型(根据自己的项目:文本、图片、语音、红包、视频、话题、提问等等)    - 可以在数据包之后加一个结束标识符,解决了传输过程中丢包,丢失头部信息的错误包读取,读到这样的就丢弃直接读下一个数据包- 拆包:获取每个包的标记,根据标记的数据长度获取数据,最后根据类型处理数据,最后读取数据包头部的边界
  • 利用缓冲区对数据进行读取

    • 创建读取数据包
    • 添加到读取的队列中
    • 从队列中取出读取任务包
    • 使用偏移量 maxLength 读取数据

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      - (void)readDataWithTimeout:(NSTimeInterval)timeout
      buffer:(NSMutableData *)buffer
      bufferOffset:(NSUInteger)offset
      maxLength:(NSUInteger)length
      tag:(long)tag
      {
      if (offset > [buffer length]) {
      LogWarn(@"Cannot read: offset > [buffer length]");
      return;
      }

      // 1. 创建读取数据包
      GCDAsyncReadPacket *packet = [[GCDAsyncReadPacket alloc] initWithData:buffer
      startOffset:offset
      maxLength:length
      timeout:timeout
      readLength:0
      terminator:nil
      tag:tag];

      dispatch_async(socketQueue, ^{ @autoreleasepool {

      LogTrace();

      if ((flags & kSocketStarted) && !(flags & kForbidReadsWrites))
      {
      // 2. 向读的队列添加任务包
      [readQueue addObject:packet];
      // 3. 从队列中取出读取任务包
      [self maybeDequeueRead];
      }
      }});

      // Do not rely on the block being run in order to release the packet,
      // as the queue might get released without the block completing.
      }

doReadData 方法时读取数据的核心方法

  • 如果读取对列中没有数据,就去判是否是上次已经读取了数据,但是因为没有目前还没有读取到数据的标记,这是正好设置了 read 方法中的超时时间,所以要断开socket 连接

  • 接收到数据后在 socket系统进程的数据缓冲区中
    • (基于TLS的分为两种)他们在各自管道中流动,完成数据解密后又流向了全局数据缓冲区
      • CFStreem
      • SSLPrebuffer

处理数据逻辑: 如果当前读取包长度给明了,则直接流向currentRead,如果数据长度不清楚,那么则去判断这一次读取的长度,和currentRead可用空间长度去对比,如果长度比currentRead可用空间小,则流向currentRead,否则先用prebuffer来缓冲。

心跳机制

  • TCP 自带的keep-alive 使用空闲时间来发并且默认超时时间太长是2 小时,
  • 如果应用使用了socks ,socks proxy会让tcp keep-alive失效,因为socks 协议只管转发TCP层具体的数据包,并不会转发TCP协议内的实现具体细节数据包
  • TCP 的长连接理论上是一直保持连接的,可以设置 TCP keep-alive的时间 但是实际情况下中,可能出现故障或者是因为防火墙的原因会自动把一定时间段内没有数据交互的连接给断开,心跳机制就可以维持长连接,保持活跃状态

  • iOS 中使用 NSTimer scheduledTimerWithTimeInterval 需要在调用之前的关键位置设置 fireDate 为未来的某个时间, 然后再需要的时候开启

  • 或者直接使用 timerWithTimeInterval 创建,然后添加到runloop
]]>
+ + + +
+ + + + + iOS 即使通讯网络知识篇 + + /2018/01/10/%E7%BD%91%E7%BB%9C%E4%B8%83%E5%B1%82%E5%8D%8F%E8%AE%AE/ + + 网络七层协议

读《图解TCP/IP》第5版以及看过的关于网络文章之后自己的理解

OSI (Open System Interconnec) 译为 开发式系统互联 七层模型从下到上一次是:物理层 -> 数据链路层 —> 网络层 -> 传输层 -> 会话层 -> 表示层 -》 应用层

数据每次经过一层之后就会在数据头部增加一个首部信息

物理层:网卡,网线,集线器,中继器,调制解调器
  • 定义设备标准,如网线,光纤接口类型,将0和1转化为电信号的强和弱,达到传输比特流目的,这层数据叫比特
数据链路层:网桥,交换机
  • 传输的地址帧,并且有检测错误功能,保证数据可靠传输, 这层数据叫帧
网络层:路由器
  • 两个计算机通信时可能会经过多个数据链路或者通信子网,网络层将数据链路层的帧组成数据包,包中有封装好的包头,其中含有逻辑地址信息(源站点和目的站点),此层为数据包选择合适的路由和交换结点,IP协议产生,这层数据叫着数据包
传输层:
  • 解决数据如何在网络中传输,这个层负责获取全部信息,因此,它必须跟踪数据单元碎片、乱序到达的 数据包和其它在传输过程中可能发生的危险。第4层为上层提供端到端(最终用户到最终用户)的透明的、可靠的数据传输服务。所为透明的传输是指在通信过程中 传输层对上层屏蔽了通信传输系统的具体细节。传输层协议的代表包括:TCP、UDP、SPX等
会话层:
  • 这一层也可以称为会晤层或对话层,在会话层及以上的高层次中,数据传送的单位不再另外命名,而是统称为报文。会话层不参与具体的传输,它提供包括访问验证和会话管理在内的建立和维护应用之间通信的机制。如服务器验证用户登录便是由会话层完成的
表示层:
  • 这一层主要解决拥护信息的语法表示问题。它将欲交换的数据从适合于某一用户的抽象语法,转换为适合于OSI系统内部使用的传送语法。即提供格式化的表示和转换数据服务。数据的压缩和解压缩, 加密和解密等工作都由表示层负责。
应用层:HTTP 协议
  • 如何包装数据

通常从上到下会分成四层

TCP/IP 通常是指TCP/IP协议族,是一组不同协议组合在一起构成的协议族

  • 应用层:FTP 应用层协议
  • 传输层:TCP,UDP 传输层协议,TCP 提供了一个可靠的服务
  • 网络层:IP,ICMP 网络层协议,传输时不可靠的
  • 链路层:以太网协议

Socket

为了解决不同计算机上进程间通信问题
服务器在指定的端口上监听,然后生成一对儿新的socket 进行通讯,一个线程对应一个socket

英文翻译为“插座”通常称为”套接字”, 本质是对于TCP/IP的封装,有客户端 socket 和 服务端socket

  • 流式 Socket (STREAM): 是一种面向连接的socket,针对于面向连接的TCP服务应用,因为安全所以效率低
  • 数据报式Socket (DATAGRAM): 是一种无连接的SOcket,正对于无连接的UDP服务应用,无序,不安全,需要在接收端分析重排或者要求重发,所以效率高
]]>
+ + + +
+ + + + + Swift加密相关 + + /2018/01/08/Swift%E5%8A%A0%E5%AF%86%E7%9B%B8%E5%85%B3/ + + 最近公司项目开发中引用了 SwiftOC 混编, 涉及到了加密相关, 使用了第三方 CryptoSwift, 以下是使用过程中的简单记录

CryptoSwift简介

数组转换

  • data 转化为字节数组

    1
    2
    let bytes = data.bytes
    print(bytes)
  • 字节数组转化为 data

    1
    2
    let data = Data(bytes: [0x01, 0x02, 0x03])
    print(data)
  • 十六进制创建字节

    let bytes_Hex = Array<UInt8>(hex: "0x010203")print(bytes_Hex)
  • 字节转十六进制数

    let hex = bytes_Hex.toHexString()print(hex)       
  • 字符串生成字节数组

    let string2bytes = "string".bytesprint(string2bytes)
  • 字节数组 base64

    let bytes1: [UInt8] = [1, 2, 3]let base64Str = bytes1.toBase64()print(base64Str!)
  • 字符串base64

    let str0 = "test"let base64Str1 = str0.bytes.toBase64()print(base64Str1!)

MD5加密

  • MD5(RFC1321)诞生于 1991 年,全称是“Message-Digest Algorithm(信息摘要算法)5”,由 MIT 的计算机安全实验室和 RSA 安全公司共同提出。
  • 之前已经有 MD2、MD3 和 MD4 几种算法。MD5 克服了 MD4 的缺陷,生成 128bit 的摘要信息串,出现之后迅速成为主流算法

  • 字节数组MD5

    let str = "测试test"let bytes = str.bytes// 写法一let digest = bytes.md5().toHexString()print(digest)// 04f47b69e3573867a5b3c1e5edb00789// 写法二let digest1 = Digest.md5(bytes).toHexString()print(digest1)// 04f47b69e3573867a5b3c1e5edb00789
  • Data的MD5值

    /// data MD5let data = Data(str.bytes)let digest3 = data.md5().toHexString()print(digest3)// 04f47b69e3573867a5b3c1e5edb00789
  • 字符串MD5

    ///写法 一let digest4 = str.md5()print(digest4)// 04f47b69e3573867a5b3c1e5edb00789// 写法二do {    var digest4_2 = MD5()    let _ = try digest4_2.update(withBytes: "测试".bytes)    let _ = try digest4_2.update(withBytes: "test".bytes)    let result = try digest4_2.finish()    print(result.toHexString())    //04f47b69e3573867a5b3c1e5edb00789} catch {}    

哈希算法

  • 字节数组SHA值

    let str = "测试test"let bytes = str.bytes//方式一let digest1 = bytes.sha1().toHexString()let digest2 = bytes.sha224().toHexString()let digest3 = bytes.sha256().toHexString()let digest4 = bytes.sha384().toHexString()let digest5 = bytes.sha512().toHexString()print(digest1, digest2, digest3, digest4 ,digest5, separator: "\n")//方式二let digest6 = Digest.sha1(bytes).toHexString()let digest7 = Digest.sha224(bytes).toHexString()let digest8 = Digest.sha256(bytes).toHexString()let digest9 = Digest.sha384(bytes).toHexString()let digest10 = Digest.sha512(bytes).toHexString()print(digest6, digest7, digest8, digest9 ,digest10, separator: "\n")
  • Data的SHA值

    let data = Data(bytes: str.bytes)let digest11 = data.sha1().toHexString()let digest12 = data.sha224().toHexString()let digest13 = data.sha256().toHexString()let digest14 = data.sha384().toHexString()let digest15 = data.sha512().toHexString()print(digest11, digest12, digest13, digest14 ,digest15, separator: "\n")
  • 字符串的SHA值

    let digest16 = str.sha1()let digest17 = str.sha224()let digest18 = str.sha256()let digest19 = str.sha384()let digest20 = str.sha512()print(digest16, digest17, digest18, digest19 ,digest20, separator: "\n")
// 方式一let digest_str = str.sha1()print(digest_str)// 方式二do {    var digest_str = SHA1()    let _ = try digest_str.update(withBytes: Array("测试".bytes))    let _ = try digest_str.update(withBytes: Array("test".bytes))    let result = try digest_str.finish()    print(result.toHexString())} catch {}

AES 加密

  • key:公共密钥

  • IV: 密钥偏移量

  • padding:key 的补码方式

    key 长度不够16 字节时,需要手动填充, zeroPadding 将其补齐至 blockSize 的整数倍zeroPadding 补齐规则:- 将长度补齐至 blockSize 参数的整数倍。比如我们将 blockSize 设置为 AES.blockSize(16)- 如果长度小于 16 字节:则尾部补 0,直到满足 16 字节。- 如果长度大于等于 16 字节,小于 32 字节:则尾部补 0,直到满足 32 字节。- 如果长度大于等于 32 字节,小于 48 字节:则尾部补 0,直到满足 48 字节。 以此类推......
  • 补码方式padding, noPadding, zeroPadding, pkcs7, pkcs5, 默认使用 pkcs7,两者写法结果相同

let iv_paddding = "1234567890123456" // 默认是let aes_padding1 = try AES(key: key.bytes, blockMode: .CBC(iv: iv_paddding.bytes))let aes_padding2 = try AES(key: key.bytes, blockMode: .CBC(iv: iv_paddding.bytes), padding: .pkcs7)            
  • ECB 模式

    let key = "hangge.com123456"let str = "E蜂通信"print("加密前 \(str)")//MARK: 使用ECB 模式, , 使用aes128 加密let aes = try AES(key: key.bytes, blockMode: .ECB)let encrpyted = try aes.encrypt(str.bytes)print("加密后 \(encrpyted)", separator: "-----") /// 解密 let decrypted = try aes.decrypt(encrpyted) print("解密后 \(decrypted)") let decryptedString = String(data: Data(decrypted), encoding: .utf8)! print("解密后字符串 \(decryptedString)")
  • CBC 模式

    //MARK: 使用CBC 模式, 需要提供一个额外的密钥偏移量 iv    /// 写法 1 --  便捷写法    // 偏移量    let iv = "1234567890123456" // 默认是    // TODO: 随机密钥偏移量    let data = Data(bytes: AES.randomIV(AES.blockSize))    let random_iv = String(data: data, encoding: .utf8)
/// 写法 2 --// 创建密码器let cbc_aes = try AES(key: key.bytes, blockMode: .CBC(iv: iv.bytes))let cbc_aes2 = try AES(key: key, iv: iv)let cbc_aes3 = try AES(key: key, iv: iv)let cbc_encrypted = try cbc_aes.encrypt(str.bytes)let cbc_encrypted2 = try cbc_aes2.encrypt(str.bytes)let cbc_encrypted3 = try cbc_aes3.encrypt(str.bytes)// 便捷写法解密let cbc_decrypted = try cbc_aes.decrypt(cbc_encrypted)let cbc_decryptedData = Data(cbc_decrypted)let cbc_decryptedStr = String(data: cbc_decryptedData, encoding: .utf8)print(cbc_decryptedStr)// 正常写法2 解密let cbc_decrypted2 = try cbc_aes2.decrypt(cbc_encrypted2)let cbc_decryptedStr2 = String(data: Data(cbc_decrypted2), encoding: .utf8)print(cbc_decryptedStr2)// 随机密钥偏移量解密let cbc_decrypted3 = try cbc_aes3.decrypt(cbc_encrypted3)let cbc_decryptedStr3 = String(data: Data(cbc_decrypted3), encoding: .utf8)print(cbc_decryptedStr3)
]]>
+ + + + + + AES, Base64, MD5 + + + +
+ + + + + Mac 安装MySQL + + /2017/12/14/Mac-%E5%AE%89%E8%A3%85MySQL/ + + 搭建 XMPP 即使通讯

安装MySQL数据库

  • 安装MySQL 数据库

    • 官网 下载安装包

      特别强调:不要一直下一步最后的时候会有个弹窗提示你生成root的临时密码,记录一下

  • 安装数据库管理工具 MySQLWorkbench

    官网安装包下载安装

  • 修改数据库 root 密码,

    打开MySQLWorkbench 随便打开一个SQL 输入安装时记录的密码,确认后提示你修改密码,

  • 如果为记住安装时密码,参考下面链接修改

安装 openfire

  • 官网 下载安装包安装

  • 安装后需重启系统偏好设置

  • 如果出现右边的管理员配置按钮不能点击,请使用 brew 安装 或者更新 Java 环境,openfire 需要 Java 环境
]]>
+ + + +
+ + + + + 本地已有项目添加Pod + + /2017/12/02/%E6%9C%AC%E5%9C%B0%E5%B7%B2%E6%9C%89%E9%A1%B9%E7%9B%AE%E6%B7%BB%E5%8A%A0Pod/ + + 上一篇中主要记录了如何使用 pod 工具自动创建 pod 库并且生成一个测试Demo,这边主要记录给已有的项目添加 pod

创建 .podsepc 文件

  • cd 到已有项目的根目录执行命令: pod spec create [ProjectName].podspec

    1
    pod spec create EmotionKeyboard.podspec

修改编辑 .podsepc 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
s.name         = "EmotionKeyboard"
s.version = "0.0.1"
s.summary = "A Emotional Keyboard."

# 此处的格式不能修改
s.description = <<-DESC

Swift Emotional Keyboard
DESC

s.homepage = "https://github.com/aTreey/EmotionKeyboard"

# 两种写法,使用下面第二种
# s.license = "MIT (example)"
s.license = { :type => "MIT", :file => "LICENSE" }

s.author = { "aTree" => "480814177@qq.com" }

s.platform = :ios, "8.0"

s.source = { :git => "https://github.com/aTreey/EmotionKeyboard.git", :tag => "#{s.version}" }


# 从 .podspec 同级别的目录下起匹配
s.source_files = "EmotionKeyboard", "EmotionKeyboard/Classes/**/*.{h,m}"

# 设置自己项目中依赖的系统库
s.framework = "UIKit"

```

## 调整文件的目录结构

- 在项目根目录下新建一个放组件代码的文件夹
- 如果项目中包含 `bundle` 文件, 需要在组件文件夹下新建一个 `Assets` 文件夹,用来存放组件中的资源

最终结果如下:

![](https://ws1.sinaimg.cn/large/a1641e1bly1fm2jl7ykslj21gc0o80xt.jpg))

- 创建并添加必要文件

- `LICENSE` 文件: 开源许可文件, 如果创建项目的时候选择了开源许可就可以自动生成忽略此步骤,如果没有直接拷贝一份其他的工程中的即可
- `README.md` 文件: 主要用来说明你开源库的信息及使用方法


## 验证本地 `.podspec` 文件

- 错误1: `.podspec `中的文件路径设置错误

![](https://ws1.sinaimg.cn/large/a1641e1bly1fm2jlboq82j20v807ejug.jpg)

- 错误2: bundle 文件资源路径错误

![](https://ws1.sinaimg.cn/large/a1641e1bly1fm2jl4shnnj20zq0cwgqf.jpg)


- 本地验证结果

- **EmotionKeyboard passed validation.** 本地验证验证通过

## 网络验证 `.podspec` 文件

- 给 `master` 分支打上 `.podspec` 文件中对应的 `tag` 标签, 提交推送,
- 执行命令
pod spec lint
1
- 网络验证结果
EmotionKeyboard.podspec passed validation.```

本文参考

给 Pod 添加资源文件

基于 Swift 创建 CocoaPods 完全指南

]]>
+ + + +
+ + + + + CocoaPods制作私有pod + + /2017/11/29/CocoaPods%E5%88%B6%E4%BD%9C%E7%A7%81%E6%9C%89pod/ + + CocoaPods原理

本文主要记录制作自己的pod库过程

CocoaPods 是自动化工具,主要用来管理一些开源的第三方框架,或者是制作自己的私有库,开源库,也可以使用它讲自己的项目组件化。

具体可以参考:

  1. 细聊 Cocoapods 与 Xcode 工程配置
  2. 你真的会用 CocoaPods 吗?

CocoaPods 安装

安装教程

创建私有索引仓库

每次使用第三方开源库的时候只需要在 Podfile 文件中指定所用的库然后 Pod install ,这是因为安装pod的时候 pod setup远程仓库(索引库) clone到了本地,此仓库中存放了所有支持pod框架的描述信息(包括每个库的各个版本)

文件目录:

~/.cocoapods/repos/master
  • 码云 或者 coding 上创建一个私有仓库

  • 添加私有库到CocoaPods

    • 格式 pod repo add [repoName] (URL)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      pod repo add PrivateSpec https://gitee.com/aapenga/PrivateSpec.git 
      ```


      ## 创建本地私有库

      - `cd` 到任意一个文件夹下, 执行 `pod lib create [项目名]`

      ```
      pod lib create PPExcelView
  • 将文件放入到生成的 Demo 文件夹 的 Classes 文件目录下
  • cdExample 下(含有 Podfile 文件) 执行

    1
    pod install

    或者

    1
    pod update

此时添加的文件出现在 Dome Pod 的 Development Pods 目录中

  • 从本地验证 pod 是否通过

    1
    pod lib lint

    如果有警告,可以使用 --private 或者 --allow-warnings 忽略

    1
    2
    pod lib lint --allow-warnings
    pod lib lint --private

    出现 PPExcelView passed validation. 为通过

  • 从远程仓库验证 pod

    1
    pod spec lint

    出现 PPExcelView.podspec passed validation 为通过

推送 .podspec 文件到私有仓库

cd.podspec 所在的文件下

pod repo push MyGitSpec PPExcelView.podspec
]]>
+ + + +
+ + + + + Alamofire源码学习总结 + + /2017/04/10/Alamofire%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0/ + + 网络请求时Path和Query 之间是用 ‘?’ 号隔开,后边是传给服务器的参数,GET请求Query是放在URL之后,POST请求是放在Body中
  • 如果参数是一个 key-value 形式,Query 格式为:key=value

  • 如果参数是一个数组 key = [value1, value2, value3 ….],

  • Query 格式为 key[]=value1&key[]=value2&key[]=value3

  • 如果参数是一个字典

1
key = [“subKey1”:”value1”, “subKey2”:”value2”, “subKey3”:”value3”….],
  • Query 的格式为
1
key[subKey1]=value1&key[subKey2]=value2&key[subKey3]=value3
  • Alamfire中的编码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
private func query(_ parameters: [String: Any]) -> String {
var components: [(String, String)] = []
for key in parameters.keys.sorted(by: <) {
let value = parameters[key]!
components += queryComponents(fromKey: key, value: value)
}
return components.map { "\($0)=\($1)" }.joined(separator: "&")
}
public func queryComponents(fromKey key: String, value: Any) -> [(String, String)] {
var components: [(String, String)] = [] // 元祖数组
if let dictionary = value as? [String: Any] { // value 为字典,key[subKey]=value 形式
for (nestedKey, value) in dictionary {
components += queryComponents(fromKey: "\(key)[\(nestedKey)]", value: value)
}
} else if let array = value as? [Any] { // value为数组, key[]=value 形式
for value in array {
components += queryComponents(fromKey: "\(key)[]", value: value)
}
} else if let value = value as? NSNumber { // value为 NSNumber
if value.isBool {
components.append((escape(key), escape((value.boolValue ? "1" : "0"))))
} else {
components.append((escape(key), escape("\(value)")))
}
} else if let bool = value as? Bool { // value 为 Bool
components.append((escape(key), escape((bool ? "1" : "0"))))
} else { // value 为字符串时 直接转义
components.append((escape(key), escape("\(value)")))
}
return components
}
]]>
+ + + + + + Alamofire + + + +
+ + + + + 知识点总结 + + /2017/03/25/%E7%9F%A5%E8%AF%86%E7%82%B9%E6%80%BB%E7%BB%93/ + + 开发中的知识点总结

textField

使用textField自定义搜索框,textField及leftView等属性
使用leftView 时需要设置model为始终显示
可以设置leftView的frame 调整图片大小
IBInsepectable IBDesignable 的使用

给属性检查器面板中添加属性设置选项
IBDesignalbe 实时显示UI控件,需要讲对应的xib和对应类绑定

增加分类

新建一个swift 文件,

使用extension,重写 get set方法
使用newValue 时需要判断,注意返回值的类型是否匹配
试图切换

一个控制器中加载不同视图,可以在loadView 方法中自定义根视图

loadView 使用注意事项
如果实现loadView 未调用 super loadView ,sb/xib实效
loadView调用时,如果根视图是nil(本质是未调用super loadView),会自动调用loadView 方法初始化根视图,形成递归调用
判断当前登陆标识的状态确定是调用 super loadView 还是 把创建自定义的根视图赋值给 view
VFL 的使用

使用之前需要先 关闭frame 的布局才能手动的添加约束 translatesAutoresizingMaskIntoConstraints = false
基本动画使用

]]>
+ + + +
+ + + + + Swift3.0学习(三) + + /2017/03/08/Swift3.0%E5%AD%A6%E4%B9%A0(%E4%B8%89)/ + + 面向对象–必选属性

构造函数时是分段构造,先构造子类,给’必选属性’设置初始值,然后再构造父类,
定义全局变量(属性)时,需要在构造函数中设置初始化变量值
super.init()是一个类构造结束
重写: override 函数重写(覆盖),如果在重写的方法中 没有super, 父类相关的一些操作就不会被执行
重载:函数名相同, 参数的类型 或者参数的个数不同 就形成了函数的重载
注意:重载构造函数,并且父类默认的构造函数 init()不重写, 父类默认的构造函数就不能够被访问,不能够确保必选属性设置初始值,只能调用重载的构造函数
面向对象–可选属性

使用KVC给对象设置值
注意: 在swift中使用KVC 基本数据类型不能声明为可选项,必须设置为必选项给定初始值

KVC实现的流程
遍历字典的键值 给对象发送 setValue: forKey消息
如果key 对象的属性不存在就将消息转发给 setVale: forUndefinedKey:
如果存在就直接设置值
注意:setVale: forUndefinedKey: 默认抛出异常 不能够super,如果super 相当于没有实现此方法

面向对象–便利构造函数

作用:方便快捷的创建对象
场景:
可检查参数是否正确来实例化
实例化控件
特点
必须以self来调用构造函数
指定的构造函数不能被重写,也不能被super调用
便利构造函数可被子类继承
可以构造失败,返回nil
面向对象 – 描述信息

重写 description 方法,返回值是string类型
使用kvc讲对象属性转换为字典
将字典转化为string类型

override var description: String {        let keys = ["name","age","number","sex"]        // 对象转为字典        let dict = self.dictionaryWithValuesForKeys(keys)        // 每个对象都有描述属性        return dict.description    }

面向对象 – 存储属性

面向对象 – 计算属性(readOnly)

只有getter,没有setter
只能取值不能赋值
每次调用都会被执行,消耗cpu
不占内存空间,依赖其他的属性来计算
面向对象 – didset 属性设置监察器

能获取 oldValue 和 newValue 的值
通常用来重写setter方法,实现视图绑定模型
在一个属性的didset中计算其他属性

缺点:消耗内存,被计算的属性占内存
优点:减少了cpu消耗
懒加载

]]>
+ + + + + + swift + + + +
+ + + + + Swift3.0学习(二) + + /2017/03/05/Swift3.0%E5%AD%A6%E4%B9%A0(%E4%BA%8C)/ + + 字符串

字符串遍历
字符串字节长度
字符串长度

let string = “我是switf,语言”

// 字符串遍历
for s in string.characters {
print(s)
}

// 字符串的长度
let length = string.characters.count;
print(length)

// 字符串字节长度 汉字对应3个字节,英文是1个字节
let characterLength = string.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
print(characterLength)

字符串拼接

let str1 = "你好"   let str2 = "不好"   let a = 20   // 第一种拼接   print("\(str1)\(str2)\(a)")   // 第二种拼接   let str = str1 + str2   let strA = str1 + String(a)   print(str)   print(strA)

截取子串 as 的使用

let subString = str1.substringToIndex("小程生生世世".endIndex);   print(subString)   let subStr = str1.substringFromIndex("听说".startIndex)   print(subStr)   let range = str1.rangeOfString("小程序")   let subStr1 = str1.substringWithRange(range!)   print(subStr1)   // 截取到某一个位置   let subStr2 = str1.substringFromIndex(str1.endIndex.advancedBy(-2))   print(subStr2)   // 限制截取几个字符   let subStr3 = str1.substringToIndex(str1.startIndex.advancedBy(5, limit: str1.characters.endIndex))   print(subStr3)String 和 NSString 之间的 使用 as 无缝转换let helloString = "我们一起飞"(helloString as NSString).substringWithRange(NSMakeRange(2, 3))数组

可以放不同类型的元素
let不可变数组,var可变数组

// 初始化一个空数组 [String]声明是一个装有字符串的数组类型   var emptyArray: [String] = [String]()// 可变数组   var array = ["哈哈","呵呵","嘿嘿"]   array.append("咯咯")   print("array = \(array)")   // 不可变数组   let array2 = ["可可","噗噗"]   // 数组拼接   let arr = array + array2   print("arr = \(arr)")   array += array2   print("array = \(array)")

数组遍历

// 1.    for i in 0 ..< tempArray.count {       print(tempArray[i])   }   // 2.    for obj in tempArray {       print("第二种方法:\(obj)")   }   // 3. 快速便利   for (index,value) in tempArray.enumerate() {       print("index = \(index), value = \(value)")   }

字典和OC类似

函数调用

必须在函数声明的下面调用内部函数

可以定义函数的外部参数和内部参数

可以在参数前面加上 ‘# ’ 表明函数的外部参数和内部参数名称一样 swift3.0 以后废弃

无参无返回值
三种写法

func demo9() -> Void {       print("无参无返回值第一种写法")   }   func demo_9() -> () {       print("无参无返回值第二种写法")   }   func demo_99() {       print("无参无返回值第三种写法")   }

有参有返回值

func demo10(width a: Int, height b: Int) -> Int {   return a * b

}

闭包

和OC中block类似,
和在函数中调用一个内部函数原理相同
可当作参数传递
在需要时执行闭包实现回调

// 定义闭包let closure = {() -> () in       print("闭包实现")   }   // 执行闭包   closure()

注意循环引用

只有两个对象相互强引用或者三个对象相互强引用形成闭环时才会形成循环引用

weak 和 unsafe_unretained的区别

__weak iOS5.0 推出,当对象被系统回收时,对象的地址会自动指向nil

____unsafe_unretained iOS4.0 推出,当对象被系统回收时,对象的地址不会自动指向nil,会造成野指针访问
解决办法:(官方推荐)weak - strong -dance 解决循环引用,AFNetworking中

尾随闭包

函数的最后一个参数时闭包的时候,函数的参数 ‘()’可以提前关闭,闭包写在 ‘()’后面,当作尾随闭包来使用

swift 和 OC 区别

swift 和 OC语法的快速的对比

XXX.init(xxx) ==> XXX.(xxx)
selector 类型 ==> ‘函数名’
对象方法的调用 是 ‘.’ self是可以省略的
枚举 枚举名 + 枚举值的名 => .枚举值的名
常量 和变量

let 声明常量 var 声明变量
变量/常量的类型是自动推到的
不同类型之间不能够直接运算 需要手动转换数据类型
可选项
‘?’ 表示表示可选项 可能有值 可能为 nil
可选项会自动带上 Optional 字样
可选项不能够直接参与运算 需要强制解包
‘!’ 表示强制解包 获取可选项中具体的值
‘??’ 快速判断可选项是否为nil 如果为 nil 就去取 ‘??’后面的默认值
控制流
if 没有非零即真的概念 必须制定明确的条件
if let 快速赋值 并且判断赋值对象是否为 nil 如果不为nil 就进入分之执行相关逻辑代码
guard let 作用和 if let 相反 好处: 可以减少一层分支嵌套
where 多重判断 注意 和 && || 不一样的
循环

for in
0..<10
0…10
字符串

更加轻量级 更加高效 是 结构体
支持快速遍历
长度
拼接
截取 as
集合 let 声明不可变的集合 var 声明可变的集合

声明 []
声明一个 空的集合
增删改查
合并
遍历
函数

func 函数名称(外部参数1 内部参数1: 类型, 外部参数2 内部参数2: 类型) -> Int {执行的代码}
函数没有返回值的三种方式
函数内部函数
闭包 () -> () 没有参数没有返回值的闭包类型

提前准备好的一段可以执行代码块
可以当做参数传递
在需要的时候执行闭包产生回调的效果
在闭包中使用self 需要考虑循环引用
函数是一种特殊的闭包

block 的循环引用的解除
weak 和 weak关键字作用类似 当属性对象被回收是 会自动指向nil
_
unsafeunretained 和 assgin关键字作用类似 当属性对象被回收是 不会自动指向nil, 会造成野指针访问 weak -strong - dance

]]>
+ + + + + + swift + + + +
+ + + + + Swift 3.0 学习 + + /2017/03/01/Swift3.0%E5%AD%A6%E4%B9%A0(%E4%B8%80)/ + + 和OC区别
  • 没有了.h 和 .m 文件,没有main.m 文件
  • 程序入口发生变化 在appdelegate中的 @UIApplicationMain
  • 每行代码可以不写 ‘;’
  • self可以不写,建议不写self,因为闭包中必须要写
  • 真假表示只有 true/ false,没有非0即真的概念
  • 类型自动推到

类型自动推到

提前指定类型

1
2
let float :Double = 9.6 
print(float)

常量和变量

  • let 声明常量,有且只有一次赋值机会
  • var 声明变量
  • 尽量选择使用let如需修改值再使用var 系统会报警告

可选项

  • Optional: 表示一个常量或者是变量可能有值可能为nil,在打印的时候会带有Optional字样
  • ? 表示是一个可选项
  • 可选项不能参与运算
  • ! 表示可选项中一定要有值,可以强制解包,解包时如果没有值会boom

  • ?? 合并空选项 快速判断可选项是否为nil, 如果为nil取后面的默认值,在基本数据类型和字符串使用较多

条件分支

  • if let

快速赋值,判断赋值对象是否为nil,如果不为nil,就赋值进入分支
生成request是url是一个可选项,需要强制解包或者合并空选项

1
2
3
4
let urlString = "http://www.baidu.com"
let url = NSURL(string: urlString)
let request = NSURLRequest(URL: url!)
print(request)
  • 以上代码使用if let实现
1
2
3
4
if let u = NSURL(string: urlString) {
let request = NSURLRequest(URL: u)
print(request)
}
  • where条件语句配合多重判断
1
2
3
4
5
6
if url != nil  {
if url?.host == "www.baidu.com" {
let request = NSURLRequest(URL: url!)
print(request)
}
}
  • 使用if let
1
2
3
4
if let u = url where u.host == "www.baidu.com" {
let request = NSURLRequest(URL: u)
print(request)
}
  • 多重判断
1
2
3
4
if let u = url, s = string {
let request = NSURLRequest(URL: u)
print(request)
}
  • guard let 和 if let 相反如果为nil,可以直接返回
1
2
3
4
5
6
7
let urlString  = "http://www.baidi.com"
let url = NSURL(string: urlString)
guard let u = url else { return }

//程序走到这个地方就表示 u 一定有值
let request = NSURLRequest(URL: u)
print(request)
  • swith语句

    • swift语句中可以case任意类型,
    • 在OC中只能case整数
    • 不需要写break,会自动跳出,
    • 如果需要实现case穿透,需要加入fallthrough语句
    • case语句中临时变量不需要写‘{ }’
    • 可同时case多个值
    • 每句case语句中至少要有一行可执行的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let string = "string1"
switch string {
case "string", "string1":
let str = "stringOne"
print(str)

case "string2":
let str = "stringTWO"
print(str)

case "string3":
print(string)

case "string4":
print(string)
default:
print("empty")
}
  • switch 中同样能够赋值和使用 where 子句
1
2
3
4
5
6
7
8
9
10
11
12
13
let point = CGPoint(x: 10, y: 10)
switch point {
case let p where p.x == 0 && p.y == 0:
print("中心点")
case let p where p.x == 0:
print("Y轴")
case let p where p.y == 0:
print("X轴")
case let p where abs(p.x) == abs(p.y):
print("对角线")
default:
print("其他")
}
  • 循环语句
1
2
3
4
5
6
7
for i in 0 ..< 10 { // 不包含10  循环范围中两边的值格式必须一直(空格问题)
print(i)
}

for _ in 0...5 {// 包含5 _ 表示忽略,不关心,只占位
print("循环语句")
}
]]>
+ + + + + + Swift + + + +
+ + + + + OC和JS交互 + + /2017/02/20/OC%E4%B8%8Ejs%E4%BA%A4%E4%BA%92%E5%AD%A6%E4%B9%A0/ + + WebView相关属性

autoResizingMask 是UIView的属性,作用是实现子控件相对于父控件的自动布局

1
2
3
4
5
6
7
8
9
autoResizingMask 是UIViewAutoresizing 默认是 UIViewAutoresizingNone

UIViewAutoresizingNone = 0,
UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
UIViewAutoresizingFlexibleWidth = 1 << 1,
UIViewAutoresizingFlexibleRightMargin = 1 << 2,
UIViewAutoresizingFlexibleTopMargin = 1 << 3,
UIViewAutoresizingFlexibleHeight = 1 << 4,
UIViewAutoresizingFlexibleBottomMargin = 1 << 5

各属性解释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
UIViewAutoresizingNone
不会随父视图的改变而改变

UIViewAutoresizingFlexibleLeftMargin

自动调整view与父视图左边距,以保证右边距不变

UIViewAutoresizingFlexibleWidth
自动调整view的宽度,保证左边距和右边距不变

UIViewAutoresizingFlexibleRightMargin
自动调整view与父视图右边距,以保证左边距不变

UIViewAutoresizingFlexibleTopMargin
自动调整view与父视图上边距,以保证下边距不变

UIViewAutoresizingFlexibleHeight
自动调整view的高度,以保证上边距和下边距不变

UIViewAutoresizingFlexibleBottomMargin
自动调整view与父视图的下边距,以保证上边距不变

OC 调用js

webViewDidFinishLoad:(UIWebView *)webView方法中

  • 删除增加结点
  • 添加点击事件

js调用OC

webView:(UIWebview )webView shouldStartLoadWithRequest:(NSURLRequest )request方法中

调用js页面点击按钮后保存图片到相册,必须调用系统提供的方法,不能自己自定义方法

]]>
+ + + + + + OC, JS + + + +
+ + + + + SDWebImage的用法及原理 + + /2017/02/08/SDWebImage%E5%AD%A6%E4%B9%A0/ + + SDWebImage 介绍

SDWebImage 是用于网络中下载且缓存图片,并设置图片到对应的控件或上

Demo: https://github.com/aTreey/DownImage.git

  • 提供了UIImageView的category用来加载网络图片并且下载的图片的缓存进行管理
  • 采用异步方式来下载,确保不会阻塞主线程,memory+disk来缓存网络图片,自动管理缓存
  • 支持GIF动画,[self.imageView setImageWithURL:[[NSBundle mainBundle] URLForResource:@”xx.gif” withExtension:nil];
  • 支持WebP格式
  • 同一个URL的网络图片不会被重复下载
  • 失效的URL不会被无限重试

SDWebImage 的使用

克隆

git clone https://github.com/rs/SDWebImage.git
使用以上命令克隆会报错,框架中Vendors文件夹中的文件未能全部下载导致报错

解决办法:https://github.com/rs/SDWebImage/blob/master/Docs/ManualInstallation.md

git clone –recursive https://github.com/rs/SDWebImage.git

文件全部下载

自定义operation 加入NSOperationQueue中

  • 自定义NSOperation,
    重写main方法
1
2
3
4
5
6
7
8
9
10
11
- (void)main {
@autoreleasepool {
NSURL *url = [NSURL URLWithString:_urlStr];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];

if (_finishBlock) {
_finishBlock(image);
}
}
}
  • 一个NSOperation对象可以通过调用start方法来执行任务,默认是同步执行的。也可以将NSOperation添加到一个NSOperationQueue(操作队列)中去执行,而且是异步执行的
    创建队列
1
2
3
4
- (NSOperationQueue *)downLoadQueue {
if (!_downLoadQueue) _downLoadQueue = [[NSOperationQueue alloc] init];
return _downLoadQueue;
}
  • 添加一个任务到队列
1
2
3
4
5
6
7
8
9
[_downLoadQueue addOperation:operation];
添加一组operation, 是否阻塞当前线程

[_downLoadQueue addOperations:@[operation] waitUntilFinished:NO];
添加一个block 形式的operation

[_downLoadQueue addOperationWithBlock:^{
NSLog(@"执行一个新的线程");
}];

注意:

  1. NSOperation 添加到 queue之后,通常短时间内就会执行,但是如果存在依赖,或者整个queue被暂停等原因,也可能会需要等待

  2. NSOperation添加到queue之后,绝不要修改NSOperation对象的状态,因为NSOperation对象可能会在任何时候运行,因此改变NSOperation对象的依赖或者数据会产生不利的影响,只能查看NSOperation 对象的状态,比如是否正在运行、等待运行、已经完成等

  • NSOperation 添加依赖,
    依赖关系不局限于相同的queue 中的NSOperation对象,可以夸队列进行依赖,但是不能循环依赖,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// NSOperation 添加依赖
{
NSOperationQueue *testQueue = [[NSOperationQueue alloc] init];
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"operation1 NSThread = %@", [NSThread currentThread]);
}];

NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"operation2 NSThead = %@", [NSThread currentThread]);
}];

// 添加依赖,1 依赖于 2,只有2 执行完之后才执行1
[operation1 addDependency:operation2];
// 加入到队列
[testQueue addOperation:operation1];
[testQueue addOperation:operation2];
}
  • 修改Operation的执行顺序
    对于添加到queue中的operation执行顺序有一下2点决定
    operation 是否已经准备好,是否添加了依赖
    根据多有operations的相对优先性来决定,优先等级是operation对象本身的一个属性,默认都是“普通”优先级,可以通过setQueuePriority方法提高和降低优先级,优先级只能用于相同 queue 中的 operation,多个queue中operation的优先级相互独立,因此不同queue中的低优先级的operation可能比高优先级的operation更早执行
    注意优先级和依赖不能相互替代,优先级只是对已经准备好的operation确定执行顺序,先满足依赖,然后再看已经准备好的operation中的优先级

  • 设置 queue 的最大并发数,队列中最多同时运行几条线程
    虽然NSOperationQueue类设计用于并发执行Operations,你也可以强制单个queue一次只能执行一个Operation。setMaxConcurrentOperationCount:方法可以配置queue的最大并发操作数量。设为1就表示queue每次只能执行一个操作。不过operation执行的顺序仍然依赖于其它因素,比如operation是否准备好和operation的优先级等。因此串行化的operation queue并不等同于GCD中的串行dispatch queue

1
2
3
4
// 每次只能执行一个操作  
queue.maxConcurrentOperationCount = 1;
// 或者这样写
[queue setMaxConcurrentOperationCount:1];
  • 取消 operations
    一旦添加到operation queue,queue就拥有了这个Operation对象并且不能被删除,只能取消。调用Operation对象的cancel方法取消单个操作,也可以调用operation queue的cancelAllOperations方法取消当前queue中的所有操作, 使用cancel属性来判断是否已经取消了

[operation cacel] 只是打了一个死亡标记, 并没有正真意义上的取消,称为 “自杀“,需要在被取消的任务中时时判断是否取消(在程序的关键处), 如果取消,结束任务, 具体是指在自定义的 operation 的main方法中结束

1
2
3
4
5
6
[queue cancelAllOperations] 由队列来取消,称为 “他杀”

// 取消单个操作
[operation cancel];
// 取消queue中所有的操作
[queue cancelAllOperations];
  • 等待operation 完成

为了最佳的性能,你应该设计你的应用尽可能地异步操作,让应用在Operation正在执行时可以去处理其它事情。如果需要在当前线程中处理operation完成后的结果,可以使用NSOperation的waitUntilFinished方法阻塞当前线程,等待operation完成。通常我们应该避免编写这样的代码,阻塞当前线程可能是一种简便的解决方案,但是它引入了更多的串行代码,限制了整个应用的并发性,同时也降低了用户体验。绝对不要在应用主线程中等待一个Operation,只能在第二或次要线程中等待。阻塞主线程将导致应用无法响应用户事件,应用也将表现为无响应。

1
2
// 会阻塞当前线程,等到某个operation执行完毕  
[operation waitUntilFinished];

除了等待单个Operation完成,你也可以同时等待一个queue中的所有操作,使用NSOperationQueue的waitUntilAllOperationsAreFinished方法。注意:在等待一个 queue时,应用的其它线程仍然可以往queue中添加Operation,因此可能会加长线程的等待时间。

// 阻塞当前线程,等待queue的所有操作执行完毕
[queue waitUntilAllOperationsAreFinished];
暂停和继续operation
如果你想临时暂停Operations的执行,可以使用queue的setSuspended:方法暂停queue。不过暂停一个queue不会导致正在执行的operation在任务中途暂停,只是简单地阻止调度新Operation执行。你可以在响应用户请求时,暂停一个queue来暂停等待中的任务。稍后根据用户的请求,可以再次调用setSuspended:方法继续queue中operation的执行

1
2
3
4
5
// 暂停queue  
[queue setSuspended:YES];

// 继续queue
[queue setSuspended:NO];

增加Manager管理类,

作用:

  • 下载队列
  • 图像缓存
  • 操作缓存(内存缓存)
  • 沙盒缓存
  • 防止错乱,取消老的未开始下载的操作
  • 图片显示逻辑:内存缓存 – 沙盒缓存 – 网络下载
  • 图片缓存逻辑:下载完成 – 缓存沙盒 – 加载到内存

注意:缓存到本地时只能保存property列表里的对象(NSData, NSDate, NSNumber, NSString, NSArray, NSDictory), 图片需要转化为二进制数据

SDWebImage

  • SDWebImageDownloader 下完图片后都需要手动设置给UIImageView
  • SDWebImageManager 下完图片后都需要手动设置给UIImageView
  • 下载后要显示可以使用 sd_setImageWithURL 方法
]]>
+ + + + + + SDWebImage + + + +
+ + + + + WKWebiView 使用 + + /2017/02/02/iOS%E9%9D%99%E6%80%81%E5%BA%93%E5%8A%A8%E6%80%81%E5%BA%93/ + + iOS中静态库动态库介绍及编译
  • 静态库

iOS中根据源码的公开情况可分为2种

  • 开源库
    源代码公开,能够看到具体的代码实现

比如 SDWebImage AFNetworking。。。

  • 闭源库
    不公开源代码, 是经过编译后的二进制文件, 看不到具体实现
    主要分为:静态库、动态库

  • 存在形式

静态库: .a 和 .framework
动态库:.tbd 和 .framework (iOS 9 之前是 .dylib, iOS 9 之后取消了 .dylib 而使用 .tbd 来代替 .dylib )

.framework 的库可以是静态库也可以是静态库, 凡是系统的 .framework 都是动态库,第三方的基本上全是静态中

  • 两者区别

静态库:链接时会被完整的复制到可执行文件中,每次被使用都会被复制
动态库:链接时不复制,程序运行时由系统动态加载到内存中,供程序调用,系统只加载一次,多个程序可以公用,节省内存

静态库编译

编译时如果使用的是模拟器,生成的 .a 只能模拟器用,真机不可用,生成的 .a 文件在 Debug-iphonesimulator 文件夹下

编译时如果使用 generic iOS Device,生成的 .a 只能真机而模拟器不可用,生成的 .a 文件在 Debug-iphoneos 文件夹下

编译静态库时,自己手动创建的类的头文件不会被导出,只是默认导出创建工程时类的头文件

导出手动添加类的头文件,点击工程名称 –> build Phases –> Copy Files 添加头文件,然后编译

  • 架构种类

模拟器:i386 32位架构 (4s~5) / x86_64 64位架构 (5s以后机型)
真机:armv7 32位架构 (4~4s) / armv7s 特殊架构 (5~5C) / arm64 64位架构 (5s 以后机型)

  • 架构查看
    lipo -info xxxx.a
    默认生成模拟器只生成一种架构 i386 x86_64;真机默认导出两种架构 armv7 / arm64

  • 合并多个架构

将模拟器的多个架构合并,项目名称–>Build Settings –> Achitectures –> Build Active Architecture Only –> Debug设置为NO
合并真机多个架构 按照以上方法运行真机即可
合并真机和模拟器架构 cd products lipo -create Debug-iphoneos/xxx.a Debug-iphonessimulator/xxx.a -output xx.a
合成 5 个架构 在Build Setting –> Architectures 中手动添加三种架构,再编译合并

  • 静态库报错

Undefined symbols for architecture (arm64 / armv7 / armv7s / i386 / x86_64),架构包导入出错,静态库有真机与模拟器

]]>
+ + + + + + WKWebView + + + +
+ + + + + 异步下载图片 + + /2017/01/18/%E5%BC%82%E6%AD%A5%E4%B8%8B%E8%BD%BD%E5%9B%BE%E7%89%87/ + + 异步下载

同步发送网络请求会卡UI线程,采用异步来下载图片。

异步下载存在的问题

  • 错行问题

    • 解决:使用MVC模式,使数据一对一可解决,
    • 在MVC中增加image属性后,会导致单个模式的体积增大
    • 内存占用
    • 当接收到内存警告时清空图片时需要遍历模型中将image置为nil
    • 将图片用字典缓存起来达到一对一, 清空内存中的图片时只需要remove字典就ok
    • 创建下载任务的字典,在开始下载时将正在下载的URL写入到字典中下载完成后将字典中对应的值移除,写入防止来回滑动时重复发送网络请求,下载完后移除是为了防止如果接受到内存警告后下载的图片都已清除,但是下载任务的字典的值为移除就不会再一次去下载图片
    • 缓存下载的图片到沙盒中

    • 获取图片下载路径最后一个分隔符后面的字符串作为图片名保存

stringByAppendingPathComponentstringByAppendingString 两个方法有区别

stringByAppendingString 方法是获取caches文件名,在文件夹名后面拼接字符串,在caches上一级目录下创建文件,和caches文件夹属于同一个级别

// 获取caches文件路径NSString *cachesPath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);NSString *URL = @"http://wwww.baidu.com/image/123456.png";NSString *imageName = [URL lastPathComponent];// 拼接路径// 此方法生成的路径在caches文件下目录下NSString *filePath = [cachesPath stringByAppendingPathComponent];// 读取图片if (内存中没有) {    // 沙盒读取    UIImage *sandBoxImage = [UIImage imageWithContetsOfFile:filePath];    **加入到内存中,方便下次加载**} if (sandBoxImage == nil) {    // 下载图片}// 写入到沙盒if (imageData != nil) {    [imageData writeToFile: filePath atomically:NO];}
]]>
+ + + + + + 异步 + + + +
+ + + + + Quartz 2D绘制形状(二) + + /2016/12/10/Quartz2D%E7%BB%98%E5%88%B6%E5%BD%A2%E7%8A%B6/ + + 使用Quartz 2D绘制圆弧,扇形,下载进度以及绘制的图形的填充和描边属性的设置.
将贝塞尔路径添加到图形上下文中
图形上下文中的状态保存和恢复
对图形上下文进行矩阵操作(平移,缩放,选转),实现一些特殊效果

使用贝塞尔曲线绘制矩形

1
2
3
4
5
6
7
{
UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, 20, 30)];
[[UIColor magentaColor] setStroke];
[[UIColor brownColor] setFill];
[path fill]; // 填充
[path stroke]; // 描边
}

圆角矩形

1
2
3
4
5
6
7
8
// 绘制圆角矩形
{
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(35, 10, 50, 70) cornerRadius:5.0];
[[UIColor magentaColor] setStroke];
[[UIColor cyanColor] setFill];
[path fill];
[path stroke];
}

绘制圆

1
2
3
4
5
6
7
8
// 画圆
{
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(90, 5, 40, 40) cornerRadius:20.0];
[[UIColor purpleColor] setStroke];
[[UIColor magentaColor] setFill];
[path fill];
[path stroke];
}

绘制扇形

1
2
3
4
5
6
CGPoint center = CGPointMake(200, 50);
UIBezierPath *sectorPath = [UIBezierPath bezierPathWithArcCenter:center radius:100 startAngle:0 endAngle:M_PI_4 clockwise:YES];
[sectorPath addLineToPoint:center];
[[UIColor brownColor] setStroke];
[sectorPath fill];
[sectorPath stroke];

绘制圆弧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 绘制圆弧
* 参数1: 圆心坐标
* 参数2: 半径
* 参数3: 开始角度
* 参数4: 结束角度 使用弧度制表示
* 参数5: clockwis顺时针方法的
*/

UIBezierPath *circuLarArcPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(100, 60) radius:50.0 startAngle:0 endAngle:M_PI_2 clockwise:YES];

[circuLarArcPath moveToPoint:CGPointMake(100, 60)];

[[UIColor magentaColor] setStroke];
[[UIColor brownColor] setFill];
[circuLarArcPath stroke];
[circuLarArcPath fill];

绘制文字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
* 绘制文字
* drawAtPoint 不能换行,当文字过多过长时不会有效果
* 使用 drawInRect 可以实现换行
*/


NSString *text = @"我是绘制文字价格就看过几个框架刚开始大家高考的感觉是快乐的关键时刻大家赶快来时的结果看了风水格局来看就哭了感觉树大根深来对抗肌肤抵抗力";

NSMutableDictionary *attributeDict = [NSMutableDictionary dictionary];
attributeDict[NSFontAttributeName] = [UIFont systemFontOfSize:18];

// 字体颜色
attributeDict[NSForegroundColorAttributeName] = [UIColor greenColor];
// 描边颜色
attributeDict[NSStrokeColorAttributeName] = [UIColor yellowColor];
// 描边宽度
attributeDict[NSStrokeWidthAttributeName] = @1;
// 阴影
NSShadow *shadow = [[NSShadow alloc] init];
shadow.shadowColor = [UIColor redColor];
shadow.shadowOffset = CGSizeMake(1.0, 1.0);

attributeDict[NSShadowAttributeName] = shadow;

// 可以换行
[text drawInRect:self.bounds withAttributes:attributeDict];

// 不换行
[text drawAtPoint:CGPointZero withAttributes:attributeDict];

绘制图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 绘制图片
* drawAtPoint 默认绘制的内容尺寸和图片的尺寸一样大
*
*/

- (void)drawImage {

UIImage *image = [UIImage imageNamed:@"wc_dk"];
UIImage *image1 = [UIImage imageNamed:@"wc_hq"];

[image drawAtPoint:CGPointMake(10, 90)];
[image1 drawAtPoint:CGPointMake(70, 80)];

// 绘制在控件内部,和控件大小一致
[image1 drawInRect:self.bounds];

// 以平铺的方式绘制在控件的内部
[image1 drawAsPatternInRect:self.bounds];
}

圆形下载进度的绘制

drawRect 方法不能手动调用,因为图形上下文不能手动创建
需要调用setNeedsDisplay方法来重绘,系统会创建图形上下文
绘制时用到的定时器,一般不使用NSTimer定时器,因为调度优先级比较低,不会被准时带,界面会出现卡顿的现象
CADisplayLink 每次刷新屏幕的时候就会调用,1s刷新 60 次
setNeedsDisplay调用此方法时并不是立即调用drawRect方法,只是给给当前控件添加刷新标记,直到下一次屏幕刷新的时候才调用drawRect 方法,正好与CADisplayLink 方法一致,所以界面流畅不会卡顿
图形上下文状态栈

UIBezierPath图形上下文和CGContextRef的图形上下文不是同一个图形上下文

可以将贝塞尔路径添加到图形上下文中

也可以保存当前的图形上下文,CGContextSaveGState(ctx);
在下一次使用时可以恢复,CGContextRestoreGState(ctx);
画两种不同状态的线条

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
/**
* 图形上下文栈的理解
* 画两条状态不同的线段
*/

// 获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();

// 第一条
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(10, 60)];
[path addLineToPoint:CGPointMake(240, 60)];

// 把路径添加到上下文
CGContextAddPath(ctx, path.CGPath);

// 保存上下的状态
CGContextSaveGState(ctx);

// 设置当前状态
[[UIColor greenColor] setStroke];
//此时设置path 的状态无效,需要设置当前上下文的状态
path.lineWidth = 5.0;
CGContextSetLineWidth(ctx, 5.0);

// 渲染上下文
CGContextStrokePath(ctx);


// 第二根

// 可使用第一根的路径,因为每次绘制完成之后都会清除
path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(60, 10)];
[path addLineToPoint:CGPointMake(60, 240)];

// 添加路径到上下文 需要转换为CGPath
CGContextAddPath(ctx, path.CGPath);

// 还原之前保存的上下文状态
CGContextRestoreGState(ctx);
CGContextStrokePath(ctx);
图形上下文矩阵

// 使用矩阵

// 获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();

// 使用贝塞尔路径
UIBezierPath *ovalPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(-150, -100, 300, 200)];
[[UIColor cyanColor] setFill];



// 以上绘制的图形有一部分看不到,通过对上下文矩阵操作来实现,平移上下文
// 操作矩阵必须在添加路径之前

// 平移
CGContextTranslateCTM(ctx, 150, 100);

// 缩放
CGContextScaleCTM(ctx, 0.5, 0.5);

// 旋转
// 可以实现点无法计算时的效果
CGContextRotateCTM(ctx, M_PI_4);



// 添加路径到上下文
CGContextAddPath(ctx, ovalPath.CGPath);

// 操作矩阵放在添加路径之后无效果
// CGContextTranslateCTM(ctx, 150, 100);

CGContextFillPath(ctx);

// 正常的绘制椭圆
// UIBezierPath *ovalPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(50, 20, 200, 100)];
// ovalPath.lineWidth = 4.0;
// [[UIColor magentaColor] setFill];
// [[UIColor yellowColor] setStroke];
// [ovalPath stroke];
// [ovalPath fill];
]]>
+ + + + + + Quartz 2D + + + +
+ + + + + GCD实现按钮定时亮起 + + /2016/12/05/GCD%E5%AE%9E%E7%8E%B0%E6%8C%89%E9%92%AE%E5%AE%9A%E6%97%B6%E4%BA%AE%E8%B5%B7/ + + 某些时候需要按钮不能连续点击,需要在一定的时间之后才可以允许交互,或者是实现类似发送验证码的按钮效果,具体做法是采用定时器

使用GCD定时器

创建定时器对象

__block NSInteger time = 30; // 需要强应用

创建队列

dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

创建dispatch源(定时器)

(dispatach_source…timer..)
  • 01参数:要创建的source 是什么类型的,

    (DISPATCH_SOURCE_TYPE_TIMER)定时器

  • 04参数:队列 —-线程 决定block 在哪个线程中调用

代码

dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);

设置定时器

  • 01参数:定时器对象

  • 02参数:开始时间 (DISPATCH_TIME_NOW) 什么时候开始执行第一次任务

  • 03参数:间隔时间 GCD时间单位:纳秒

  • 04参数:leewayInSeconds精准度:允许的误差: 0 表示绝对精准

code:

dispatch_source_set_timer(_timer, dispatch_walltime(NULL, 0), 1.0 * NSEC_PER_SEC, 0);

定时器每隔一段时间就要执行任务(block回调)

dispatch_source_set_event_handler(_timer, ^{    if (time <= 0) {        dispatch_source_cancel(_timer);        dispatch_async(dispatch_get_main_queue(), ^{            // 设置按钮的样式            [self.button setTitle:@"重新获取验证码" forState:UIControlStateNormal];            [self.button setTitleColor:[UIColor redColor] forState:UIControlStateNormal];            [self.button setUserInteractionEnabled:YES];        });    } else {        NSInteger seconds = time;        dispatch_async(dispatch_get_main_queue(), ^{            [self.button setTitle:[NSString stringWithFormat:@"重新发送(%.2ld)", seconds] forState:UIControlStateNormal];            [self.button setTitleColor:[UIColor lightGrayColor] forState:UIControlStateNormal];            [self.button setUserInteractionEnabled:NO];        });        time--;    }});

启动定时器(默认是停止的)

dispatch_resume(timer);
]]>
+ + + + + + GCD + + + +
+ + + + + Quartz2D绘制曲线 + + /2016/12/04/Quartz2D%E7%BB%98%E5%88%B6%E6%9B%B2%E7%BA%BF/ + + 使用函数绘制曲线

Quartz 2D提供了CGContextAddCurveToPoint() 函数和CGContextAddQuadCurveToPoint()两个函数来向当前上下文添加曲线,前者用于添加贝塞尔曲线,后者用于添加二次曲线。
确定一条贝塞尔曲线需要4个点:开始点、第一个控制点、第二个控制点和结束点。

确定一条二次曲线需要三个点:开始点、控制点和结束点。

添加贝塞尔曲线

CGContextAddCurveToPoint()CGContextRef ctx = UIGraphicsGetCurrentContext();// 添加曲线路径// 设置起点CGContextMoveToPoint(ctx, 0, 0);
  • 参数1: 上下文对象
  • 参数2: 控制点1X坐标
  • 参数3: 控制点1Y坐标
  • 参数4: 控制点2X坐标
  • 参数5: 控制点2Y坐标
  • 参数6: 终点x坐标
  • 参数7: 终点Y坐标

code:

CGContextAddCurveToPoint(ctx, 30, 200, 300, 20, 300, 300);// 设置颜色[[UIColor magentaColor] setStroke];// 渲染CGContextStrokePath(ctx);// 关闭图形上下文CGContextClosePath(ctx);添加二次曲线CGContextAddQuadCurveToPoint()// 获取图形上下文CGContextRef ctx = UIGraphicsGetCurrentContext();// 起始点CGContextMoveToPoint(ctx, 20, 10);
  • 参数: 图形上下文
  • 参数: 控制点坐标
  • 参数: 结束点坐标

添加二次曲线

CGContextAddQuadCurveToPoint(ctx, 30, 200, 200, 40);[[UIColor cyanColor] setStroke];CGContextStrokePath(ctx);// 关闭图形上下文CGContextClosePath(ctx);

绘制形状

使用绘制二次曲线的函数绘制花瓣

程序中CGContextAddFlower代码分别添加5瓣花朵路径、6瓣花朵路径、7瓣花朵路径,然后使用不同的颜色来填充这些路径。注意到上面的程序并未在每次添加花朵路径后立即关闭,这也是允许的,而且每次填充路径时并不会再次填充前一次已经填充过的路径。这是因为只用程序绘制了CGContextRef当前所包含的路径,系统会自动清除已经绘制的路径。

注意:每次绘制完成后,CGContextRef会自动清除已经绘制完成的路径

该方法负责绘制花朵。
n:该参数控制花朵的花瓣数;dx、dy:控制花朵的位置;size:控制花朵的大小;
length:控制花瓣的长度

void CGContextAddFlower(CGContextRef c , NSInteger n                        , CGFloat dx , CGFloat dy , CGFloat size , CGFloat length){    CGContextMoveToPoint(c , dx , dy + size);  // 移动到指定点    CGFloat dig = 2 * M_PI / n;    // 采用循环添加n段二次曲线路径    for(int i = 1; i < n + 1 ; i++)    {        // 计算控制点坐标        CGFloat ctrlX = sin((i - 0.5) * dig) * length + dx;        CGFloat ctrlY= cos((i - 0.5 ) * dig) * length + dy;        // 计算结束点的坐标        CGFloat x = sin(i * dig) * size + dx;        CGFloat y =cos(i * dig) * size + dy;        // 添加二次曲线路径        CGContextAddQuadCurveToPoint(c, ctrlX , ctrlY , x , y);    }}

绘制花瓣

- (void)drawStar{    CGContextRef ctx = UIGraphicsGetCurrentContext();  // 获取绘图的CGContextRef    CGContextBeginPath(ctx);  // 开始添加路径    CGContextAddFlower(ctx , 5 , 50 , 100 , 30 , 80);  // 添加5瓣花朵的路径    CGContextSetRGBFillColor(ctx, 1, 0, 0, 1);  // 设置填充颜色    CGContextFillPath(ctx);    CGContextAddFlower(ctx , 6 , 160 , 100 , 30 , 80);  // 添加6瓣花朵的路径    CGContextSetRGBFillColor(ctx, 1, 1, 0, 1);  // 设置填充颜色    CGContextFillPath(ctx);    CGContextAddFlower(ctx , 7 , 270 , 100 , 30 , 80);  // 添加7瓣花朵的路径    CGContextSetRGBFillColor(ctx, 1, 0, 1, 1);  // 设置填充颜色    CGContextFillPath(ctx);    CGContextClosePath(ctx);  // 关闭路径}
]]>
+ + + + + + Quartz2D + + + +
+ + + + + Quartz2D 初识 + + /2016/12/04/Quartz2D%E5%88%9D%E8%AF%86/ + + Quartz2D 二维绘图引擎,支持iOS和Mac系统么,可以实现的功能有

  • 绘制图形: 线条,三角形,矩形,圆,弧等
  • 绘制文字
  • 绘制图像或者图片
  • 读取生成PDF
  • 截图/裁剪图片
  • 自定义UI控件
  • 手势解锁
  • 图形上下文

保存绘图信息和状态

确定输出目标(PDF,Bitmap或者显示器)

Bitmap Graphics ContextPDF Granhics ContextWindow Graphics ContextLayer Graphics Context

绘制线段

- (void)drawLine {    // 获取图形上下文    // 所用的是UIGraphics上下文    CGContextRef ctx = UIGraphicsGetCurrentContext();    // 创建描述路径    CGMutablePathRef path = CGPathCreateMutable();    // 设置起点    // path : 表示给那个路径设置起点    CGPathMoveToPoint(path, NULL, 10, 10);    CGPathAddLineToPoint(path, NULL, 80, 80);    // 添加路径到上下文    CGContextAddPath(ctx, path);    // 渲染    CGContextStrokePath(ctx);}

系统底层自动将路径添加到图形上下文

- (void)drawLine2 {    CGContextRef ctx = UIGraphicsGetCurrentContext();    // 描述路径    // 此方法底层会自动将路径添加到图形上下文    CGContextMoveToPoint(ctx, 50, 50);    CGContextAddLineToPoint(ctx, 100, 100);    CGContextSetLineWidth(ctx, 5);    [[UIColor redColor] set];    // 渲染    CGContextStrokePath(ctx);}

使用C语言函数的封装

- (void)drawLine3 {    // 使用UIKit 已经封装的功能,面向对象    // 贝塞尔路径    UIBezierPath *path = [UIBezierPath bezierPath];    [path moveToPoint:CGPointMake(10, 10)];    [path addLineToPoint:CGPointMake(50, 50)];    // 绘制路径    [path stroke];}

绘制多条线段

  • 绘制多条线,状态不好管理
  • 默认下一条线的起点是上一条线的终点
  • 绘制多天不连接的线时需要重新设置起点
  • 为了方便管理,应该让每一条线对应一条路径
  • 使用UIBeizerPath

code:

- (void)drawMoreLine {    CGContextRef ctx = UIGraphicsGetCurrentContext();    // 描述路径    CGContextMoveToPoint(ctx, 20, 20);    CGContextAddLineToPoint(ctx, 100, 100);    // 设置第二条线的起点    CGContextMoveToPoint(ctx, 20, 30);    CGContextAddLineToPoint(ctx, 150, 100);    // 设置绘图状态要在渲染之前,都是给上下文设置    // 描边颜色    [[UIColor greenColor] setStroke];    // 线宽    CGContextSetLineWidth(ctx, 10);    // 连接样式    // kCGLineJoinMiter,    斜接    // kCGLineJoinRound,    圆角    // kCGLineJoinBevel     平切    CGContextSetLineJoin(ctx, kCGLineJoinBevel);    // 顶角样式,线的顶端效果    CGContextSetLineCap(ctx, kCGLineCapRound);    // 渲染    CGContextStrokePath(ctx);}

使用UIBezierPath 绘制多条直线并设置不同状态

- (void)drawUIBezierPahtState {    // 第一条线    UIBezierPath *path = [UIBezierPath bezierPath];    [path moveToPoint:CGPointMake(10, 10)];    [path addLineToPoint:CGPointMake(80, 80)];    path.lineWidth = 20;    path.lineJoinStyle = kCGLineJoinRound;    path.lineCapStyle = kCGLineCapRound;    [[UIColor redColor] setStroke];    [path stroke];    // 第二条线    {        UIBezierPath *path = [UIBezierPath bezierPath];        [path moveToPoint:CGPointMake(40, 70)];        [path addLineToPoint:CGPointMake(150, 40)];        path.lineWidth = 20;        path.lineJoinStyle = kCGLineJoinRound;        path.lineCapStyle = kCGLineCapRound;        [[UIColor greenColor] setStroke];        [path stroke];    }}
]]>
+ + + + + + Quartz2D + + + +
+ + + + + OC中运行时 + + /2016/11/22/OC%20RunTime%E5%AD%A6%E4%B9%A0/ + + RunTime
对于C语言,函数的调用在编译的时候会决定调用哪个函数,而对于面向对象的语言对于[调用某个对象的方法或函数]就叫「消息传递」。所以在 Java,C++ 这些语言里[调用某个对象的方法或函数]和 Objective-C 里[向某个对象发送消息]在概念上就是一样的事情,

RunTime 简介

  • RunTime 简称运行时,OC中通常叫运行时机制,主要的是消息机制
  • 消息机制:在OC中,属于动态调用,在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用

RunTime 作用

  • 发送消息:让对象发送消息
  • 交换方法
  • 动态添加方法
  • 动态获取属性和方法
  • 字典转模型
  • 给分类添加属性

原理:给分类添加一个属性,重写了setter、getter方法,但是没有生成 _成员变量,需要通过运行时关联到分类中, 其本质上就是给这个类添加关联,存值和取值
code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
self.oldurlPath = urlPath;

- (void)setOldurlPath:(NSString *)oldurlPath {

// RunTime 关联
// 第一个参数:给哪个对象添加关联
// 第二个参数:关联的key,通过这个key获取
// 第三个参数:关联的value
// 第四个参数:关联的策略
objc_setAssociatedObject(self, key, oldurlPath, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (NSString *)oldurlPath {
return objc_getAssociatedObject(self, key);
}
]]>
+ + + + + + RunTime + + + +
+ + + + + WKWebiView 使用 + + /2016/11/22/Quartz2D%E7%BB%98%E5%88%B6%E7%BA%BF%E6%9D%A1/ + + WKWebiView iOS8.0 出现,性能要远远好于 iOS 2.0时出现的 UIWebView,主要记录WKWebView 的创建和代理方法

创建WKWebView

WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];self.webView = webView;[self.view addSubview:webView];webView.UIDelegate = self;webView.navigationDelegate = self;[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.baidu.com"]]];

遵守协议 WKNavgationDelegate WKUIDelegate

代理方法

  • WKNavgationDelegate 方法

    • 发送请求之前决定是否跳转

      1
      2
      3
      4
      5
      6
      7
      - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {

      decisionHandler(WKNavigationActionPolicyAllow);
      // 不允许跳转
      decisionHandler(WKNavigationActionPolicyCancel);
      NSLog(@"decidePolicyForNavigationAction");
      }
    • 收到相应之后决定是否跳转

      1
      2
      3
      4
      5
      6
      7
      8
      9
      - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {

      decisionHandler(WKNavigationResponsePolicyAllow);

      decisionHandler(WKNavigationResponsePolicyCancel);

      NSLog(@"decidePolicyForNavigationResponse");

      }
    • 开始加载时调用

      1
      2
      3
      4
      - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation {

      NSLog(@"didStartProvisionalNavigation");
      }
    • 接受到服务器的跳转请求时调用

      1
      2
      3
      - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
      NSLog(@"didReceiveServerRedirectForProvisionalNavigation");
      }
- 页面加载失败时调用 **存在缓存问题**
1
2
3
4
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

NSLog(@"error= %@", error);
}
- 内容开始返回时调用
1
2
3
4
- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation {

NSLog(@"didCommitNavigation");
}
  • 内容返回成功后调用

    1
    2
    3
    4
    - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {

    NSLog(@"didFinishNavigation");
    }
  • 开始返回错误时调用

    1
    2
    3
    4
    - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

    NSLog(@"didFailNavigation");
    }
  • 目前不知道什么时候调用

    1
    2
    3
    - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler {
    NSLog(@"didReceiveAuthenticationChallenge");
    }
    • 目前不知道什么时候调用

      1
      2
      3
      4
      - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView API_AVAILABLE(macosx(10.11), ios(9.0)) {

      NSLog(@"webViewWebContentProcessDidTerminate");
      }
]]>
+ + + + + + WKWebView + + + +
+ + + + + WKWebiView 使用 + + /2016/11/22/Swift3.0%E5%AD%A6%E4%B9%A0(%E4%BA%94)/ + + WKWebiView iOS8.0 出现,性能要远远好于 iOS 2.0时出现的 UIWebView,主要记录WKWebView 的创建和代理方法

创建WKWebView

WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];self.webView = webView;[self.view addSubview:webView];webView.UIDelegate = self;webView.navigationDelegate = self;[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.baidu.com"]]];

遵守协议 WKNavgationDelegate WKUIDelegate

代理方法

  • WKNavgationDelegate 方法

    • 发送请求之前决定是否跳转

      1
      2
      3
      4
      5
      6
      7
      - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {

      decisionHandler(WKNavigationActionPolicyAllow);
      // 不允许跳转
      decisionHandler(WKNavigationActionPolicyCancel);
      NSLog(@"decidePolicyForNavigationAction");
      }
    • 收到相应之后决定是否跳转

      1
      2
      3
      4
      5
      6
      7
      8
      9
      - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {

      decisionHandler(WKNavigationResponsePolicyAllow);

      decisionHandler(WKNavigationResponsePolicyCancel);

      NSLog(@"decidePolicyForNavigationResponse");

      }
    • 开始加载时调用

      1
      2
      3
      4
      - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation {

      NSLog(@"didStartProvisionalNavigation");
      }
    • 接受到服务器的跳转请求时调用

      1
      2
      3
      - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
      NSLog(@"didReceiveServerRedirectForProvisionalNavigation");
      }
- 页面加载失败时调用 **存在缓存问题**
1
2
3
4
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

NSLog(@"error= %@", error);
}
- 内容开始返回时调用
1
2
3
4
- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation {

NSLog(@"didCommitNavigation");
}
  • 内容返回成功后调用

    1
    2
    3
    4
    - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {

    NSLog(@"didFinishNavigation");
    }
  • 开始返回错误时调用

    1
    2
    3
    4
    - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

    NSLog(@"didFailNavigation");
    }
  • 目前不知道什么时候调用

    1
    2
    3
    - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler {
    NSLog(@"didReceiveAuthenticationChallenge");
    }
    • 目前不知道什么时候调用

      1
      2
      3
      4
      - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView API_AVAILABLE(macosx(10.11), ios(9.0)) {

      NSLog(@"webViewWebContentProcessDidTerminate");
      }
]]>
+ + + + + + WKWebView + + + +
+ + + + + WKWebiView 使用 + + /2016/11/22/Swift3.0%E5%AD%A6%E4%B9%A0(%E5%9B%9B)/ + + WKWebiView iOS8.0 出现,性能要远远好于 iOS 2.0时出现的 UIWebView,主要记录WKWebView 的创建和代理方法

创建WKWebView

WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];self.webView = webView;[self.view addSubview:webView];webView.UIDelegate = self;webView.navigationDelegate = self;[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.baidu.com"]]];

遵守协议 WKNavgationDelegate WKUIDelegate

代理方法

  • WKNavgationDelegate 方法

    • 发送请求之前决定是否跳转

      1
      2
      3
      4
      5
      6
      7
      - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {

      decisionHandler(WKNavigationActionPolicyAllow);
      // 不允许跳转
      decisionHandler(WKNavigationActionPolicyCancel);
      NSLog(@"decidePolicyForNavigationAction");
      }
    • 收到相应之后决定是否跳转

      1
      2
      3
      4
      5
      6
      7
      8
      9
      - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {

      decisionHandler(WKNavigationResponsePolicyAllow);

      decisionHandler(WKNavigationResponsePolicyCancel);

      NSLog(@"decidePolicyForNavigationResponse");

      }
    • 开始加载时调用

      1
      2
      3
      4
      - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation {

      NSLog(@"didStartProvisionalNavigation");
      }
    • 接受到服务器的跳转请求时调用

      1
      2
      3
      - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
      NSLog(@"didReceiveServerRedirectForProvisionalNavigation");
      }
- 页面加载失败时调用 **存在缓存问题**
1
2
3
4
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

NSLog(@"error= %@", error);
}
- 内容开始返回时调用
1
2
3
4
- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation {

NSLog(@"didCommitNavigation");
}
  • 内容返回成功后调用

    1
    2
    3
    4
    - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {

    NSLog(@"didFinishNavigation");
    }
  • 开始返回错误时调用

    1
    2
    3
    4
    - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

    NSLog(@"didFailNavigation");
    }
  • 目前不知道什么时候调用

    1
    2
    3
    - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler {
    NSLog(@"didReceiveAuthenticationChallenge");
    }
    • 目前不知道什么时候调用

      1
      2
      3
      4
      - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView API_AVAILABLE(macosx(10.11), ios(9.0)) {

      NSLog(@"webViewWebContentProcessDidTerminate");
      }
]]>
+ + + + + + WKWebView + + + +
+ + + + + WKWebiView 使用 + + /2016/11/22/WKWebiView%E4%BD%BF%E7%94%A8/ + + WKWebiView iOS8.0 出现,性能要远远好于 iOS 2.0时出现的 UIWebView,主要记录WKWebView 的创建和代理方法

创建WKWebView

WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];self.webView = webView;[self.view addSubview:webView];webView.UIDelegate = self;webView.navigationDelegate = self;[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.baidu.com"]]];

遵守协议 WKNavgationDelegate WKUIDelegate

代理方法

  • WKNavgationDelegate 方法

    • 发送请求之前决定是否跳转

      1
      2
      3
      4
      5
      6
      7
      - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {

      decisionHandler(WKNavigationActionPolicyAllow);
      // 不允许跳转
      decisionHandler(WKNavigationActionPolicyCancel);
      NSLog(@"decidePolicyForNavigationAction");
      }
    • 收到相应之后决定是否跳转

      1
      2
      3
      4
      5
      6
      7
      8
      9
      - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {

      decisionHandler(WKNavigationResponsePolicyAllow);

      decisionHandler(WKNavigationResponsePolicyCancel);

      NSLog(@"decidePolicyForNavigationResponse");

      }
    • 开始加载时调用

      1
      2
      3
      4
      - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation {

      NSLog(@"didStartProvisionalNavigation");
      }
    • 接受到服务器的跳转请求时调用

      1
      2
      3
      - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
      NSLog(@"didReceiveServerRedirectForProvisionalNavigation");
      }
- 页面加载失败时调用 **存在缓存问题**
1
2
3
4
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

NSLog(@"error= %@", error);
}
- 内容开始返回时调用
1
2
3
4
- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation {

NSLog(@"didCommitNavigation");
}
  • 内容返回成功后调用

    1
    2
    3
    4
    - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {

    NSLog(@"didFinishNavigation");
    }
  • 开始返回错误时调用

    1
    2
    3
    4
    - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

    NSLog(@"didFailNavigation");
    }
  • 目前不知道什么时候调用

    1
    2
    3
    - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler {
    NSLog(@"didReceiveAuthenticationChallenge");
    }
    • 目前不知道什么时候调用

      1
      2
      3
      4
      - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView API_AVAILABLE(macosx(10.11), ios(9.0)) {

      NSLog(@"webViewWebContentProcessDidTerminate");
      }
]]>
+ + + + + + WKWebView + + + +
+ + + + + git 使用 + + /2016/11/20/git%E5%AD%A6%E4%B9%A0%E6%80%BB%E7%BB%93/ + + 使用 git 提交代码
  • 初始化工作目录

    1
    git init
  • 配置用户名和邮箱(当前仓库)

    1
    2
    git config user.name "aTreey"
    git config user.email "480814177@qq.com"
  • 配置全局的用户名和密码

    1
    2
    git config --global user.name "aTreey"
    git config --global user.email "480814177@qq.com"
  • 创建文件

    1
    touch main.m
  • 添加文件到暂缓区

    1
    git add main.m
  • 查看文件状态

    1
    git status
  • 新添加的文件或者新修改的文件在工作区中,没有添加到暂缓区中

1
Untracked files: (红色)
  • 需要添加到暂缓区中
1
git add main.m
  • 出现一下提示方可提交
1
Changes to be committed: (绿色)
  • 给git起别名

    1
    2
    git config alias.st "status"
    git config alias.ci "commit -m"
  • 查看所有版本库日志(只能查看当前版本的以前日志)

    1
    git log
  • 查看指定文件的版本库日志

    1
    git log 文件名
  • 查看所有的操作的日志

    1
    git reflog

版本会退

  • 强制回退到当前版本

    1
    git reset --hard HEAD
  • 强制回退到当前版本的上一个版本

    1
    git reset --hard HEAD^
  • 强制回退到当前版本的上上个版本

    1
    git reset --hard HEAD^^
  • 强制回退到当前版本的前100个版本

    1
    git reset --hard HEAD~100
  • 强制回退指定版本

    1
    git reset --hard HEAD 版本号前七位
]]>
+ + + + + + git + + + +
+ + + + + TableView 使用 + + /2016/11/05/TableView%E4%BD%BF%E7%94%A8%E6%80%BB%E7%BB%93/ + + tableView 使用总结
  • 字典的 key enumerator 的使用

  • tableViewCell 的 selected 方法可以监听cell 的选中, 使用selected 参数

  • 网络慢时点击左边cell右边数据不能及时刷新, 用户看到的还是上一个cell显示的数据

    • 解决办法: 在网络加载之前就reload数据,让显示空白
  • 点击左边的cell时 上拉加载更多控件的显示和隐藏逻辑

    • 解决办法: 在每次刷新右边数据时,控制footer的显示
  • 在numberOfRowsInSection方法中控制

  • 每次点击cell之前要结束上一个没有刷新完的,防止奔溃

  • 加载更多时

    在加载第一页时设置currentPage为1,在加载更多的逻辑中 ++currentPage
    点击cell加载时上一个加载更多的网络数据还没有回来,在当前请求之前结束掉所有的网络请求
    停止之后的刷新控件状态会有错误,没有更多还是还可以加载状态不确定
    在点击左边cell后刷新右边的之前需要检查右边tableView 的footer的状态
    下拉刷新

  • 第一次下拉刷新之后,第二次下拉刷新,将数据添加到数组中会导致两次数据重复

    • 解决办法:清除以前的所有数据
  • 下拉刷新完毕后结束刷新后需要检查footer的状态

  • 网络慢时连续点击cell,连发多个请求成功回调时数据显示错乱,或者是数据已经回来了但是还是显示上一个请求正在请求。。。

    • 解决办法:点击左边某个类别时,结束所有的刷新(上拉和下拉)
  • 连续点击发送请求

    • 解决办法:保存请求参数, 比较上一次参数和当前参数, 如果 != 直接return,不让进入成功回调解析数据, 失败的时候也是同样 return,不提示用户
  • 控制器正在请求数据时控制器销毁, 导致奔溃

    • 解决办法:在dealloc 中停止所有的操作

cell 的frame 设置

  • cell 之间的间隙以及cell的边距 重写frame 在super 之前设置

    • 设置frame 的x ,width, height 等属性
  • 分页的2种做法

    • 下拉刷新

      page 加载最新的 加载第一页 浪费用户的流量
      加载比当前更新的数据, 需要服务器支持,将当前最新的数据id发送给服务器,服务器加载比当前id还大的数据插入到最前面

    • 上拉加载

      将页码发给服务器

      将最后面的数据的id发给服务器,

      加载比当前id更小的数据

]]>
+ + + + + + TableView + + + +
+ + + + + Hexo个人博客搭建 + + /2016/11/05/Hexo%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2%E6%90%AD%E5%BB%BA/ + + 安装Homebrew
1
/usr/bin/ruby -e " (curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

安装git

1
sudo brew install git

安装node.js

安装hexo

  • 需要输入密码: sudo 管理员权限 -g 全局安装
1
sudo npm install -g hexo

初始化 执行 hexo init 命令

  • 指定安装文件 blog
1
hexo init blog
  • cd 到 文件夹 blog 下,安装 npm
1
npm install
  • 开启服务
1
hexo s

关联 github

  • 添加 ssh key 到github

  • 设置 Mac 显示隐藏文件

    • 在终端执行一下命令后,重启Finder

      1
      defaults write com.apple.finder AppleShowAllFiles -bool true
  • 检查 SSH keys 是否存在 github

    • 打开 Finder 前往(command + shit + G)

      1
      ~/.ssh/id_rsa.pub
  • 如果有文件说明存在 SSH key, 打开 id_rsa.pub 文件,复制里面的信息

  • 进入 Github –> Settings –> SSH keys –> add SSH key
    任意添加一个title 粘贴内容到key 里,点击Add key 绿色按钮即可。

创建名字为 用户名.github.io 的仓库

自行百度。。。。

配置 hexo

  • vim 打开 _config.yml修改 deploy 模块
1
vim _config.yml
1
2
3
4
deploy:
type: git
repository: https://github.com/用户名/用户.github.io.git
branch: master

冒号后需要加空格

生成 hexo 的静态页面

1
hexo generate 后者 hexo g
  • 报以下错
1
2
ERROR Local hexo not found in ~/blog
ERROR Try runing: 'npm install hexo --save'
  • 执行下面代码
1
npm install hexo --save

部署 hexo

1
hexo deploy 或者 hexo d
  • 报以下错误
1
ERROR Deployer not found: git

解决方法:

1
2
npm install hexo-deployer-git --save
hexo d
  • 配置 Deloyment 前,需要配置自己的身份信息
1
2
git config --global user.name "yourname"
git config --global user.email "youremail"

如果执行完以上命令还是不行,删除blog文件下的 .deploy_git 文件重新 hexo deploy

]]>
+ + + + + + Hexo + + + +
+ + + + + iOS 弹框视图 + + /2016/10/27/iOS%E5%BC%B9%E6%A1%86%E5%BC%8F%E5%9B%BE/ + + UIActionSheet UIAlertView UIAlertViewController 的区别

UIActionSheet

  • iOS 8.3 之后过期
  • 模态弹出,显示在试图的底部 类似菜单提供选择
1
2
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"title" delegate:self cancelButtonTitle:@"取消" destructiveButtonTitle:nil otherButtonTitles:@"按钮1", @"按钮2", nil];
[actionSheet showInView:self.view];

UIAlertView

  • iOS 9.0 之后过期
  • 模态弹出,显示在试图的中间位置 类似一个对话框提供选择
1
2
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"考勤方式" message:nil delegate:self cancelButtonTitle:@"取消" otherButtonTitles:faceBtn, fingerPrintBtn, nil];
[alertView show];

UIAlertViewController

  • iOS 8.0 起可以使用
  • style : 默认样式, 类似 UIActionSheet 列表形式
  • style 为:UIAlertControllerStyleAlert 对话框形式
  • 设置按钮的样式

如果修改 UIAlertAction 中stye 为:

UIAlertActionStyleCancel
取消按钮会和其他按钮分开

如果修改 UIAlertAction 中stye 为:

UIAlertActionStyleDestructive
取消按钮会以红色警告的方式显示, 但是按钮之间不会分开

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"考勤方式" message:nil preferredStyle:UIAlertControllerStyleActionSheet];
UIAlertAction *faceBtn = [UIAlertAction actionWithTitle:@"人脸识别" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {}];

UIAlertAction *fingerPrintBtn = [UIAlertAction actionWithTitle:@"指纹认证" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {

}];

UIAlertAction *cancelBtn = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {

}];

[alert addAction:faceBtn];
[alert addAction:fingerPrintBtn];
[alert addAction:cancelBtn];
[self presentViewController:alert animated:YES completion:nil];
]]>
+ + + + + + AlertView + + + +
+ + + + + 本地缓存 + + /2016/10/20/%E6%9C%AC%E5%9C%B0%E7%BC%93%E5%AD%98/ + + 保存图片到沙盒
1
2
3
4
5
6
- (void)saveImage:(UIImage *)image InSanBoxWithIndexPath:(NSInteger)indexPath {

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *filePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:@"pic_%ld.png", indexPath]]; // 保存文件的名称
[UIImagePNGRepresentation(image)writeToFile:filePath atomically:YES];
}

获取沙盒中的图片

1
2
3
4
5
6
7
- (UIImage *)fetchImageFromSanBoxWithIndexPath:(NSInteger)indexPath
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *filePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:@"pic_%ld.png", indexPath]]; // 保存文件的名称
UIImage *img = [UIImage imageWithContentsOfFile:filePath];
return img;
}

删除沙盒中的图片

1
2
3
4
5
6
7
8
9
10
11
12
- (BOOL)deleteImageAtIndexPath:(NSInteger)index;
{

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *filePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:@"pic_%ld.png", index]];

if([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
return [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
}

return NO;
}

修改图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
- (UIImage *)fixOrientation:(UIImage *)aImage {

// No-op if the orientation is already correct
if (aImage.imageOrientation == UIImageOrientationUp)
return aImage;

// We need to calculate the proper transformation to make the image upright.
// We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.
CGAffineTransform transform = CGAffineTransformIdentity;

switch (aImage.imageOrientation) {
case UIImageOrientationDown:
case UIImageOrientationDownMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.width, aImage.size.height);
transform = CGAffineTransformRotate(transform, M_PI);
break;

case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.width, 0);
transform = CGAffineTransformRotate(transform, M_PI_2);
break;

case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
transform = CGAffineTransformTranslate(transform, 0, aImage.size.height);
transform = CGAffineTransformRotate(transform, -M_PI_2);
break;
default:
break;
}

switch (aImage.imageOrientation) {
case UIImageOrientationUpMirrored:
case UIImageOrientationDownMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.width, 0);
transform = CGAffineTransformScale(transform, -1, 1);
break;

case UIImageOrientationLeftMirrored:
case UIImageOrientationRightMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.height, 0);
transform = CGAffineTransformScale(transform, -1, 1);
break;
default:
break;
}

// Now we draw the underlying CGImage into a new context, applying the transform
// calculated above.
CGContextRef ctx = CGBitmapContextCreate(NULL, aImage.size.width, aImage.size.height,
CGImageGetBitsPerComponent(aImage.CGImage), 0,
CGImageGetColorSpace(aImage.CGImage),
CGImageGetBitmapInfo(aImage.CGImage));
CGContextConcatCTM(ctx, transform);
switch (aImage.imageOrientation) {
case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
// Grr...
CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.height,aImage.size.width), aImage.CGImage);
break;

default:
CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.width,aImage.size.height), aImage.CGImage);
break;
}
CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
UIImage *img = [UIImage imageWithCGImage:cgimg];
CGContextRelease(ctx);
CGImageRelease(cgimg);
return img;
}
]]>
+ + + + + + 本地缓存 + + + +
+ + + + + SQL 使用 + + /2016/10/13/SQlite%E4%BD%BF%E7%94%A8%E5%AD%A6%E4%B9%A0/ + + 常用SQL 语句

创建一张表

create table if not exists t_product(productId integer, productName text, productPrice real);

删除一张表

drop table if exists t_product;

插入一条记录

如果不写某个字段和对应值就会为NUll

insert into t_product(productId, productName, productPrice) values (0001, 'iPhone7', 6100.8);

修改记录

update t_product set productId = 20002 where productName = 'iPhone5s';

删除

delete from t_product where productName = 'iphone5s';

注意:条件语句不能单独存在,只能在修改/删除/查询之后

简单查询

准备数据生成sql语句文件

NSMutableString *mutableString = [NSMutableString string];int productIdValue = arc4random_uniform(1000);NSString *productNameValue = [NSString stringWithFormat:@"iPhone_%d",1 + arc4random_uniform(8)];CGFloat productPriceValue = 3000 + arc4random_uniform(1000);for (NSInteger i = 0; i < 1000; i++) {    [mutableString appendFormat:@"insert into t_product(productId, productName, productPrice) values (%d, %@, %f); \r\n", productIdValue, productNameValue, productPriceValue];}// 写入文件[mutableString writeToFile:@"/Users/Apeng/work/Demo/Sqlite/products.sql" atomically:YES encoding:NSUTF8StringEncoding error:NULL];

生成的sql语句文件

SQL 查询语句

select *(表示字段)from t_product(表名)where(条件语句) productId > 100 (字段满足的条件) ;select * from t_product where productPrice < 5000; 


or: 或

and: 且

分页查询

limit 0,5 ( limit 索引,每页取得数据条数 )

第一页 limit 0,5;

第二页 limit 5,5;

第三页 limit 10,5;

第四页 limit 15,5;

第N页 limit (N - 1)* 5

select * from t_product where productName = 'iPhone_8' and productPrice < 3300 order by productPrice limit 5,5;

排序查询

order by 默认是从小到大,一般是在某一个结果之后

默认升序

// 默认升序select * from t_product where productName = 'iPhone_8' and productPrice < 3300 order by productPrice;// 降序select * from t_product where productName = 'iPhone_8' and productPrice < 3300 order by productPrice desc;

模糊查询

关键字搜索 ,% 是指通配符

SQL 顺序

select * from 表名 条件语句(模糊匹配)排序语句 分页语句select * from t_product where productName like '%_4' order by productPrice limit 0,5;

主键

数据库的约定俗称,自增长,建议创建表时增加主键,也可以之后再设计表

CREATE TABLE IF NOT EXISTS t_class (id integer PRIMARY KEY, className text, classNO integer); 

外键约束

当两张表有关联时,需要使用外键约束,一张表中的字段名所对应的值只能来自于另一张表中

添加外键约束之后的变化

多表查询

t_department(部门表)和 t_employee(员工表)两张表

需求:在 t_employee 表中查出部门研发部的人员

1 嵌套查询:

先查根据部门名称查出部ID,

然后再用部门ID查出员工表中的员工

以上两步的代码可合并

根据部门名称查出部门ID

SELECT id from t_department WHERE departmentName = '研发部';

根据部门id查出这个部门的人员

SELECT * from t_employee WHERE departmentID = 1;

合并后为

select * from t_employee where departmentID = (SELECT id from t_department WHERE departmentName = '研发部'); 

结果为:

  1. 链接查询

给表起别名,给字段起别名

select employee.*, depart.departmentName deptN from t_department depart, t_employee employee;

/*多表查询之链接查询/

select t_employee.*, t_department.departmentName FROM t_department, t_employee;

消除笛卡尔积

select employee.*, depart.departmentName deptN from t_department depart, t_employee employee where employee.departmentID = depart.id and employee.departmentID = 1;

]]>
+ + + + + + SQL + + + +
+ + + + +
diff --git "a/source/_posts/Alamofire\346\272\220\347\240\201\345\255\246\344\271\240.md" "b/source/_posts/Alamofire\346\272\220\347\240\201\345\255\246\344\271\240.md" deleted file mode 100644 index b5ca2f3..0000000 --- "a/source/_posts/Alamofire\346\272\220\347\240\201\345\255\246\344\271\240.md" +++ /dev/null @@ -1,62 +0,0 @@ ---- -title: Alamofire源码学习总结 -date: 2017-04-10 15:10:31 -tags: Alamofire ---- - -#### 网络请求时Path和Query 之间是用 ‘?’ 号隔开,后边是传给服务器的参数,GET请求Query是放在URL之后,POST请求是放在Body中 - -- 如果参数是一个 key-value 形式,Query 格式为:key=value - -- 如果参数是一个数组 key = [value1, value2, value3 ….], - -- Query 格式为 key[]=value1&key[]=value2&key[]=value3 - -- 如果参数是一个字典 - -``` - key = [“subKey1”:”value1”, “subKey2”:”value2”, “subKey3”:”value3”….], -``` - -- Query 的格式为 - -``` -key[subKey1]=value1&key[subKey2]=value2&key[subKey3]=value3 -``` - -- Alamfire中的编码 - -``` -private func query(_ parameters: [String: Any]) -> String { - var components: [(String, String)] = [] - for key in parameters.keys.sorted(by: <) { - let value = parameters[key]! - components += queryComponents(fromKey: key, value: value) - } - return components.map { "\($0)=\($1)" }.joined(separator: "&") - } -public func queryComponents(fromKey key: String, value: Any) -> [(String, String)] { - var components: [(String, String)] = [] // 元祖数组 - if let dictionary = value as? [String: Any] { // value 为字典,key[subKey]=value 形式 - for (nestedKey, value) in dictionary { - components += queryComponents(fromKey: "\(key)[\(nestedKey)]", value: value) - } - } else if let array = value as? [Any] { // value为数组, key[]=value 形式 - for value in array { - components += queryComponents(fromKey: "\(key)[]", value: value) - } - } else if let value = value as? NSNumber { // value为 NSNumber - if value.isBool { - components.append((escape(key), escape((value.boolValue ? "1" : "0")))) - } else { - components.append((escape(key), escape("\(value)"))) - } - } else if let bool = value as? Bool { // value 为 Bool - components.append((escape(key), escape((bool ? "1" : "0")))) - } else { // value 为字符串时 直接转义 - components.append((escape(key), escape("\(value)"))) - } - return components - } - -``` \ No newline at end of file diff --git "a/source/_posts/Block\345\255\246\344\271\240\346\216\242\347\251\266.md" "b/source/_posts/Block\345\255\246\344\271\240\346\216\242\347\251\266.md" deleted file mode 100644 index bcf4962..0000000 --- "a/source/_posts/Block\345\255\246\344\271\240\346\216\242\347\251\266.md" +++ /dev/null @@ -1,104 +0,0 @@ ---- -title: Block学习探究 -date: 2018-01-19 00:06:55 -tags: ---- - -### 代码及结果 - -- 普通局部变量 - - ``` - - (void)block1 { - - NSInteger num = 100; - - // 定义一个block - dispatch_block_t block = ^ { - NSLog(@"block1 -- numer = %zd", num); - }; - - num = 200; - - block(); - } - ``` - -- 使用__block修饰变量 - - ``` - - (void)block2 { - - __block NSInteger num = 100; - - // 定义一个block - dispatch_block_t block = ^ { - NSLog(@"block2 -- numer = %zd", num); - }; - - num = 200; - - block(); - } - - ``` - -- 全局变量 - - ``` - - (void)block3 { - - - // 定义一个block - dispatch_block_t block = ^ { - NSLog(@"block3 -- numer = %zd", blockNum); - }; - - blockNum = 200; - - block(); - } - - ``` - - -- 静态常量 - - ``` - - (void)block4 { - - static NSInteger num = 100; - - // 定义一个block - dispatch_block_t block = ^ { - NSLog(@"block4 -- numer = %zd", num); - }; - - num = 200; - - block(); - } - - ``` - -### 运行结果 - - 2018-01-19 00:04:13.759416+0800 CycleRetain[91251:2382380] block1 -- numer = 100 - 2018-01-19 00:04:13.760206+0800 CycleRetain[91251:2382380] block2 -- numer = 200 - 2018-01-19 00:04:13.760473+0800 CycleRetain[91251:2382380] block3 -- numer = 200 - 2018-01-19 00:04:13.760603+0800 CycleRetain[91251:2382380] block4 -- numer = 200 - -### 原理及本质 - - -block 根据创建位置不同,共有三种:栈block,堆block,全局block - -使用__block本质就是为了保证栈上和堆上block内访问和修改的是同一个变量,具体的实现是将__block 修饰的变动自动封装成一个结构体,让他在堆上创建 -基于OC 底层的runtime 机制 -block 在内部会有一个指向结构体的指针,当调用block的时候其实就是让block找出对应的指针所指的函数地址进行调用。并传入了block自己本身 - - - - - - diff --git "a/source/_posts/Charles \346\212\223\345\214\205.md" "b/source/_posts/Charles \346\212\223\345\214\205.md" deleted file mode 100644 index ce96a75..0000000 --- "a/source/_posts/Charles \346\212\223\345\214\205.md" +++ /dev/null @@ -1,128 +0,0 @@ -Charles 抓包 - - -## 原理 - -简单理解如下: - -- 正常请求 - - 客户端 ——> 服务器 - - -- Charles 抓包 - - 客户端 ——> 代理 (Charles)——> 服务器 - -## 安装 - -[Charles官网](https://www.charlesproxy.com/) - -破解版本请自行百度 - -**重要说明安装之前请暂时关闭电脑的VPN** - -**重要说明安装之前请暂时关闭电脑的VPN** - -**重要说明安装之前请暂时关闭电脑的VPN** - -### 启动Charles - -### 安装Charles证书 - -![](https://ws3.sinaimg.cn/large/006tNc79ly1fyuumd2nkoj320y0j6jtz.jpg) - - 如果安装失败,请检查以前是否已经安装过,如果已经安装过并且已经失效,请删除失效证书后再安装 - -### 打开钥匙串--> 选择Charles CA -> 双击证书 --> 信任证书 - - -![](https://ws3.sinaimg.cn/large/006tNc79ly1fyuuq2z76mj31as0ri76z.jpg) - - -### 设置 https - - - Proxy -> SSL Proxying Settings -> SSL Proxying -> Add - - Host: * 为需要过滤的域名地址, - - *: 表示不过滤Port, 固定为443 `*`表示任意端口 - -![](https://ws3.sinaimg.cn/large/006tNc79ly1fyuv3ii2bkj30wm0ocmye.jpg) - -## Mac 抓包 - -- Proxy -> macOS Proxy (☑️) - - ![](https://ws2.sinaimg.cn/large/006tNc79ly1fyuv7zqgaij31v00l0wik.jpg) - -## 真机抓包 - -此时可以将 macOS Proxy 选项中的 ✅ 去掉, - -### Mac与iPhone连接必须同一网络 - -### 设置代理端口号 - -![](https://ws1.sinaimg.cn/large/006tNc79ly1fyuve5e6mbj31240u0wnu.jpg) - -### 手机安装证书 - -- 安装手机证书 - - ![](https://ws3.sinaimg.cn/large/006tNc79ly1fyuvn5p8ijj31zi0iuq5h.jpg) - -- 安装完成后弹窗 - - ![](https://ws4.sinaimg.cn/large/006tNc79ly1fyuvln2bycj31b20cq0tz.jpg) - - -- 查看mac 地址,为手机设置代理 - - 1. 根据安装手机证书后的弹窗显示IP配置 - - 2. Charles --> Help --> Loca IP Address - - ![](https://ws4.sinaimg.cn/large/006tNc79ly1fyuvgh515wj30uo0k0dhh.jpg) - - 3. 打开电脑网络设置查看 - - ![](https://ws2.sinaimg.cn/large/006tNc79ly1fyuvic8h7vj30yo0u0dl3.jpg) - - -- 设置代理 - - 第一步 - ![](https://ws1.sinaimg.cn/large/006tNc79ly1fyuvzrnksjj30oo0g8t96.jpg) - - 第二步 - ![](https://ws3.sinaimg.cn/large/006tNc79ly1fyuvztfs46j30pu16uq4f.jpg) - - 第三步 - ![](https://ws4.sinaimg.cn/large/006tNc79ly1fyuvzv0wdzj30q216wq46.jpg) - -- 根据弹窗提示,在手机浏览器下载并安装证书 - - ![](https://ws3.sinaimg.cn/large/006tNc79ly1fzej631vkbj30ve06gmya.jpg) - - -- 手机信任证书 - - - 设置 --> 通用 --> 关于本机 --> 证书信任设置 - - ![](https://ws3.sinaimg.cn/large/006tNc79ly1fyuvzqy9shj31310u0doe.jpg) - -### 模拟器抓包 - -- 安装模拟器证书 - - ![](https://ws4.sinaimg.cn/large/006tNc79ly1fyux3vn7cbj31d80imaij.jpg) - -- 勾选 `macOS Proxy` 选项 - - ![](https://ws4.sinaimg.cn/large/006tNc79ly1fyux3cejcij30e40j4gmi.jpg) - - -如果失败,请先关闭模拟器,重新启动Charles 再打开模拟器 - -### 使用浏览器打开网页提示不是私密链接 - -[解决办法](https://blog.csdn.net/qq_31411389/article/details/79615688) \ No newline at end of file diff --git "a/source/_posts/CocoaPods\345\210\266\344\275\234\347\247\201\346\234\211pod.md" "b/source/_posts/CocoaPods\345\210\266\344\275\234\347\247\201\346\234\211pod.md" deleted file mode 100644 index a967a2e..0000000 --- "a/source/_posts/CocoaPods\345\210\266\344\275\234\347\247\201\346\234\211pod.md" +++ /dev/null @@ -1,91 +0,0 @@ ---- -title: CocoaPods制作私有pod -date: 2017-11-29 20:57:56 -tags: ---- - -## CocoaPods原理 - -**本文主要记录制作自己的pod库过程** - -CocoaPods 是自动化工具,主要用来管理一些开源的第三方框架,或者是制作自己的私有库,开源库,也可以使用它讲自己的项目组件化。 - -具体可以参考: - -1. [细聊 Cocoapods 与 Xcode 工程配置](https://juejin.im/post/58730a25a22b9d0058971144) -2. [你真的会用 CocoaPods 吗?](https://juejin.im/post/59f2c7eaf265da432c2318e5) - -## CocoaPods 安装 - -[安装教程](http://www.pluto-y.com/cocoapods-getting-stared/) - - -## 创建私有索引仓库 -每次使用第三方开源库的时候只需要在 `Podfile` 文件中指定所用的库然后 `Pod install` ,这是因为安装pod的时候 `pod setup` 从[远程仓库](https://github.com/CocoaPods/Specs)(索引库) clone到了本地,此仓库中存放了所有支持pod框架的描述信息(包括每个库的各个版本) - -文件目录: - - ~/.cocoapods/repos/master - -- 在 [码云](https://gitee.com/) 或者 [coding]() 上创建一个私有仓库 - -- 添加私有库到CocoaPods - - - 格式 `pod repo add [repoName] (URL)` - - ``` - pod repo add PrivateSpec https://gitee.com/aapenga/PrivateSpec.git - ``` - - -## 创建本地私有库 - - - `cd` 到任意一个文件夹下, 执行 `pod lib create [项目名]` - - ``` - pod lib create PPExcelView - ``` - - - 将文件放入到生成的 Demo 文件夹 的 `Classes` 文件目录下 - - `cd` 到 `Example` 下(含有 `Podfile` 文件) 执行 - - ``` - pod install - ``` - 或者 - - ``` - pod update - ``` - - 此时添加的文件出现在 `Dome` Pod 的 `Development Pods` 目录中 - - - 从本地验证 pod 是否通过 - - ``` - pod lib lint - ``` - 如果有警告,可以使用 `--private` 或者 `--allow-warnings` 忽略 - - ``` - pod lib lint --allow-warnings - pod lib lint --private - ``` - **出现 `PPExcelView passed validation.` 为通过** - - - 从远程仓库验证 pod - - ``` - pod spec lint - ``` - **出现 `PPExcelView.podspec passed validation` 为通过** - -## 推送 `.podspec` 文件到私有仓库 - -`cd` 到 `.podspec` 所在的文件下 - - pod repo push MyGitSpec PPExcelView.podspec - - - - \ No newline at end of file diff --git "a/source/_posts/ES6\350\257\255\346\263\225.md" "b/source/_posts/ES6\350\257\255\346\263\225.md" deleted file mode 100644 index 4984b71..0000000 --- "a/source/_posts/ES6\350\257\255\346\263\225.md" +++ /dev/null @@ -1,184 +0,0 @@ -# JS ES6 语法 - -### 1. let var 变量,作用域 - - var a = 10; - - if (a > 10) { - let b = 5; // let 只能在代码块内被访问 - - console.log(b); - } - - // 访问 b 报错 - console.log(b); - console.log(a); - - -### 2. const 只能分配一次值 - -{ - const const_a = 1; - console.log(const_a) - - // 报错 - const_a = 2; - - // 声明一个常量数组 - const const_array = []; - const_array.push(1); - const_array.push(2); - // 再次赋值时会报错 - const_array = [3]; - -} - -### 3. 解构语法 -{ - // 1.解构数组 - function getArray() { - return [10, 20, 30]; - } - - let [a,b,c] = getArray() - console.log(a, b, c) - - - // 函数返回一个对象 - function getObj() { - return {dessert: '面包', drink: '水', play: 'Game'}; - } - - // 2.解构对象 - let {des: dessert, dri: drink, pla: play} = getObj() - console.log(des, dri, pla); -} - -### 4. 模版字符串 -{ - let name = '姓名:', password = '密码:' - let str_4 = name + '李老师' + '----' + password + '北京太热。。。' - console.log(str_4) - - // 使用 `${}` 包裹会生成字符串 - let str_41 = `${name}李老师----${password}北京太热` - console.log(str_41) - - // 多行显示时直接回车换行 - let str_42 = `${name}李老师 - ---- - ${password}北京太热` - console.log(str_42) -} - -### 5. 带标签的模版字符串 - -{ - let name = '姓名:', password = '密码:' - // 多行显示时直接回车换行 - let str_5 = strTest`${name}李老师 ---- ${password}北京太热` - console.log(str_5) - - - // strings 字符中包含的被插值分割的子字符串已数组形式展现 - // values 字符串中的插值数组 - function strTest(strings, ...values) { - console.log(strings) - console.log(values) - } - - function strTest1(strings, ...values) { - let result = ''; - for (let i = 0; i < values.length; i++) { - result += strings[i] - result += values[i] - } - return result - } -} - -### 6. 判断字符串是否包含其他字符 - -{ - let name = '姓名:', password = '密码:' - let str_6 = `${name}李老师---${password}北京真热` - console.log(str_6.startsWith(`${name}`)) - console.log(str_6.endsWith(`${name}`)) - console.log(str_6.includes('李老师')) -} - -### 7. 函数默认参数 -{ - function funcDefaultParame(param1 = 'a', param2 = 'b', param3 = 'c') { - return `${param1}--${param2}--${param3}` - } - console.log(funcDefaultParame(1, 2,3)) -} - - -### 8. "..."展开操作符 - - -{ - let a = [1,2,3]; - console.log(a); - console.log(...a) - - let b = [...a, 4, 5]; - console.log(a); - console.log(b) -} - -### 9. '... '剩余操作符, 一般用在函数参数, 可以看作是一个可以展开的参数 - - - // ... param3表示函数可以接收除了param1, param2,两个参数外,多个参数,都会放在param3中 - function operator(param1, param2, ...param3) { - console.log(param1, param2, param3) - } - operator('a','b','c','d','e','f','g') - - - - -### 10. 解构对象解构函数 - - // 当函数参数是对象是可以按照对象解构 - function getObjcValue(param1, param2, {value1, value2} = {}) { - console.log(param1, param2, value1, value2) - } - - getObjcValue(1,2,{value1: -3,value2: -4}) - - - -### 11. 获取函数名称 - - - function getObjcValue(param1, param2, {value1, value2} = {}) { - console.log(param1, param2, value1, value2) - } - console.log(getObjcValue.name) - - let funName = function getfunctionName() { } - console.log(funName.name) - - -### 12. 箭头函数 - - - // 原来普通写法 - var funType = function arrowfunction(param) { - return param - } - - // 函数参数 => 函数返回值 - // '=>' 的左边表示箭头函数的参数,多个时表示为 (param1, param2, param3) - // '=>' 的右边表示箭头函数的返回值,如果返回值是一个对象时用 { } 表示 - let arrowFunction = (param1, param2) => { - return `${param1}${param2}` - - -} - -### 13 \ No newline at end of file diff --git "a/source/_posts/Flutterr\345\205\245\351\227\250.md" "b/source/_posts/Flutterr\345\205\245\351\227\250.md" deleted file mode 100644 index 3bbcaee..0000000 --- "a/source/_posts/Flutterr\345\205\245\351\227\250.md" +++ /dev/null @@ -1,165 +0,0 @@ ---- -title: Flutter 入门.md -date: 2019-06-18 23:18:43 -tags: Flutter ---- - -flutter安装 - -flutter入门 - -## flutter 安装 - - -## flutter 项目代码 - -fluter 中一切皆是组件 - -``` -// 导入头文件 -import 'package:flutter/material.dart'; - -// 程序的入口,main 函数 -void main() => (MyApp()); - -// 定义类, 继承 - -class MyApp extends StatelessWidget { - // 重写 - @override - - Widget build(BuildContext context) { - - // 返回一窗口 - return MaterialApp( - title:'Flutter', - home: Scaffold( - appBar: AppBar( - title: Text('flutter 初体验') - ), - - body: Center( - child: Text('flutter 学习之路'), - ), - ), - ); - } -} - -``` - - -``` - -import 'package:flutter/material.dart'; - -void main() => runApp(MyApp()); - -class MyApp extends StatelessWidget { - // This widget is the root of your application. - @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'Flutter Demo', - theme: ThemeData( - // This is the theme of your application. - // - // Try running your application with "flutter run". You'll see the - // application has a blue toolbar. Then, without quitting the app, try - // changing the primarySwatch below to Colors.green and then invoke - // "hot reload" (press "r" in the console where you ran "flutter run", - // or simply save your changes to "hot reload" in a Flutter IDE). - // Notice that the counter didn't reset back to zero; the application - // is not restarted. - primarySwatch: Colors.blue, - ), - home: MyHomePage(title: 'Flutter Demo Home Page'), - ); - } -} - -class MyHomePage extends StatefulWidget { - MyHomePage({Key key, this.title}) : super(key: key); - - // This widget is the home page of your application. It is stateful, meaning - // that it has a State object (defined below) that contains fields that affect - // how it looks. - - // This class is the configuration for the state. It holds the values (in this - // case the title) provided by the parent (in this case the App widget) and - // used by the build method of the State. Fields in a Widget subclass are - // always marked "final". - - final String title; - - @override - _MyHomePageState createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - int _counter = 0; - - void _incrementCounter() { - setState(() { - // This call to setState tells the Flutter framework that something has - // changed in this State, which causes it to rerun the build method below - // so that the display can reflect the updated values. If we changed - // _counter without calling setState(), then the build method would not be - // called again, and so nothing would appear to happen. - _counter++; - }); - } - - @override - Widget build(BuildContext context) { - // This method is rerun every time setState is called, for instance as done - // by the _incrementCounter method above. - // - // The Flutter framework has been optimized to make rerunning build methods - // fast, so that you can just rebuild anything that needs updating rather - // than having to individually change instances of widgets. - return Scaffold( - appBar: AppBar( - // Here we take the value from the MyHomePage object that was created by - // the App.build method, and use it to set our appbar title. - title: Text(widget.title), - ), - body: Center( - // Center is a layout widget. It takes a single child and positions it - // in the middle of the parent. - child: Column( - // Column is also layout widget. It takes a list of children and - // arranges them vertically. By default, it sizes itself to fit its - // children horizontally, and tries to be as tall as its parent. - // - // Invoke "debug painting" (press "p" in the console, choose the - // "Toggle Debug Paint" action from the Flutter Inspector in Android - // Studio, or the "Toggle Debug Paint" command in Visual Studio Code) - // to see the wireframe for each widget. - // - // Column has various properties to control how it sizes itself and - // how it positions its children. Here we use mainAxisAlignment to - // center the children vertically; the main axis here is the vertical - // axis because Columns are vertical (the cross axis would be - // horizontal). - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - '你可以点击按钮来修改文字,我是热重载的结果', - ), - Text( - '$_counter', - style: Theme.of(context).textTheme.display1, - ), - ], - ), - ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: Icon(Icons.add), - ), // This trailing comma makes auto-formatting nicer for build methods. - ); - } -} -``` \ No newline at end of file diff --git "a/source/_posts/Flutter\344\270\255GridView\347\273\204\344\273\266.md" "b/source/_posts/Flutter\344\270\255GridView\347\273\204\344\273\266.md" deleted file mode 100644 index 28a92da..0000000 --- "a/source/_posts/Flutter\344\270\255GridView\347\273\204\344\273\266.md" +++ /dev/null @@ -1,77 +0,0 @@ ---- -title: GridView组件使用.md -date: 2019-06-18 23:42:43 -tags: Flutter GridView ---- - -## GridView 组件使用 - -- padding: const EdgeInsets.all(10.0) 设置边距为10 -- crossAxisCount:横轴子元素的数量crossAxisCount。 -- mainAxisSpacing:主轴方向的间距。 -- crossAxisSpacing:横轴元素的间距。 -- childAspectRatio:元素的宽高比 - -简单例子代码 - -``` -class MyGridView2 extends StatelessWidget{ - @override - Widget build(BuildContext context) { - // TODO: implement build - return Container( - child: new GridView( - // 设置边距 - padding: const EdgeInsets.all(10.0), - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - - // 横轴 数量 - crossAxisCount: 3, - crossAxisSpacing: 10, - mainAxisSpacing: 10.0, - // 宽高比:默认是1.0 - childAspectRatio: 1.5, - ), - - children: [ - new Image.network('https://github.com/Jinxiansen/SwiftUI/raw/master/images/example/Text.png', fit: BoxFit.cover), - new Image.network('https://p.ampmake.com/mall/product/9666f00e-f02a-46ce-9b7c-090bf6aba9aa.png', fit: BoxFit.cover), - new Image.network('https://p.ampmake.com/mall/product/d6fdc891-eec3-47ce-b837-df726c73a9b8.png', fit: BoxFit.cover), - new Image.network('https://p.ampmake.com/mall/product/0cccf224-57d1-4a3e-bf06-9df5fed0f4e0.png', fit: BoxFit.cover), - new Image.network('https://p.ampmake.com/mall/product/2bf5c0d7-dc3a-45d2-ab1b-40fe737aa79a.jpg', fit: BoxFit.cover), - new Image.network('https://p.ampmake.com/mall/product/26153760-b964-45d5-b811-7f2bf5844102.jpg', fit: BoxFit.cover), - new Image.network('https://p.ampmake.com/mall/product/a20bb53c-b710-4b39-9607-91bef06ee54e.png', fit: BoxFit.cover), - ], - ) - ); - } -} -``` - - -以前的写法 -``` -class MyGridView extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Container( - child: new GridView.count( - padding: const EdgeInsets.all(15.0), - // 横轴 item 数量 - crossAxisCount: 3, - // 横轴 间距 - crossAxisSpacing: 10.0, - children: [ - const Text('我是第一个'), - const Text('我是第二个'), - const Text('我是第三个'), - const Text('我是第四个'), - const Text('我是第五个'), - const Text('我是第六个'), - const Text('我是第七个'), - ], - ), - ); - } -} -``` \ No newline at end of file diff --git "a/source/_posts/Flutter\344\270\255\345\270\203\345\261\200.md" "b/source/_posts/Flutter\344\270\255\345\270\203\345\261\200.md" deleted file mode 100644 index e7a789f..0000000 --- "a/source/_posts/Flutter\344\270\255\345\270\203\345\261\200.md" +++ /dev/null @@ -1,380 +0,0 @@ -# Flutter中布局 - -## 线性布局 - -### 主轴、副轴 - -- main轴:如果用column组件,垂直就是主轴,如果用Row组件,那水平就是主轴 - -- cross轴:幅轴,是和主轴垂直的方向。用Row组件,那垂直就是幅轴,Column组件,水平方向就是副轴 - -### Row 水平方向布局组件 - -- 非自适应布局:根据元素本身大小来布局 -- 自适应布局:使用 Expanded 布局 - -非自适应布局 -``` -class MyRowNoChangeLayout extends StatelessWidget { - @override - Widget build(BuildContext context) { - return new Container( - child: Row( - children: [ - new RaisedButton( - onPressed: (){ - print('点击了---- 红色'); - }, - - color: Colors.red, - child: new Text('按钮一红色'), - - ), - - new RaisedButton( - onPressed: (){ - print('点击了---- 黄色'); - }, - - color: Colors.yellow, - child: new Text('按钮一黄色'), - - ), - new RaisedButton( - onPressed: (){ - print('点击了---- 绿色'); - }, - - color: Colors.green, - child: new Text('按钮一绿色'), - - ) - ], - ), - ); - } -} -``` - - -自适应布局 - -``` -class MyRowAutoLayout extends StatelessWidget { - @override - Widget build(BuildContext context) { - return new Container( - child: Row( - children: [ - Expanded( - child: new RaisedButton( - onPressed: (){ - print('点击了---- 红色'); - }, - - color: Colors.red, - child: new Text('按钮一红色'), - ), - ), - - Expanded( - child: new RaisedButton( - onPressed: (){ - print('点击了---- 黄色'); - }, - - color: Colors.yellow, - child: new Text('按钮一黄色'), - - ) - ), - - Expanded( - child: new RaisedButton( - onPressed: (){ - print('点击了---- 绿色'); - }, - - color: Colors.green, - child: new Text('按钮一绿色'), - - ) - ) - ], - ), - ); - } -} -``` - -非自动布局和自动布局混合使用 - -``` -class MyRowLayout extends StatelessWidget { - @override - Widget build(BuildContext context) { - return new Container( - child: Row( - children: [ - new RaisedButton( - onPressed: (){ - print('点击了---- 红色'); - }, - - color: Colors.red, - child: new Text('按钮一红色'), - ), - - Expanded( - child: new RaisedButton( - onPressed: (){ - print('点击了---- 黄色'); - }, - - color: Colors.yellow, - child: new Text('按钮一黄色'), - - ) - ), - - new RaisedButton( - onPressed: (){ - print('点击了---- 绿色'); - }, - - color: Colors.green, - child: new Text('按钮一绿色'), - - ) - ], - ), - ); - } -} -``` - -### Column 垂直方向布局组件 - -``` -class MyColunmLayout extends StatelessWidget { - @override - Widget build(BuildContext context) { - return new Container( - color: Colors.cyan, - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - mainAxisSize: MainAxisSize.max, - // 设置主轴的对齐方式 - mainAxisAlignment: MainAxisAlignment.center, - // 设置横轴的对齐方式 - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - new RaisedButton ( - onPressed: (){ - print('点击了---- 红色'); - }, - - color: Colors.red, - child: new Text('按钮一红色'), - ), - - new RaisedButton( - onPressed: (){ - print('点击了---- 绿色'); - }, - color: Colors.green, - child: new Text('按钮一绿色'), - ), - - new Text('文字内容', - textAlign: TextAlign.center, - ), - new Text('测试文字', - textAlign: TextAlign.right, - ) - ], - ), - ); - } -} -``` -## 层叠布局 -``` -// stack层叠布局 - -class MyStackLayout extends StatelessWidget { - @override - Widget build(BuildContext context) { - // 返回一个 Stack 布局 - return new Stack( - // 设置位置 - alignment: const FractionalOffset(0.5, .9), - // 圆角组件 - children: [ - new CircleAvatar( - backgroundImage: new NetworkImage('https://p.ampmake.com/mall/product/26153760-b964-45d5-b811-7f2bf5844102.jpg'), - // backgroundImage: new Image.network('https://p.ampmake.com/mall/product/26153760-b964-45d5-b811-7f2bf5844102.jpg'), - radius: 100.0, - ), - - // 容器组件 - new Container( - // 设置容器掩饰 - decoration: BoxDecoration( - color: Colors.blue, - // border - border: Border.all( - color: Colors.red, - width: 3, - ), - ), - - padding: const EdgeInsets.all(10.0), - - child: new Text('stack 布局', - textAlign: TextAlign.center, - maxLines: 1, - // 文字样式 - style: TextStyle( - color: Colors.yellow, - textBaseline: TextBaseline.ideographic, - // 下划线 - decoration: TextDecoration.underline, - // 下划线颜色 - decorationColor: Colors.orange, - // 下划线样式 - decorationStyle: TextDecorationStyle.double, - ), - ), - ) - ], - - ); - } -} -``` - -### Positioned组件 - -- bottom: 距离层叠组件下边的距离 -- left:距离层叠组件左边的距离 -- top:距离层叠组件上边的距离 -- right:距离层叠组件右边的距离 -- width: 层叠定位组件的宽度 -- height: 层叠定位组件的高度 - -``` -class MyMoreStackLayout extends StatelessWidget { - @override - Widget build(BuildContext context) { - return new Stack( - children: [ - new CircleAvatar( - backgroundImage: NetworkImage('https://p.ampmake.com/mall/product/26153760-b964-45d5-b811-7f2bf5844102.jpg'), - radius: 100.0, - ), - - new Positioned( - top: 5, - left: 50, - // right: 5, - width: 100, - child: new RaisedButton( - onPressed: (){ - print("多个组件层叠布局"); - }, - - color: Colors.deepOrange, - child: Text('测试按钮'), - ), - ), - - new Positioned( - bottom: 10, - right: 10, - left: 10, - child: new Text('flutter 多个试图 stack布局', - maxLines: 1, - textAlign: TextAlign.center, - style: TextStyle( - color: Colors.lightGreen, - fontSize: 20, - fontStyle: FontStyle.italic - ), - ) - ) - ], - ); - } -} -``` -### Card 布局 - -``` -class MyCardLayout extends StatelessWidget { - @override - Widget build(BuildContext context) { - return new Card( - child: Column( - children: [ - new ListTile( - title: new Text('card 布局1'), - subtitle: new Text('子标题1', - textAlign: TextAlign.right, - style: TextStyle( - color: Colors.lightGreen - ), - ), - leading: new Icon(Icons.account_balance, color: Colors.purple) - ), - - new Divider( - color: Colors.cyan - ), - - new ListTile( - title: new Text('card 布局2'), - subtitle: new Text('子标题2', - textAlign: TextAlign.right, - style: TextStyle( - color: Colors.lightGreen - ), - ), - leading: new Icon(Icons.account_box, color: Colors.yellow) - ), - - new Divider( - color: Colors.red, - ), - - new ListTile( - title: new Text('card 布局3'), - subtitle: new Text('子标题3', - textAlign: TextAlign.right, - style: TextStyle( - color: Colors.lightGreen - ), - ), - leading: new Icon(Icons.account_circle, color: Colors.red) - ), - - new Divider( - color: Colors.orangeAccent, - ), - - new ListTile( - title: new Text('card 布局4'), - subtitle: new Text('子标题4', - textAlign: TextAlign.right, - style: TextStyle( - color: Colors.lightGreen - ), - ), - leading: new Icon(Icons.camera_enhance, color: Colors.brown) - ) - ], - ), - ); - } -} -``` \ No newline at end of file diff --git "a/source/_posts/Flutter\344\270\255\345\272\225\351\203\250TabBar.md" "b/source/_posts/Flutter\344\270\255\345\272\225\351\203\250TabBar.md" deleted file mode 100644 index 75a7666..0000000 --- "a/source/_posts/Flutter\344\270\255\345\272\225\351\203\250TabBar.md" +++ /dev/null @@ -1,85 +0,0 @@ -# Flutter中底部TabBar - -## 主入口 - - - -## 自定义底部导航栏组件 -``` -import 'package:flutter/material.dart'; - -/** - * StatefulWidget : 具有可变状态(state)的窗口组件 - * 要根据变化状态,调整state 的值 - */ - - -// 继承于 StatefulWidget - -class BottomNavigationWidget extends StatefulWidget { - @override - _BottomNavigationWidgetState createState() => _BottomNavigationWidgetState(); -} - -// 继承于 State -class _BottomNavigationWidgetState extends State { - // 定义icon的颜色,因为需要变化 - final _BottomNavigationColor = Colors.blue; - - @override - Widget build(BuildContext context) { - return Scaffold( - bottomNavigationBar: BottomNavigationBar( - items: [ - BottomNavigationBarItem( - icon: Icon( - Icons.comment, - color: _BottomNavigationColor, - ), - - title: Text( - '社区', - style: TextStyle( - color: _BottomNavigationColor - ), - ) - ), - - BottomNavigationBarItem( - icon: Icon( - Icons.camera, - color: _BottomNavigationColor, - ), - - title: Text( - '理想ONE', - style: TextStyle( - color: _BottomNavigationColor - ), - ) - ), - - BottomNavigationBarItem( - icon: Icon( - Icons.computer, - color: _BottomNavigationColor, - ), - - title: Text( - '我', - style: TextStyle( - color: _BottomNavigationColor - ), - ) - ) - - ], - ), - body: Center( - - ), - ); - } -} - -``` \ No newline at end of file diff --git "a/source/_posts/Flutter\344\270\255\346\234\254\345\234\260\345\233\276\347\211\207\350\265\204\346\272\220\344\275\277\347\224\250.md" "b/source/_posts/Flutter\344\270\255\346\234\254\345\234\260\345\233\276\347\211\207\350\265\204\346\272\220\344\275\277\347\224\250.md" deleted file mode 100644 index 42ef618..0000000 --- "a/source/_posts/Flutter\344\270\255\346\234\254\345\234\260\345\233\276\347\211\207\350\265\204\346\272\220\344\275\277\347\224\250.md" +++ /dev/null @@ -1,22 +0,0 @@ -# Flutter中本地图片资源使用 - -## 新建图片文件目录 - -![](http://ww2.sinaimg.cn/large/006tNc79ly1g4bgdobfvvj30k80wy43y.jpg) - -## 添加图片到项目 -手动投入图片到新建的文件目录 -## 声明图片资源 - -找到项目中 pubspec.yaml 文件,找到 assets: 选项 按照注释代码格式声明图片路径 - -``` - assets: - - lib/assets/ideaOne.png -``` - -## 使用图片资源 - -``` -Image.asset('lib/assets/ideaOne.png') -``` \ No newline at end of file diff --git "a/source/_posts/Flutter\345\270\270\347\224\250\347\273\204\344\273\266.md" "b/source/_posts/Flutter\345\270\270\347\224\250\347\273\204\344\273\266.md" deleted file mode 100644 index b09944e..0000000 --- "a/source/_posts/Flutter\345\270\270\347\224\250\347\273\204\344\273\266.md" +++ /dev/null @@ -1,245 +0,0 @@ -flutter 常用组件 - -## 组件 - -- StatefulWidget - - 具有可变状态的窗口部件,使用应用时可随时变化,例如进度条 -- StatelessWidget - - 不可变窗口部件 - -## Text 组件 - -``` -// Text 组件 -class MyText extends StatelessWidget{ - @override - Widget build(BuildContext context){ - return Text('reacctNative、 flutter 学习之路是非常坚信不疑的,作为一个程序员,因该对于技术有着追求,不断的提高自己才是王道,可是道理都懂依然过不好这一生', - maxLines: 2, - overflow: TextOverflow.fade, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 20.0, - color: Colors.deepOrange, - decoration: TextDecoration.underline, - decorationColor: Colors.redAccent, - decorationStyle: TextDecorationStyle.dotted - ), - ); - } -} -``` - -## COntainer 组件 - -``` - -// Container容器组件 -class MyContainer extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Container( - child: Text('Container 容器中有文字', - style: TextStyle( - backgroundColor: Colors.cyan, - ), - ), - alignment: Alignment.center, // 容器中内容对其方式 - width: 300.0, - height: 200.0, - padding: const EdgeInsets.all(10), // 容器内边距 - margin: const EdgeInsets.fromLTRB(10, 5, 10, 5), // container 和外部控件的边距 - decoration: new BoxDecoration( - gradient: const LinearGradient( - colors: [Colors.lightBlue, Colors.greenAccent, Colors.yellow] - ), - - // 设置 border 的宽度 - border: Border.all(width: 5, color: Colors.black) - - ), - ); - } -} - -``` - -## Image 组件 - -### fit属性 -- BoxFit.fill:全图显示,图片会被拉伸,并充满父容器。 -- BoxFit.contain:全图显示,显示原比例,可能会有空隙。 -- BoxFit.cover:显示可能拉伸,可能裁切,充满(图片要充满整个容器,还不变形)。 -- BoxFit.fitWidth:宽度充满(横向充满),显示可能拉伸,可能裁切。 -- BoxFit.fitHeight :高度充满(竖向充满),显示可能拉伸,可能裁切。 -- BoxFit.scaleDown:效果和contain差不多,但是此属性不允许显示超过源图片大小,可小不可大。 - - -### 图片的混合模式 -- color:是要混合的颜色,如果你只设置color是没有意义的。 -- colorBlendMode: 是混合模式。 - -### repeat 图片填充模式 -- ImageRepeat.repeat : 横向和纵向都进行重复,直到铺满整个画布。 -- ImageRepeat.repeatX: 横向重复,纵向不重复。 -- ImageRepeat.repeatY:纵向重复,横向不重复 - -``` -class MyImage extends StatelessWidget { - @override - Widget build(BuildContext context){ - return Container( - child: new Image.network( - // 'https://images.xiaozhuanlan.com/photo/2019/28ed76123023e46483fcf0ae5c2ba11b.png', - 'https://p.ampmake.com/mall/product/9666f00e-f02a-46ce-9b7c-090bf6aba9aa.png', - - // 1. 设置填充模式 - // fit: BoxFit.scaleDown, - // fit: BoxFit.none, - // fit: BoxFit.fill, - // fit: BoxFit.fitHeight, - // fit: BoxFit.fitWidth, - - // 保持原图比例 - // fit: BoxFit.contain, - - // 充满整个容器,图片被裁切 - // fit: BoxFit.cover, - // scale: 1.0, - - // 2. 图片混合 - color: Colors.red, - colorBlendMode: BlendMode.overlay, - - // 3. reap - repeat: ImageRepeat.repeat, - ), - width: 300.0, - height: 200.0, - color: Colors.lightBlue, - ); - } -} -``` - -## ListView 列表组件 - - -``` -class MyListView extends StatelessWidget{ - @override - Widget build(BuildContext contex){ - - return ListView( - children: [ - new ListTile( - leading: new Icon(Icons.camera), - title: Text('相册'), - ), - - new ListTile( - leading: new Icon(Icons.camera_alt), - title: Text('相机'), - ), - - new ListTile( - leading: new Icon(Icons.camera_enhance), - title: Text('相机1'), - ), - - new ListTile( - leading: new Icon(Icons.camera_front), - title: Text('相机2'), - ), - - new ListTile( - leading: new Icon(Icons.camera_rear), - title: Text('相机3'), - ), - ], - ); - } -} -``` - -### ListView.builder 适合列表项比较多(或者无限)的情况 - -``` -class MyListViewBuilder extends StatelessWidget { - - @override - Widget build(BuildContext context){ - return ListView.builder( - itemCount: 100, // 数量 - itemExtent: 40, // Tile 高度 - itemBuilder: (BuildContext context, int index){ - return ListTile(title: Text("$index")); - } - ); - } -} -``` - -### 横向列表 - -``` -class MyHorizontalListView extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Container( - height: 100, - child: ListView( - // 设置滚动方式 - scrollDirection: Axis.horizontal, - children: [ - new Container( - width: 180.0, - color: Colors.green, - ), - new Container( - width: 180.0, - color: Colors.yellow, - ), - new Container( - width: 180.0, - color: Colors.blue, - ), - new Container( - width: 180.0, - color: Colors.cyan, - ), - new Container( - width: 180.0, - color: Colors.purple, - ), - ], - ), - ); - } -} -``` - -### 动态列表和构造方法的创建 - -``` -class MyDynamicListView extends StatelessWidget { - // 声明参数 - final List items; - // 构造方法 - MyDynamicListView({Key key, @required this.items}):super(key:key); - @override - Widget build(BuildContext context) { - return Container( - child: new ListView.builder( - itemCount: items.length, - itemBuilder: (context, index){ - return ListTile( - title: new Text('通过构造方法创建的动态列表${items[index]}'), - ); - }, - ), - ); - } -} -``` -## GridView 网格列表的使用 \ No newline at end of file diff --git "a/source/_posts/Flutter\346\211\223\345\214\205.md" "b/source/_posts/Flutter\346\211\223\345\214\205.md" deleted file mode 100644 index d93ce93..0000000 --- "a/source/_posts/Flutter\346\211\223\345\214\205.md" +++ /dev/null @@ -1,33 +0,0 @@ -# Flutter 打包 - -### app 图标替换 - -#### Android 图标更换 -- 替换图中路径的图片资源即可, -- 注意图片名字需要是 ic_launcher -- 每个尺寸对应不同的文件大小 -![](http://ww2.sinaimg.cn/large/006tNc79ly1g4bgpxs583j30ko1000wv.jpg) - - -- 如果要修改启动图片名称需要先在主配置文件中修改 - -![](http://ww4.sinaimg.cn/large/006tNc79ly1g4bgwlvax4j31li0o4aik.jpg) - -#### iOS 图标更换 - - - -#### 修改app图标名称 - -- 找到 Android 主配置文件 -- 修改 android:label="app_flutter" 中名称 -![](http://ww4.sinaimg.cn/large/006tNc79ly1g4bgrcevo1j31kt0u0asy.jpg) - -#### 修改app 的名称 - -### 打包 - -#### 生成key -- 安装 java JDK -- - diff --git "a/source/_posts/Flutter\351\241\265\351\235\242\345\257\274\350\210\252.md" "b/source/_posts/Flutter\351\241\265\351\235\242\345\257\274\350\210\252.md" deleted file mode 100644 index c9d785e..0000000 --- "a/source/_posts/Flutter\351\241\265\351\235\242\345\257\274\350\210\252.md" +++ /dev/null @@ -1,180 +0,0 @@ -# Flutter页面导航 - -## 导航栏的使用 -程序主入口 - -``` -class MyApp extends StatelessWidget { - - // 声明参数 - final List items; - - // 构造函数 - MyApp({Key key, @required this.items}):super(key: key); // 调用父类的方法 - - - // 重写 - @override - - Widget build(BuildContext context) { - - // 返回一窗口 - return MaterialApp( - title:'Flutter --', - // 基础组件学习 - // home: MyLearningFlutterHome(), - - home: FirstScreen(), - ); - } -} -``` - -### 导航栏页面跳转参数传递 - -商品类 -``` -class Product { - final String title; // 定义商品标题 - final String description; - // 定义构造方法 - Product(this.title, this.description); -} -``` - -第一级页面 -``` -class FirstScreen extends StatelessWidget { - @override - Widget build(BuildContext context) { - return new Scaffold( - appBar: AppBar( - title: new Text('第一个页面'), - ), - - body: Center( - child: RaisedButton( - child: new Text('点击跳转详情页'), - onPressed: (){ - Navigator.push(context, new MaterialPageRoute( - builder: (context) => (new SecondScreen()) - // builder: (context){ - // new SecondScreen(); - // } - )); - }, - ), - ), - ); - } -} -``` - -详情页面 -``` -class SecondScreen extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text('详情页面') - ), - - body: Center( - child: RaisedButton( - child: Text('返回上一级页面'), - - onPressed: () => (Navigator.pop(context)), - - // onPressed: (){ - // Navigator.pop(context); - // }, - ), - ), - ); - } -} -``` - -### 导航栏页面返回反向参数传递 - -使用异步请求和等待 - -商品列表页面 - -``` -class ProductListView extends StatelessWidget { - // 定义变量,里面是一个对象 - final List products; - - // 使用构造方法 函数的方式接受参数 - ProductListView({Key key, @required this.products}):super(key:key); // 设置必参,调用父类方法 - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text('商品列表'), - ), - - // 使用 ListView.builder 动态构建 - body: ListView.builder( - itemCount: products.length, - itemBuilder: (context, index) { - return ListTile( - title: Text(products[index].title), - onLongPress: (){ - print("长按事件响应"); - }, - - - // 点击时传入参数 - onTap: (){ - print('点击事件'); - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => ProducterDetailView(product:products[index]) - ) - ); - }, - ); - }, - ), - ); - } -} -``` - -商品详情页面点击按钮返回上一级页面,并带有参数 -``` -class ProducterDetailView extends StatelessWidget { - final Product product; - - ProducterDetailView({Key key, @required this.product}):super(key:key); - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text('Product ${product.title} 详情页面') - ), - - body: Center( - // 有多控件 - child: Column( - children: [ - Text('Product ${product.title} desc ${product.description}'), - RaisedButton( - child: Text('back Product List'), - color: Colors.blue, - onPressed: (){ - Navigator.pop(context, 'selected Id = ${product.title}'); - }, - ) - ], - ), - ), - ); - } -} -``` diff --git "a/source/_posts/GCD \344\277\241\345\217\267\351\207\217.md" "b/source/_posts/GCD \344\277\241\345\217\267\351\207\217.md" deleted file mode 100644 index e37302f..0000000 --- "a/source/_posts/GCD \344\277\241\345\217\267\351\207\217.md" +++ /dev/null @@ -1,76 +0,0 @@ -GCD信号量用法 - -对于异步网络请求相互依赖问题一般用三种方式解决: -- 网络请求嵌套 -- 使用 BlockOperation -- GCD DispatchGroup -- GCD semaphore(信号量) - - - -### GCD 信号量 - -- semaphore 值 <= 0 阻塞 -- semaphore semaphore.signal() 使信号量值增加 -- semaphore.wait() 等待,可设置时长 - -request -- 3 依赖 request -- 1 和 request -- 2,1 和 2 无需关注顺序 - -``` - private func gcdSemaphore() { - let semaphore = DispatchSemaphore(value: 0) - DispatchQueue.global().async { - for i in 0...300 { - print("request -- 1") - } - semaphore.signal() - } - - DispatchQueue.global().async { - for i in 0...200 { - print("request -- 2") - } - semaphore.signal() - } - - - DispatchQueue.global().async { - semaphore.wait() - semaphore.wait() - for i in 0...160 { - print("request -- 3") - } - } - } -``` - -request -- 3 ,request -- 2 和 request -- 1,依次相互依赖 - -``` -private func gcdSemaphore() { - let semaphore = DispatchSemaphore(value: 0) - DispatchQueue.global().async { - for i in 0...300 { - print("request -- 1") - } - semaphore.signal() - } - - DispatchQueue.global().async { - semaphore.wait() - for i in 0...200 { - print("request -- 2") - } - semaphore.signal() - } - - - DispatchQueue.global().async { - semaphore.wait() - for i in 0...160 { - print("request -- 3") - } - } - } - -``` diff --git "a/source/_posts/GCD\345\256\236\347\216\260\346\214\211\351\222\256\345\256\232\346\227\266\344\272\256\350\265\267.md" "b/source/_posts/GCD\345\256\236\347\216\260\346\214\211\351\222\256\345\256\232\346\227\266\344\272\256\350\265\267.md" deleted file mode 100644 index df6e17d..0000000 --- "a/source/_posts/GCD\345\256\236\347\216\260\346\214\211\351\222\256\345\256\232\346\227\266\344\272\256\350\265\267.md" +++ /dev/null @@ -1,77 +0,0 @@ ---- -title: GCD实现按钮定时亮起 -date: 2016-12-05 23:59:45 -tags: GCD ---- - -**某些时候需要按钮不能连续点击,需要在一定的时间之后才可以允许交互,或者是实现类似发送验证码的按钮效果,具体做法是采用定时器** - - -## 使用GCD定时器 - -### 创建定时器对象 - - __block NSInteger time = 30; // 需要强应用 - -### 创建队列 - - dispatch_queue_t queue = dispatch_get_global_queue(0, 0); - - -### 创建dispatch源(定时器) - - (dispatach_source…timer..) - -- 01参数:要创建的source 是什么类型的, - - `(DISPATCH_SOURCE_TYPE_TIMER)定时器` - -- 04参数:队列 —-线程 决定block 在哪个线程中调用 - -代码 - - dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); - -### 设置定时器 - -- 01参数:定时器对象 - -- 02参数:开始时间 (DISPATCH_TIME_NOW) 什么时候开始执行第一次任务 - -- 03参数:间隔时间 GCD时间单位:纳秒 - -- 04参数:leewayInSeconds精准度:允许的误差: 0 表示绝对精准 - -code: - - dispatch_source_set_timer(_timer, dispatch_walltime(NULL, 0), 1.0 * NSEC_PER_SEC, 0); - - -### 定时器每隔一段时间就要执行任务(block回调) - - dispatch_source_set_event_handler(_timer, ^{ - if (time <= 0) { - dispatch_source_cancel(_timer); - dispatch_async(dispatch_get_main_queue(), ^{ - - // 设置按钮的样式 - [self.button setTitle:@"重新获取验证码" forState:UIControlStateNormal]; - [self.button setTitleColor:[UIColor redColor] forState:UIControlStateNormal]; - [self.button setUserInteractionEnabled:YES]; - }); - } else { - - NSInteger seconds = time; - dispatch_async(dispatch_get_main_queue(), ^{ - [self.button setTitle:[NSString stringWithFormat:@"重新发送(%.2ld)", seconds] forState:UIControlStateNormal]; - [self.button setTitleColor:[UIColor lightGrayColor] forState:UIControlStateNormal]; - [self.button setUserInteractionEnabled:NO]; - }); - time--; - } - }); - - -### 启动定时器(默认是停止的) - - dispatch_resume(timer); diff --git "a/source/_posts/Hexo\344\270\252\344\272\272\345\215\232\345\256\242\346\220\255\345\273\272.md" "b/source/_posts/Hexo\344\270\252\344\272\272\345\215\232\345\256\242\346\220\255\345\273\272.md" deleted file mode 100644 index 97f3d2a..0000000 --- "a/source/_posts/Hexo\344\270\252\344\272\272\345\215\232\345\256\242\346\220\255\345\273\272.md" +++ /dev/null @@ -1,142 +0,0 @@ ---- -title: Hexo个人博客搭建 -date: 2016-11-05 22:15:50 -tags: Hexo ---- - -## 安装Homebrew - -- 参考官网教程 - -- 在终端中输入,如果失败需要用sudo - -``` - /usr/bin/ruby -e " (curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" -``` - -## 安装git -``` - sudo brew install git -``` -## 安装node.js - -- [下载安装] (https://nodejs.org/en/download) - - -## 安装hexo - -- 需要输入密码: sudo 管理员权限 -g 全局安装 - -``` - sudo npm install -g hexo -``` - -## 初始化 执行 hexo init 命令 - -- 指定安装文件 blog - -``` - hexo init blog -``` - -- cd 到 文件夹 blog 下,安装 npm - -``` - npm install -``` -- 开启服务 - -``` - hexo s -``` - -- 复制终端中的 http://localhost:4000/ 到浏览器中打开 - -## 关联 github - -- 添加 ssh key 到github - -- 设置 Mac 显示隐藏文件 - - - 在终端执行一下命令后,重启Finder - - ``` - defaults write com.apple.finder AppleShowAllFiles -bool true - ``` - -- 检查 SSH keys 是否存在 github - - - 打开 Finder 前往(command + shit + G) - - ``` - ~/.ssh/id_rsa.pub - ``` - -- 如果有文件说明存在 SSH key, 打开 id_rsa.pub 文件,复制里面的信息 -- 进入 Github –> Settings –> SSH keys –> add SSH key -任意添加一个title 粘贴内容到key 里,点击Add key 绿色按钮即可。 - - -## 创建名字为 用户名.github.io 的仓库 - -自行百度。。。。 - -## 配置 hexo - -- vim 打开 _config.yml修改 deploy 模块 - -``` - vim _config.yml -``` -``` -deploy: -type: git -repository: https://github.com/用户名/用户.github.io.git -branch: master -``` -**冒号后需要加空格** - -## 生成 hexo 的静态页面 -``` - hexo generate 后者 hexo g -``` - -- 报以下错 - -``` -ERROR Local hexo not found in ~/blog -ERROR Try runing: 'npm install hexo --save' -``` - -- 执行下面代码 - -``` - npm install hexo --save -``` -## 部署 hexo - -``` - hexo deploy 或者 hexo d -``` - -- 报以下错误 - -``` -ERROR Deployer not found: git -``` - -**解决方法:** - -``` -npm install hexo-deployer-git --save -hexo d -``` - -- 配置 Deloyment 前,需要配置自己的身份信息 - -``` - git config --global user.name "yourname" - git config --global user.email "youremail" -``` - -**如果执行完以上命令还是不行,删除blog文件下的 .deploy_git 文件重新 hexo deploy** diff --git "a/source/_posts/JavaScript\345\237\272\347\241\200.md" "b/source/_posts/JavaScript\345\237\272\347\241\200.md" deleted file mode 100644 index 4afe273..0000000 --- "a/source/_posts/JavaScript\345\237\272\347\241\200.md" +++ /dev/null @@ -1,145 +0,0 @@ -js 学习 - -## 数据类型 - -``` -var fullName; -// undefined - -fullName -// undefined - -fullName + 2 -// NaN - -``` - -- 判断类型名称 - - ``` - fullName = '张' - "张" - typeof(fullName) - // "string" - ``` - - ``` - var weight = 100; - undefined - typeof(weight) - // "number" - - ``` - - - ``` - var firstName = '王', lastName = '张' - undefined - firstName + lastName - // "王张" - ``` - -- 类型不相同时转换后在拼接 - - ``` - var weightIncrease = '2.5斤' - undefined - weight + weightIncrease - // "1002.5斤" - ``` - -## 数组 - - - - var array = []; - undefined - array.length - // 0 - typeof(array) - // "object" - - - -- 增加元素 - - ``` - array.push('1') - // 4 - array - // (4) ["ab", "cbd", "fcg", "1"] - - ``` - -- 数组前面添加 - -``` -array.unshift('0') -// 6 -array -// (6) ["0", "ab", "cbd", "fcg", "1", Array(2)] -``` - -- 删除最后元素 - - ``` - array.pop() // 返回删除的元素 - // (2) ["2", "3"] - array - // (5) ["0", "ab", "cbd", "fcg", "1"] - ``` -- 删除第一个元素 - - ``` - array.shift() - // "0" - array - // (4) ["ab", "cbd", "fcg", "1"] - ``` -- 删除指定的值,但数组元素没有被删 - - ``` - delete array[2] - // true - array - // (4) ["ab", "cbd", empty, "1"] - ``` - -- 删除指定的元素 - - ``` - array.splice(1) - // (3) ["cbd", empty, "1"] - array - // ["ab"] - ``` - -## 函数 - -### 调用调用 - -``` -alertMessage(); - -function alertMessageWithParamter(message) { - alert(message) -} - -alertMessageWithParamter('有参数的函数') -alertMessageWithParamter(250) -alertMessageWithParamter( - console.log('test') -) - -``` -### 函数表达式,使用函数声明的方式 - -``` -var alertMessage_expression = function expression_alert (message) { - alert(message) -} - -alertMessage_expression('匿名函数调用') - -expression_alert('函数表达式') -``` \ No newline at end of file diff --git "a/source/_posts/Mac-\345\256\211\350\243\205MySQL.md" "b/source/_posts/Mac-\345\256\211\350\243\205MySQL.md" deleted file mode 100644 index e0ec7d9..0000000 --- "a/source/_posts/Mac-\345\256\211\350\243\205MySQL.md" +++ /dev/null @@ -1,38 +0,0 @@ ---- -title: Mac 安装MySQL -date: 2017-12-14 22:52:19 -tags: ---- -搭建 `XMPP` 即使通讯 - - -## 安装MySQL数据库 - -- 安装MySQL 数据库 - - [官网](https://www.mysql.com/) 下载安装包 - - **特别强调:不要一直`下一步`最后的时候会有个弹窗提示你生成root的临时密码,记录一下** - -- 安装数据库管理工具 `MySQLWorkbench` - - 官网安装包下载安装 - -- 修改数据库 root 密码, - - 打开`MySQLWorkbench` 随便打开一个SQL 输入安装时记录的密码,确认后提示你修改密码, - -- 如果为记住安装时密码,参考下面链接修改 - - [](https://www.cnblogs.com/sunny3096/p/7954129.html) - -## 安装 openfire - -- [官网](https://www.igniterealtime.org/downloads/index.jsp) 下载安装包安装 - -- 安装后需重启系统偏好设置 -- 如果出现右边的管理员配置按钮不能点击,请使用 `brew` 安装 或者更新 `Java` 环境,`openfire` 需要 `Java` 环境 - - - - - \ No newline at end of file diff --git "a/source/_posts/Mac\345\256\211\350\243\205VirtualBox\350\277\236\346\216\245U\347\233\230.md" "b/source/_posts/Mac\345\256\211\350\243\205VirtualBox\350\277\236\346\216\245U\347\233\230.md" deleted file mode 100644 index 1c92f4c..0000000 --- "a/source/_posts/Mac\345\256\211\350\243\205VirtualBox\350\277\236\346\216\245U\347\233\230.md" +++ /dev/null @@ -1,18 +0,0 @@ ---- -title: Mac安装VirtualBox连接U盘 -date: 2018-02-03 02:41:32 -tags: ---- - -最近因为某些原因需要再Mac上安装虚拟机,所以选择了VirtualBox安装了 -win10系统,但是需要再虚拟机的win10中访问U盘数据,遇到的问题是不能连接并且读取到U盘,错误提示不能分配USB内存。。。 - -网上找了好多办法都胡扯,根本没有解决... - -解决方法: -1.升级VirtualBox到最新版 -2.关闭当前的虚拟机中的操作系统 -3.安装增强工具 -4.重启VirtualBox,回到Mac桌面推出U盘即可 - -最重要的就是在第四步 diff --git "a/source/_posts/MasonrySnapkit\344\275\277\347\224\250\346\200\273\347\273\223.md" "b/source/_posts/MasonrySnapkit\344\275\277\347\224\250\346\200\273\347\273\223.md" deleted file mode 100644 index d087cdb..0000000 --- "a/source/_posts/MasonrySnapkit\344\275\277\347\224\250\346\200\273\347\273\223.md" +++ /dev/null @@ -1,246 +0,0 @@ -## Masonry使用 - -基本使用官网Demo非常详细,以下总结的是常见问题和技巧 - -### 安装遇到的问题 -- pod 安装后出现⚠️,项目运行报错 - ``` - [UIView mas_makeConstraints:]: unrecognized selector sent to instance - ``` - [github](https://github.com/SnapKit/Masonry/issues/188) - - [解决方法](https://www.jianshu.com/p/3c3fc3cf1218) - -- 使用`lastBaseline`属性`crash` - - [解决办法](https://github.com/SnapKit/Masonry/issues/353) - -### 使用时注意点 - -- `edgesForExtendedLayout` 方法 - - 重写此方法的目的是为了确定布局位置从什么位置开始什么位置结束, - ``` - - (UIRectEdge)edgesForExtendedLayout { - return UIRectEdgeNone; - } - ``` - - **重写后** - ![](https://ws1.sinaimg.cn/large/006tNc79ly1fsgcn3fmo0j30s61iyalk.jpg) - - **重写前** - ![](https://ws4.sinaimg.cn/large/006tNc79ly1fsgcokvpdwj30s61iy7eq.jpg) - -- `translucent` 设置`UINavigationBar`是否半透明, 如果设置了导航栏的`style` 为`UIBarStyleBlackTranslucent` 就为true - - **设置为false不透明效果** - ![](https://ws4.sinaimg.cn/large/006tNc79ly1fsgg7ugz8mj30ow0nqq4h.jpg) - - -- `extendedLayoutIncludesOpaqueBars` 导航栏不透明条件下是否可以扩展,默认是NO不可以扩展 - - ``` - self.navigationController?.navigationBar.isTranslucent = false - self.extendedLayoutIncludesOpaqueBars = true - ``` - - **设置不透明,设置可以扩展此时从坐标的(0,0)点开始布局** - ![](https://ws2.sinaimg.cn/large/006tNc79ly1fsggem13mjj30ow0j83zy.jpg) - -- `inset` 的使用,当约束控件的属性相同时不用再考虑正负值的问题,例如要约束一个控件左边距离另一控件的右边位置时还需要使用负值情况 - - ``` - [yellowdView makeConstraints:^(MASConstraintMaker *make) { - make.height.equalTo(@150); - make.top.equalTo(self).inset(220); - make.leading.equalTo(self).inset(10); - make.trailing.equalTo(greenView.leading).inset(10); - make.width.equalTo(greenView); - }]; - ``` -- `edges`的使用,约束边距更方便 - - ``` - [edgeView remakeConstraints:^(MASConstraintMaker *make) { - make.edges.equalTo(lasView).insets(UIEdgeInsetsMake(10, 5, 5, 5)); - }]; - ``` - -- `Priority` 设置约束的优先级 - - - `priorityHigh` - - `priorityMedium` - - `priorityLow` - -- `makeConstraints`、`updateConstraints`、`remakeConstraints`三者区别 - - - `makeConstraints` 添加约束 - - `updateConstraints` 更新约束,更新之前会查找一边控件已经存在的约束,没有就添加,有就更新到最新的约束 - - `remakeConstraints`删除控件以前的所有约束重新添加约束 - -- 设置控件或者某一约束的'key' 方便冲突时⚠️查找 - - ``` - greenView.mas_key = @"greenView"; - [greenView makeConstraints:^(MASConstraintMaker *make) { - make.height.equalTo(yellowdView).key(@"greenView-height"); - make.top.equalTo(yellowdView).key(@"greenView-top"); - make.width.equalTo(yellowdView).key(@"greenView-width"); - make.trailing.equalTo(self).inset(10).key(@"greenView-trailing"); - // 冲突约束 -// make.leading.equalTo(greenView.trailing).inset(10).key(@"greenView-leading"); - }]; - ``` - -- 使用 `MASAttachKeys`批量给`view`设置key - - ``` - MASAttachKeys(greenView, yellowdView); - ``` - - ![](https://ws3.sinaimg.cn/large/006tNc79ly1fsgalvd4l6j31a20modnp.jpg) - -- `dividedBy`和 `multipliedBy` 约束宽高比 - - ``` - [topInnerView makeConstraints:^(MASConstraintMaker *make) { - // 设置自己的宽高比是3:1 - make.width.equalTo( topInnerView.height).multipliedBy(3); // 乘因数 - - // 设置宽高并且设置他们的优先级最低 - make.width.height.lessThanOrEqualTo(topView); - make.width.height.equalTo(topView).priorityLow(); - - // 设置位置 - make.top.equalTo(topView); - }]; - ``` -- 多个控件批量约束 - - ``` - NSValue *sizeValue = [NSValue valueWithCGSize:CGSizeMake(100, 100)]; - [@[blueView, redView, yellowView] makeConstraints:^(MASConstraintMaker *make) { - make.size.equalTo(sizeValue); - }]; - - [@[blueView, redView, yellowView] mas_makeConstraints:^(MASConstraintMaker *make) { - make.top.equalTo(self).inset(20); - }]; - - [blueView makeConstraints:^(MASConstraintMaker *make) { - make.left.equalTo(self).inset(20); - }]; - - [redView makeConstraints:^(MASConstraintMaker *make) { - make.left.equalTo(blueView.right).inset(10); - }]; - - [yellowView makeConstraints:^(MASConstraintMaker *make) { - make.left.equalTo(redView.right).inset(10); - }]; - ``` - - ![批量约束](https://ws1.sinaimg.cn/large/006tNc79ly1fsgaz670hvj30s61iyn2l.jpg) - - **以上代码也可以简化为** - - ``` - [redView makeConstraints:^(MASConstraintMaker *make) { - make.left.equalTo(blueView.right).inset(10); - }]; - - [yellowView makeConstraints:^(MASConstraintMaker *make) { - make.left.equalTo(redView.right).inset(10); - }]; - - - [blueView makeConstraints:^(MASConstraintMaker *make) { - make.top.left.equalTo(self); - make.size.equalTo(@[redView, yellowView, sizeValue]); - }]; - - ``` - ![](https://ws2.sinaimg.cn/large/006tNc79ly1fsgbhz8lw5j30s61iyte5.jpg) - -## Snapkit 使用 - -- Swift 中`edgesForExtendedLayout:UIRectEdge` 扩展布局的边缘是一个属性,默认是`All` - - ``` - self.edgesForExtendedLayout = [] - ``` - - 在Swift中闭包参数可以使用$0、$1、$2 一次代表第一个,第二个,第三个参数 - -- 使用$0 来代替闭包参数 - - ``` - nullDataImageView.snp.makeConstraints { - $0.top.equalTo(view).offset(44) - $0.centerX.equalTo(view) - $0.width.height.equalTo(200) - } - ``` - -- `deactivate` 和 `activate` 使用 - - - `activate` 激活指定的约束 - - `deactivate` 移除指定的约束 - - 添加约束 - - ``` - nullDataImageView.snp.makeConstraints { - nullDataViewTopConstraint = $0.top.equalTo(view).offset(80).constraint - nullDataViewLeftConstraint = $0.left.equalTo(view).inset(10).constraint - $0.width.height.equalTo(200) - } - ``` - - 移除对应约束,设置约束再激活约束 - ``` - nullDataViewLeftConstraint?.deactivate() - nullDataViewLeftConstraint?.update(inset: 100) - nullDataViewLeftConstraint?.activate() - UIView.animate(withDuration: 1.5) { - self.view.layoutIfNeeded() - } - ``` - -- 更新约束 - - - 添加约束 - - ``` - nullDataImageView.snp.makeConstraints { - nullDataViewTopConstraint = $0.top.equalTo(view).offset(64).constraint - $0.centerX.equalTo(view) - $0.width.height.equalTo(200) - } - ``` - - - 更新约束 - - ``` - nullDataViewTopConstraint?.update(inset: 84) - UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseOut, animations: { - self.view.layoutIfNeeded() - }) { (finished) in - print("动画执行完毕") - } - ``` - -- 添加`debug`模式中的 `key`使用`labeled()`方法 - - ``` - nullDataImageView.snp.makeConstraints { - nullDataViewTopConstraint = $0.top.equalTo(view).offset(80).constraint - nullDataViewLeftConstraint = $0.left.equalTo(view).inset(10).constraint - $0.width.height.equalTo(200).labeled("width和height约束") - } - ``` - -- 其他注意点都和`Masnory`相同 - - diff --git "a/source/_posts/OC RunTime\345\255\246\344\271\240.md" "b/source/_posts/OC RunTime\345\255\246\344\271\240.md" deleted file mode 100644 index 387a32b..0000000 --- "a/source/_posts/OC RunTime\345\255\246\344\271\240.md" +++ /dev/null @@ -1,45 +0,0 @@ ---- -title: OC中运行时 -date: 2016-11-22 11:10:31 -tags: RunTime ---- - - -RunTime -对于C语言,函数的调用在编译的时候会决定调用哪个函数,而对于面向对象的语言对于[调用某个对象的方法或函数]就叫「消息传递」。所以在 Java,C++ 这些语言里[调用某个对象的方法或函数]和 Objective-C 里[向某个对象发送消息]在概念上就是一样的事情, - -### RunTime 简介 - -- RunTime 简称运行时,OC中通常叫运行时机制,主要的是消息机制 -- 消息机制:在OC中,属于动态调用,在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用 - -### RunTime 作用 - -- 发送消息:让对象发送消息 -- 交换方法 -- 动态添加方法 -- 动态获取属性和方法 -- 字典转模型 -- 给分类添加属性 - -原理:给分类添加一个属性,重写了setter、getter方法,但是没有生成 _成员变量,需要通过运行时关联到分类中, 其本质上就是给这个类添加关联,存值和取值 -code - -``` -self.oldurlPath = urlPath; - -- (void)setOldurlPath:(NSString *)oldurlPath { - -// RunTime 关联 -// 第一个参数:给哪个对象添加关联 -// 第二个参数:关联的key,通过这个key获取 -// 第三个参数:关联的value -// 第四个参数:关联的策略 -objc_setAssociatedObject(self, key, oldurlPath, OBJC_ASSOCIATION_COPY_NONATOMIC); -} - -- (NSString *)oldurlPath { - return objc_getAssociatedObject(self, key); -} - -``` \ No newline at end of file diff --git "a/source/_posts/OC\344\270\216js\344\272\244\344\272\222\345\255\246\344\271\240.md" "b/source/_posts/OC\344\270\216js\344\272\244\344\272\222\345\255\246\344\271\240.md" deleted file mode 100644 index c9ae6b9..0000000 --- "a/source/_posts/OC\344\270\216js\344\272\244\344\272\222\345\255\246\344\271\240.md" +++ /dev/null @@ -1,59 +0,0 @@ ---- -title: OC和JS交互 -date: 2017-02-20 20:10:41 -tags: OC, JS ---- - -### WebView相关属性 - -autoResizingMask 是UIView的属性,作用是实现子控件相对于父控件的自动布局 - -``` -autoResizingMask 是UIViewAutoresizing 默认是 UIViewAutoresizingNone - -UIViewAutoresizingNone = 0, -UIViewAutoresizingFlexibleLeftMargin = 1 << 0, -UIViewAutoresizingFlexibleWidth = 1 << 1, -UIViewAutoresizingFlexibleRightMargin = 1 << 2, -UIViewAutoresizingFlexibleTopMargin = 1 << 3, -UIViewAutoresizingFlexibleHeight = 1 << 4, -UIViewAutoresizingFlexibleBottomMargin = 1 << 5 -``` -各属性解释: - -``` -UIViewAutoresizingNone -不会随父视图的改变而改变 - -UIViewAutoresizingFlexibleLeftMargin - -自动调整view与父视图左边距,以保证右边距不变 - -UIViewAutoresizingFlexibleWidth -自动调整view的宽度,保证左边距和右边距不变 - -UIViewAutoresizingFlexibleRightMargin -自动调整view与父视图右边距,以保证左边距不变 - -UIViewAutoresizingFlexibleTopMargin -自动调整view与父视图上边距,以保证下边距不变 - -UIViewAutoresizingFlexibleHeight -自动调整view的高度,以保证上边距和下边距不变 - -UIViewAutoresizingFlexibleBottomMargin -自动调整view与父视图的下边距,以保证上边距不变 -``` -### OC 调用js - -`webViewDidFinishLoad:(UIWebView *)webView `方法中 - -- 删除增加结点 -- 添加点击事件 - -### js调用OC - - -`webView:(UIWebview )webView shouldStartLoadWithRequest:(NSURLRequest )request `方法中 - -调用js页面点击按钮后保存图片到相册,必须调用系统提供的方法,不能自己自定义方法 \ No newline at end of file diff --git "a/source/_posts/Quartz2D\345\210\235\350\257\206.md" "b/source/_posts/Quartz2D\345\210\235\350\257\206.md" deleted file mode 100644 index 72489cc..0000000 --- "a/source/_posts/Quartz2D\345\210\235\350\257\206.md" +++ /dev/null @@ -1,153 +0,0 @@ ---- -title: Quartz2D 初识 -date: 2016-12-04 13:34:28 -tags: Quartz2D ---- - - -**Quartz2D 二维绘图引擎,支持iOS和Mac系统么,可以实现的功能有** - -- 绘制图形: 线条,三角形,矩形,圆,弧等 -- 绘制文字 -- 绘制图像或者图片 -- 读取生成PDF -- 截图/裁剪图片 -- 自定义UI控件 -- 手势解锁 -- 图形上下文 - -## 保存绘图信息和状态 -确定输出目标(PDF,Bitmap或者显示器) - - Bitmap Graphics Context - PDF Granhics Context - Window Graphics Context - Layer Graphics Context - - -绘制线段 - - - (void)drawLine { - // 获取图形上下文 - // 所用的是UIGraphics上下文 - CGContextRef ctx = UIGraphicsGetCurrentContext(); - - // 创建描述路径 - CGMutablePathRef path = CGPathCreateMutable(); - // 设置起点 - // path : 表示给那个路径设置起点 - CGPathMoveToPoint(path, NULL, 10, 10); - CGPathAddLineToPoint(path, NULL, 80, 80); - - // 添加路径到上下文 - CGContextAddPath(ctx, path); - - // 渲染 - CGContextStrokePath(ctx); - } - - - -系统底层自动将路径添加到图形上下文 - - - (void)drawLine2 { - - CGContextRef ctx = UIGraphicsGetCurrentContext(); - - // 描述路径 - // 此方法底层会自动将路径添加到图形上下文 - CGContextMoveToPoint(ctx, 50, 50); - CGContextAddLineToPoint(ctx, 100, 100); - - CGContextSetLineWidth(ctx, 5); - - [[UIColor redColor] set]; - // 渲染 - CGContextStrokePath(ctx); - } - - -使用C语言函数的封装 - - - (void)drawLine3 { - // 使用UIKit 已经封装的功能,面向对象 - // 贝塞尔路径 - - UIBezierPath *path = [UIBezierPath bezierPath]; - [path moveToPoint:CGPointMake(10, 10)]; - [path addLineToPoint:CGPointMake(50, 50)]; - - // 绘制路径 - [path stroke]; - } - - -绘制多条线段 - -- 绘制多条线,状态不好管理 -- 默认下一条线的起点是上一条线的终点 -- 绘制多天不连接的线时需要重新设置起点 -- 为了方便管理,应该让每一条线对应一条路径 -- 使用UIBeizerPath - -code: - - - (void)drawMoreLine { - CGContextRef ctx = UIGraphicsGetCurrentContext(); - - // 描述路径 - CGContextMoveToPoint(ctx, 20, 20); - CGContextAddLineToPoint(ctx, 100, 100); - - // 设置第二条线的起点 - CGContextMoveToPoint(ctx, 20, 30); - CGContextAddLineToPoint(ctx, 150, 100); - - // 设置绘图状态要在渲染之前,都是给上下文设置 - // 描边颜色 - [[UIColor greenColor] setStroke]; - // 线宽 - CGContextSetLineWidth(ctx, 10); - // 连接样式 - // kCGLineJoinMiter, 斜接 - // kCGLineJoinRound, 圆角 - // kCGLineJoinBevel 平切 - CGContextSetLineJoin(ctx, kCGLineJoinBevel); - // 顶角样式,线的顶端效果 - CGContextSetLineCap(ctx, kCGLineCapRound); - - // 渲染 - CGContextStrokePath(ctx); - } - - -使用UIBezierPath 绘制多条直线并设置不同状态 - - - (void)drawUIBezierPahtState { - - // 第一条线 - UIBezierPath *path = [UIBezierPath bezierPath]; - [path moveToPoint:CGPointMake(10, 10)]; - [path addLineToPoint:CGPointMake(80, 80)]; - - path.lineWidth = 20; - path.lineJoinStyle = kCGLineJoinRound; - path.lineCapStyle = kCGLineCapRound; - [[UIColor redColor] setStroke]; - - [path stroke]; - - // 第二条线 - { - UIBezierPath *path = [UIBezierPath bezierPath]; - [path moveToPoint:CGPointMake(40, 70)]; - [path addLineToPoint:CGPointMake(150, 40)]; - - path.lineWidth = 20; - path.lineJoinStyle = kCGLineJoinRound; - path.lineCapStyle = kCGLineCapRound; - [[UIColor greenColor] setStroke]; - - [path stroke]; - } - } \ No newline at end of file diff --git "a/source/_posts/Quartz2D\347\273\230\345\210\266\345\275\242\347\212\266.md" "b/source/_posts/Quartz2D\347\273\230\345\210\266\345\275\242\347\212\266.md" deleted file mode 100644 index 2a8e6c7..0000000 --- "a/source/_posts/Quartz2D\347\273\230\345\210\266\345\275\242\347\212\266.md" +++ /dev/null @@ -1,246 +0,0 @@ ---- -title: Quartz 2D绘制形状(二) -date: 2016-12-10 21:58:31 -tags: Quartz 2D ---- - - -使用Quartz 2D绘制圆弧,扇形,下载进度以及绘制的图形的填充和描边属性的设置. -将贝塞尔路径添加到图形上下文中 -图形上下文中的状态保存和恢复 -对图形上下文进行矩阵操作(平移,缩放,选转),实现一些特殊效果 - -### 使用贝塞尔曲线绘制矩形 - -``` -{ - UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, 20, 30)]; - [[UIColor magentaColor] setStroke]; - [[UIColor brownColor] setFill]; - [path fill]; // 填充 - [path stroke]; // 描边 -} - -``` - -### 圆角矩形 -``` -// 绘制圆角矩形 -{ - UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(35, 10, 50, 70) cornerRadius:5.0]; - [[UIColor magentaColor] setStroke]; - [[UIColor cyanColor] setFill]; - [path fill]; - [path stroke]; -} - -``` -### 绘制圆 -``` -// 画圆 -{ - UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(90, 5, 40, 40) cornerRadius:20.0]; - [[UIColor purpleColor] setStroke]; - [[UIColor magentaColor] setFill]; - [path fill]; - [path stroke]; -} -``` - -### 绘制扇形 -``` -CGPoint center = CGPointMake(200, 50); -UIBezierPath *sectorPath = [UIBezierPath bezierPathWithArcCenter:center radius:100 startAngle:0 endAngle:M_PI_4 clockwise:YES]; -[sectorPath addLineToPoint:center]; -[[UIColor brownColor] setStroke]; -[sectorPath fill]; -[sectorPath stroke]; -``` -### 绘制圆弧 - -``` -/** -* 绘制圆弧 -* 参数1: 圆心坐标 -* 参数2: 半径 -* 参数3: 开始角度 -* 参数4: 结束角度 使用弧度制表示 -* 参数5: clockwis顺时针方法的 -*/ - -UIBezierPath *circuLarArcPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(100, 60) radius:50.0 startAngle:0 endAngle:M_PI_2 clockwise:YES]; - -[circuLarArcPath moveToPoint:CGPointMake(100, 60)]; - -[[UIColor magentaColor] setStroke]; -[[UIColor brownColor] setFill]; -[circuLarArcPath stroke]; -[circuLarArcPath fill]; - -``` -### 绘制文字 - - -``` -/** - * 绘制文字 - * drawAtPoint 不能换行,当文字过多过长时不会有效果 - * 使用 drawInRect 可以实现换行 - */ - - -NSString *text = @"我是绘制文字价格就看过几个框架刚开始大家高考的感觉是快乐的关键时刻大家赶快来时的结果看了风水格局来看就哭了感觉树大根深来对抗肌肤抵抗力"; - -NSMutableDictionary *attributeDict = [NSMutableDictionary dictionary]; -attributeDict[NSFontAttributeName] = [UIFont systemFontOfSize:18]; - -// 字体颜色 -attributeDict[NSForegroundColorAttributeName] = [UIColor greenColor]; -// 描边颜色 -attributeDict[NSStrokeColorAttributeName] = [UIColor yellowColor]; -// 描边宽度 -attributeDict[NSStrokeWidthAttributeName] = @1; -// 阴影 -NSShadow *shadow = [[NSShadow alloc] init]; -shadow.shadowColor = [UIColor redColor]; -shadow.shadowOffset = CGSizeMake(1.0, 1.0); - -attributeDict[NSShadowAttributeName] = shadow; - -// 可以换行 -[text drawInRect:self.bounds withAttributes:attributeDict]; - -// 不换行 -[text drawAtPoint:CGPointZero withAttributes:attributeDict]; - -``` -### 绘制图片 - -``` -/** - * 绘制图片 - * drawAtPoint 默认绘制的内容尺寸和图片的尺寸一样大 - * - */ - -- (void)drawImage { - - UIImage *image = [UIImage imageNamed:@"wc_dk"]; - UIImage *image1 = [UIImage imageNamed:@"wc_hq"]; - - [image drawAtPoint:CGPointMake(10, 90)]; - [image1 drawAtPoint:CGPointMake(70, 80)]; - - // 绘制在控件内部,和控件大小一致 - [image1 drawInRect:self.bounds]; - - // 以平铺的方式绘制在控件的内部 - [image1 drawAsPatternInRect:self.bounds]; -} -``` - -### 圆形下载进度的绘制 - -drawRect 方法不能手动调用,因为图形上下文不能手动创建 -需要调用setNeedsDisplay方法来重绘,系统会创建图形上下文 -绘制时用到的定时器,一般不使用NSTimer定时器,因为调度优先级比较低,不会被准时带,界面会出现卡顿的现象 -CADisplayLink 每次刷新屏幕的时候就会调用,1s刷新 60 次 -setNeedsDisplay调用此方法时并不是立即调用drawRect方法,只是给给当前控件添加刷新标记,直到下一次屏幕刷新的时候才调用drawRect 方法,正好与CADisplayLink 方法一致,所以界面流畅不会卡顿 -图形上下文状态栈 - -UIBezierPath图形上下文和CGContextRef的图形上下文不是同一个图形上下文 - -可以将贝塞尔路径添加到图形上下文中 - -也可以保存当前的图形上下文,CGContextSaveGState(ctx); -在下一次使用时可以恢复,CGContextRestoreGState(ctx); -画两种不同状态的线条 - -``` -/** -* 图形上下文栈的理解 -* 画两条状态不同的线段 -*/ - -// 获取上下文 -CGContextRef ctx = UIGraphicsGetCurrentContext(); - -// 第一条 -UIBezierPath *path = [UIBezierPath bezierPath]; -[path moveToPoint:CGPointMake(10, 60)]; -[path addLineToPoint:CGPointMake(240, 60)]; - -// 把路径添加到上下文 -CGContextAddPath(ctx, path.CGPath); - -// 保存上下的状态 -CGContextSaveGState(ctx); - -// 设置当前状态 -[[UIColor greenColor] setStroke]; -//此时设置path 的状态无效,需要设置当前上下文的状态 -path.lineWidth = 5.0; -CGContextSetLineWidth(ctx, 5.0); - -// 渲染上下文 -CGContextStrokePath(ctx); - - -// 第二根 - -// 可使用第一根的路径,因为每次绘制完成之后都会清除 -path = [UIBezierPath bezierPath]; -[path moveToPoint:CGPointMake(60, 10)]; -[path addLineToPoint:CGPointMake(60, 240)]; - -// 添加路径到上下文 需要转换为CGPath -CGContextAddPath(ctx, path.CGPath); - -// 还原之前保存的上下文状态 -CGContextRestoreGState(ctx); -CGContextStrokePath(ctx); -图形上下文矩阵 - -// 使用矩阵 - - // 获取上下文 - CGContextRef ctx = UIGraphicsGetCurrentContext(); - - // 使用贝塞尔路径 - UIBezierPath *ovalPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(-150, -100, 300, 200)]; - [[UIColor cyanColor] setFill]; - - - - // 以上绘制的图形有一部分看不到,通过对上下文矩阵操作来实现,平移上下文 - // 操作矩阵必须在添加路径之前 - - // 平移 - CGContextTranslateCTM(ctx, 150, 100); - - // 缩放 - CGContextScaleCTM(ctx, 0.5, 0.5); - - // 旋转 - // 可以实现点无法计算时的效果 - CGContextRotateCTM(ctx, M_PI_4); - - - - // 添加路径到上下文 - CGContextAddPath(ctx, ovalPath.CGPath); - - // 操作矩阵放在添加路径之后无效果 - // CGContextTranslateCTM(ctx, 150, 100); - - CGContextFillPath(ctx); - - // 正常的绘制椭圆 -// UIBezierPath *ovalPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(50, 20, 200, 100)]; -// ovalPath.lineWidth = 4.0; -// [[UIColor magentaColor] setFill]; -// [[UIColor yellowColor] setStroke]; -// [ovalPath stroke]; -// [ovalPath fill]; - -``` \ No newline at end of file diff --git "a/source/_posts/Quartz2D\347\273\230\345\210\266\346\233\262\347\272\277.md" "b/source/_posts/Quartz2D\347\273\230\345\210\266\346\233\262\347\272\277.md" deleted file mode 100644 index 34ad5e5..0000000 --- "a/source/_posts/Quartz2D\347\273\230\345\210\266\346\233\262\347\272\277.md" +++ /dev/null @@ -1,120 +0,0 @@ ---- -title: Quartz2D绘制曲线 -date: 2016-12-04 23:53:04 -tags: Quartz2D ---- - -使用函数绘制曲线 - -Quartz 2D提供了CGContextAddCurveToPoint() 函数和CGContextAddQuadCurveToPoint()两个函数来向当前上下文添加曲线,前者用于添加贝塞尔曲线,后者用于添加二次曲线。 -确定一条贝塞尔曲线需要4个点:开始点、第一个控制点、第二个控制点和结束点。 - -确定一条二次曲线需要三个点:开始点、控制点和结束点。 - -添加贝塞尔曲线 - - CGContextAddCurveToPoint() - - CGContextRef ctx = UIGraphicsGetCurrentContext(); - - // 添加曲线路径 - // 设置起点 - CGContextMoveToPoint(ctx, 0, 0); - - - - * 参数1: 上下文对象 - * 参数2: 控制点1X坐标 - * 参数3: 控制点1Y坐标 - * 参数4: 控制点2X坐标 - * 参数5: 控制点2Y坐标 - * 参数6: 终点x坐标 - * 参数7: 终点Y坐标 - -code: - - CGContextAddCurveToPoint(ctx, 30, 200, 300, 20, 300, 300); - - // 设置颜色 - [[UIColor magentaColor] setStroke]; - - // 渲染 - CGContextStrokePath(ctx); - - // 关闭图形上下文 - CGContextClosePath(ctx); - 添加二次曲线 - CGContextAddQuadCurveToPoint() - - // 获取图形上下文 - CGContextRef ctx = UIGraphicsGetCurrentContext(); - - // 起始点 - CGContextMoveToPoint(ctx, 20, 10); - - - * 参数: 图形上下文 - * 参数: 控制点坐标 - * 参数: 结束点坐标 - -添加二次曲线 - - CGContextAddQuadCurveToPoint(ctx, 30, 200, 200, 40); - - [[UIColor cyanColor] setStroke]; - CGContextStrokePath(ctx); - // 关闭图形上下文 - CGContextClosePath(ctx); - - -绘制形状 - -使用绘制二次曲线的函数绘制花瓣 - -程序中CGContextAddFlower代码分别添加5瓣花朵路径、6瓣花朵路径、7瓣花朵路径,然后使用不同的颜色来填充这些路径。注意到上面的程序并未在每次添加花朵路径后立即关闭,这也是允许的,而且每次填充路径时并不会再次填充前一次已经填充过的路径。这是因为只用程序绘制了CGContextRef当前所包含的路径,系统会自动清除已经绘制的路径。 - -**注意:每次绘制完成后,CGContextRef会自动清除已经绘制完成的路径** - - - 该方法负责绘制花朵。 - n:该参数控制花朵的花瓣数;dx、dy:控制花朵的位置;size:控制花朵的大小; - length:控制花瓣的长度 - - - void CGContextAddFlower(CGContextRef c , NSInteger n - , CGFloat dx , CGFloat dy , CGFloat size , CGFloat length) - { - CGContextMoveToPoint(c , dx , dy + size); // 移动到指定点 - CGFloat dig = 2 * M_PI / n; - // 采用循环添加n段二次曲线路径 - for(int i = 1; i < n + 1 ; i++) - { - // 计算控制点坐标 - CGFloat ctrlX = sin((i - 0.5) * dig) * length + dx; - CGFloat ctrlY= cos((i - 0.5 ) * dig) * length + dy; - // 计算结束点的坐标 - CGFloat x = sin(i * dig) * size + dx; - CGFloat y =cos(i * dig) * size + dy; - // 添加二次曲线路径 - CGContextAddQuadCurveToPoint(c, ctrlX , ctrlY , x , y); - } - } - - -绘制花瓣 - - - (void)drawStar - { - CGContextRef ctx = UIGraphicsGetCurrentContext(); // 获取绘图的CGContextRef - CGContextBeginPath(ctx); // 开始添加路径 - CGContextAddFlower(ctx , 5 , 50 , 100 , 30 , 80); // 添加5瓣花朵的路径 - CGContextSetRGBFillColor(ctx, 1, 0, 0, 1); // 设置填充颜色 - CGContextFillPath(ctx); - CGContextAddFlower(ctx , 6 , 160 , 100 , 30 , 80); // 添加6瓣花朵的路径 - CGContextSetRGBFillColor(ctx, 1, 1, 0, 1); // 设置填充颜色 - CGContextFillPath(ctx); - CGContextAddFlower(ctx , 7 , 270 , 100 , 30 , 80); // 添加7瓣花朵的路径 - CGContextSetRGBFillColor(ctx, 1, 0, 1, 1); // 设置填充颜色 - CGContextFillPath(ctx); - CGContextClosePath(ctx); // 关闭路径 - } diff --git "a/source/_posts/Quartz2D\347\273\230\345\210\266\347\272\277\346\235\241.md" "b/source/_posts/Quartz2D\347\273\230\345\210\266\347\272\277\346\235\241.md" deleted file mode 100644 index d8356ca..0000000 --- "a/source/_posts/Quartz2D\347\273\230\345\210\266\347\272\277\346\235\241.md" +++ /dev/null @@ -1,122 +0,0 @@ ---- -title: WKWebiView 使用 -date: 2016-11-22 11:10:31 -tags: WKWebView ---- - - -#### WKWebiView iOS8.0 出现,性能要远远好于 iOS 2.0时出现的 UIWebView,主要记录WKWebView 的创建和代理方法 - - -## 创建WKWebView - - WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds]; - self.webView = webView; - [self.view addSubview:webView]; - webView.UIDelegate = self; - webView.navigationDelegate = self; - [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.baidu.com"]]]; - -遵守协议 WKNavgationDelegate WKUIDelegate - -## 代理方法 - -- WKNavgationDelegate 方法 - - - 发送请求之前决定是否跳转 - - ``` - - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { - - decisionHandler(WKNavigationActionPolicyAllow); - // 不允许跳转 - decisionHandler(WKNavigationActionPolicyCancel); - NSLog(@"decidePolicyForNavigationAction"); - } - - ``` - - 收到相应之后决定是否跳转 - - ``` - - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler { - - decisionHandler(WKNavigationResponsePolicyAllow); - - decisionHandler(WKNavigationResponsePolicyCancel); - - NSLog(@"decidePolicyForNavigationResponse"); - - } - ``` - - - 开始加载时调用 - - ``` - - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation { - - NSLog(@"didStartProvisionalNavigation"); - } - ``` - - - 接受到服务器的跳转请求时调用 - - ``` - - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation { - NSLog(@"didReceiveServerRedirectForProvisionalNavigation"); - } - ``` - - - - - 页面加载失败时调用 **存在缓存问题** - - ``` - - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error { - - NSLog(@"error= %@", error); - } - ``` - - - - 内容开始返回时调用 - - ``` - - (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation { - - NSLog(@"didCommitNavigation"); - } - ``` - - - 内容返回成功后调用 - - ``` - - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation { - - NSLog(@"didFinishNavigation"); - } - - ``` - - 开始返回错误时调用 - - ``` - - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error { - - NSLog(@"didFailNavigation"); - } - ``` - - - 目前不知道什么时候调用 - - ``` - - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler { - NSLog(@"didReceiveAuthenticationChallenge"); - } - ``` - - 目前不知道什么时候调用 - - ``` - - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView API_AVAILABLE(macosx(10.11), ios(9.0)) { - - NSLog(@"webViewWebContentProcessDidTerminate"); - } - ``` diff --git "a/source/_posts/RN\346\212\245\351\224\231.md" "b/source/_posts/RN\346\212\245\351\224\231.md" deleted file mode 100644 index c77f694..0000000 --- "a/source/_posts/RN\346\212\245\351\224\231.md" +++ /dev/null @@ -1,152 +0,0 @@ -RN学习中遇到的错误总结 - -### 执行 npm install 时报错 - -#### 错误1. `Unexpected end of JSON input while parsing near '...er":"0.4.0"},"bin":{"'` - -本地有`package.json`文件,执行 npm install 报错,或者clone 的项目执行npm install 也报同样的错误, 可能是因为缓存导致, - -解决办法: - -1. 设置镜像源,以前可能大部分都是用的淘宝 - - ``` - npm set registry https://registry.npmjs.org/ - ``` - -2. 清除缓存 - - ``` - npm cache clean –-force - ``` - -3. 如果提示失败,使用`sudo` - - ``` - sudo npm cache clean --force - ``` - - - -#### 错误2. `xcrun: error: unable to find utility "simctl", not a developer tool or in PATH` - -可能是因为 Xcode 版本问题引起,XCode 偏好设置中 Command line Tools 中为选择版本问题导致(我的情况是安装了Xcode 10 beta版导致的) - -解决办法: - -![](https://ws3.sinaimg.cn/large/006tNbRwly1fut616x7ivj31840uc77y.jpg) - - -``` -The following build commands failed: - CompileC /Users/liepin/LiePinWorkspace/RNDemo/GitHubPopular/ios/build/Build/Intermediates.noindex/RCTWebSocket.build/Debug-iphonesimulator/RCTWebSocket.build/Objects-normal/x86_64/RCTSRWebSocket.o RCTSRWebSocket.m normal x86_64 objective-c com.apple.compilers.llvm.clang.1_0.compiler -(1 failure) -Installing build/Build/Products/Debug-iphonesimulator/GitHubPopular.app -An error was encountered processing the command (domain=NSPOSIXErrorDomain, code=2): -Failed to install the requested application -An application bundle was not found at the provided path. -Provide a valid path to the desired application bundle. -Print: Entry, ":CFBundleIdentifier", Does Not Exist - -Command failed: /usr/libexec/PlistBuddy -c Print:CFBundleIdentifier build/Build/Products/Debug-iphonesimulator/GitHubPopular.app/Info.plist -Print: Entry, ":CFBundleIdentifier", Does Not Exist - -``` - -#### 错误3. `Error: Cannot find module '../lib/utils/unsupported.js'` - -安装的node 版本不是稳定的版本,需要删除后重新安装 - -``` -sudo rm -rf /usr/local/lib/node_modules/npm -brew reinstall node -``` - -#### 错误4. `Couldn't find preset "module:metro-react-native-babel-preset" when running jest` - -[解决方法](https://github.com/facebook/metro/issues/242) - - -### 已有项目集成RN - - -报错1. 文件路径错误,查看你的`node_modules` 文件夹是在当前目录还是上级目录 - - [!] No podspec found for `React` in `../node_modules/react-native - -- `../` 是指父级目录 - -- `./` 是指当前目录 - - -报错 2. yoga Y 大小写为问题 - - [!] The name of the given podspec `yoga` doesn't match the expected one `Yoga`_modules/react-native/ReactCommon/yoga" - - -### pod install 之后 - -#### 报错 1. - -![](https://ws2.sinaimg.cn/large/006tNbRwly1fuwfgkx6z1j31kw10nu0x.jpg) - -解决办法: pod 'React', :subspecs 中加入 'CxxBridge', 'DevSupport' - - pod 'React', :path => './node_modules/react-native', :subspecs => [ - 'Core', - 'RCTText', - 'RCTNetwork', - 'RCTWebSocket', # 这个模块是用于调试功能的 - 'CxxBridge', # Include this for RN >= 0.47 - 'DevSupport', # Include this to enable In-App Devmenu if RN >= 0.43 -] - - -#### 报错 2. - -`RCTAnimation/RCTValueAnimatedNode.h' file not found` - -解决方法: - -将 - -``` -#import -``` -替换为 - -``` -#import "RCTValueAnimatedNode.h" - -``` - -[修改头文件导入方式](https://github.com/facebook/react-native/issues/13198) - - -#### 报错3 版本不匹配 `react native version mismatch javascript version 0.54.3 native 0.57.2` - -- 关闭所有的 terminal,或者是集成开发环境的命令行窗口 - - ``` - watchman watch-del-all - react-native start --reset-cache - ``` -- 重新打开一个terminal窗口 - - ``` - react-native run-ios - ``` - - [问题描述和解决1](https://blog.csdn.net/awy1988/article/details/80336913) - - [问题描述和解决2](https://stackoverflow.com/questions/47763824/react-native-version-mismatch) - -#### 报错4 `Unable to resolve module "schedule/tracking"` - -缺少一些开发依赖的库 - - npm i schedule@0.4.0 --save-dev - - -[问题描述和解决](https://github.com/facebook/react-native/issues/21150) - \ No newline at end of file diff --git "a/source/_posts/React-Native\345\274\200\345\217\221iOS\346\211\223\345\214\205.md" "b/source/_posts/React-Native\345\274\200\345\217\221iOS\346\211\223\345\214\205.md" deleted file mode 100644 index a651b74..0000000 --- "a/source/_posts/React-Native\345\274\200\345\217\221iOS\346\211\223\345\214\205.md" +++ /dev/null @@ -1,85 +0,0 @@ -# React-Native开发iOS打包 - - -## 打jsbudnle包 - -#### 创建打包后存放文件目录 - -- cd 到 React-Native 项目根目录下 并且 进入 ios 文件目录下 -- 创建 bundle 文件夹 - - ``` - mkdir bundle - ``` - -#### 配置 React-Native 打包命令 - -- 打包命令 - - ``` - react-native bundle - ``` - -- 在React Native项目的根目录下执行命令 - - ``` - react-native bundle --entry-file index.js --platform ios --dev false --bundle-output ./ios/bundle/index.jsbundle --assets-dest ./ios/bundle - ``` - - - `--entry-file`: ios或者android入口的js名称,比如 `index.js` - - - `--platform`: 平台名称(ios或者android) - - - `--dev`: 设置为`false`的时候将会对`JavaScript`代码进行优化处理。 - - - `--bundle-output`: 生成的jsbundle文件的名称和路径,比如 `./ios/bundle/index.jsbundle` - - - `--assets-dest`: 图片以及其他资源存放的目录 - - -#### 使用打包命令 - -- 将打包命令添加到 `packger.json` 中 - - ``` - "scripts": { - "start": "node node_modules/react-native/local-cli/cli.js start", - "test": "jest", - "bundle-ios": "node node_modules/react-native/local-cli/cli.js bundle --entry-file index.js --platform ios --dev false --bundle-output ./ios/bundle/index.jsbundle --assets-dest ./ios/bundle" - }, - ``` - -- 再次打包只需要 `React Native`项目的根目录下执行命令,不用再次输入类似上面的命令 - - ``` - npm run bundle-ios - ``` - -## 集成到Xcode(iOS原生项目中) - -- 添加已经打好的`.jsbundle` 离线包 - - ![](https://ws1.sinaimg.cn/large/006tNbRwly1fw4adqh37aj318e130gpd.jpg) - - -- iOS 项目代码配置 - - ``` - private func loadReactNativeTest() { - // debug 测试加载本地文件 - let url = URL(string: "http://localhost:8081/index.bundle?platform=ios")! - - // 加载已经加入的离线包 - let url2 = Bundle.main.url(forResource: "index", withExtension: "jsbundle") - - let rootView = RCTRootView( - bundleURL: url, - moduleName: "MyApp", //这里的名字要和index.js中相同 - initialProperties: nil, - launchOptions: nil - ) - view = rootView - } - - ``` - \ No newline at end of file diff --git "a/source/_posts/ReactNative\344\270\255ref\344\275\277\347\224\250.md" "b/source/_posts/ReactNative\344\270\255ref\344\275\277\347\224\250.md" deleted file mode 100644 index c1a1ec9..0000000 --- "a/source/_posts/ReactNative\344\270\255ref\344\275\277\347\224\250.md" +++ /dev/null @@ -1,142 +0,0 @@ -Reat Native ref 使用 - -### State 状态的使用 -在父控件中指定,整个生命周期讲不再改变 -需求:一段闪烁的文字, Props 属性显示文字内容创建时初始化,State控制随时间变化是否显示,在父控件中 - -``` -class Blink extends Component{ - // 构造 - constructor(props) { - super(props); - // 初始状态 - this.state = {showWithText:true}; - this.state = {} - setInterval(() => { - this.setState(previousState => { - return {showWithText: !previousState.showWithText} - }); - }, 100); - } - render() { - let display= this.state.showWithText ? this.props.text : ' '; - return( - {display} - ); - } -} -``` - -``` -export default class App extends Component { - render() { - return( - - - - - - - - ); - } -} -``` - -### Props 属性的使用 -``` -// 定义一个组件 -class Greeting extends Component { - render() { - return( - Hello {this.props.name}! - - ); - } -} -``` - -``` -export default class App extends Component { - render() { - return( - - - - 使用Props属性 - - - - - - - - - ); - } -} -``` - -#### ref的使用 - -- 可以理解为组件渲染后指向组件的一个应用,可以通过ref获取到真实的组件(类似指针?) - -定义 - -``` -ref='scrollView' - -``` -获取 - -``` -let scrollView = this.refs.scrollView -let scrollView = this.refs['scrollView'] -``` -- 可以通过ref访问组件的属性和方法 - -#### ES6 定时器 - -- 开启定时器 - -``` -let scrollView = this.refs.scrollView; -// 2.4timer 定时器的写法 -this.timer = setInterval( - ()=>{ - var tempPage = 0; - // 修改banner索引 - if ((this.state.currentPage+1) >= ImageData.data.length) { - tempPage = 0; - } else { - tempPage = this.state.currentPage+1; - } - - // 更新状态 - this.setState({ - currentPage: tempPage - }) - - let offSet_x = width * tempPage - scrollView.scrollResponderScrollTo({x:offSet_x, y:0, animated: true}) - }, - this.state.duration -); -``` - -- 暂停定时器 - -``` -this.timer && clearInterval(this.timer) -``` diff --git "a/source/_posts/SDWebImage\345\255\246\344\271\240.md" "b/source/_posts/SDWebImage\345\255\246\344\271\240.md" deleted file mode 100644 index 0b80a45..0000000 --- "a/source/_posts/SDWebImage\345\255\246\344\271\240.md" +++ /dev/null @@ -1,183 +0,0 @@ ---- -title: SDWebImage的用法及原理 -date: 2017-02-08 23:17:50 -tags: SDWebImage ---- - -### SDWebImage 介绍 - -SDWebImage 是用于网络中下载且缓存图片,并设置图片到对应的控件或上 - -Demo: - -- 提供了UIImageView的category用来加载网络图片并且下载的图片的缓存进行管理 -- 采用异步方式来下载,确保不会阻塞主线程,memory+disk来缓存网络图片,自动管理缓存 -- 支持GIF动画,[self.imageView setImageWithURL:[[NSBundle mainBundle] URLForResource:@”xx.gif” withExtension:nil]; -- 支持WebP格式 -- 同一个URL的网络图片不会被重复下载 -- 失效的URL不会被无限重试 - -### SDWebImage 的使用 - -克隆 - -git clone https://github.com/rs/SDWebImage.git -使用以上命令克隆会报错,框架中Vendors文件夹中的文件未能全部下载导致报错 - - - -解决办法:https://github.com/rs/SDWebImage/blob/master/Docs/ManualInstallation.md - -git clone --recursive https://github.com/rs/SDWebImage.git - - -文件全部下载 - - -自定义operation 加入NSOperationQueue中 - -- 自定义NSOperation, -重写main方法 - -``` -- (void)main { -@autoreleasepool { - NSURL *url = [NSURL URLWithString:_urlStr]; - NSData *data = [NSData dataWithContentsOfURL:url]; - UIImage *image = [UIImage imageWithData:data]; - - if (_finishBlock) { - _finishBlock(image); - } -} -} - -``` - -- 一个NSOperation对象可以通过调用start方法来执行任务,默认是同步执行的。也可以将NSOperation添加到一个NSOperationQueue(操作队列)中去执行,而且是异步执行的 -创建队列 - -``` -- (NSOperationQueue *)downLoadQueue { - if (!_downLoadQueue) _downLoadQueue = [[NSOperationQueue alloc] init]; - return _downLoadQueue; -} - -``` - - -- 添加一个任务到队列 - -``` -[_downLoadQueue addOperation:operation]; -添加一组operation, 是否阻塞当前线程 - -[_downLoadQueue addOperations:@[operation] waitUntilFinished:NO]; -添加一个block 形式的operation - -[_downLoadQueue addOperationWithBlock:^{ - NSLog(@"执行一个新的线程"); -}]; -``` - -**注意:** - -1. NSOperation 添加到 queue之后,通常短时间内就会执行,但是如果存在依赖,或者整个queue被暂停等原因,也可能会需要等待 - -2. NSOperation添加到queue之后,绝不要修改NSOperation对象的状态,因为NSOperation对象可能会在任何时候运行,因此改变NSOperation对象的依赖或者数据会产生不利的影响,只能查看NSOperation 对象的状态,比如是否正在运行、等待运行、已经完成等 - -- NSOperation 添加依赖, -依赖关系不局限于相同的queue 中的NSOperation对象,可以夸队列进行依赖,但是不能循环依赖, - -``` -// NSOperation 添加依赖 -{ - NSOperationQueue *testQueue = [[NSOperationQueue alloc] init]; - NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{ - NSLog(@"operation1 NSThread = %@", [NSThread currentThread]); - }]; - - NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{ - NSLog(@"operation2 NSThead = %@", [NSThread currentThread]); - }]; - - // 添加依赖,1 依赖于 2,只有2 执行完之后才执行1 - [operation1 addDependency:operation2]; - // 加入到队列 - [testQueue addOperation:operation1]; - [testQueue addOperation:operation2]; -} - -``` - -- 修改Operation的执行顺序 -对于添加到queue中的operation执行顺序有一下2点决定 -operation 是否已经准备好,是否添加了依赖 -根据多有operations的相对优先性来决定,优先等级是operation对象本身的一个属性,默认都是“普通”优先级,可以通过setQueuePriority方法提高和降低优先级,优先级只能用于相同 queue 中的 operation,多个queue中operation的优先级相互独立,因此不同queue中的低优先级的operation可能比高优先级的operation更早执行 -注意优先级和依赖不能相互替代,优先级只是对已经准备好的operation确定执行顺序,先满足依赖,然后再看已经准备好的operation中的优先级 - -- 设置 queue 的最大并发数,队列中最多同时运行几条线程 -虽然NSOperationQueue类设计用于并发执行Operations,你也可以强制单个queue一次只能执行一个Operation。setMaxConcurrentOperationCount:方法可以配置queue的最大并发操作数量。设为1就表示queue每次只能执行一个操作。不过operation执行的顺序仍然依赖于其它因素,比如operation是否准备好和operation的优先级等。因此串行化的operation queue并不等同于GCD中的串行dispatch queue - -``` -// 每次只能执行一个操作 -queue.maxConcurrentOperationCount = 1; -// 或者这样写 -[queue setMaxConcurrentOperationCount:1]; -``` - -- 取消 operations -一旦添加到operation queue,queue就拥有了这个Operation对象并且不能被删除,只能取消。调用Operation对象的cancel方法取消单个操作,也可以调用operation queue的cancelAllOperations方法取消当前queue中的所有操作, 使用cancel属性来判断是否已经取消了 - -[operation cacel] 只是打了一个死亡标记, 并没有正真意义上的取消,称为 “自杀“,需要在被取消的任务中时时判断是否取消(在程序的关键处), 如果取消,结束任务, 具体是指在自定义的 operation 的main方法中结束 - -``` -[queue cancelAllOperations] 由队列来取消,称为 “他杀” - -// 取消单个操作 -[operation cancel]; -// 取消queue中所有的操作 -[queue cancelAllOperations]; -``` - -- 等待operation 完成 - -为了最佳的性能,你应该设计你的应用尽可能地异步操作,让应用在Operation正在执行时可以去处理其它事情。如果需要在当前线程中处理operation完成后的结果,可以使用NSOperation的waitUntilFinished方法阻塞当前线程,等待operation完成。通常我们应该避免编写这样的代码,阻塞当前线程可能是一种简便的解决方案,但是它引入了更多的串行代码,限制了整个应用的并发性,同时也降低了用户体验。绝对不要在应用主线程中等待一个Operation,只能在第二或次要线程中等待。阻塞主线程将导致应用无法响应用户事件,应用也将表现为无响应。 -``` -// 会阻塞当前线程,等到某个operation执行完毕 -[operation waitUntilFinished]; -``` -除了等待单个Operation完成,你也可以同时等待一个queue中的所有操作,使用NSOperationQueue的waitUntilAllOperationsAreFinished方法。注意:在等待一个 queue时,应用的其它线程仍然可以往queue中添加Operation,因此可能会加长线程的等待时间。 - -// 阻塞当前线程,等待queue的所有操作执行完毕 -[queue waitUntilAllOperationsAreFinished]; -暂停和继续operation -如果你想临时暂停Operations的执行,可以使用queue的setSuspended:方法暂停queue。不过暂停一个queue不会导致正在执行的operation在任务中途暂停,只是简单地阻止调度新Operation执行。你可以在响应用户请求时,暂停一个queue来暂停等待中的任务。稍后根据用户的请求,可以再次调用setSuspended:方法继续queue中operation的执行 -``` -// 暂停queue -[queue setSuspended:YES]; - -// 继续queue -[queue setSuspended:NO]; -``` - - -### 增加Manager管理类, - -作用: - -- 下载队列 -- 图像缓存 -- 操作缓存(内存缓存) -- 沙盒缓存 -- 防止错乱,取消老的未开始下载的操作 -- 图片显示逻辑:内存缓存 – 沙盒缓存 – 网络下载 -- 图片缓存逻辑:下载完成 – 缓存沙盒 – 加载到内存 - -注意:缓存到本地时只能保存property列表里的对象(NSData, NSDate, NSNumber, NSString, NSArray, NSDictory), 图片需要转化为二进制数据 - -### SDWebImage - -- SDWebImageDownloader 下完图片后都需要手动设置给UIImageView -- SDWebImageManager 下完图片后都需要手动设置给UIImageView -- 下载后要显示可以使用 sd_setImageWithURL 方法 \ No newline at end of file diff --git "a/source/_posts/SQlite\344\275\277\347\224\250\345\255\246\344\271\240.md" "b/source/_posts/SQlite\344\275\277\347\224\250\345\255\246\344\271\240.md" deleted file mode 100644 index 16288db..0000000 --- "a/source/_posts/SQlite\344\275\277\347\224\250\345\255\246\344\271\240.md" +++ /dev/null @@ -1,185 +0,0 @@ ---- -title: SQL 使用 -date: 2016-10-13 22:30:54 -tags: SQL ---- - -## 常用SQL 语句 - -创建一张表 - - create table if not exists t_product(productId integer, productName text, productPrice real); - -删除一张表 - - drop table if exists t_product; - -插入一条记录 - -如果不写某个字段和对应值就会为NUll - - insert into t_product(productId, productName, productPrice) values (0001, 'iPhone7', 6100.8); - - -修改记录 - - update t_product set productId = 20002 where productName = 'iPhone5s'; - -删除 - - delete from t_product where productName = 'iphone5s'; - -**注意:条件语句不能单独存在,只能在修改/删除/查询之后** - -## 简单查询 - -准备数据生成sql语句文件 - - NSMutableString *mutableString = [NSMutableString string]; - int productIdValue = arc4random_uniform(1000); - NSString *productNameValue = [NSString stringWithFormat:@"iPhone_%d",1 + arc4random_uniform(8)]; - CGFloat productPriceValue = 3000 + arc4random_uniform(1000); - - for (NSInteger i = 0; i < 1000; i++) { - [mutableString appendFormat:@"insert into t_product(productId, productName, productPrice) values (%d, %@, %f); \r\n", productIdValue, productNameValue, productPriceValue]; - } - - // 写入文件 - [mutableString writeToFile:@"/Users/Apeng/work/Demo/Sqlite/products.sql" atomically:YES encoding:NSUTF8StringEncoding error:NULL]; - - -生成的sql语句文件 - -![](http://i1.piimg.com/567571/588e899a67c1cce6.png) - -SQL 查询语句 - - select *(表示字段)from t_product(表名)where(条件语句) productId > 100 (字段满足的条件) ; - - select * from t_product where productPrice < 5000; - -![](http://p1.bpimg.com/567571/54d2c6ade469b653.png) -or: 或 - -![](http://p1.bqimg.com/567571/c8cd40b4271ec880.png) - -and: 且 - -![](http://i1.piimg.com/567571/a6b404e93f8bc0eb.png) - -分页查询 - -limit 0,5 ( limit 索引,每页取得数据条数 ) - -第一页 limit 0,5; - -第二页 limit 5,5; - -第三页 limit 10,5; - -第四页 limit 15,5; - -第N页 limit (N - 1)* 5 - - select * from t_product where productName = 'iPhone_8' and productPrice < 3300 order by productPrice limit 5,5; - -![](http://i1.piimg.com/567571/29e2799f64db4884.png) - -排序查询 - -order by 默认是从小到大,一般是在某一个结果之后 - -默认升序 - - // 默认升序 - select * from t_product where productName = 'iPhone_8' and productPrice < 3300 order by productPrice; - - // 降序 - select * from t_product where productName = 'iPhone_8' and productPrice < 3300 order by productPrice desc; - - -![](http://p1.bqimg.com/567571/c69e4c2790b900b6.png) - -模糊查询 - -关键字搜索 ,% 是指通配符 - -SQL 顺序 - - select * from 表名 条件语句(模糊匹配)排序语句 分页语句 - - select * from t_product where productName like '%_4' order by productPrice limit 0,5; - -![](http://i1.piimg.com/567571/4727877c1b85cb0e.png) - - -主键 - -数据库的约定俗称,自增长,建议创建表时增加主键,也可以之后再设计表 - - CREATE TABLE IF NOT EXISTS t_class (id integer PRIMARY KEY, className text, classNO integer); - - -外键约束 - -当两张表有关联时,需要使用外键约束,一张表中的字段名所对应的值只能来自于另一张表中 - -![](http://p1.bqimg.com/567571/6b8c31aca88003d0.png) - -添加外键约束之后的变化 - -![](http://p1.bpimg.com/567571/4e0c9af7ec980748.png) - - -多表查询 - -有 `t_department`(部门表)和 `t_employee`(员工表)两张表 - -需求:在 `t_employee` 表中查出部门研发部的人员 - -1 嵌套查询: - -先查根据部门名称查出部ID, - -![](http://p1.bqimg.com/567571/29459ada951407a8.png) - -然后再用部门ID查出员工表中的员工 -![](http://i1.piimg.com/567571/0636fdca9e552d99.png) - -以上两步的代码可合并 - -根据部门名称查出部门ID - - SELECT id from t_department WHERE departmentName = '研发部'; - -根据部门id查出这个部门的人员 - - SELECT * from t_employee WHERE departmentID = 1; - -合并后为 - - select * from t_employee where departmentID = (SELECT id from t_department WHERE departmentName = '研发部'); - -结果为: - -![](http://i1.piimg.com/567571/fd184b9d631bb19f.png) - - -2. 链接查询 - -给表起别名,给字段起别名 - - select employee.*, depart.departmentName deptN from t_department depart, t_employee employee; - - -/**多表查询之链接查询*/ - - select t_employee.*, t_department.departmentName FROM t_department, t_employee; - -![](http://i1.piimg.com/567571/d5bd63824decf7f6.png) - -消除笛卡尔积 - - select employee.*, depart.departmentName deptN from t_department depart, t_employee employee where employee.departmentID = depart.id and employee.departmentID = 1; - -![](http://i1.piimg.com/567571/b326df9d3150885d.png) diff --git "a/source/_posts/Swift \345\215\217\350\256\256\344\275\277\347\224\250.md" "b/source/_posts/Swift \345\215\217\350\256\256\344\275\277\347\224\250.md" deleted file mode 100644 index 1697183..0000000 --- "a/source/_posts/Swift \345\215\217\350\256\256\344\275\277\347\224\250.md" +++ /dev/null @@ -1,146 +0,0 @@ -# Swift 协议使用 - -## 对UIView 扩展 - -### 按钮/文本框抖动动画 - -- 声明协议 - - - 协议中定义属性:遵循该协议的类型都具有此属性 - - 必须明确规定该属性是可读的 `{get}` 或者可写的 `{set}`,或是可读可写的 `{get set}` - - 使用static修饰声明一个类类型属性 - - - 协议中定义方法 - -``` -protocol Shakable {} -extension Shakable where Self: UIView { - func shakeAnimation() { - let animation = CABasicAnimation(keyPath: "position") - animation.duration = 0.08 - animation.repeatCount = 5 - animation.autoreverses = true - animation.fromValue = NSValue(cgPoint: CGPoint(x: self.center.x - 4, y: self.center.y)) - animation.toValue = NSValue(cgPoint: CGPoint(x: self.center.x + 4, y: self.center.y)) - layer.add(animation, forKey: "position") - } -} -``` - -- 自定义UI控件并遵守协议 - -``` -class MyButton: UIButton, Shakable { } -class MySwitch: UISwitch, Shakable { } -class MyTextField: UITextField, Shakable { } - -``` - -- 使用 - -``` -let mySwitch = MySwitch() -mySwitch.isOn = true -self.mySwitch = mySwitch -view.addSubview(mySwitch) -mySwitch.translatesAutoresizingMaskIntoConstraints = false -mySwitch.topAnchor.constraint(equalTo: view.bottomAnchor, constant: 10).isActive = true -mySwitch.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10).isActive = true - -// 调用 -mySwitch.shakeAnimation() -``` - - -## UITableViewCell使用 - -### 定义协议 - -``` -protocol ReusableView: class { } - -extension ReusableView where Self: UIView { - static var reuseIdentifier: String { - return String(describing: self) - } -} - -``` - -### 对tableView扩展 - -``` -extension UITableView { - /// 注册cell - func register(T: T.Type) where T: ReusableView { - print(T.reuseIdentifier) - self.register(T.self, forCellReuseIdentifier: T.reuseIdentifier) - } - - /// 调用cell - func dequeueReusableCell(_ indexPath: IndexPath) -> T where T: ReusableView { - print(T.reuseIdentifier) - return self.dequeueReusableCell(withIdentifier: T.reuseIdentifier, for: indexPath) as! T - } -} -``` - -### 用法 - -- 创建自定义的cell,需要遵守协议 - -``` -class TableProtocolCell: UITableViewCell, ReusableView { - override var reuseIdentifier: String? { - return "cell" - } - override init(style: UITableViewCellStyle, reuseIdentifier: String?) { - super.init(style: style, reuseIdentifier: reuseIdentifier) - self.backgroundColor = .cyan - self.textLabel?.text = "通过协议和泛型创建的cell" - self.textLabel?.textColor = .blue - } - required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -class TableProtocolCell2: UITableViewCell, ReusableView { - override init(style: UITableViewCellStyle, reuseIdentifier: String?) { - super.init(style: style, reuseIdentifier: reuseIdentifier) - self.backgroundColor = .red - self.textLabel?.text = "cell1 - 通过协议和泛型创建的" - self.textLabel?.textAlignment = .right - } - required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} -``` - -- 注册和使用 - -``` -class TableViewProtocolController: UITableViewController { - override func viewDidLoad() { - super.viewDidLoad() - tableView.register(T: TableProtocolCell.self) - tableView.register(T: TableProtocolCell2.self) - } - - override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return 10 - } - - override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - var cell = UITableViewCell() - if indexPath.row % 2 == 0 { - cell = tableView.dequeueReusableCell(indexPath) as TableProtocolCell - } else { - cell = tableView.dequeueReusableCell(indexPath) as TableProtocolCell2 - } - return cell - } -} - -``` diff --git "a/source/_posts/Swift \345\237\272\347\241\200\347\237\245\350\257\206.md" "b/source/_posts/Swift \345\237\272\347\241\200\347\237\245\350\257\206.md" deleted file mode 100644 index 765617f..0000000 --- "a/source/_posts/Swift \345\237\272\347\241\200\347\237\245\350\257\206.md" +++ /dev/null @@ -1,515 +0,0 @@ -Swift 学习 - -## 枚举 - -#### 1. 定义 rawValue 为 Int 类型,初始值为1, - -``` -enum Month: Int { - case January = 1 - case February - case March - case April - case May - case June - case July - case August - case September - case October - case November - case December -} - -``` - -#### 2. rawValue 的整型值可以不连续 - -``` -enum Season: Int { - case Spring = 1 - case Summer = 5 - case Autumn = 10 - case Winter = 40 -} -``` - - -#### 3. 枚举可以是字符串,字符串是`变量名`和 `rawValue` 的值相等 -``` -enum ProgrammerLanguae: String { - case Swift - case OC = "Objective-C" - case C = "语言" - case RN = "React-Native" - case Java -} - -``` - -使用`rawValue` - -``` -func residueNewYear(month: Month) -> Int { - return 12 - month.rawValue -} - -``` - -调用枚举的Moth的构造函数生成一个Month - -``` -let month = Month(rawValue: 5) -let swift = ProgrammerLanguae.Swift.rawValue -let oc = ProgrammerLanguae.OC -let RN = ProgrammerLanguae.RN.rawValue -``` - -#### 4. 枚举关联值 associate value -``` -enum Status { - case success(Int) - case fail(String) - case null // 未关联值 -} - -func associateValueTest(isSuccess: Bool) -> Status { - if isSuccess { - return .success(200) - } - return .fail("失败") -} -``` - -#### 5. 枚举associateValue多个值 - -* 本质是关联了一个元祖(value0, value1, value2...) - -``` -enum Shape { - case Square(width: Double) - case Reactangle(width: Double, height: Double) - case Circle(x: Double, y: Double, radius: Double) - case Point -} - -let square = Shape.Square(width: 20) -let reactangle = Shape.Reactangle(width: 10, height: 20) -let circle = Shape.Circle(x: 10, y: 10, radius: 20) -let point = Shape.Point - -private func area(shape: Shape) -> Double { - switch shape { - case let .Square(width): - return width * width - case let .Reactangle(width, height): - return width * height - case let .Circle(_, _, radius): - return radius * radius * Double.pi - case .Point: - return 0 - } -} -``` - -#### 6. 递归枚举 - -- 定义一个递归算术表达式 -- 使用 indirect 来修饰 - -``` -indirect enum ArithmeticalExpression { - case Number(Int) - case Addition(ArithmeticalExpression, ArithmeticalExpression) // + 时两边也是一个表达式 - case Multiplication(ArithmeticalExpression, ArithmeticalExpression) // * 时两边也是一个表达式 - -// indirect case Addition(ArithmeticalExpression, ArithmeticalExpression) // + 时两边也是一个表达式 -// indirect case Multiplication(ArithmeticalExpression, ArithmeticalExpression) // * 时两边也是一个表达式 -} -``` - -- 递归表达式使用 (2+3) * 4 - -``` -let two = ArithmeticalExpression.Number(2) -let one = ArithmeticalExpression.Number(3) -let sum = ArithmeticalExpression.Addition(two, one) -let indirectEnumResult = ArithmeticalExpression.Multiplication(sum, ArithmeticalExpression.Number(4)) -``` - -- 计算表达式值的函数 - -``` -private func calculate(expression: ArithmeticalExpression) -> Int { - switch expression { - case let .Number(value): - return value - case let .Addition(left, right): - return calculate(expression: left) + calculate(expression: right) - case let .Multiplication(left, right): - return calculate(expression: left) * calculate(expression: right) - } -} -``` - -## 结构体 - -- 结构体中的属性值没有初始化时必须使用构造函数来初始化,否则报错 -- - -``` -struct Location { - var latitude: Double - var longitude: Double - var placeName: String? -} - -let location1 = Location(latitude: 37.3230, longitude: -122.0322, placeName: "测试") -``` - -- 结构体中属性如果都赋了初始值就可以直接初始化 - -``` -struct Location2 { - var latitude: Double = 0 - var longitude: Double = 0 -} - -let location2 = Location2() - -``` - -- 如果未指定初始值,swift 就不会默认初始化为(不初始化) -- 当属性值为可选值时此时允许值为nil,那么就允许不通过构造函数来初始化赋值 -- let 只有一次赋值机会 -- 不管是类还是结构体都应该提供一个全参数的构造函数 `init(latitude: Double, longitude: Double, placeName: String?)` - -``` -struct Location3 { - let latitude: Double - let longtitude: Double - var placeName: String? - - init(coordinateString: String) { - let index = coordinateString.index(of: ",") - let index1 = coordinateString.index(after: index!) - let test_1 = coordinateString.prefix(upTo: index!) - let test_2 = coordinateString.suffix(from: index1) - - latitude = Double(test_1)! - longtitude = Double(test_2)! - } - - init(latitude: Double, longitude: Double, placeName: String?) { - self.latitude = latitude - self.longtitude = longitude - self.placeName = placeName - } -} - -let test3 = Location3(coordinateString: "12,45") -``` - -## 类 - -## 属性和方法 - -#### 计算型属性 - -#### 类型属性 - -#### 类型方法 - -#### 属性管擦器 - -#### 延迟属性 - -#### 访问控制 - -#### 单利模式 - - - -## 继承和构造函数 - -#### swift 中继承 - -#### 多态性 - -#### 属性和函数重载 - -#### 子类两段式构造 - - -- 子类构造函数分为两段,第一段是构造自己,第二段是构造父类 - - 父类中构造函数 - - 子类要设置一个自己的构造函数 - - 子类构造函数中需要先初始化自己的属性 - - 然后在构造函数中调用父类初始化父类构造函数中的相关属性 - - 父类 - - ``` - class Father { - var name: String - var sex: Int = 0 - var old: Int = 32 - var desc: String { - return "我是\(name)" - } - init(name: String) { - self.name = name - } - - func run() { - print("Father can Running") - } - } - ``` - - 子类 - - ``` - class Son: Father { - - var isSwimming = true - - var computer: String - - override var desc: String { - return "Son is \(name)" - } - - init(name: String, computer: String) { - self.computer = computer - super.init(name: name) - } - } - - ``` -- 子类两段构造都完成后子类的初始化才完成,可以使用`self`调用属性或者方法 - - ``` - init(name: String, computer: String) { - self.computer = computer - self.getToys() // 此代码报错,子类初始化未完成 - super.init(name: name) - } - - ``` - -#### 便利构造函数和指定构造函数 - -- 便利构造函数只能调用自己的指定构造函数 -- 指定构造函数通过一系列的调用都会调用super.init() -- 便利构造函数无法调用super.init() - -``` -class Father { - var name: String - var sex: Int = 0 - var old: Int = 32 - var desc: String { - return "我是\(name)" - } - init(name: String) { - self.name = name - } - - init(name: String, old: Int) { - self.name = name - self.old = old - } - - func run() { - print("Father can Running") - } -} - -class Son: Father { - var isSwimming = true - var computer: String - - override var desc: String { - return "Son is \(name)" - } - - // 子类指定构造函数,调用了父类的指定构造函数 - init(name: String, computer: String) { - self.computer = computer - super.init(name: name) - } - - // 子类便利构造函数,调用了指定构造函数 - convenience override init(name: String) { - let computer = "iMac" - self.init(name: name, computer: computer) - } -} - -``` - -#### 构造函数继承 - -- 子类有可能会继承父类的构造函数 -- 子类没有实现父类的任何指定构造函数;则自动继承父类的所有指定构造函数, 因为继承了指定构造函数所以同时便利构造函数也被继承 - -父类 -``` -class Father { - var name: String - var sex: Int = 0 - var old: Int = 32 - var desc: String { - return "我是\(name)" - } - - /// Father指定构造函数-1 - init(name: String) { - self.name = name - } - - /// Father指定构造函数-2 - init(name: String, old: Int) { - self.name = name - self.old = old - } - - /// Father便利构造函数 - convenience init(old: Int) { - self.init(name: "Father", old: old) - } - - func run() { - print("Father can Running") - } -} -``` - -子类 - -``` -class Son: Father { - var isSwimming = true - var computer: String - var job: String - - override var desc: String { - return "Son is \(name)" - } - - /// 子类重载的指定构造函数-1 - convenience override init(name: String) { - self.init(name: name, computer: "Dell") - } - - /// 子类重载的指定构造函数-2 - override convenience init(name: String, old: Int) { - self.init(name: name, computer: "acer") - } - - - /// 子类自己的指定构造函数,调用了父类的指定构造函数 - init(name: String, computer: String) { - self.computer = computer - self.job = "C#" - super.init(name: name) - } - - // 子类便利构造函数,调用了自己指定构造函数 - convenience init(computer: String) { - let name = "小张" - self.init(name: name, computer: computer) - } -} - -``` - -未实现父类任何的指定构造函数 - -``` -class Grandson: Son { - var toy: String = "dog toys" -} -``` - -子类可以调用的构造函数 - -``` -let grandSon0 = Grandson(old: 4) -let grandSon1 = Grandson(computer: "Mi") -let grandSon2 = Grandson(name: "小王") -let grandSon3 = Grandson(name: "小虎", computer: "👽") -let grandSon4 = Grandson(name: "小李", old: 8) -``` - -- 子类实现了父类所有的指定构造函数,则自动继承父类的所有便利构造函数 - -``` -let son0 = Son(old: 30) -let son1 = Son(computer: "Mi") -let son2 = Son(name: "小王") -let son3 = Son(name: "小虎", computer: "👽") -let son4 = Son(name: "小李", old: 8) -``` - -#### required 构造函数 - -- 父类中有被 required 关键词修饰的构造函数子类必须实现此构造函数 -- 子类实现的此构造函数不需要再使用 override 关键词修饰,需要 required 修饰 - -子类 - -``` -class Father { - var name: String - /// Father指定构造函数-1 - // required 修饰的指定构造函数子类中必须实现 - required init(name: String) { - self.name = name - } -} -``` - -父类 - -``` -class Son: Father { - var isSwimming = true - var computer: String - - /// 子类重载的指定构造函数-1 - convenience required init(name: String) { - self.init(name: name, computer: "Dell") - } - - /// 子类自己的指定构造函数,调用了父类的指定构造函数 - init(name: String, computer: String) { - self.computer = computer - self.job = "C#" - super.init(name: name) - } -} -``` - -- 子类如果实现了自己的指定构造函数,那么 required 修饰指定构造函数就初始化失败,因为自己实现了指定构造函数所以不能继承父类中的构造函数,此时可以自己实现被required 修饰的便利构造函数 - - -``` -class Son1: Son { - var sport: String - - // 指定构造函数,需要调用父类的指定构造函数 - init(name: String, sport: String) { - self.sport = sport - Son.init(name: name) - } - - // 父类中有被 required 关键词修饰的必须实现的构造函数 - convenience required init(name: String) { - // fatalError("init(name:) has not been implemented") - // 调用自己的指定构造函数, - self.init(name: name, sport: "") - } -} -``` - diff --git "a/source/_posts/Swift3.0\345\255\246\344\271\240(\344\270\200).md" "b/source/_posts/Swift3.0\345\255\246\344\271\240(\344\270\200).md" deleted file mode 100644 index 852ebcb..0000000 --- "a/source/_posts/Swift3.0\345\255\246\344\271\240(\344\270\200).md" +++ /dev/null @@ -1,169 +0,0 @@ ---- -title: Swift 3.0 学习 -date: 2017-03-01 22:39:10 -tags: Swift ---- - -## 和OC区别 - -- 没有了.h 和 .m 文件,没有main.m 文件 -- 程序入口发生变化 在appdelegate中的 @UIApplicationMain -- 每行代码可以不写 ‘;’ -- self可以不写,建议不写self,因为闭包中必须要写 -- 真假表示只有 true/ false,没有非0即真的概念 -- 类型自动推到 - - -## 类型自动推到 - -## 提前指定类型 -``` -let float :Double = 9.6 -print(float) -``` - -## 常量和变量 - -- let 声明常量,有且只有一次赋值机会 -- var 声明变量 -- 尽量选择使用let如需修改值再使用var 系统会报警告 - - -## 可选项 - -- Optional: 表示一个常量或者是变量可能有值可能为nil,在打印的时候会带有Optional字样 -- ? 表示是一个可选项 -- 可选项不能参与运算 -- ! 表示可选项中一定要有值,可以强制解包,解包时如果没有值会boom - -- ?? 合并空选项 快速判断可选项是否为nil, 如果为nil取后面的默认值,在基本数据类型和字符串使用较多 - -## 条件分支 - -- if let - -快速赋值,判断赋值对象是否为nil,如果不为nil,就赋值进入分支 -生成request是url是一个可选项,需要强制解包或者合并空选项 - -``` -let urlString = "http://www.baidu.com" -let url = NSURL(string: urlString) -let request = NSURLRequest(URL: url!) -print(request) -``` - -- 以上代码使用if let实现 - -``` -if let u = NSURL(string: urlString) { - let request = NSURLRequest(URL: u) - print(request) -} - -``` -- where条件语句配合多重判断 - -``` -if url != nil { - if url?.host == "www.baidu.com" { - let request = NSURLRequest(URL: url!) - print(request) - } -} - -``` - -- 使用if let - -``` -if let u = url where u.host == "www.baidu.com" { - let request = NSURLRequest(URL: u) - print(request) -} -``` - - -- 多重判断 - -``` -if let u = url, s = string { - let request = NSURLRequest(URL: u) - print(request) -} - -``` - -- guard let 和 if let 相反如果为nil,可以直接返回 - -``` -let urlString = "http://www.baidi.com" -let url = NSURL(string: urlString) -guard let u = url else { return } - -//程序走到这个地方就表示 u 一定有值 -let request = NSURLRequest(URL: u) -print(request) -``` - -- swith语句 - - - swift语句中可以case任意类型, - - 在OC中只能case整数 - - 不需要写break,会自动跳出, - - 如果需要实现case穿透,需要加入fallthrough语句 - - case语句中临时变量不需要写‘{ }’ - - 可同时case多个值 - - 每句case语句中至少要有一行可执行的代码 - -``` -let string = "string1" - switch string { - case "string", "string1": - let str = "stringOne" - print(str) - - case "string2": - let str = "stringTWO" - print(str) - - case "string3": - print(string) - - case "string4": - print(string) - default: - print("empty") - } - -``` - -- switch 中同样能够赋值和使用 where 子句 - -``` -let point = CGPoint(x: 10, y: 10) -switch point { -case let p where p.x == 0 && p.y == 0: - print("中心点") -case let p where p.x == 0: - print("Y轴") -case let p where p.y == 0: - print("X轴") -case let p where abs(p.x) == abs(p.y): - print("对角线") -default: - print("其他") -} -``` - -- 循环语句 - -``` -for i in 0 ..< 10 { // 不包含10 循环范围中两边的值格式必须一直(空格问题) - print(i) - } - -for _ in 0...5 {// 包含5 _ 表示忽略,不关心,只占位 - print("循环语句") -} - -``` \ No newline at end of file diff --git "a/source/_posts/Swift3.0\345\255\246\344\271\240(\344\270\211).md" "b/source/_posts/Swift3.0\345\255\246\344\271\240(\344\270\211).md" deleted file mode 100644 index 1b5517b..0000000 --- "a/source/_posts/Swift3.0\345\255\246\344\271\240(\344\270\211).md" +++ /dev/null @@ -1,70 +0,0 @@ ---- -title: Swift3.0学习(三) -date: 2017-03-08 23:29:43 -tags: swift ---- - -面向对象–必选属性 - -构造函数时是分段构造,先构造子类,给’必选属性’设置初始值,然后再构造父类, -定义全局变量(属性)时,需要在构造函数中设置初始化变量值 -super.init()是一个类构造结束 -重写: override 函数重写(覆盖),如果在重写的方法中 没有super, 父类相关的一些操作就不会被执行 -重载:函数名相同, 参数的类型 或者参数的个数不同 就形成了函数的重载 -注意:重载构造函数,并且父类默认的构造函数 init()不重写, 父类默认的构造函数就不能够被访问,不能够确保必选属性设置初始值,只能调用重载的构造函数 -面向对象–可选属性 - -使用KVC给对象设置值 -注意: 在swift中使用KVC 基本数据类型不能声明为可选项,必须设置为必选项给定初始值 - -KVC实现的流程 -遍历字典的键值 给对象发送 setValue: forKey消息 -如果key 对象的属性不存在就将消息转发给 setVale: forUndefinedKey: -如果存在就直接设置值 -注意:setVale: forUndefinedKey: 默认抛出异常 不能够super,如果super 相当于没有实现此方法 - -面向对象–便利构造函数 - -作用:方便快捷的创建对象 -场景: -可检查参数是否正确来实例化 -实例化控件 -特点 -必须以self来调用构造函数 -指定的构造函数不能被重写,也不能被super调用 -便利构造函数可被子类继承 -可以构造失败,返回nil -面向对象 – 描述信息 - -重写 description 方法,返回值是string类型 -使用kvc讲对象属性转换为字典 -将字典转化为string类型 - - override var description: String { - let keys = ["name","age","number","sex"] - - // 对象转为字典 - let dict = self.dictionaryWithValuesForKeys(keys) - - // 每个对象都有描述属性 - return dict.description - } - -面向对象 – 存储属性 - -面向对象 – 计算属性(readOnly) - -只有getter,没有setter -只能取值不能赋值 -每次调用都会被执行,消耗cpu -不占内存空间,依赖其他的属性来计算 -面向对象 – didset 属性设置监察器 - -能获取 oldValue 和 newValue 的值 -通常用来重写setter方法,实现视图绑定模型 -在一个属性的didset中计算其他属性 - -缺点:消耗内存,被计算的属性占内存 -优点:减少了cpu消耗 -懒加载 - diff --git "a/source/_posts/Swift3.0\345\255\246\344\271\240(\344\272\214).md" "b/source/_posts/Swift3.0\345\255\246\344\271\240(\344\272\214).md" deleted file mode 100644 index 64bbaad..0000000 --- "a/source/_posts/Swift3.0\345\255\246\344\271\240(\344\272\214).md" +++ /dev/null @@ -1,233 +0,0 @@ ---- -title: Swift3.0学习(二) -date: 2017-03-05 23:36:21 -tags: swift ---- - - -字符串 - -字符串遍历 -字符串字节长度 -字符串长度 - -let string = "我是switf,语言" - - // 字符串遍历 - for s in string.characters { - print(s) - } - - // 字符串的长度 - let length = string.characters.count; - print(length) - - // 字符串字节长度 汉字对应3个字节,英文是1个字节 - let characterLength = string.lengthOfBytesUsingEncoding(NSUTF8StringEncoding) - print(characterLength) - -字符串拼接 - - let str1 = "你好" - let str2 = "不好" - let a = 20 - - // 第一种拼接 - print("\(str1)\(str2)\(a)") - - // 第二种拼接 - let str = str1 + str2 - let strA = str1 + String(a) - print(str) - print(strA) - - -截取子串 as 的使用 - - let subString = str1.substringToIndex("小程生生世世".endIndex); - print(subString) - - let subStr = str1.substringFromIndex("听说".startIndex) - print(subStr) - - let range = str1.rangeOfString("小程序") - let subStr1 = str1.substringWithRange(range!) - - print(subStr1) - - // 截取到某一个位置 - let subStr2 = str1.substringFromIndex(str1.endIndex.advancedBy(-2)) - print(subStr2) - - // 限制截取几个字符 - let subStr3 = str1.substringToIndex(str1.startIndex.advancedBy(5, limit: str1.characters.endIndex)) - print(subStr3) - String 和 NSString 之间的 使用 as 无缝转换 - let helloString = "我们一起飞" - (helloString as NSString).substringWithRange(NSMakeRange(2, 3)) - 数组 - -可以放不同类型的元素 -let不可变数组,var可变数组 - - // 初始化一个空数组 [String]声明是一个装有字符串的数组类型 - var emptyArray: [String] = [String]() - - // 可变数组 - var array = ["哈哈","呵呵","嘿嘿"] - array.append("咯咯") - print("array = \(array)") - - // 不可变数组 - let array2 = ["可可","噗噗"] - - // 数组拼接 - let arr = array + array2 - print("arr = \(arr)") - - array += array2 - print("array = \(array)") - -数组遍历 - - // 1. - for i in 0 ..< tempArray.count { - print(tempArray[i]) - } - - - // 2. - for obj in tempArray { - print("第二种方法:\(obj)") - } - - - // 3. 快速便利 - for (index,value) in tempArray.enumerate() { - print("index = \(index), value = \(value)") - } - -字典和OC类似 - -函数调用 - -必须在函数声明的下面调用内部函数 - -可以定义函数的外部参数和内部参数 - -可以在参数前面加上 ‘# ’ 表明函数的外部参数和内部参数名称一样 swift3.0 以后废弃 - -无参无返回值 -三种写法 - - func demo9() -> Void { - print("无参无返回值第一种写法") - } - - - func demo_9() -> () { - print("无参无返回值第二种写法") - } - - func demo_99() { - print("无参无返回值第三种写法") - } -有参有返回值 - - func demo10(width a: Int, height b: Int) -> Int { - return a * b - } - -闭包 - -和OC中block类似, -和在函数中调用一个内部函数原理相同 -可当作参数传递 -在需要时执行闭包实现回调 - - // 定义闭包 - let closure = {() -> () in - print("闭包实现") - } - - // 执行闭包 - closure() - -注意循环引用 - -只有两个对象相互强引用或者三个对象相互强引用形成闭环时才会形成循环引用 - -weak 和 unsafe_unretained的区别 - -__weak iOS5.0 推出,当对象被系统回收时,对象的地址会自动指向nil - -____unsafe_unretained iOS4.0 推出,当对象被系统回收时,对象的地址不会自动指向nil,会造成野指针访问 -解决办法:(官方推荐)weak - strong -dance 解决循环引用,AFNetworking中 - -尾随闭包 - -函数的最后一个参数时闭包的时候,函数的参数 ‘()’可以提前关闭,闭包写在 ‘()’后面,当作尾随闭包来使用 - - - -swift 和 OC 区别 - -swift 和 OC语法的快速的对比 - -XXX.init(xxx) ==> XXX.(xxx) -selector 类型 ==> ‘函数名’ -对象方法的调用 是 ‘.’ self是可以省略的 -枚举 枚举名 + 枚举值的名 => .枚举值的名 -常量 和变量 - -let 声明常量 var 声明变量 -变量/常量的类型是自动推到的 -不同类型之间不能够直接运算 需要手动转换数据类型 -可选项 -‘?’ 表示表示可选项 可能有值 可能为 nil -可选项会自动带上 Optional 字样 -可选项不能够直接参与运算 需要强制解包 -‘!’ 表示强制解包 获取可选项中具体的值 -‘??’ 快速判断可选项是否为nil 如果为 nil 就去取 ‘??’后面的默认值 -控制流 -if 没有非零即真的概念 必须制定明确的条件 -if let 快速赋值 并且判断赋值对象是否为 nil 如果不为nil 就进入分之执行相关逻辑代码 -guard let 作用和 if let 相反 好处: 可以减少一层分支嵌套 -where 多重判断 注意 和 && || 不一样的 -循环 - -for in -0..<10 -0…10 -字符串 - -更加轻量级 更加高效 是 结构体 -支持快速遍历 -长度 -拼接 -截取 as -集合 let 声明不可变的集合 var 声明可变的集合 - -声明 [] -声明一个 空的集合 -增删改查 -合并 -遍历 -函数 - -func 函数名称(外部参数1 内部参数1: 类型, 外部参数2 内部参数2: 类型) -> Int {执行的代码} -函数没有返回值的三种方式 -函数内部函数 -闭包 () -> () 没有参数没有返回值的闭包类型 - -提前准备好的一段可以执行代码块 -可以当做参数传递 -在需要的时候执行闭包产生回调的效果 -在闭包中使用self 需要考虑循环引用 -函数是一种特殊的闭包 - - - -block 的循环引用的解除 -__weak 和 weak关键字作用类似 当属性对象被回收是 会自动指向nil -___unsafeunretained 和 assgin关键字作用类似 当属性对象被回收是 不会自动指向nil, 会造成野指针访问 weak -strong - dance \ No newline at end of file diff --git "a/source/_posts/Swift3.0\345\255\246\344\271\240(\344\272\224).md" "b/source/_posts/Swift3.0\345\255\246\344\271\240(\344\272\224).md" deleted file mode 100644 index d8356ca..0000000 --- "a/source/_posts/Swift3.0\345\255\246\344\271\240(\344\272\224).md" +++ /dev/null @@ -1,122 +0,0 @@ ---- -title: WKWebiView 使用 -date: 2016-11-22 11:10:31 -tags: WKWebView ---- - - -#### WKWebiView iOS8.0 出现,性能要远远好于 iOS 2.0时出现的 UIWebView,主要记录WKWebView 的创建和代理方法 - - -## 创建WKWebView - - WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds]; - self.webView = webView; - [self.view addSubview:webView]; - webView.UIDelegate = self; - webView.navigationDelegate = self; - [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.baidu.com"]]]; - -遵守协议 WKNavgationDelegate WKUIDelegate - -## 代理方法 - -- WKNavgationDelegate 方法 - - - 发送请求之前决定是否跳转 - - ``` - - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { - - decisionHandler(WKNavigationActionPolicyAllow); - // 不允许跳转 - decisionHandler(WKNavigationActionPolicyCancel); - NSLog(@"decidePolicyForNavigationAction"); - } - - ``` - - 收到相应之后决定是否跳转 - - ``` - - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler { - - decisionHandler(WKNavigationResponsePolicyAllow); - - decisionHandler(WKNavigationResponsePolicyCancel); - - NSLog(@"decidePolicyForNavigationResponse"); - - } - ``` - - - 开始加载时调用 - - ``` - - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation { - - NSLog(@"didStartProvisionalNavigation"); - } - ``` - - - 接受到服务器的跳转请求时调用 - - ``` - - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation { - NSLog(@"didReceiveServerRedirectForProvisionalNavigation"); - } - ``` - - - - - 页面加载失败时调用 **存在缓存问题** - - ``` - - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error { - - NSLog(@"error= %@", error); - } - ``` - - - - 内容开始返回时调用 - - ``` - - (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation { - - NSLog(@"didCommitNavigation"); - } - ``` - - - 内容返回成功后调用 - - ``` - - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation { - - NSLog(@"didFinishNavigation"); - } - - ``` - - 开始返回错误时调用 - - ``` - - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error { - - NSLog(@"didFailNavigation"); - } - ``` - - - 目前不知道什么时候调用 - - ``` - - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler { - NSLog(@"didReceiveAuthenticationChallenge"); - } - ``` - - 目前不知道什么时候调用 - - ``` - - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView API_AVAILABLE(macosx(10.11), ios(9.0)) { - - NSLog(@"webViewWebContentProcessDidTerminate"); - } - ``` diff --git "a/source/_posts/Swift3.0\345\255\246\344\271\240(\345\233\233).md" "b/source/_posts/Swift3.0\345\255\246\344\271\240(\345\233\233).md" deleted file mode 100644 index d8356ca..0000000 --- "a/source/_posts/Swift3.0\345\255\246\344\271\240(\345\233\233).md" +++ /dev/null @@ -1,122 +0,0 @@ ---- -title: WKWebiView 使用 -date: 2016-11-22 11:10:31 -tags: WKWebView ---- - - -#### WKWebiView iOS8.0 出现,性能要远远好于 iOS 2.0时出现的 UIWebView,主要记录WKWebView 的创建和代理方法 - - -## 创建WKWebView - - WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds]; - self.webView = webView; - [self.view addSubview:webView]; - webView.UIDelegate = self; - webView.navigationDelegate = self; - [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.baidu.com"]]]; - -遵守协议 WKNavgationDelegate WKUIDelegate - -## 代理方法 - -- WKNavgationDelegate 方法 - - - 发送请求之前决定是否跳转 - - ``` - - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { - - decisionHandler(WKNavigationActionPolicyAllow); - // 不允许跳转 - decisionHandler(WKNavigationActionPolicyCancel); - NSLog(@"decidePolicyForNavigationAction"); - } - - ``` - - 收到相应之后决定是否跳转 - - ``` - - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler { - - decisionHandler(WKNavigationResponsePolicyAllow); - - decisionHandler(WKNavigationResponsePolicyCancel); - - NSLog(@"decidePolicyForNavigationResponse"); - - } - ``` - - - 开始加载时调用 - - ``` - - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation { - - NSLog(@"didStartProvisionalNavigation"); - } - ``` - - - 接受到服务器的跳转请求时调用 - - ``` - - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation { - NSLog(@"didReceiveServerRedirectForProvisionalNavigation"); - } - ``` - - - - - 页面加载失败时调用 **存在缓存问题** - - ``` - - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error { - - NSLog(@"error= %@", error); - } - ``` - - - - 内容开始返回时调用 - - ``` - - (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation { - - NSLog(@"didCommitNavigation"); - } - ``` - - - 内容返回成功后调用 - - ``` - - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation { - - NSLog(@"didFinishNavigation"); - } - - ``` - - 开始返回错误时调用 - - ``` - - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error { - - NSLog(@"didFailNavigation"); - } - ``` - - - 目前不知道什么时候调用 - - ``` - - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler { - NSLog(@"didReceiveAuthenticationChallenge"); - } - ``` - - 目前不知道什么时候调用 - - ``` - - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView API_AVAILABLE(macosx(10.11), ios(9.0)) { - - NSLog(@"webViewWebContentProcessDidTerminate"); - } - ``` diff --git "a/source/_posts/SwiftLint\344\275\277\347\224\250.md" "b/source/_posts/SwiftLint\344\275\277\347\224\250.md" deleted file mode 100644 index 6f479f1..0000000 --- "a/source/_posts/SwiftLint\344\275\277\347\224\250.md" +++ /dev/null @@ -1,3 +0,0 @@ -SwiftLint 将 swift 强制检查代码风格 - -[教程](https://github.com/realm/SwiftLint/blob/master/README_CN.md) diff --git "a/source/_posts/Swift\344\270\213\346\240\207\345\222\214\350\277\220\347\256\227\347\254\246\351\207\215\350\275\275.md" "b/source/_posts/Swift\344\270\213\346\240\207\345\222\214\350\277\220\347\256\227\347\254\246\351\207\215\350\275\275.md" deleted file mode 100644 index 35abe14..0000000 --- "a/source/_posts/Swift\344\270\213\346\240\207\345\222\214\350\277\220\347\256\227\347\254\246\351\207\215\350\275\275.md" +++ /dev/null @@ -1,280 +0,0 @@ -Swift下标和运算符重载 - -### 字典数组下标 - - var array = [1,2,3,4,5,6] - let temp = array[0] // 通过下标访问 - let temp2 = array[2] // 通过下标访问 - -### 结构体下标 - -- 对于结构体不是直接使用下标访问,会直接报错 - 直接使用下标获取属性值报错: `Type 'Vector_3' has no subscript members` - -- 可以通过实现 `subscript(index: Int) -> Double?`方法让结构体支持下标访问,可以理解为一个特殊的函数,需要传入参数和返回值 -- 可以增加第二种下标--根据坐标轴获取属性值 -- 重写 subscript 的 set 方法 可以使用下标修改属性值 - -``` -struct Vector_3 { - var x = 0.0 - var y = 0.0 - var z = 0.0 - - // 根据索引获取 - subscript(index: Int) -> Double? { - - // 重写了get方法 实现通过下标获取属性值 - get { - switch index { - case 0: return x - case 1: return y - case 2: return z - default: return nil - } - } - - // 重写 set 方法,实现g通过下标设置属性值,使用系统默认的newValue - set { - guard let newValue = newValue else {return} - switch index { - case 0: x = newValue - case 1: y = newValue - case 2: z = newValue - default: return - } - } - } - - // 增加第二种下标--根据坐标轴获取属性值 - subscript(axis: String) -> Double? { - - get { - switch axis { - case "X", "x": return x - case "Y", "y": return y - case "Z", "z": return z - default: return nil - } - } - - set { - guard let newValue = newValue else {return} - switch axis { - case "X", "x": x = newValue - case "Y", "y": y = newValue - case "Z", "z": z = newValue - default: return - } - } - } -} - -``` - -### 下标访问 - -结构体使用下标获取值 - -``` -var v = Vector_3(x: 1, y: 2, z: 3) -print(v[0], v[1], v[2], v[3], v[100]) // Optional(1.0) Optional(2.0) Optional(3.0) nil nil -print(v["x"], v["y"], v["z"], v["i"]) // Optional(1.0) Optional(2.0) Optional(3.0) nil - -``` - -结构体使用下标修改值 - -``` -v[0] = 101 -v[1] = 202 -v[2] = 303 -v[3] = 400 -v[100] = 51 -print(v[0], v[1], v[2], v[3], v[100]) // Optional(101.0) Optional(202.0) Optional(303.0) nil nil -``` - -``` -v["x"] = 100 -v["y"] = 200 -v["z"] = 300 -v["i"] = 50 -print(v["x"], v["y"], v["z"], v["i"]) // Optional(100.0) Optional(200.0) Optional(300.0) nil - -``` - -### 多维下标 - -定义一个关于矩阵的结构体 - - struct Matrix { - var data: [[Double]] - let row: Int - let column: Int - - init(row: Int, column: Int) { - self.row = row - self.column = column - data = [[Double]]() - for _ in 0.. Double { - get { - assert(row >= 0 && row < self.row && column >= 0 && column < self.column, "下标不合法") - return data[row][column] - } - - set { - assert(row >= 0 && row < self.row && column >= 0 && column < self.column, "下标不合法") - data[row][column] = newValue - } - } - - // 通过下标 [row][column] 方式访问 - subscript(row: Int) -> [Double] { - get { - assert(row >= 0 && row < self.row, "下标不合法") - // 直接返回数组,数组本身有下标 - return data[row] - } - - set { - assert(newValue.count == column, "下标不合法") - data[row] = newValue - } - } - } - - -### 运算符重载 - -- 重载 + 运算符 - -``` -func +(one: Vector_3, other: Vector_3) -> Vector_3 { - return Vector_3(x: one[0]! + other[0]!, y: one[1]! + other[1]!, z: one[2]! + other[2]!) - -// return Vector_3(x: one.x + other.x, y: one.y + other.y, z: one.z + other.z) -} - -``` - -- 两个参数时相减 - -``` -func -(one: Vector_3, other: Vector_3) -> Vector_3 { - return Vector_3(x: one.x - other.x, y: one.y - other.y, z: one.z - other.z) -} - -``` -- 一个参数时去反, 需要 prefix 修饰 - -``` -prefix func -(a: Vector_3) -> Vector_3 { - return Vector_3(x: -a.x, y: -a.y, z: -a.z) -} -``` - -- 向量相乘/向量和常量相乘 - -``` -func *(one: Vector_3, other: Vector_3) -> Double { - return (one.x * other.x) + (one.y * other.y) + (one.z * other.z) -} - -``` - -- 两个参数不能交换,需要重载两次 `*` - -``` -func *(one: Vector_3, a: Double) -> Vector_3 { - return Vector_3(x: a * one.x, y: a * one.y, z: a * one.z) -} - -func *(a: Double, one: Vector_3) -> Vector_3 { - return one * a - - // 也可采用下面写法 -// return Vector_3(x: a * one.x, y: a * one.y, z: a * one.z) -} - -``` - -- 修改自身参数,不需要返回值 - -``` -func +=(one: inout Vector_3, other: Vector_3) { - // 已经重载过 + 运算符,可以直接调用 - one = one + other -} - -func ==(one: Vector_3, other: Vector_3) -> Bool { - return one.x == other.x && - one.y == other.y && - one.z == other.z -} - -func !=(one: Vector_3, other: Vector_3) -> Bool { - return !(one == other) - - // 也可采用下面写法 - return one.x != other.x || - one.y != other.y || - one.z != other.z -} - -func <(one: Vector_3, other: Vector_3) -> Bool { - if one.x != other.x {return one.x < other.x} - if one.y != other.y {return one.y < other.y} - if one.z != other.z {return one.z < other.z} - return false -} - -func <=(one: Vector_3, other: Vector_3) -> Bool { - return one < other || one == other - - // 也可采用下面写法 - return one.x > other.x && - one.y > other.x && - one.z > other.z -} - -func >(one: Vector_3, other: Vector_3) -> Bool { - return (one <= other) -} - -``` - -### 自定义操作符 - -`postfix` 声明前后缀关键词, `operator ` 操作符关键词 - -- a+++ - -``` -声明后置操作符 -postfix operator +++ -postfix func +++(vector: inout Vector_3) -> Vector_3 { - vector += Vector_3(x: 1.0, y: 1.0, z: 1.0) - return vector -} - -``` - -- +++a - -``` -// 声明前置操作符 -prefix operator +++ -prefix func +++(vector: inout Vector_3) -> Vector_3 { - let temp = vector - vector += Vector_3(x: 1.0, y: 1.0, z: 1.0) - return temp -} - -``` diff --git "a/source/_posts/Swift\345\212\240\345\257\206\347\233\270\345\205\263.md" "b/source/_posts/Swift\345\212\240\345\257\206\347\233\270\345\205\263.md" deleted file mode 100644 index febaa57..0000000 --- "a/source/_posts/Swift\345\212\240\345\257\206\347\233\270\345\205\263.md" +++ /dev/null @@ -1,257 +0,0 @@ ---- -title: Swift加密相关 -date: 2018-01-08 21:33:01 -tags: AES, Base64, MD5 ---- - - -最近公司项目开发中引用了 `Swift` 与 `OC` 混编, 涉及到了加密相关, 使用了第三方 `CryptoSwift`, 以下是使用过程中的简单记录 - -### CryptoSwift简介 - -- github: -- 使用Swift 编写的加密工具包,支持多种加密算法,如:MD5、SHA1、AES-128, AES-192, AES-256... 等等 - -### 数组转换 - -- `data` 转化为字节数组 - - ``` - let bytes = data.bytes - print(bytes) - ``` - -- 字节数组转化为 `data` - - ``` - let data = Data(bytes: [0x01, 0x02, 0x03]) - print(data) - ``` -- 十六进制创建字节 - - let bytes_Hex = Array(hex: "0x010203") - print(bytes_Hex) - -- 字节转十六进制数 - - let hex = bytes_Hex.toHexString() - print(hex) - - -- 字符串生成字节数组 - - let string2bytes = "string".bytes - print(string2bytes) - - -- 字节数组 base64 - - let bytes1: [UInt8] = [1, 2, 3] - let base64Str = bytes1.toBase64() - print(base64Str!) - -- 字符串base64 - - let str0 = "test" - let base64Str1 = str0.bytes.toBase64() - print(base64Str1!) - - -### MD5加密 - -- MD5(RFC1321)诞生于 1991 年,全称是“Message-Digest Algorithm(信息摘要算法)5”,由 MIT 的计算机安全实验室和 RSA 安全公司共同提出。 -- 之前已经有 MD2、MD3 和 MD4 几种算法。MD5 克服了 MD4 的缺陷,生成 128bit 的摘要信息串,出现之后迅速成为主流算法 - -- 字节数组MD5 - - let str = "测试test" - let bytes = str.bytes - - // 写法一 - let digest = bytes.md5().toHexString() - print(digest) - // 04f47b69e3573867a5b3c1e5edb00789 - - // 写法二 - let digest1 = Digest.md5(bytes).toHexString() - print(digest1) - // 04f47b69e3573867a5b3c1e5edb00789 - -- Data的MD5值 - - /// data MD5 - let data = Data(str.bytes) - let digest3 = data.md5().toHexString() - print(digest3) - // 04f47b69e3573867a5b3c1e5edb00789 - -- 字符串MD5 - - ///写法 一 - let digest4 = str.md5() - print(digest4) - // 04f47b69e3573867a5b3c1e5edb00789 - - // 写法二 - do { - var digest4_2 = MD5() - - let _ = try digest4_2.update(withBytes: "测试".bytes) - let _ = try digest4_2.update(withBytes: "test".bytes) - let result = try digest4_2.finish() - print(result.toHexString()) - //04f47b69e3573867a5b3c1e5edb00789 - } catch { - - } - -### 哈希算法 - -- 字节数组SHA值 - - let str = "测试test" - let bytes = str.bytes - - //方式一 - let digest1 = bytes.sha1().toHexString() - let digest2 = bytes.sha224().toHexString() - let digest3 = bytes.sha256().toHexString() - let digest4 = bytes.sha384().toHexString() - let digest5 = bytes.sha512().toHexString() - print(digest1, digest2, digest3, digest4 ,digest5, separator: "\n") - - //方式二 - let digest6 = Digest.sha1(bytes).toHexString() - let digest7 = Digest.sha224(bytes).toHexString() - let digest8 = Digest.sha256(bytes).toHexString() - let digest9 = Digest.sha384(bytes).toHexString() - let digest10 = Digest.sha512(bytes).toHexString() - print(digest6, digest7, digest8, digest9 ,digest10, separator: "\n") - - -- Data的SHA值 - - let data = Data(bytes: str.bytes) - let digest11 = data.sha1().toHexString() - let digest12 = data.sha224().toHexString() - let digest13 = data.sha256().toHexString() - let digest14 = data.sha384().toHexString() - let digest15 = data.sha512().toHexString() - print(digest11, digest12, digest13, digest14 ,digest15, separator: "\n") - - - -- 字符串的SHA值 - - let digest16 = str.sha1() - let digest17 = str.sha224() - let digest18 = str.sha256() - let digest19 = str.sha384() - let digest20 = str.sha512() - print(digest16, digest17, digest18, digest19 ,digest20, separator: "\n") - - - // 方式一 - let digest_str = str.sha1() - print(digest_str) - - // 方式二 - do { - var digest_str = SHA1() - let _ = try digest_str.update(withBytes: Array("测试".bytes)) - let _ = try digest_str.update(withBytes: Array("test".bytes)) - let result = try digest_str.finish() - print(result.toHexString()) - } catch { - - } - -### AES 加密 - -- key:公共密钥 - -- IV: 密钥偏移量 - -- padding:key 的补码方式 - - key 长度不够16 字节时,需要手动填充, zeroPadding 将其补齐至 blockSize 的整数倍 - zeroPadding 补齐规则: - - 将长度补齐至 blockSize 参数的整数倍。比如我们将 blockSize 设置为 AES.blockSize(16) - - 如果长度小于 16 字节:则尾部补 0,直到满足 16 字节。 - - 如果长度大于等于 16 字节,小于 32 字节:则尾部补 0,直到满足 32 字节。 - - 如果长度大于等于 32 字节,小于 48 字节:则尾部补 0,直到满足 48 字节。 以此类推...... - -- 补码方式padding, noPadding, zeroPadding, pkcs7, pkcs5, 默认使用 pkcs7,两者写法结果相同 - - - let iv_paddding = "1234567890123456" // 默认是 - let aes_padding1 = try AES(key: key.bytes, blockMode: .CBC(iv: iv_paddding.bytes)) - let aes_padding2 = try AES(key: key.bytes, blockMode: .CBC(iv: iv_paddding.bytes), padding: .pkcs7) - - -- ECB 模式 - - let key = "hangge.com123456" - let str = "E蜂通信" - print("加密前 \(str)") - - //MARK: 使用ECB 模式, , 使用aes128 加密 - let aes = try AES(key: key.bytes, blockMode: .ECB) - let encrpyted = try aes.encrypt(str.bytes) - print("加密后 \(encrpyted)", separator: "-----") - - /// 解密 - let decrypted = try aes.decrypt(encrpyted) - print("解密后 \(decrypted)") - let decryptedString = String(data: Data(decrypted), encoding: .utf8)! - print("解密后字符串 \(decryptedString)") - - -- CBC 模式 - - //MARK: 使用CBC 模式, 需要提供一个额外的密钥偏移量 iv - - /// 写法 1 -- 便捷写法 - // 偏移量 - let iv = "1234567890123456" // 默认是 - - // TODO: 随机密钥偏移量 - let data = Data(bytes: AES.randomIV(AES.blockSize)) - let random_iv = String(data: data, encoding: .utf8) - - - - /// 写法 2 -- - // 创建密码器 - let cbc_aes = try AES(key: key.bytes, blockMode: .CBC(iv: iv.bytes)) - - let cbc_aes2 = try AES(key: key, iv: iv) - - - let cbc_aes3 = try AES(key: key, iv: iv) - - let cbc_encrypted = try cbc_aes.encrypt(str.bytes) - - let cbc_encrypted2 = try cbc_aes2.encrypt(str.bytes) - - let cbc_encrypted3 = try cbc_aes3.encrypt(str.bytes) - - - // 便捷写法解密 - let cbc_decrypted = try cbc_aes.decrypt(cbc_encrypted) - let cbc_decryptedData = Data(cbc_decrypted) - let cbc_decryptedStr = String(data: cbc_decryptedData, encoding: .utf8) - print(cbc_decryptedStr) - - // 正常写法2 解密 - let cbc_decrypted2 = try cbc_aes2.decrypt(cbc_encrypted2) - let cbc_decryptedStr2 = String(data: Data(cbc_decrypted2), encoding: .utf8) - print(cbc_decryptedStr2) - - - // 随机密钥偏移量解密 - let cbc_decrypted3 = try cbc_aes3.decrypt(cbc_encrypted3) - let cbc_decryptedStr3 = String(data: Data(cbc_decrypted3), encoding: .utf8) - print(cbc_decryptedStr3) - - \ No newline at end of file diff --git "a/source/_posts/Swift\345\215\217\350\256\256\345\210\253\345\220\215.md" "b/source/_posts/Swift\345\215\217\350\256\256\345\210\253\345\220\215.md" deleted file mode 100644 index b812219..0000000 --- "a/source/_posts/Swift\345\215\217\350\256\256\345\210\253\345\220\215.md" +++ /dev/null @@ -1,430 +0,0 @@ -Swift协议和别名 - -### 协议 - -某些属性的集合,可以有多个协议组成,具有的特点 - - 1. 可以声明函数,但是不能实现,由实现这协议的具体结构或者类来实现 - 2. 声明的函数如果有参数也不能设置参数的默认值, 默认参数被认为也是函的一种实现 - 3. 可以声明属性,必须是计算型属性 只能使用var 修饰,不能是存储型属,必须在 {} 中明确指出属性的读取类型 例如 {get set} - 4. 遵守协议的类可选择存储型属性或者计算型属性来实现协议中定义的属性 - 5. protocol Person: class { } 表示此协议只能被类来遵守,结构体能遵守 - 6. 协议可以看着是一种类型,如果协议中的属性是只读的,如果是协议类型只能访问get 方法, 但是遵守协议的具体类或者结构可以设置属性的读和写 - - 7. 协议中的构造函数 - - - 如果父类中遵守了协议中的构造函数,子类中必须重写父类中的构造函数并且使用require修饰构造函数,表示继承此类的子类也必须实现该构造函数 - - 如果class 使用了final 修饰,表示不能被继承,此时不需要 require 关键词修饰 - -### 类型别名 - -- 定义别名 `typealias `, 供扩展使用 - - ``` - typealias Length = Double - extension Double { - var km: Length {return self * 1_000.0} - var m: Length {return self * 100.0} - var cm: Length {return self / 10.0} - /// 英尺 - var ft: Length { return self / 3.28084} - } - - ``` - - 使用 - - ``` - let distance: Length = 10.5.km - ``` - - - 定义别名防止硬编码 - - ``` - // 音频采样率 - typealias AudioSample = UInt64 - - ``` - - -### 协议中 `typealias` - -**协议中定义一个属性,该属性的类型根据不同的情况返回不同的类型(我理解为类似泛型),具体的类型由遵守此协议的类或者结构指定** - -- 定义协议中别名 - -``` -protocol WeightCalculable { - // 属性重量根据不同体积返回的可能是Int,可以可能是double类型,所以使用别名, 协议中使用associatedtype关键词, - associatedtype WeightType - var weight: WeightType { get } -} - -``` - -扩展 Int 中的吨单位 - -``` -extension Int { - typealias Weight = Int - var t: Weight {return self * 1_000} -} - -``` - -- 使用协议中别名 - - - - 一部手机的时使用该协议`WeightCalculable` - -``` -class iPhone: WeightCalculable { - typealias WeightType = Double - var weight: WeightType { - return 0.5 - } -} -``` - - - 一辆大卡时使用该协议`WeightCalculable` - -``` -class Car: WeightCalculable { - typealias WeightType = Int - let weight: WeightType - - init(weight: Int) { - self.weight = weight - } -} -``` - -``` -let bigCar = Car(weight: 8_000.t) // 8 吨 -``` - - -### 面向协议编程 - -- 定义比赛协议,具有 赢/输/总场次/胜率 等特性 - -``` -protocol Recordable: CustomStringConvertible { - var wins: Int { get } - var losses: Int { get } - - func winningPerent() -> Double -} -``` - -- 协议中方法或者属性的默认实现 - -``` -extension Recordable { - // CustomStringConvertible 协议默认实现 - var description: String { - return String(format: "WINS: %d---LOSSES: %d", [wins, losses]) - } - - // 默认实现总场数计算型属性 - var totalGames: Int { - return wins + losses - } - - // 默认实现的方法 - func shoutWins() { - print("come on", wins, "times!") - } -} -``` - - -#### 篮球比赛: - -- Equatable 协议 - 相等比较 需要重载 `==` -- Comparable 协议 - 是否可比较 ,重载 `<` 可直接调用sort()函数 -- CustomStringConvertible 协议 - 打印信息,需重写 `description` -- Recordable协议 - 比赛协议,没有平局 - -``` -struct BasketballRecord:Equatable, Comparable, CustomStringConvertible, Recordable { - var wins: Int - var losses: Int - - - /// 在具体定义的类型中实现 协议中的属性或者方法就会覆盖协议中的 - var totalGames: Int = 200 - - var description: String { - return "本次比赛胜: " + String(wins) + "负" + "\(losses)" - } - - // 胜率 - func winningPerent() -> Double { - return (Double(wins) / Double(totalGames)) - } - - // swift4.1 后,不需要再重写此方法,swift 会自动合成,如果不想让某一个属性参与比较,就需要重写该方法 - static func == (lhs: BasketballRecord, rhs: BasketballRecord) -> Bool { - return lhs.wins == rhs.wins && lhs.losses == rhs.losses - } - - // Comparable - static func < (lhs: BasketballRecord, rhs: BasketballRecord) -> Bool { - if lhs.wins != rhs.wins { - return lhs.wins < rhs.wins - } - return lhs.losses > rhs.losses - } -} -``` - -#### 足球比赛: - -有平局的情况,原来的协议不能满足,需要增加一个平局的协议 - -- 定义平局协议 - -``` -protocol Tieable { - /// 平局,可读写 - var ties: Int { get set } - -} -``` - -- 足球比赛需要遵守的协议 `Recordable` 和 `Tieable` - - - 总场数需要加上平局的场数,需要实现totalGames的get方法 - - -``` -struct FootableRecord: Recordable, Tieable { - var wins: Int - var losses: Int - // 平局数 - var ties: Int - - // 总场数 - var totalGames: Int { - return wins + losses + ties - } - - var description: String { - return "本次比赛胜: " + String(wins) + "负" + "\(losses)" - } - - func winningPerent() -> Double { - return (Double(wins) / Double(totalGames)) - } -} -``` - -- 足球比赛中以上写法存在的问题 - - - 所有有平局的比赛计算总场次的逻辑都是一样,每次都要写一次实现 - - 单一修改 Recordable 协议不能解决问题, - - 存在平局和没有平局两种情况 totalGames 的计算逻辑不相同 - -- 解决办法: - - 扩展遵守了`Recordable`协议的类型,前提条件是:这个类型遵守了 Tieable 同时也遵守了 `Recordable ` 协议,可以理解为如果该类型遵守了 `Tieable `协议之后,`Recordable` 协议中的 totalGames 属性实现是另一种方式了,不在是以前的 `totalGames` 中直接返回 `wins + losses` 的值 - - -定义协议 - -``` -protocol Recordable: CustomStringConvertible { - var wins: Int { get } - var losses: Int { get } - - func winningPerent() -> Double -} -``` - -协议默认实现 - -``` -extension Recordable { - var description: String { - return String(format: "WINS: %d---LOSSES: %d", [wins, losses]) - } - - // 默认实现总场数计算型属性 - var totalGames: Int { - return wins + losses - } - - // 默认实现胜率 - func winningPerent() -> Double { - return (Double(wins) / Double(totalGames)) - } - - // 默认实现的方法 - func shoutWins() { - print("come on", wins, "times!") - } -} - -``` - -扩展遵守了Tieable 的 类型的 Recordable 协议 - -``` -extension Recordable where Self: Tieable { - var totalGames: Int { - return wins + losses + ties - } -} - -``` - -### 协议聚合 - -如果对某一类型需要限制他必须遵守某些协议,可以使用协议聚合来定义 - -比如: 有个奖赏协议 - -``` -protocol Prizable { - func isPrizable() -> Bool -} - -``` - -篮球比赛遵守此协议 - -``` -// 篮球比赛奖赏 -extension BasketballRecord: Prizable { - func isPrizable() -> Bool { - return totalGames > 10 && winningPerent() > 0.5 - } -} - -``` - -足球比赛奖赏遵守此协议 - -``` -extension FootableRecord: Prizable { - func isPrizable() -> Bool { - return wins > 1 - } -} -``` - -现在有某一个学生也遵守了此协议 - -``` -struct Student: Prizable { - var name: String - var score: Int - - func isPrizable() -> Bool { - return score >= 60 - } -} - -``` - -定义奖赏的方法,参数类型必须是遵守了此协议的结构或者类型 - -``` -private func award(_ one: Prizable) { - if one.isPrizable() { - print(one) - print("恭喜获得奖励") - } else { - print(one) - print("很遗憾") - } -} -``` - -如果说 BasketballRecord 这个类还遵守了其他的协议,例如遵守了 `Recordable` 协议, 并且这个协议也遵守了 `CustomStringConvertible` 并且默认实现了`description` 的`get` 方法 - -``` -// MARK: - 比赛协议,具有 赢。输。总场次。胜率 特性 -protocol Recordable: CustomStringConvertible { - var wins: Int { get } - var losses: Int { get } - - func winningPerent() -> Double -} - -``` - -`Recordable` 默认实现 - -``` - -extension Recordable { - var description: String { - return String(format: "WINS: %d---LOSSES: %d", [wins, losses]) - } - - // 默认实现总场数计算型属性 - var totalGames: Int { - return wins + losses - } - - // 默认实现胜率 - func winningPerent() -> Double { - return (Double(wins) / Double(totalGames)) - } - - // 默认实现的方法 - func shoutWins() { - print("come on", wins, "times!") - } -} -``` - -此时如果 `Student` 类还是调用 `award` 方法的话,print(one) 打印的信息将是`Recordable `中默认实现的内容,因此需要约束`award`函数的参数必须遵守两个协议让`Student` 也重写自己的`description `属性的`get`方法,不能再让 `Prizable` 扩展 默认实现 - -- swift 3 写法: protocol -- swift 4 写法: A & B - -``` -private func award2(_ one: Prizable & CustomStringConvertible) { - if one.isPrizable() { - print(one) - print("恭喜获得奖励") - } else { - print(one) - print("很遗憾") - } -} -``` - -### 泛型约束 - - -- 定义一个函数,找出一个学生数组中分数最大的 - - 参数:一个学生数组,都遵守了 Comparable 的类型 - - 返回值:某个遵守了 Comparable 的类型实例 - - 此时函数报错 `Protocol 'Comparable' can only be used as a generic constraint because it has Self or associated type requirements`, - - 因为 Comparable 协议中定义的方法 public static func < (lhs: Self, rhs: Self) -> Bool 的参数类型是Self,是具体的某个类型 - -``` -func maxScore(seq: [Comparable]) -> Comparable { } -``` - - -- 如果需要定义一个函数实现在一个数组中找出需要奖励的人的名字该如何实现呢 - - 参数:遵守两个协议 Comparable 和 Prizable 协议, 并且使用泛型 - - 返回值:返回值是可选值,有可能没有任何奖励的对象 - -``` -func topPrizable(seq: [T]) -> T? { - return seq.reduce(nil) { (tempTop: T?, condender: T) in - guard condender.isPrizable() else { return tempTop } - // 解包 condender 失败, 上一层验证了他必须是奖励的那个 - guard let tempTop = tempTop else { return condender } - return max(tempTop, condender) - } -} - -``` - diff --git "a/source/_posts/Swift\345\215\217\350\256\256\346\211\251\345\261\225.md" "b/source/_posts/Swift\345\215\217\350\256\256\346\211\251\345\261\225.md" deleted file mode 100644 index 4d73d83..0000000 --- "a/source/_posts/Swift\345\215\217\350\256\256\346\211\251\345\261\225.md" +++ /dev/null @@ -1,184 +0,0 @@ -Swift协议扩展 - -### 定义结构体 - -``` -struct Point { - var x = 0.0 - var y = 0.0 - -} - -struct Size { - var height = 0.0 - var width = 0.0 -} - -struct Rectangle { - var origin = Point() - var size = Size() - - init(origin: Point, size: Size) { - self.origin = origin - self.size = size - } -} - -``` - -### 扩展 - -- 扩展方法 - -``` -extension Rectangle { - mutating func translate(x: Double, y: Double) { - self.origin.x += x - self.origin.y += y - } -} - -``` - -- 只能扩展计算型的属性,不能扩展存储型属性, 存储型属性需要在定义类或者结构体时声明 - -``` -extension Rectangle { - var center: Point { - get { - let center_x = origin.x + size.width / 2.0 - let center_y = origin.y + size.height / 2.0 - return Point(x: center_x, y: center_y) - } - - set { - origin.x = newValue.x - size.width / 2.0 - origin.y = newValue.y - size.height / 2.0 - } - } -} - -``` - -- 扩展构造方法 - - - 类中不能扩展指定构造方法,只能在结构体中扩展 - - 结构体中不能扩展便利构造方法m,只能在类中扩展 - -``` - -extension Rectangle { - init(center: Point, size: Size) { - let origin_x = center.x - size.width / 2.0 - let origin_y = center.y - size.height / 2.0 - self.origin = Point(x: origin_x, y: origin_y) - self.size = size - } - - // 结构体中不能扩展便利构造函数 Delegating initializers in structs are not marked with 'convenience' -// convenience init(center: Point, size: Size) { -// let origin_x = center.x - size.width / 2.0 -// let origin_y = center.y - size.height / 2.0 -// self.origin = Point(x: origin_x, y: origin_y) -// self.size = size -// } -} - -``` - -- 扩展嵌套类型 - -``` -extension Rectangle { - enum Vertex: Int { - case left_top - case left_bottom - case right_bottom - case right_top - } - - // 获取某一个顶点坐标 - func point(of vertex: Vertex) -> Point { - switch vertex { - case .left_top: - return origin - case .left_bottom: - return Point(x: origin.x, y: origin.y + size.height) - case .right_bottom: - return Point(x: origin.x + size.width, y: origin.y + size.height) - case .right_top: - return Point(x: origin.x + size.width, y: origin.y) - } - } -} - -``` - -- 扩展下标,根据传入的索引获取对应顶点坐标 - -``` -extension Rectangle { - subscript(index: Int) -> Point? { - assert(0 <= index && index < 4, "传入值非法") - return point(of: Vertex(rawValue: index)!) - } -} -``` - -### 扩展系统方法 - -``` -extension Int { - /// 平方 - var square: Int { - return self * self - } - - /// 立方 - var cube: Int { - return self * self * self - } - - /// 判断数组是否在某一个范围内 - func inRange(clousedLeft left: Int, openRight right: Int) -> Bool { - return self >= left && self > right - } - - /// 重复执行操作 - func repeatitions(task: () -> ()) { - for _ in 0.. ()) { - // 系统变长函数 - for i in Swift.stride(from: 0, to: 21, by: 3) { - print(i) - } - - for _ in Swift.stride(from: from, to: to, by: by) { - task() - } - } -} - -``` -扩展后使用 - -``` - print(2.square) - print(3.cube) - print(4.inRange(clousedLeft: 0, openRight: 4)) - 4.repeatitions { - print("extension") - } - - - // 使用变长函数 - 4.stride(from: 0, to: 8, by: 4) { - print("stride") - } - -``` diff --git "a/source/_posts/Swift\346\263\233\345\236\213.md" "b/source/_posts/Swift\346\263\233\345\236\213.md" deleted file mode 100644 index fa8a828..0000000 --- "a/source/_posts/Swift\346\263\233\345\236\213.md" +++ /dev/null @@ -1,54 +0,0 @@ -## Swift 泛型 - -### 泛型定义--`wikipedia` - -1. 在程序编码中一些包含类型参数的类型,也就是说泛型的参数只可以代表类,不能代表个别对象。(这是当今较常见的定义) -2. 在程序编码中一些包含参数的类。其参数可以代表类或对象等等。(现在人们大多把这称作模板) - -### 泛型的用处 - -泛型可以实现传入一个指定类型后做一些操作然后返回后还是原来指定的类型,你可以指定特定的类型(特定是指约束) - -### 泛型和 `Any` 的区别 - -区别:`Any`能代表任何类型,但是不能返回某个具体的类型,都需要使用 as 去转化,而泛型正好相反 - -- Any 可以代表任何类型,除了class之外还可以代表struct,enum, -- `AnyObject` 可以代码任何 `class` 的类型, swift 中基本类型, `Array` 和 `Dictionary` 都是 `struct` 类型,都不能使用 `AnyObject` 来接受 - -[Swift中Any 和 AnyObject用法](http://swifter.tips/any-anyobject/) - -### 泛型函数和泛型参数 - -`swapTwoValues`是一个泛型函数,可以接收或者返回任何类型 - -``` -func swapTwoValues(inout a: T, inout b: T) { - let temporaryA = a - a = b - b = temporaryA -} -``` - -- `swapTwoValues` 函数名后面的 `` 表示这个 `T` 类型是函数所定义的一个类型, `T` 只是一个类型的占位符,不用关心具体类型 - -- `(inout a: T, inout b: T)` 中a和b两个参数类型`T`是泛型参数 - -### 类型约束 - -两个泛型参数的泛型函数 - -``` -func someFunction(someT: T, someU: U) { - // function body goes here -} -``` - -- 泛型参数`T`约束条件:必须是`SomeClass`类的子类 -- 泛型参数`U`约束条件:必须遵守`SomeProtocol`协议 -- 使用where指定约束 - - - - - diff --git "a/source/_posts/Swift\351\224\231\350\257\257\345\244\204\347\220\206.md" "b/source/_posts/Swift\351\224\231\350\257\257\345\244\204\347\220\206.md" deleted file mode 100644 index 26e84ff..0000000 --- "a/source/_posts/Swift\351\224\231\350\257\257\345\244\204\347\220\206.md" +++ /dev/null @@ -1,28 +0,0 @@ -Swift 中错误处理 - - -### assert 断言 - -只在debug 模式下程序会自动退出 - -``` -public func assert(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line) -``` - -### assertionFailure 断言错误 - -只在debug 模式下程序会自动退出, 没有条件 - -``` -public func assertionFailure(_ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line) -``` - -### precondition - -releas 模式程序也会退出 - -``` -public func precondition(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line) -``` - -### throw \ No newline at end of file diff --git "a/source/_posts/Swift\351\241\271\347\233\256\344\270\255\351\233\206\346\210\220RN.md" "b/source/_posts/Swift\351\241\271\347\233\256\344\270\255\351\233\206\346\210\220RN.md" deleted file mode 100644 index 84fa50f..0000000 --- "a/source/_posts/Swift\351\241\271\347\233\256\344\270\255\351\233\206\346\210\220RN.md" +++ /dev/null @@ -1,148 +0,0 @@ -Swift已有项目中集成ReactNative - -本教程针对于已经有的Swift项目集成ReactNative - -### 创建新的空目录 - -为了不影响原有的swift项目目录结构,建议先创建一个空的文件夹,例如`SwiftRN` - -![](https://ws3.sinaimg.cn/large/0069RVTdly1fuymbnmjazj30vc0bqgm4.jpg) - -### 创建package.json文件 - -终端进入创建的`SwiftRN`文件目录下,创建package.json文件 - -``` -touch package.json -``` - -##### 修改package.json文件,指定各个版本 - -``` -{ - "name": "SwiftRN", - "version": "0.0.1", - "private": true, - "scripts": { - "start": "node node_modules/react-native/local-cli/cli.js start", - "test": "jest" - }, - "dependencies": { - "react": "16.3.1", - "react-native": "^0.56.0" - } -} -``` - -##### 执行npm install - -在创建package.json所在的文件目录下执行,在此过程中可以遇到的错误可能较多 - -``` -npm install -``` - -##### 添加index.js文件 - -``` -touch index.js -``` - -##### 修改文件内容 - -``` -import { - AppRegistry, - Text, - View, -} from 'react-native'; - -import React, { Component } from 'react'; - -// 用于最后在项目中测试 -class SwiftRNHomeView extends Component { - render() { - return( - - Hellow world - - ); - } -} - - -class MyApp extends Component { - render() { - return ; - } -} - -AppRegistry.registerComponent('SwiftRN', () => MyApp); -``` - -### 配置swift项目 - -将新创建的Swift文件目录下的所有文件拷贝到Swift项目所在的根目录下 - -![](https://ws3.sinaimg.cn/large/0069RVTdly1fuymlkt3lgj30no0giq3s.jpg) - -### 使用Pod集成RN - -swift项目采用了pod管理第三方库,在Podfile文件中加入下面代码 - -``` -pod 'React', :path => './node_modules/react-native', :subspecs => [ - 'Core', - 'RCTText', - 'RCTNetwork', - 'RCTWebSocket', # 这个模块是用于调试功能的 - 'CxxBridge', # Include this for RN >= 0.47,不引入的话会报错误或者警告 - 'DevSupport', # Include this to enable In-App Devmenu if RN >= 0.43,不引入的话会报错误或者警告 - 'RCTAnimation', 不引入的话会报错误或者警告 -] - - -# 如果你的RN版本 >= 0.42.0,请加入下面这行,不引入的话会报错误或者警告 -pod "yoga", :path => "./node_modules/react-native/ReactCommon/yoga" -pod 'DoubleConversion', :podspec => './node_modules/react-native/third-party-podspecs/DoubleConversion.podspec' -pod 'glog', :podspec => './node_modules/react-native/third-party-podspecs/glog.podspec' -pod 'Folly', :podspec => './node_modules/react-native/third-party-podspecs/Folly.podspec' -``` - -#### 创建桥接文件 -- 如果原项目中有SwiftDemo-Bridging-Header.h类似这个文件可以跳过。 -- 如果从来没有创建过桥接文件,创建一个OC的类会自动弹出是否需要创建桥接文件,直接创建即可,最后把无用的OC文件删掉 - -#### 引用RN头文件 -在桥接文件中导入头文件 - -``` -#import -#import -``` - -#### 使用RN页面 - -在模拟器中使用RN的页面, 确保在 info.plist 文件中添加了`Allow Arbitrary Loads=YES`项 - -- moduleName 对应的名字和index.js中 AppRegistry.registerComponent('SwiftRN', () => MyApp)注册的名字必须相同 - -``` -import UIKit - -class RNTestController: UIViewController { - - override func viewDidLoad() { - super.viewDidLoad() - let url = URL(string: "http://localhost:8081/index.bundle?platform=ios")! - let rootView = RCTRootView( - bundleURL: url, - moduleName: "SwiftRN", //这里的名字必须和AppRegistry.registerComponent('MyApp', () => MyApp)中注册的名字一样 - initialProperties: nil, - launchOptions: nil - ) - view = rootView - } -} - -``` diff --git "a/source/_posts/TableView\344\275\277\347\224\250\346\200\273\347\273\223.md" "b/source/_posts/TableView\344\275\277\347\224\250\346\200\273\347\273\223.md" deleted file mode 100644 index c54ccdf..0000000 --- "a/source/_posts/TableView\344\275\277\347\224\250\346\200\273\347\273\223.md" +++ /dev/null @@ -1,70 +0,0 @@ ---- -title: TableView 使用 -date: 2016-11-05 22:59:46 -tags: TableView ---- - -## tableView 使用总结 - -- 字典的 key enumerator 的使用 - -- tableViewCell 的 selected 方法可以监听cell 的选中, 使用selected 参数 - -- 网络慢时点击左边cell右边数据不能及时刷新, 用户看到的还是上一个cell显示的数据 - - - 解决办法: 在网络加载之前就reload数据,让显示空白 - -- 点击左边的cell时 上拉加载更多控件的显示和隐藏逻辑 - - - 解决办法: 在每次刷新右边数据时,控制footer的显示 - - -- 在numberOfRowsInSection方法中控制 - -- 每次点击cell之前要结束上一个没有刷新完的,防止奔溃 - -- 加载更多时 - - 在加载第一页时设置currentPage为1,在加载更多的逻辑中 ++currentPage - 点击cell加载时上一个加载更多的网络数据还没有回来,在当前请求之前结束掉所有的网络请求 - 停止之后的刷新控件状态会有错误,没有更多还是还可以加载状态不确定 - 在点击左边cell后刷新右边的之前需要检查右边tableView 的footer的状态 - 下拉刷新 - -- 第一次下拉刷新之后,第二次下拉刷新,将数据添加到数组中会导致两次数据重复 - - - 解决办法:清除以前的所有数据 - -- 下拉刷新完毕后结束刷新后需要检查footer的状态 - -- 网络慢时连续点击cell,连发多个请求成功回调时数据显示错乱,或者是数据已经回来了但是还是显示上一个请求正在请求。。。 - - - 解决办法:点击左边某个类别时,结束所有的刷新(上拉和下拉) - -- 连续点击发送请求 - - - 解决办法:保存请求参数, 比较上一次参数和当前参数, 如果 != 直接return,不让进入成功回调解析数据, 失败的时候也是同样 return,不提示用户 - -- 控制器正在请求数据时控制器销毁, 导致奔溃 - - - 解决办法:在dealloc 中停止所有的操作 - -## cell 的frame 设置 - -- cell 之间的间隙以及cell的边距 重写frame 在super 之前设置 - - 设置frame 的x ,width, height 等属性 - -- 分页的2种做法 - - - 下拉刷新 - - page 加载最新的 加载第一页 浪费用户的流量 - 加载比当前更新的数据, 需要服务器支持,将当前最新的数据id发送给服务器,服务器加载比当前id还大的数据插入到最前面 - - - 上拉加载 - - 将页码发给服务器 - - 将最后面的数据的id发给服务器, - - 加载比当前id更小的数据 \ No newline at end of file diff --git "a/source/_posts/WKWebiView\344\275\277\347\224\250.md" "b/source/_posts/WKWebiView\344\275\277\347\224\250.md" deleted file mode 100644 index d8356ca..0000000 --- "a/source/_posts/WKWebiView\344\275\277\347\224\250.md" +++ /dev/null @@ -1,122 +0,0 @@ ---- -title: WKWebiView 使用 -date: 2016-11-22 11:10:31 -tags: WKWebView ---- - - -#### WKWebiView iOS8.0 出现,性能要远远好于 iOS 2.0时出现的 UIWebView,主要记录WKWebView 的创建和代理方法 - - -## 创建WKWebView - - WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds]; - self.webView = webView; - [self.view addSubview:webView]; - webView.UIDelegate = self; - webView.navigationDelegate = self; - [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.baidu.com"]]]; - -遵守协议 WKNavgationDelegate WKUIDelegate - -## 代理方法 - -- WKNavgationDelegate 方法 - - - 发送请求之前决定是否跳转 - - ``` - - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { - - decisionHandler(WKNavigationActionPolicyAllow); - // 不允许跳转 - decisionHandler(WKNavigationActionPolicyCancel); - NSLog(@"decidePolicyForNavigationAction"); - } - - ``` - - 收到相应之后决定是否跳转 - - ``` - - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler { - - decisionHandler(WKNavigationResponsePolicyAllow); - - decisionHandler(WKNavigationResponsePolicyCancel); - - NSLog(@"decidePolicyForNavigationResponse"); - - } - ``` - - - 开始加载时调用 - - ``` - - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation { - - NSLog(@"didStartProvisionalNavigation"); - } - ``` - - - 接受到服务器的跳转请求时调用 - - ``` - - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation { - NSLog(@"didReceiveServerRedirectForProvisionalNavigation"); - } - ``` - - - - - 页面加载失败时调用 **存在缓存问题** - - ``` - - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error { - - NSLog(@"error= %@", error); - } - ``` - - - - 内容开始返回时调用 - - ``` - - (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation { - - NSLog(@"didCommitNavigation"); - } - ``` - - - 内容返回成功后调用 - - ``` - - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation { - - NSLog(@"didFinishNavigation"); - } - - ``` - - 开始返回错误时调用 - - ``` - - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error { - - NSLog(@"didFailNavigation"); - } - ``` - - - 目前不知道什么时候调用 - - ``` - - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler { - NSLog(@"didReceiveAuthenticationChallenge"); - } - ``` - - 目前不知道什么时候调用 - - ``` - - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView API_AVAILABLE(macosx(10.11), ios(9.0)) { - - NSLog(@"webViewWebContentProcessDidTerminate"); - } - ``` diff --git "a/source/_posts/Xcode\344\270\255\344\275\277\347\224\250Debug Memory Graph\346\243\200\346\237\245\345\206\205\345\255\230\346\263\204\346\274\217.md" "b/source/_posts/Xcode\344\270\255\344\275\277\347\224\250Debug Memory Graph\346\243\200\346\237\245\345\206\205\345\255\230\346\263\204\346\274\217.md" deleted file mode 100644 index 02b6813..0000000 --- "a/source/_posts/Xcode\344\270\255\344\275\277\347\224\250Debug Memory Graph\346\243\200\346\237\245\345\206\205\345\255\230\346\263\204\346\274\217.md" +++ /dev/null @@ -1,16 +0,0 @@ -# Xcode中使用Debug Memory Graph检查内存泄漏 - - -## 勾选如下图所示选项 - -![](https://ws3.sinaimg.cn/large/006tNc79ly1g1xy4gg3b5j31ds0s0q7u.jpg) - -## 运行程序后点击 - -![](https://diycode.b0.upaiyun.com/photo/2018/18f8590ab4f9d33296a379a94ae94ccb.png) - -## 内存泄露定位 - -![](https://ws1.sinaimg.cn/large/006tNc79ly1g1xyo08z80j31yu0u00zf.jpg) - -![](https://ws3.sinaimg.cn/large/006tNc79ly1g1xyovrs7kj321q0u0tln.jpg) diff --git "a/source/_posts/flutter\345\256\211\350\243\205.md" "b/source/_posts/flutter\345\256\211\350\243\205.md" deleted file mode 100644 index 0356324..0000000 --- "a/source/_posts/flutter\345\256\211\350\243\205.md" +++ /dev/null @@ -1,151 +0,0 @@ -flutter安装 - -``` -// 导入头文件 -import 'package:flutter/material.dart'; - -void main() => (MyApp()); - -// 定义类, 继承 - -class MyApp extends StatelessWidget { - // 重写 - @override - - Widget build(BuildContext context) { - - // 返回一窗口 - return MaterialApp( - title:'Flutter', - home: Scaffold( - appBar: AppBar( - title: Text('flutter 初体验') - ), - - body: Center( - child: Text('flutter 学习之路'), - ), - ), - ); - } -} - -``` - - -``` - -import 'package:flutter/material.dart'; - -void main() => runApp(MyApp()); - -class MyApp extends StatelessWidget { - // This widget is the root of your application. - @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'Flutter Demo', - theme: ThemeData( - // This is the theme of your application. - // - // Try running your application with "flutter run". You'll see the - // application has a blue toolbar. Then, without quitting the app, try - // changing the primarySwatch below to Colors.green and then invoke - // "hot reload" (press "r" in the console where you ran "flutter run", - // or simply save your changes to "hot reload" in a Flutter IDE). - // Notice that the counter didn't reset back to zero; the application - // is not restarted. - primarySwatch: Colors.blue, - ), - home: MyHomePage(title: 'Flutter Demo Home Page'), - ); - } -} - -class MyHomePage extends StatefulWidget { - MyHomePage({Key key, this.title}) : super(key: key); - - // This widget is the home page of your application. It is stateful, meaning - // that it has a State object (defined below) that contains fields that affect - // how it looks. - - // This class is the configuration for the state. It holds the values (in this - // case the title) provided by the parent (in this case the App widget) and - // used by the build method of the State. Fields in a Widget subclass are - // always marked "final". - - final String title; - - @override - _MyHomePageState createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - int _counter = 0; - - void _incrementCounter() { - setState(() { - // This call to setState tells the Flutter framework that something has - // changed in this State, which causes it to rerun the build method below - // so that the display can reflect the updated values. If we changed - // _counter without calling setState(), then the build method would not be - // called again, and so nothing would appear to happen. - _counter++; - }); - } - - @override - Widget build(BuildContext context) { - // This method is rerun every time setState is called, for instance as done - // by the _incrementCounter method above. - // - // The Flutter framework has been optimized to make rerunning build methods - // fast, so that you can just rebuild anything that needs updating rather - // than having to individually change instances of widgets. - return Scaffold( - appBar: AppBar( - // Here we take the value from the MyHomePage object that was created by - // the App.build method, and use it to set our appbar title. - title: Text(widget.title), - ), - body: Center( - // Center is a layout widget. It takes a single child and positions it - // in the middle of the parent. - child: Column( - // Column is also layout widget. It takes a list of children and - // arranges them vertically. By default, it sizes itself to fit its - // children horizontally, and tries to be as tall as its parent. - // - // Invoke "debug painting" (press "p" in the console, choose the - // "Toggle Debug Paint" action from the Flutter Inspector in Android - // Studio, or the "Toggle Debug Paint" command in Visual Studio Code) - // to see the wireframe for each widget. - // - // Column has various properties to control how it sizes itself and - // how it positions its children. Here we use mainAxisAlignment to - // center the children vertically; the main axis here is the vertical - // axis because Columns are vertical (the cross axis would be - // horizontal). - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - '你可以点击按钮来修改文字,我是热重载的结果', - ), - Text( - '$_counter', - style: Theme.of(context).textTheme.display1, - ), - ], - ), - ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: Icon(Icons.add), - ), // This trailing comma makes auto-formatting nicer for build methods. - ); - } -} - - -``` \ No newline at end of file diff --git "a/source/_posts/git\345\255\246\344\271\240\346\200\273\347\273\223.md" "b/source/_posts/git\345\255\246\344\271\240\346\200\273\347\273\223.md" deleted file mode 100644 index c6de41f..0000000 --- "a/source/_posts/git\345\255\246\344\271\240\346\200\273\347\273\223.md" +++ /dev/null @@ -1,120 +0,0 @@ ---- -title: git 使用 -date: 2016-11-20 11:10:31 -tags: git ---- - - - -## 使用 git 提交代码 - -- 初始化工作目录 - - ``` - git init - ``` - -- 配置用户名和邮箱(当前仓库) - - ``` - git config user.name "aTreey" - git config user.email "480814177@qq.com" - ``` - -- 配置全局的用户名和密码 - - ``` - git config --global user.name "aTreey" - git config --global user.email "480814177@qq.com" - ``` -- 创建文件 - - ``` - touch main.m - ``` - -- 添加文件到暂缓区 - - ``` - git add main.m - ``` -- 查看文件状态 - - ``` - git status - ``` -- 新添加的文件或者新修改的文件在工作区中,没有添加到暂缓区中 - - - ``` - Untracked files: (红色) - ``` -- 需要添加到暂缓区中 - - - ``` - git add main.m - ``` -- 出现一下提示方可提交 - - - ``` - Changes to be committed: (绿色) - ``` - -- 给git起别名 - - ``` - git config alias.st "status" - git config alias.ci "commit -m" - ``` - -- 查看所有版本库日志(只能查看当前版本的以前日志) - - ``` - git log - ``` -- 查看指定文件的版本库日志 - - ``` - git log 文件名 - ``` - - -- 查看所有的操作的日志 - - ``` - git reflog - ``` - -## 版本会退 -- 强制回退到当前版本 - - ``` - git reset --hard HEAD - ``` - -- 强制回退到当前版本的上一个版本 - - ``` - git reset --hard HEAD^ - ``` - - -- 强制回退到当前版本的上上个版本 - - ``` - git reset --hard HEAD^^ - ``` - -- 强制回退到当前版本的前100个版本 - - ``` - git reset --hard HEAD~100 - ``` - -- 强制回退指定版本 - - ``` - git reset --hard HEAD 版本号前七位 - ``` \ No newline at end of file diff --git "a/source/_posts/iOS \345\212\250\346\200\201\350\256\276\347\275\256TableHeaderView\351\253\230\345\272\246.md" "b/source/_posts/iOS \345\212\250\346\200\201\350\256\276\347\275\256TableHeaderView\351\253\230\345\272\246.md" deleted file mode 100644 index 8cc353f..0000000 --- "a/source/_posts/iOS \345\212\250\346\200\201\350\256\276\347\275\256TableHeaderView\351\253\230\345\272\246.md" +++ /dev/null @@ -1,43 +0,0 @@ -## iOS动态设置`tableHeaderView`高度 - -#### 需求 - -动态的设置`tableHeaderView`的高度 - -#### 遇到的问题: - -- 设置`tableHeaderView` 的`frame`无法实现,有时候会出现`tableView` 中间的 `cell` 不显示 -- 有时候会导致`headerView`和`cell`内容重叠 - -- 通过计算控件高度设置frame方式麻烦 - -#### 正确的步骤 -- 创建HeaderView -- 取出`UITableView`的`tableHeaderView` -- 设置`frame`大小,调整headerView布局 -- 重新给`UITableView`的`tableHeaderView` - - -#### 使用AutoLayout -给`UITableView`增加分类 - -``` -extension UITableView { - /// set tableHeaderView - func setTableHeaderView(_ headerView: UIView) { - headerView.translatesAutoresizingMaskIntoConstraints = false - self.tableHeaderView = headerView - // autolayout - NSLayoutConstraint(item: headerView, attribute: .centerX, relatedBy: .equal, toItem: self, attribute: .centerX, multiplier: 1.0, constant: 0).isActive = true - NSLayoutConstraint(item: headerView, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1.0, constant: 0).isActive = true - NSLayoutConstraint(item: headerView, attribute: .width, relatedBy: .equal, toItem: self, attribute: .width, multiplier: 1.0, constant: 0).isActive = true - } - /// update - func updateHeaderView() { - guard let headerView = self.tableHeaderView else { return } - headerView.layoutIfNeeded() - let header = self.tableHeaderView - self.tableHeaderView = header - } -} -``` diff --git "a/source/_posts/iOS\345\215\263\346\227\266\351\200\232\350\256\257\345\256\236\347\216\260\344\272\214.md" "b/source/_posts/iOS\345\215\263\346\227\266\351\200\232\350\256\257\345\256\236\347\216\260\344\272\214.md" deleted file mode 100644 index 32c6d94..0000000 --- "a/source/_posts/iOS\345\215\263\346\227\266\351\200\232\350\256\257\345\256\236\347\216\260\344\272\214.md" +++ /dev/null @@ -1,188 +0,0 @@ ---- -title: iOS即时通讯实现二 -date: 2018-01-12 21:54:26 -tags: ---- - -## 实现IM通信所用协议 - -几个协议理解 - -xmpp 是基于xml协议主要是易于扩展,早起用于PC时代的产品使用,流量大,耗电大交互复杂其实并不适合移动互联网时代的IM产品 - -MQTT 协议 适配多平台,需自己扩展好友,群组等功能 - -protobut 协议 - -## 封装socket,自己实现 - -所要考虑的问题 - -- 传输协议选择 - - TCP ? 一般公司都选用(技术不是很成熟) - - UDP ?QQ 腾讯增加自己的私有协议,保证数据传递的可靠性,解决了丢包,乱序的问题 - -- 聊天协议选择及代表框架 - - Socket ? CocoaAsyncSocket - - WebSockt ?是传输通讯协议,基于socket封装的一个协议 SocketRocket - - MQTT ?MQTTKit 聊天协议为上层协议 - - protobut 协议 ? - - XMPP ?XMPPFramework 聊天协议为上层协议 - - 自定义 -- 传输数据格式 - - Json? - - XML? - - ProtocolBuffer? - -- 细节相关 - - TCP 长连接如何保持, - - 心跳机制 - - 重连机制 - - 数据安全机制 - -### CocoaAsyncSocket - -- CocoaAsyncSocket 的 delegate 用来设置代理处理各个回调 -- delegateQueue -- socketQueue 串行队列,贯穿全类并没有任何加锁,确保了socket 操作中每一步都是线程安全的 -- 创建了两个读写队列(本质是数组) - - `NSMutableArray *readQueue;` - - `NSMutableArray *writeQueue;` - -- 全局数据缓冲: 当前socket未获取完的数据大小 - - `GCDAsyncSocketPreBuffer *preBuffer;` - -- 交替延时变量: 用于进行另一服务端地址请求的延时 - - `alternateAddressDelay` - -- connect - -- disconnect - -## 数据粘包,断包 - -- 粘包: 如果客户端同一时间发送几条数据,而服务器只收到一大条数据 - - - 原因: - - 由于传输的是数据流,经过TCP传输后,TCP使用了优化算法将多次间隔较小且数据量小的数据合并成一个大的数据块,因此三条数据合并成了一条 - - **TCP,UDP都可能造成粘包的原因** - - - 发送端需要等缓冲区满了才发送出去,做成粘包 - - 接收方不及时接收缓冲区的包,造成多个包接收 - -- 断包: 因为一次发送很大的数据包,缓冲区有限,会分段发送或读取数据 - - -## CocoaAsyncSocket的封包,拆包处理 - -- 读取数据方法: - - // 有数据时调用,读取当前消息队列中的未读消息 - - (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag - - - **每次读取数据,每次都必须手动调用上述 readData 方法超时设置为不超时才能触发消息回调的代理** - - **因此在第一连接时调用,再在接到消息时调用,上述方法就可以达到每次收到消息都会触发读取消息的代理** - - 以上做法存在的问题就是没有考虑数据的拆包会有粘包情况,这时候需要用下面两个`read`方法,1. 读取指定长度、2.读取指定边界 - - // 读取特定长度数据时调用 - - (void)readDataToLength:(NSUInteger)length withTimeout:(NSTimeInterval)timeout tag:(long)tag - - // 读到特定的 data 边界时调用 - - (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag - - - **解决办法及具体思路** - - - 封包:给每个数据包增加一个长度或者加一个开始结束标记,表明数据的长度和类型(根据自己的项目:文本、图片、语音、红包、视频、话题、提问等等) - - 可以在数据包之后加一个结束标识符,解决了传输过程中丢包,丢失头部信息的错误包读取,读到这样的就丢弃直接读下一个数据包 - - - 拆包:获取每个包的标记,根据标记的数据长度获取数据,最后根据类型处理数据,最后读取数据包头部的边界 - -- 利用缓冲区对数据进行读取 - - 创建读取数据包 - - 添加到读取的队列中 - - 从队列中取出读取任务包 - - 使用偏移量 `maxLength ` 读取数据 - - ``` - - (void)readDataWithTimeout:(NSTimeInterval)timeout - buffer:(NSMutableData *)buffer - bufferOffset:(NSUInteger)offset - maxLength:(NSUInteger)length - tag:(long)tag -{ - if (offset > [buffer length]) { - LogWarn(@"Cannot read: offset > [buffer length]"); - return; - } - - // 1. 创建读取数据包 - GCDAsyncReadPacket *packet = [[GCDAsyncReadPacket alloc] initWithData:buffer - startOffset:offset - maxLength:length - timeout:timeout - readLength:0 - terminator:nil - tag:tag]; - - dispatch_async(socketQueue, ^{ @autoreleasepool { - - LogTrace(); - - if ((flags & kSocketStarted) && !(flags & kForbidReadsWrites)) - { - // 2. 向读的队列添加任务包 - [readQueue addObject:packet]; - // 3. 从队列中取出读取任务包 - [self maybeDequeueRead]; - } - }}); - - // Do not rely on the block being run in order to release the packet, - // as the queue might get released without the block completing. -} - - ``` - -doReadData 方法时读取数据的核心方法 - -- 如果读取对列中没有数据,就去判是否是上次已经读取了数据,但是因为没有目前还没有读取到数据的标记,这是正好设置了 read 方法中的超时时间,所以要断开socket 连接 - - - -![](https://www.jianshu.com/p/fdd3d429bdb3) - - -- 接收到数据后在 socket系统进程的数据缓冲区中 - - (基于TLS的分为两种)他们在各自管道中流动,完成数据解密后又流向了全局数据缓冲区 - - CFStreem - - SSLPrebuffer - -处理数据逻辑: 如果当前读取包长度给明了,则直接流向currentRead,如果数据长度不清楚,那么则去判断这一次读取的长度,和currentRead可用空间长度去对比,如果长度比currentRead可用空间小,则流向currentRead,否则先用prebuffer来缓冲。 - - -### 心跳机制 -- TCP 自带的keep-alive 使用空闲时间来发并且默认超时时间太长是2 小时, -- 如果应用使用了socks ,socks proxy会让tcp keep-alive失效,因为socks 协议只管转发TCP层具体的数据包,并不会转发TCP协议内的实现具体细节数据包 -- TCP 的长连接理论上是一直保持连接的,可以设置 TCP keep-alive的时间 但是实际情况下中,可能出现故障或者是因为防火墙的原因会自动把一定时间段内没有数据交互的连接给断开,心跳机制就可以维持长连接,保持活跃状态 - -- iOS 中使用 NSTimer `scheduledTimerWithTimeInterval` 需要在调用之前的关键位置设置 fireDate 为未来的某个时间, 然后再需要的时候开启 -- 或者直接使用 `timerWithTimeInterval` 创建,然后添加到`runloop`中 - - - - - - - - - - - - - - diff --git "a/source/_posts/iOS\345\274\271\346\241\206\345\274\217\345\233\276.md" "b/source/_posts/iOS\345\274\271\346\241\206\345\274\217\345\233\276.md" deleted file mode 100644 index 7716ae3..0000000 --- "a/source/_posts/iOS\345\274\271\346\241\206\345\274\217\345\233\276.md" +++ /dev/null @@ -1,64 +0,0 @@ ---- -title: iOS 弹框视图 -date: 2016-10-27 00:58:20 -tags: AlertView ---- - -**`UIActionSheet` `UIAlertView` `UIAlertViewController` 的区别** - -## UIActionSheet - -- iOS 8.3 之后过期 -- 模态弹出,显示在试图的底部 类似菜单提供选择 - -``` -UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"title" delegate:self cancelButtonTitle:@"取消" destructiveButtonTitle:nil otherButtonTitles:@"按钮1", @"按钮2", nil]; -[actionSheet showInView:self.view]; -``` - - -## UIAlertView - -- iOS 9.0 之后过期 -- 模态弹出,显示在试图的中间位置 类似一个对话框提供选择 - -``` -UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"考勤方式" message:nil delegate:self cancelButtonTitle:@"取消" otherButtonTitles:faceBtn, fingerPrintBtn, nil]; -[alertView show]; -``` - -## UIAlertViewController - -- iOS 8.0 起可以使用 -- `style` : 默认样式, 类似 `UIActionSheet` 列表形式 -- `style` 为:`UIAlertControllerStyleAlert` 对话框形式 -- 设置按钮的样式 - -如果修改 UIAlertAction 中stye 为: - -UIAlertActionStyleCancel -取消按钮会和其他按钮分开 - -如果修改 UIAlertAction 中stye 为: - -UIAlertActionStyleDestructive -取消按钮会以红色警告的方式显示, 但是按钮之间不会分开 - -``` -UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"考勤方式" message:nil preferredStyle:UIAlertControllerStyleActionSheet]; -UIAlertAction *faceBtn = [UIAlertAction actionWithTitle:@"人脸识别" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { }]; - -UIAlertAction *fingerPrintBtn = [UIAlertAction actionWithTitle:@"指纹认证" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { - -}]; - -UIAlertAction *cancelBtn = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { - -}]; - -[alert addAction:faceBtn]; -[alert addAction:fingerPrintBtn]; -[alert addAction:cancelBtn]; -[self presentViewController:alert animated:YES completion:nil]; - -``` \ No newline at end of file diff --git "a/source/_posts/iOS\351\200\232\350\277\207URL\350\216\267\345\217\226HTML\344\270\255\346\240\207\351\242\230.md" "b/source/_posts/iOS\351\200\232\350\277\207URL\350\216\267\345\217\226HTML\344\270\255\346\240\207\351\242\230.md" deleted file mode 100644 index 866b0be..0000000 --- "a/source/_posts/iOS\351\200\232\350\277\207URL\350\216\267\345\217\226HTML\344\270\255\346\240\207\351\242\230.md" +++ /dev/null @@ -1,120 +0,0 @@ -iOS通过URL获取HTML中标题 - -### 获取标题 - -##### String扩展一个方法 - -``` -private let patternTitle = "(?<=title>).*(?= String? { - let url = URL(string: urlString)! - do { - let html = try String(contentsOf: url, encoding: .utf8) - let range = html.range(of: patternTitle, options: String.CompareOptions.regularExpression, range: html.startIndex.. build Phases –> Copy Files 添加头文件,然后编译 - - - -- 架构种类 - -模拟器:i386 32位架构 (4s~5) / x86_64 64位架构 (5s以后机型) -真机:armv7 32位架构 (4~4s) / armv7s 特殊架构 (5~5C) / arm64 64位架构 (5s 以后机型) - -- 架构查看 -lipo -info xxxx.a -默认生成模拟器只生成一种架构 i386 x86_64;真机默认导出两种架构 armv7 / arm64 - -- 合并多个架构 - -将模拟器的多个架构合并,项目名称–>Build Settings –> Achitectures –> Build Active Architecture Only –> Debug设置为NO -合并真机多个架构 按照以上方法运行真机即可 -合并真机和模拟器架构 cd products lipo -create Debug-iphoneos/xxx.a Debug-iphonessimulator/xxx.a -output xx.a -合成 5 个架构 在Build Setting –> Architectures 中手动添加三种架构,再编译合并 - -- 静态库报错 - -Undefined symbols for architecture (arm64 / armv7 / armv7s / i386 / x86_64),架构包导入出错,静态库有真机与模拟器 \ No newline at end of file diff --git "a/source/_posts/iOS\351\235\242\350\257\225OC\350\257\255\350\250\200\347\211\271\346\200\247.md" "b/source/_posts/iOS\351\235\242\350\257\225OC\350\257\255\350\250\200\347\211\271\346\200\247.md" deleted file mode 100644 index 43c42c6..0000000 --- "a/source/_posts/iOS\351\235\242\350\257\225OC\350\257\255\350\250\200\347\211\271\346\200\247.md" +++ /dev/null @@ -1,152 +0,0 @@ -# OC 语言特性 - -## 分类 - -### 原理 - -由运行时来决议的,不同分类当中含有同名方法 谁最终生效取决于谁最终参与编译,最后参编译的同名分类方法会最终生效,假如分类方法中添加的方法和宿主类中的某一个方法名相同,分类中的方法会覆盖宿主类中的同名方法,覆盖是指在消息传递过程中优先查找数组靠前的元素,如果查找到了同名方法就直接调用,实际上宿主类的同名方法实现仍然是存在的,我们可可以通过一些手段调用到原有类的同名方法的实现 - -### 使用场景 - -- 申明私有方法,分类的 .m 文件中申明私有方法啊,对外不暴露 -- 对类中的代码进行抽取分解 -- 把Framework的私有方法公开化 - -### 特点 -- 运行时决议,运行时通过runtime 才把分类中的方法添加到了宿主类上 -- 可以为系统类添加方法 -- 有多个分类中存在同名方法时,最后编译的方法会最先生效 -- 分类中有和宿主类同名方法时,底层中通过内存copy将分类方法“覆盖”宿主类方法 -- 名字相同的分类编译会报错 - -### 分类中可以添加的内容 - -- 实例方法 -- 类方法 -- 协议 -- 属性,实际上只是声明了 getter / setter 方法,并没有添加成员变量 - -### 关联对象技术 - -关联对象由 AssocicationsManager 管理并在 AssociationsHashMap 上存储 -所有对象的关联内容都在同一个全局容器中 - -- 给分类添加成员变量,通过关联对象的方式 - - -## 扩展 - -### 使用场景 -- 声明私有属性 -- 声明私有方法 -- 声明私有成员变量 - -### 特点 -- 编译时决议 -- 只以声明的形式存在,多数情况下寄生在宿主类的.m中 -- 不能为系统添加扩展 - -## 代理 -- 代理设计模式,传递方式是一对一 -- 使用weak 避免循环引用 - -## 通知 - -使用观察者模式来实现的用于跨层传递消息的机制 - - -## KVO - -KVO 是系统对观察者模式的又一实现,使用isa 混写技术(isa - swizzling)来动态运行时为某一个类添加一个子类重写了它的setter 方法,同时将原有类的isa指针指向了新创建的类上 - - -- KVO 是OC对观察者模式的又一实现 -- apple 使用isa 混写技术(isa - swizzling)来实现KVO - - 给我A类注册一个观察者时本质上是调用系统的 Observer for keyPath 方法,系统会创建一个NSKVONotifiying_A 的类 -- 使用 setter 方法设置值KVO才能生效 -- 使用setValue:forKey: 改变值KVO才能生效 -- 使用下划线的成员变量直接修改值需要手动添加KVO才能生效(增加 willChangeValueForKey 和 didChangeValueForKey 两个方法) - - - -## KVC -apple提供的键值编码技术 - -- (nullable id)valueForKey:(NSString *)key; -- (void)setValue:(nullable id)value forKey:(NSString *)key; - - 以上两个方法中的key是没有任何限制的,只要知道对应的成员变量名称,就可以对私有的成员变量设置和操作,这违背了面向对象的编程思想 - -**(void)setValue:(nullable id)value forKey:(NSString *)key; 方法调用流程** - -- 先判断是否有跟key 相关的setter 方法,如果有就直接调用,结束调用 -- 如果没有再判断是否存在实例变量,如果存在直接给赋值,结束调用 -- 如果说实例变量不存在,会去调用 `- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;` 抛出异常 - - -## 属性关键字 - -### 读写权限 - -- readonly - -- readwrite 系统默认 - -### 原子性 -- atomic (系统默认)保证赋值和获取(对成员属性的直接获取和赋值)时线程安全,并不代表操作和访问,如果atomic修饰的一个数组,只能保证独具改数组的读取和赋值时线程安全,并不能保证对数据操作(增加/删除数组元素)时是安全的 -- nonatomic - -### 引用技术 - -- assign - - 修饰基本数据类型, - - 修饰对象时不改变其引用计数, - - 会产生悬垂指针 -- weak - - 不改变被修饰对象的引用计数, - - 所指对象被释放之后指针自动置为nil - -**两者区别:** -- weak可以修饰对象,而assign 既可以修饰对象也可以修饰基本数据类型 -- assign 修饰的对象,对象释放后指针仍然指向原对象的内存地址,而weak 修饰的对象,释放后会自动置为nil - -**问题1** - -weak 修饰的对象为什么释放后会自动置为nil - -**问题2** - -`@property (copy) NSMutableArray *array;` 有什么问题 - -- 被copy修饰后,如果赋值过来的是NSMutableArray,copy之后就是NSArray,如果赋值过来的是NSAarray,copy 之后仍然是NSArray,有可能会调用array的添加元素方法会导致crash - -### copy 关键词 - - ![](https://ws2.sinaimg.cn/large/006tNc79ly1g1uh6lwwe2j320s0lm7bm.jpg) - - - 浅拷贝:内存地址的复制,让目标对象指针和源对象指向同一块内存空间, - - 会增加对象的引用计数 - - 并没有一个新的内存分配 - - - - 深拷贝:目标对象指针和源对象指针分别指向两块内容相同的内存空间 - - 不会增加源对象的引用计数 - - 有新对象的内存分配 - -## MRC 重写 retain修饰的变量的setter 方法 - -``` - -- (void)setObj:(id)obj { - if (_obj != obj) { - [_obj release]; - } - _obj = [obj retain]; - -} -``` - -判断是为了防止异常处理,如果不做 if 判断,当传入的 obj 对象正好是原来的_obj 对象,对原对象尽行releas 操作,实际上也会对传入的对象进行releas 操作进行释放,此时如果再通过obj指针访问废弃的对象时就会导致carsh - - - diff --git "a/source/_posts/is \345\205\263\351\224\256\345\255\227\347\261\273\345\236\213\346\243\200\346\237\245.md" "b/source/_posts/is \345\205\263\351\224\256\345\255\227\347\261\273\345\236\213\346\243\200\346\237\245.md" deleted file mode 100644 index 3ee0a8c..0000000 --- "a/source/_posts/is \345\205\263\351\224\256\345\255\227\347\261\273\345\236\213\346\243\200\346\237\245.md" +++ /dev/null @@ -1,82 +0,0 @@ -swift类型检查和类型转换 - -### is 关键字类型检查 - -判断父类和子类之间的关系 - -``` -func typeCheck() { - let programmer = Programmer(name: "老张-php") - if dev is iOSDeveloper { - print("iOS 开发者") - } else { - print("其他开发者") - } - - if programmer is Coder { - print("老张-php是一农个码农") - } else { - print("") - } -} -``` - -### as 关键字类型转换 - -``` -let ios = iosDev as? Programmer -let ios2 = iosDev as! Coder -``` - -### 判断是否遵守了协议 - -swift 中协议可以当作是一个类型使用 - -``` -et ios = iosDev as? Programmer - let ios2 = iosDev as Coder - - // 是否遵守了 Person 协议 - if iosDev is Person { - print("遵守了Person协议") - } - - let devs = [dev, iosDev, programmer] as [Any] - - for dev in devs { - if let dev = dev as? Person { - print(dev) - print("遵守Person协议") - } else { - print("未遵守Person协议") - } - } -``` - -### AnyObject Any - - -``` -var anyObjectArray: [AnyObject] = [CGFloat(0.5) as AnyObject, - 1 as AnyObject, - "string" as AnyObject, - iOSODev] - -``` - -swift是面向函数编程, 函数是一等公民,数组中可以存入一个函数,函数表达的是一个过程,不是一个名词或者物体,所以函数不是一个对象,需要使用 any 关键字 - -``` -var anyArray: [Any] = [CGFloat(0.5), - 1, - "string", - iOSODev] -``` - -放入函数 - -``` -anyArray.append({ (a: Int) -> (Int) in return a * a }) -``` - - diff --git "a/source/_posts/npm\345\222\214cnpm.md" "b/source/_posts/npm\345\222\214cnpm.md" deleted file mode 100644 index a373d1d..0000000 --- "a/source/_posts/npm\345\222\214cnpm.md" +++ /dev/null @@ -1,67 +0,0 @@ -# ReactNative配置.npm 和cnpm 相关 - -### npm -Nodejs的包管理工具 - -### npm 常用命令 - -- 更新或卸载 - - ``` - npm update/uninstall moduleName - ``` -- 查看当前目录下已安装的包 - - ``` - npm list - ``` -- 查看全局安装的包的路径 - - ``` - npm root -g - ``` -- 全部命令 - - ``` - npm help - ``` - -### cnpm - -因为npm安装插件是从国外服务器下载,受网络影响大,可能出现异常,cnpm [淘宝镜像](https://npm.taobao.org/) - -### 安装 - -``` -npm install -g cnpm --registry=https://registry.npm.taobao.org -``` - -### 设置镜像源 - -``` -cnpm set registry http://registry.cnpm.taobao.com -npm set registry http://registry.cnpm.taobao.com -``` - - -### 配置 .npmrc 文件 - -前端项目开发离不开安装各种npm依赖包,可以选择远程的仓库也可以选择本地的仓库,但更改仓库地址需要在安装时控制台打命令,比较麻烦, 而npmrc可以很方便地解决上面问题 - -### .npmrc 原理 - -当安装项目的依赖包时,会查找并读取项目根目录下的.npmrc文件的配置,自动指定仓库地址 - -- 打开 .npmrc - - ``` - open .npmrc - ``` - -- 编辑文件, 指定特定的库的镜像源, 比如以`@test`开头的库镜像为:`http://registry.cnpm.test.com/` 而其他的正常 - - ``` - @test:registry=http://registry.cnpm.lietou.com/ - registry=https://registry.npm.taobao.org/ - - ``` diff --git "a/source/_posts/swift\344\270\255\345\274\272\345\274\225\347\224\250\345\276\252\347\216\257\345\222\214weak\345\205\263\351\224\256\345\255\227.md" "b/source/_posts/swift\344\270\255\345\274\272\345\274\225\347\224\250\345\276\252\347\216\257\345\222\214weak\345\205\263\351\224\256\345\255\227.md" deleted file mode 100644 index b2a234c..0000000 --- "a/source/_posts/swift\344\270\255\345\274\272\345\274\225\347\224\250\345\276\252\347\216\257\345\222\214weak\345\205\263\351\224\256\345\255\227.md" +++ /dev/null @@ -1,251 +0,0 @@ -Swift中内存管理 - -### 强引用循环和 weak 关键字 - -房东 - -``` -class Landlord { - var name: String - var house: House? - - init(name: String) { - self.name = name - } - - deinit { - print("Landlord内存释放") - } -} -``` - -房子 - -``` -class House { - var name: String - var landlord: Landlord? - - init(person: String) { - self.name = person - } - - deinit { - print("House内存释放") - } -} - -``` - -循环引用 - -``` -func strongRetain() { - var landlord: Landlord? = Landlord(name: "老李") - var house: House? = House(person: "望京府邸") - // 房东有个房子 - landlord?.house = house - // 房子有个主人 - house?.landlord = landlord - - landlord = nil - house = nil -} -``` - -- 虽然 landlord = nil 和 house = nil,但是 Landlord 和 House 内存空间并没有释放 (并未执行 deinit 函数) - -- 原因:因为两个内存空间相互引用,引用计数不为0,形成了强循环引用 - -- 解决办法:使用weak 关键字来修饰两者中其中一个变量 - - - 使用weak修饰的变量必须是可选类型,可以赋值为nil - - 使用weak修饰必须是var 类型变量 - -``` -class House { - var name: String - weak var landlord: Landlord? - - init(person: String) { - self.name = person - } - - deinit { - print("House内存释放") - } -} -``` - - -### unowned 关键字的使用 - -#### 相互循环引用的两个变量有一个是可选的 - -有个身份证的类,每个身份证肯定对应一个人 - -``` -class ID { - var number: String - let person: Landlord - - init(person: Landlord, number: String) { - self.person = person - self.number = number - } - - deinit { - print("ID内存释放") - } -} - -``` - -房东 - -``` -class Landlord { - var name: String - var house: House? - /// 身份证可以为nil, - var id: ID? - - init(name: String) { - self.name = name - } - - deinit { - print("Landlord内存释放") - } -} - -``` - -循环引用 - -``` -func unownedTest() { - var person: Landlord? = Landlord(name: "老王") - var id: ID? = ID(person: person!, number: "10010") - - person?.id = id - person = nil - id = nil -} - -``` - -- person 和 id 之间有强引用关系 -- 每个id必须有一个对应的人并且是固定的只能使用let 修饰,不能为nil, 而id可以为空 -- unowned和 weak 作用相同都是弱引用,只是unowned 修改的变量不能是nil (可选型的) -- unowned只能使用在 class 类型上,不能修饰函数类型,例如闭包存在循环引用时不能使用unowned修饰 -- 解决方法: - - 此时可以通过使用 weak 修饰 id 的方式,因为 id 可以为nil - - let 修饰并且不能为nil 这种情况可以使用 unowned 修饰解决 - - ``` - class ID { - var number: String - unowned let person: Landlord - - init(person: Landlord, number: String) { - self.person = person - self.number = number - } - - deinit { - print("ID内存释放") - } - } - ``` - -**unowned有一定的危险性** - -因为unowned 修饰的对象不能赋值为nil,所以执行下面的代码会crash, - -``` -func unownedTest() { - var person: Landlord? = Landlord(name: "老王") - var id: ID? = ID(person: person!, number: "10010") - - person?.id = id - - // 提前释放 person 内存 - person = nil - - // 获取身份证对应的人 - print(id?.person) // 此处奔溃, - let owner = id?.person - id = nil - } -``` - -正确的写法因该是先将🆔设置为nil,再将 Person 设置为nil,再调用 print(id?.person) 就不会再报错 - -``` -func unownedTest() { - var person: Landlord? = Landlord(name: "老王") - var id: ID? = ID(person: person!, number: "10010") - - person?.id = id - - id = nil - - // 提前释放 person 内存 - person = nil - - // 获取身份证对应的人 - print(id?.person) // 此处奔溃, - let owner = id?.person - } -``` - -#### 相互循环引用的两个变量都不是可选的 - -使用隐式可选类型 - -### 闭包中的强引用循环 - -- 并不是所有的闭包都会产生循环引用,只有相互引用的两个对象会产生循环引用 - -- 使用swift闭包捕获列表,在闭包的声明参数之前使用 [unowned self], 不需要解包 - -- 使用 unowned表示这个对象不能为空,可能存在风险,如果对象为空就会crash - - -使用 unowned 对象不能为空,可能存在风险 - -``` -class Closure { - var closure: ((Int) -> Void)? - - init() { - closure = { [unowned self] index in - print(self) - print(index) - } - } -} -``` - -使用 weak, 此时需要解包 - -``` -closure = { [weak self] index in - // 解包 - if let `self` = self { - print(self) - print(index) - } - } -``` - -强制解包 -``` -closure = { [weak self] index in - // 强制解包 - print(self!) - print(index) - } -``` - diff --git "a/source/_posts/typescripe \345\256\211\350\243\205\345\217\212\350\257\255\346\263\225.md" "b/source/_posts/typescripe \345\256\211\350\243\205\345\217\212\350\257\255\346\263\225.md" deleted file mode 100644 index 2b2aac4..0000000 --- "a/source/_posts/typescripe \345\256\211\350\243\205\345\217\212\350\257\255\346\263\225.md" +++ /dev/null @@ -1,296 +0,0 @@ -## ReactNative 中使用 TypeScript - - -> 从RN0.57版本开始已经可以直接支持typescript,无需任何配置。在保留根入口文件index.js的前提下,其他文件都可以直接使用.ts或.tsx后缀。但这一由babel进行的转码过程并不进行实际的类型检查,因此仍然需要编辑器和插件来共同进行类型检查 - - -### TypeScript和JavaScript对比 - -[两者深度对比](https://juejin.im/entry/5a52ed336fb9a01cbd586f9f) - -最大的区别: - -- TypeScript是强类型语⾔和静态类型语⾔(需要指定类型) -- JavaScript是弱类型语⾔和动态类型语⾔(不需要指定类型) - - -### TypeScript - -由微软开发的自由和开源的编程语言,是JavaScript的一个严格超集,并添加了可选的静态类型和基于类的面向对象编程 - - -[TypeScript定义](https://zh.wikipedia.org/wiki/TypeScript) - -### 安装及语法 - -[教程](https://www.tslang.cn/docs/home.html) - -##### 安装 - -``` -npm install -g typescript -``` - -##### 安装 typescript 依赖管理器 typings - -``` -npm install -g typings -``` - - -##### 安装 typings 依赖 - -``` -typings install npm~react --save -typings install dt~react-native --globals --save -``` - -##### 使用 yarn 安装 - -如果已经安装了 yarn 安装如下: - -``` -yarn add tslib @types/react @types/react-native -yarn add --dev react-native-typescript-transformer typescript - -``` - -##### 创建 tsconfig.json 配置文件 - -- cd 到 ReactNative 项目根文件夹下 - - ``` - tsc --init - ``` -- 修改 tsconfig.json 文件 - - 如果一个目录下存在一个tsconfig.json文件,那么它意味着这个目录是TypeScript项目的根目录。 tsconfig.json文件中指定了用来编译这个项目的根文件和编译选项 - - [tsconfig.json文件配置详解](https://www.tslang.cn/docs/handbook/tsconfig-json.html) - - [TypeScript配置文件tsconfig简析](https://github.com/hstarorg/HstarDoc/blob/master/%E5%89%8D%E7%AB%AF%E7%9B%B8%E5%85%B3/TypeScript%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6tsconfig%E7%AE%80%E6%9E%90.md) - - **务必设置"jsx":"react" -注意多余的注释可能会不兼容,需要移除** - - ``` - { - "compilerOptions": { - "target": "es6", - "allowJs": true, - "jsx": "react", - "outDir": "./dist", - "sourceMap": true, - "noImplicitAny": false - }, - - // "files"属性 :指定一个包含相对或绝对文件路径的列表。 "include"和"exclude"属性指定一个文件glob匹配模式列表 - - "include": [ - "typings/**/*.d.ts", - "src/**/*.ts", - "src/**/*.tsx" - ], - "exclude": [ - "node_modules" - ] - } - ``` - - -- cd 到项目根目录中新建存放 typescripe 源代码的文件夹 - - -##### 建立 rn-cli.config.js 文件,使用支持typescript的transfomer - -``` -module.exports = { - getTransformModulePath() { - return require.resolve('react-native-typescript-transformer'); - }, - getSourceExts() { - return ['ts', 'tsx']; - } -} -``` - -##### 修改 .babelrc 文件 - -``` -{ - "presets": ["react-native"], - "plugins": ["transform-decorators-legacy"] -} -``` - -##### 修改项目中文件导入 - -- 入口`index.js`和`App.js`的文件名请不要修改,项目根目录下新建`src`文件夹 -- 在`src`文件夹下新建 `.tsx` 后缀的文件 -- `.tsx` 后缀的文件引用react的写法有所区别 - - ``` - import * as React from 'react' - import { Text, View } from 'react-native'; - import { Component } from 'react'; - - export default class HelloWorld extends Component{ - render() { - return ( - - Hello! - Typescript - - ); - } - } - ``` - -- 在`App.js`中使用 - - `App.js` 导入方式修改为如下方式 - - ``` - import './src'; - ``` - - - 导入 .tsx 类型的组建 - - ``` - import HelloWorld from "./src/HelloWorld"; - ``` - -### 配置jsconfig - -`jsconfig.json`目录中存在文件表明该目录是JavaScript项目的根目录。该jsconfig.json文件指定根文件和JavaScript语言服务提供的功能选项 - -[jsconfig详解](https://code.visualstudio.com/docs/languages/jsconfig) - -[jsconfig.json](https://jeasonstudio.gitbooks.io/vscode-cn-doc/content/md/%E8%AF%AD%E8%A8%80/javascript.html?q=) - - -##### 新建 `jsconfig.json` 文件 - -``` -touch jsconfig.json -``` - -##### 编辑 `jsconfig.json` 文件 - -``` -{ - "compilerOptions": { - "allowJs": true, - "allowSyntheticDefaultImports": true, - "emitDecoratorMetadata": true - }, - "exclude": [ - "node_modules", - "rnApp/Tools" // 项目根目录下新建的存放RN中所用的一些工具类 - ] -} - -``` - - -### package.json 文件配置 - -``` -{ - "name": "RNProject", - "version": "0.0.1", - "private": true, - "scripts": { // 通过设置这个可以使npm调用一些命令脚本,封装一些功能 - "start": "node node_modules/react-native/local-cli/cli.js start", - "test": "jest" - }, - "dependencies": { // 发布后依赖的包 - "@types/react": "^16.4.16", - "@types/react-native": "^0.57.4", - "react": "16.5.0", - "react-native": "0.57.2", - "react-transform-hmr": "^1.0.4", - "tslib": "^1.9.3" - }, - - "devDependencies": { // 开发中依赖的其它包 - "babel-jest": "23.6.0", - "jest": "23.6.0", - "metro-react-native-babel-preset": "0.48.0", - "react-native-typescript-transformer": "^1.2.10", - "react-test-renderer": "16.5.0", - "typescript": "^3.1.2" - }, - - - "jest": { // JavaScript 的单元测试框架 - "preset": "react-native" - } -} -``` - -##### `–save` 和 `-–save-dev` 区别 - -- --save参数表示将该模块写入dependencies属性, -- --save-dev表示将该模块写入devDependencies属性。 - -##### 添加开发依赖库 - -- 类型检查 - - ``` - npm install @types/jest --save-dev - npm install @types/react --save-dev - npm install @types/react-native --save-dev - - ``` -- 装饰器转化核心插件 - - ``` - npm install babel-plugin-transform-decorators-legacy --save-dev - ``` - -- 测试框架 - - ``` - npm install react-addons-test-utils --save-dev - npm install react-native-mock --save-dev - ``` - -##### 删除依赖库 - -``` -npm uninstall @types/jest --save -``` - -``` -npm uninstall @types/jest --save-dev -``` - -##### 彻底删除,-d 表示 devDependencies, -O 表示 optionalDependencies 中 - -``` -npm uninstall -s -D -O @types/jest - -``` - -### 报错汇总 - -##### 错误1 `Cannot find module 'react-transform-hmr/lib/index.js'` - - [解决1](https://stackoverflow.com/questions/50142561/cannot-find-module-react-transform-hmr-lib-index-js?noredirect=1&lq=1) - - [解决2](https://github.com/facebook/react-native/issues/21530) - -- 安装 `react-transform-hmr` - - ``` - npm install react-transform-hmr --save - ``` - -- 清除缓存重新启动服务 - - ``` - npm start --reset-cache - ``` - - diff --git "a/source/_posts/\343\200\21252\344\270\252\346\226\271\346\263\225\347\254\254\344\270\200\347\253\240\343\200\213.md" "b/source/_posts/\343\200\21252\344\270\252\346\226\271\346\263\225\347\254\254\344\270\200\347\253\240\343\200\213.md" deleted file mode 100644 index 60ee5c5..0000000 --- "a/source/_posts/\343\200\21252\344\270\252\346\226\271\346\263\225\347\254\254\344\270\200\347\253\240\343\200\213.md" +++ /dev/null @@ -1,335 +0,0 @@ -《52个方法-第一章》 - -### 一、OC起源 - -- OC使用的是“消息结构”而不是“函数调用” -- 两者区别: - - 消息结构:在运行时所执行的代码由运行环境来决定,无论是否多态总会在运行时才会去查找所要执行的方法 - ``` - NSObject *objc = [NSObject new]; - [objc performWith: parameter1 and: parameter2]; - ``` - - - 函数调用:由编译器决定,如果函数多态,编译时就要查处到底应该执行那个函数实现 - - ``` - Object *objc = new Object; - objc -> perform(parameter1, parameter2); - ``` - -- 消息结构中编译器不会关心接收消息的对象是何种类型,接收消息的对象问题也要在运行时处理,此过程叫做“动态绑定” - -**要点:** - -1. OC语言使用的动态绑定的消息结构,在运行时所执行的代码由运行环境来决定,无论是否多态总会在运行时才会去查找所要执行的方法, -2. OC 中对象总是分配在“堆空间”,而绝不会分配在”栈“上,不带 “*”的变量或者结构体等可能会使用“栈空间” - -### 二、类头文件尽量少导入其他头文件 - -**要点:** - -1. 除非必要,否则不要引入头文件,使用向前声明降低类之间的耦合 -2. 有时无法使用向前声明,应该把该类遵守协议的声明放到“class-continuation分类”(匿名分类)中。 -3. 如果“class-continuation分类”也不行的话,就把协议单独放在一个文件中,再将其引入### 三、多用字面量语法,少用与之等价的方法 - -### 三、多用字面量语法,少用与之等价的方法 - - -``` -- (void)tempMethond { - // OC1.0 起,支持简单方式创建基本类型对象 - - // - 使用字符串字面量:简介易读 - NSString *someString = @"Effective Objective-C 2.0"; - // 传统方式 - NSString *someStrongOld = [NSString stringWithFormat:@"Effective Objective-C 2.0"]; - - - // - 字面数值 - NSNumber *someNumber = @1; - NSNumber *floatNumber = @2.5f; - NSNumber *doubleNumber = @3.1415926; - NSNumber *boolNumber = @YES; - NSNumber *charNumber = @'a'; - // 字面量语法用于表达式 - int x = 5; - float y = 6.23f; - NSNumber *expressionNumber = @(x * y); - - - // - 字面量数组 - NSArray *animals = @[@"cat", @"dog", @"mouse", @"badger"]; - NSString *dog = animals[1]; - - // arrayWithObjects 和 字面量 的区别: - // arrayWithObjects 方法依次处理各个参数,直到发现nil为止 - // 字面量数组遇到nil直接抛出异常 - NSArray *animalsOld = [NSArray arrayWithObjects:@"cat", @"dog", @"mouse", @"badger", nil]; - NSString *dogOld = [animalsOld objectAtIndex:1]; - - - // - 字面量字典 - // 如果有nil直接抛出异常 - NSDictionary *personData = @{@"firtName": @"Matt", - @"lastName": @"Galloway", - @"age": @28}; - NSString *lastName = personData[@"lastName"]; - - // dictionaryWithObjectsAndKeys 方法依次处理各个参数,直到发现nil为止 - NSDictionary *personDataOld = [NSDictionary dictionaryWithObjectsAndKeys:@"Matt",@"firtName", - @"Galloway", @"lastName", - [NSNumber numberWithInteger:28], @"age", nil]; - - NSString *lastNameOld = [personData objectForKey:@"lastName"]; - - - // - 可变数组和字典 - NSMutableArray *mutableArray = animals.mutableCopy; - mutableArray[1] = @"dog"; - - NSMutableDictionary *mutableDict = [personData mutableCopy]; - mutableDict[@"lastName"] = @"Galloway"; -} -``` - -**要点:** - - 1. 使用字面量语法创建字符串,数值,数组,字典简明扼要 - 2. 通过下标操作获取数组,字典对应的元素 - 3. 使用字面量创建数组,字典时若有nil ,会抛出异常,确保值里不含nil### 五、用枚举表示状态、选项、状态码 - - -### 四、多用类型常量,少用#define预处理指令 - -##### 宏定义 - -- 缺点: - - 可以把所有相同的字符串值都替换为定义的值, - - 定义出来的常量没有具体的类型信息 - - 如果有人重新定义了宏定义,不会有警告或者提醒,导致程序中值不一样 - -##### 类型常量 - -- 命名规则: - - 如果只是在实现文件.m 中使用,则在前面加字母k - - 如果此常量在类之外也可见,则以类名为前缀,(第19条详细了解命名习惯) - -- 声明位置: - - 总是喜欢在头文件中声明宏定义,可能和常量名冲突, - - 所有引入这个头文件的类都会出现宏定义或者常量,相当于声明了一个名叫kAnimationDuration的全局变量 - - 应该加上前缀,表明其所属的具体类,例如:EOCViewClassAnimationDuration - -- 常量从右往左解读依次是: 具体类型,不可修改 - - ``` - // const 修饰的是常量不希望别人修改, NSString * 指向NSString对象 - extern NSString *const EOCStringConstant; - - - // 两者写法都可以 - extern const NSTimeInterval EOCAnimatedViewAnimationDuration; - extern NSTimeInterval const EOCAnimatedViewAnimationDuration1; - ``` - -- 如果不打算公开常量,可以定义在 .m 实现文件里 - - ``` - static const NSTimeInterval kAnimationDuration = 0.3; - ``` - -- static关键字 - - static 修饰意味着该变量仅在定义此变量的编译单元(实现文件.m)中可见,作用域就是当前.m文件 - - ``` - static const NSTimeInterval kAnimationDuration = 0.3; - ``` - - 如果需要声明一个外部可见的常值变量,需要在头文件中声明并使用 extern 关键词修饰 "extern - - 在 .h 文件中 - - ``` - extern NSString *const EOCStringConstant; - - // 两者写法都可以 - extern const NSTimeInterval EOCAnimatedViewAnimationDuration; - extern NSTimeInterval const EOCAnimatedViewAnimationDuration1; - ``` - - 在 .m 文件中 - - ``` - NSString *const EOCStringConstant = @"VALUE"; - const NSTimeInterval EOCAnimatedViewAnimationDuration = 0.3; - NSTimeInterval const EOCAnimatedViewAnimationDuration1 = 0.3; - ``` - -**要点:** - -1. 定义常量时static const定义编译单元(实现文件.m)中可见的常量,无需为其名称加前缀 -2. 在头文件中使用extern 声明全局常量,并在实现文件中定义其值,这种常量要出现在全局符号表中,所以加相关类作为前缀 - -### 五、用枚举表示状态、选项、状态码 - -- 普通枚举 - - ``` - enum EOCConnectionState { - EOCConnectionStateDisconnected, // 断开连接 - EOCConnectionStateConnecting, // 连接中... - EOCConnectionStateConnected, // 已连接 - }; - - ``` - 普通枚举如果要使用如下: - - ``` - enum EOCConnectionState status = EOCConnectionStateConnected; - ``` - - 如果每次都不需要输入enum关键字,只需要typedef关键字重新定义枚举类型 - - ``` - EOCConnectionState status1 = EOCConnectionStateConnecting; - ``` - -- 设置枚举的初始值 - - ``` - enum EOCConnectionStateConnectionState { - EOCConnectionStateConnectionStateDisconnected = 1, // 断开连接 - EOCConnectionStateConnectionStateConnecting, // 连接中... - EOCConnectionStateConnectionStateConnected, // 已连接 - }; - ``` - -- 可选枚举 - - - << 位操作符(左移), 表示往左移动N位,使用 位运算符 | (或)来组合使用 - - 使用位运算符 &(与) 判断是否开启了某个选项 - - ``` - enum UIViewAutoresizing { - UIViewAutoresizingNone = 0, - UIViewAutoresizingFlexibleLeftMargin = 1 << 0, - UIViewAutoresizingFlexibleWidth = 1 << 1, - UIViewAutoresizingFlexibleRightMargin = 1 << 2, - UIViewAutoresizingFlexibleTopMargin = 1 << 3, - UIViewAutoresizingFlexibleHeight = 1 << 4, - UIViewAutoresizingFlexibleBottomMargin = 1 << 5 - } - ``` - -- 使用宏定义枚举 - - 普通枚举类型(老式语法定义枚举) - - ``` - typedef enum : NSUInteger { - EOCConnectionState2DisConnected, - EOCConnectionState2DisConnecting, - EOCConnectionState2Connected, - } EOCConnectionState2; - ``` - - NS_ENUM 宏定义的枚举展开后: - - ``` - typedef enum EOCConnectionState_3 : NSUInteger EOCConnectionState_3; - enum EOCConnectionState_3 : NSUInteger { - EOCConnectionState_3DisConnected, - EOCConnectionState_3DisConnecting, - EOCConnectionState_3Connected, - }; - ``` - -- 定义新特性枚举 - - ``` - typedef NS_ENUM(NSUInteger, EOCConnectionState3) { - EOCConnectionState3DisConnected, - EOCConnectionState3DisConnecting, - EOCConnectionState3Connected, - }; - ``` - -- 枚举使用 - - .h文件 - - ``` - #import - - typedef enum EOCConnectionState EOCConnectionState; - - typedef NS_ENUM(NSUInteger, EOCConnectionState3) { - EOCConnectionState3DisConnected, - EOCConnectionState3DisConnecting, - EOCConnectionState3Connected, - }; - - - @interface EOCEnum : NSObject - @property (nonatomic, assign) enum EOCConnectionState state; - @property (nonatomic, assign) EOCConnectionState3 state3; - @end - - ``` - - .m 文件中 - - ``` - #import "EOCEnum.h" - - @implementation EOCEnum - - - (void)testEnum { - // 未使用typedef 关键字 - enum EOCConnectionState status = EOCConnectionStateConnected; - NSLog(@"%u", status); - - // 使用typedef 关键字 - EOCConnectionState status1 = EOCConnectionStateConnecting; - NSLog(@"%u", status1); - - UIViewAutoresizing resizing = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - if (resizing & UIViewAutoresizingFlexibleWidth) { - // 设置了 UIViewAutoresizingFlexibleWidth 约束 - } - } - - // UIKit 试图所支持的设置显示方向 - - (UIInterfaceOrientationMask)supportedInterfaceOrientations { - return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft; - } - - // 枚举使用 - - (void)userEnum { - switch (_state3) { - case EOCConnectionState3DisConnected: - // ... - break; - - case EOCConnectionState3DisConnecting: - // ... - break; - - case EOCConnectionState3Connected: - // ... - break; - - default: // 最好不用,防止以后增加一种状态机之后,编译器不发出警告 - break; - } - } - - @end - ``` - - -**要点:** - -1. 使用枚举表示状态机的状态、传递给方法的选项以及状态码等值,给枚举值起名时要注重易懂 -2. 如果一个类型可以使用状态机的状态来表示,并且多个选项可同时使用,那么就定义为可选的枚举类型,枚举各值定义为2的幂,以便通过“按位或”操作组合 -3. 使用NS_ENUM 和 NS_OPTIONS 宏来定义枚举,并指明底层的数据类型。这样可以确保枚举是用开发者所选的底层数据类型实现出来的,而不会采用编译器所选的类型 -4. switch语句处理枚举类型时不要实现default 分支,如果以后加入枚举的新状态之后,编译器会发出警告 - diff --git "a/source/_posts/\344\270\200\350\207\252\345\212\250\345\274\225\347\224\250\350\256\241\346\225\260.md" "b/source/_posts/\344\270\200\350\207\252\345\212\250\345\274\225\347\224\250\350\256\241\346\225\260.md" deleted file mode 100644 index 80e546c..0000000 --- "a/source/_posts/\344\270\200\350\207\252\345\212\250\345\274\225\347\224\250\350\256\241\346\225\260.md" +++ /dev/null @@ -1,71 +0,0 @@ -自动引用计数 - -## 什么是自动引用计数 - -内存管理中对引用采取自动计数的技术 - -## 内存管理 - -- 自己生成的对象,自己所持有 - - alloc - - new - - copy - - mutableCopy -- 非自己生成的对象,自己也能持有 - - retain -- 不再需要自己持有的对象时释放 - - release -- 非自己持有的对象无法释放 - -- 废弃对象 - - dealloc - - - - - - -## __strong - -__strong 修饰符表示对对象的“强引用” - - -### __unsafe_unretained 修饰符 - -__unsafe_unretained 是不安全的所有权修饰符, 被它修饰的变量不属于编译器的内存管理对象 - -``` -id __unsafe_unretained objc = [[NSObject alloc] init]; -``` - -Xcode 提示:`Assigning retained object to unsafe_unretained variable; object will be released after assignment` - -将保留对象赋给unsafe_unretain变量;对象将在赋值后释放 - -``` - id __unsafe_unretained obj1 = nil; - - { - id __unsafe_unretained obj = [[NSObject alloc] init]; - - // 自己生成并持有对象 - // 因为obj0 变量是__strong,强引用,所以自己持有对象 - id __strong obj0 = [[NSObject alloc] init]; - - // obj0 变量赋值给你obj1 - // ojb1 变量不持有对象的强应用,也不持有弱引用 - obj1 = obj0; - - - // 输出 obj1 变量表示的对象 - NSLog(@"A: %@", obj1); - } // obj0变量 超出了其作用域,强引用失效自动释放自己持有的对象,除此之外没有其他持有者,所以废弃该对象 - - - - - // 输出 obj1 变量表示的对象 - // obj1 变量表示的对象因为无持有者已经销毁,坏的内存访问 - NSLog(@"B: %@", obj1); - -``` diff --git "a/source/_posts/\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223-\344\272\214.md" "b/source/_posts/\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223-\344\272\214.md" deleted file mode 100644 index cd49481..0000000 --- "a/source/_posts/\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223-\344\272\214.md" +++ /dev/null @@ -1,90 +0,0 @@ ---- -title: 面试中常见问题总结(二) -date: 2018-01-18 10:23:13 -tags: ---- - - -### 非正式协议和正式协议 - -非正式协议:凡是给NSObject或者其子类添加的类别都是非正式协议 -正式协议:使用 @protocol 方式声明的方法列表,有 @require 和 @optional 两种类型 - - - -### KVC 键值编码 - - 之所以可以使用就是因为给 NObject 增加了 NSKeyValueCoding 非正式协议,NSObject 实现了协议,OC中几乎所有的对象都支持KVC,获取值的方法是不通过属性的setter、getter 方法,而是通过属性名称的key间接访问了实例变量,**可以访问私有变量** - - ``` - // 官方注释 - /* Send -setObject:forKey: to the receiver, unless the value is nil, in which case send -removeObjectForKey:. - */ - - (void)setValue:(nullable ObjectType)value forKey:(NSString *)key; - ``` - - -如果为nil, 就会自动过滤掉, 此方法常用在字典中过滤空值 - -// forKeyPath: 用于符合属性,能够通过层层访问内部属性,相当于调用属性点语法, 拥有forKey 的所有功能 - - ``` - - (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath; - - ``` - -- 实现原理 - - 利用 setValue: forKeyPath: 赋值 - 1. 底层实现还是使用 runtime 给对象发消息,先去查找属性是否有setter方法,存在setter 就会通过 setter 方法复制, - 2. 如果没有setter方法,接着查找下是否划线的成员变量,如果有直接给赋值 - 3. 如果没有下划线的成员变量,查找是否存在和key值相同的属性,如果有就直接给属性赋值 - 4. 如果没有和key值相同的属性,就会执行 setValue: forUndefinedKey: 抛出一个异常程序奔溃,这也是字典转模型中需要实现这个方法的原因 - - -### KVO 键值监听 - -利用一个key值来找到某个属性并监听某个属性的值的变化发送给观察者,可以理解为简单的观察者模式 - -- KVO 使用 - 1. 为目标对象的属性添加观察者 - - ``` - - (void)testKVO { - Person *p = [[Person alloc] init]; - self.person = p; - p.name = @"哈哈"; - - [p addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil]; - - p.name = @"我不是原来的我!你能监听到我 ???"; - p.name = @"看我七十二变"; - } - - ``` - - - 2. 实现监听的方法 - - ``` - #pragma mark - kvo 回调方法 - - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { - - NSLog(@"KVO监听到的对象是 = %@, 值 = %@ , change = %@", [object class], keyPath, change); - } - - ``` - - 3. 移除观察者 - - ``` - - (void)dealloc { - - [self removeObserver:self forKeyPath:@"name"]; - NSLog(@"-----控制器销毁-----"); - } - ``` - -- KVO原理 - - 类的属性被添加了观察者后,OC底层就会通过Runtime 动态的创建一个这个类的派生类,当前类内部的isa 指针指向了这个派生类,在派生类中重写基类被观察属性的 setter 方法,调用前后会调用 willChangeValueForKey, setValueForKey, didChangeValue 方法, 从而达到监听属性值的方法 - diff --git "a/source/_posts/\345\274\202\346\255\245\344\270\213\350\275\275\345\233\276\347\211\207.md" "b/source/_posts/\345\274\202\346\255\245\344\270\213\350\275\275\345\233\276\347\211\207.md" deleted file mode 100644 index b4a5b57..0000000 --- "a/source/_posts/\345\274\202\346\255\245\344\270\213\350\275\275\345\233\276\347\211\207.md" +++ /dev/null @@ -1,57 +0,0 @@ ---- -title: 异步下载图片 -date: 2017-01-18 21:40:36 -tags: 异步 ---- - -### 异步下载 - -同步发送网络请求会卡UI线程,采用异步来下载图片。 - -**异步下载存在的问题** - -- 错行问题 - - - 解决:使用MVC模式,使数据一对一可解决, - - 在MVC中增加image属性后,会导致单个模式的体积增大 - - 内存占用 - - 当接收到内存警告时清空图片时需要遍历模型中将image置为nil - - 将图片用字典缓存起来达到一对一, 清空内存中的图片时只需要remove字典就ok - - 创建下载任务的字典,在开始下载时将正在下载的URL写入到字典中下载完成后将字典中对应的值移除,写入防止来回滑动时重复发送网络请求,下载完后移除是为了防止如果接受到内存警告后下载的图片都已清除,但是下载任务的字典的值为移除就不会再一次去下载图片 - - 缓存下载的图片到沙盒中 - - - 获取图片下载路径最后一个分隔符后面的字符串作为图片名保存 - -`stringByAppendingPathComponent` 和 `stringByAppendingString` 两个方法有区别 - -**stringByAppendingString 方法是获取caches文件名,在文件夹名后面拼接字符串,在caches上一级目录下创建文件,和caches文件夹属于同一个级别** - - - // 获取caches文件路径 - NSString *cachesPath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); - - NSString *URL = @"http://wwww.baidu.com/image/123456.png"; - - NSString *imageName = [URL lastPathComponent]; - - // 拼接路径 - // 此方法生成的路径在caches文件下目录下 - NSString *filePath = [cachesPath stringByAppendingPathComponent]; - - - // 读取图片 - if (内存中没有) { - // 沙盒读取 - UIImage *sandBoxImage = [UIImage imageWithContetsOfFile:filePath]; - - **加入到内存中,方便下次加载** - } - - if (sandBoxImage == nil) { - // 下载图片 - } - - // 写入到沙盒 - if (imageData != nil) { - [imageData writeToFile: filePath atomically:NO]; - } \ No newline at end of file diff --git "a/source/_posts/\346\234\254\345\234\260\345\267\262\346\234\211\351\241\271\347\233\256\346\267\273\345\212\240Pod.md" "b/source/_posts/\346\234\254\345\234\260\345\267\262\346\234\211\351\241\271\347\233\256\346\267\273\345\212\240Pod.md" deleted file mode 100644 index 8571027..0000000 --- "a/source/_posts/\346\234\254\345\234\260\345\267\262\346\234\211\351\241\271\347\233\256\346\267\273\345\212\240Pod.md" +++ /dev/null @@ -1,100 +0,0 @@ ---- -title: 本地已有项目添加Pod -date: 2017-12-02 00:38:18 -tags: ---- - -上一篇中主要记录了如何使用 `pod` 工具自动创建 `pod` 库并且生成一个测试Demo,这边主要记录给已有的项目添加 `pod` - -## 创建 .podsepc 文件 - -- `cd` 到已有项目的根目录执行命令: `pod spec create [ProjectName].podspec` - - ``` - pod spec create EmotionKeyboard.podspec - ``` - -## 修改编辑 `.podsepc` 文件 - - ``` - s.name = "EmotionKeyboard" - s.version = "0.0.1" - s.summary = "A Emotional Keyboard." - - # 此处的格式不能修改 - s.description = <<-DESC - - Swift Emotional Keyboard - DESC - - s.homepage = "https://github.com/aTreey/EmotionKeyboard" - - # 两种写法,使用下面第二种 - # s.license = "MIT (example)" - s.license = { :type => "MIT", :file => "LICENSE" } - - s.author = { "aTree" => "480814177@qq.com" } - - s.platform = :ios, "8.0" - - s.source = { :git => "https://github.com/aTreey/EmotionKeyboard.git", :tag => "#{s.version}" } - - - # 从 .podspec 同级别的目录下起匹配 - s.source_files = "EmotionKeyboard", "EmotionKeyboard/Classes/**/*.{h,m}" - - # 设置自己项目中依赖的系统库 - s.framework = "UIKit" - - ``` - -## 调整文件的目录结构 - -- 在项目根目录下新建一个放组件代码的文件夹 -- 如果项目中包含 `bundle` 文件, 需要在组件文件夹下新建一个 `Assets` 文件夹,用来存放组件中的资源 - - 最终结果如下: - - ![](https://ws1.sinaimg.cn/large/a1641e1bly1fm2jl7ykslj21gc0o80xt.jpg)) - -- 创建并添加必要文件 - - - `LICENSE` 文件: 开源许可文件, 如果创建项目的时候选择了开源许可就可以自动生成忽略此步骤,如果没有直接拷贝一份其他的工程中的即可 - - `README.md` 文件: 主要用来说明你开源库的信息及使用方法 - - -## 验证本地 `.podspec` 文件 - -- 错误1: `.podspec `中的文件路径设置错误 - - ![](https://ws1.sinaimg.cn/large/a1641e1bly1fm2jlboq82j20v807ejug.jpg) - -- 错误2: bundle 文件资源路径错误 - - ![](https://ws1.sinaimg.cn/large/a1641e1bly1fm2jl4shnnj20zq0cwgqf.jpg) - - -- 本地验证结果 - - - **EmotionKeyboard passed validation.** 本地验证验证通过 - -## 网络验证 `.podspec` 文件 - -- 给 `master` 分支打上 `.podspec` 文件中对应的 `tag` 标签, 提交推送, -- 执行命令 - - ``` - pod spec lint - ``` -- 网络验证结果 - - ``` - EmotionKeyboard.podspec passed validation. - ``` - - -**本文参考** - -[给 Pod 添加资源文件](http://blog.xianqu.org/2015/08/pod-resources/) - -[基于 Swift 创建 CocoaPods 完全指南](http://swift.gg/2016/12/15/cocoapods-making-guide/) \ No newline at end of file diff --git "a/source/_posts/\346\234\254\345\234\260\347\274\223\345\255\230.md" "b/source/_posts/\346\234\254\345\234\260\347\274\223\345\255\230.md" deleted file mode 100644 index 9ade78d..0000000 --- "a/source/_posts/\346\234\254\345\234\260\347\274\223\345\255\230.md" +++ /dev/null @@ -1,129 +0,0 @@ ---- -title: 本地缓存 -date: 2016-10-20 01:20:41 -tags: 本地缓存 ---- - -## 保存图片到沙盒 - -``` -- (void)saveImage:(UIImage *)image InSanBoxWithIndexPath:(NSInteger)indexPath { - - NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES); - NSString *filePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:@"pic_%ld.png", indexPath]]; // 保存文件的名称 - [UIImagePNGRepresentation(image)writeToFile:filePath atomically:YES]; -} - - -``` - -## 获取沙盒中的图片 - -``` -- (UIImage *)fetchImageFromSanBoxWithIndexPath:(NSInteger)indexPath -{ - NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES); - NSString *filePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:@"pic_%ld.png", indexPath]]; // 保存文件的名称 - UIImage *img = [UIImage imageWithContentsOfFile:filePath]; - return img; -} - -``` - -## 删除沙盒中的图片 - -``` -- (BOOL)deleteImageAtIndexPath:(NSInteger)index; -{ - - NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES); - NSString *filePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:@"pic_%ld.png", index]]; - - if([[NSFileManager defaultManager] fileExistsAtPath:filePath]) { - return [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil]; - } - - return NO; -} -``` - - -## 修改图片 - -``` -- (UIImage *)fixOrientation:(UIImage *)aImage { - - // No-op if the orientation is already correct - if (aImage.imageOrientation == UIImageOrientationUp) - return aImage; - - // We need to calculate the proper transformation to make the image upright. - // We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored. - CGAffineTransform transform = CGAffineTransformIdentity; - - switch (aImage.imageOrientation) { - case UIImageOrientationDown: - case UIImageOrientationDownMirrored: - transform = CGAffineTransformTranslate(transform, aImage.size.width, aImage.size.height); - transform = CGAffineTransformRotate(transform, M_PI); - break; - - case UIImageOrientationLeft: - case UIImageOrientationLeftMirrored: - transform = CGAffineTransformTranslate(transform, aImage.size.width, 0); - transform = CGAffineTransformRotate(transform, M_PI_2); - break; - - case UIImageOrientationRight: - case UIImageOrientationRightMirrored: - transform = CGAffineTransformTranslate(transform, 0, aImage.size.height); - transform = CGAffineTransformRotate(transform, -M_PI_2); - break; - default: - break; - } - - switch (aImage.imageOrientation) { - case UIImageOrientationUpMirrored: - case UIImageOrientationDownMirrored: - transform = CGAffineTransformTranslate(transform, aImage.size.width, 0); - transform = CGAffineTransformScale(transform, -1, 1); - break; - - case UIImageOrientationLeftMirrored: - case UIImageOrientationRightMirrored: - transform = CGAffineTransformTranslate(transform, aImage.size.height, 0); - transform = CGAffineTransformScale(transform, -1, 1); - break; - default: - break; - } - - // Now we draw the underlying CGImage into a new context, applying the transform - // calculated above. - CGContextRef ctx = CGBitmapContextCreate(NULL, aImage.size.width, aImage.size.height, - CGImageGetBitsPerComponent(aImage.CGImage), 0, - CGImageGetColorSpace(aImage.CGImage), - CGImageGetBitmapInfo(aImage.CGImage)); - CGContextConcatCTM(ctx, transform); - switch (aImage.imageOrientation) { - case UIImageOrientationLeft: - case UIImageOrientationLeftMirrored: - case UIImageOrientationRight: - case UIImageOrientationRightMirrored: - // Grr... - CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.height,aImage.size.width), aImage.CGImage); - break; - - default: - CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.width,aImage.size.height), aImage.CGImage); - break; - } - CGImageRef cgimg = CGBitmapContextCreateImage(ctx); - UIImage *img = [UIImage imageWithCGImage:cgimg]; - CGContextRelease(ctx); - CGImageRelease(cgimg); - return img; -} - -``` \ No newline at end of file diff --git "a/source/_posts/\347\237\245\350\257\206\347\202\271\346\200\273\347\273\223.md" "b/source/_posts/\347\237\245\350\257\206\347\202\271\346\200\273\347\273\223.md" deleted file mode 100644 index bb6bdc9..0000000 --- "a/source/_posts/\347\237\245\350\257\206\347\202\271\346\200\273\347\273\223.md" +++ /dev/null @@ -1,36 +0,0 @@ ---- -title: 知识点总结 -date: 2017-03-25 13:41:28 -tags: ---- - -开发中的知识点总结 - -textField - -使用textField自定义搜索框,textField及leftView等属性 -使用leftView 时需要设置model为始终显示 -可以设置leftView的frame 调整图片大小 -IBInsepectable IBDesignable 的使用 - -给属性检查器面板中添加属性设置选项 -IBDesignalbe 实时显示UI控件,需要讲对应的xib和对应类绑定 - -增加分类 - -新建一个swift 文件, - -使用extension,重写 get set方法 -使用newValue 时需要判断,注意返回值的类型是否匹配 -试图切换 - -一个控制器中加载不同视图,可以在loadView 方法中自定义根视图 - -loadView 使用注意事项 -如果实现loadView 未调用 super loadView ,sb/xib实效 -loadView调用时,如果根视图是nil(本质是未调用super loadView),会自动调用loadView 方法初始化根视图,形成递归调用 -判断当前登陆标识的状态确定是调用 super loadView 还是 把创建自定义的根视图赋值给 view -VFL 的使用 - -使用之前需要先 关闭frame 的布局才能手动的添加约束 translatesAutoresizingMaskIntoConstraints = false -基本动画使用 diff --git "a/source/_posts/\347\254\254\344\270\203\347\253\240Blocks\351\235\242\350\257\225.md" "b/source/_posts/\347\254\254\344\270\203\347\253\240Blocks\351\235\242\350\257\225.md" deleted file mode 100644 index 90cf836..0000000 --- "a/source/_posts/\347\254\254\344\270\203\347\253\240Blocks\351\235\242\350\257\225.md" +++ /dev/null @@ -1,143 +0,0 @@ -# Blocks - -## Block 是什么? - -Blocks 是 C 语言的扩充功能,将函数及其执行上下文封装起来的对象,带有自动变量(局部变量)的匿名函数 - -- 因为block 底层C++ 结构体中存在着 isa 指针,所以是一个对象 -- 因为block 底层C++ 结构体中存在着一个函数指针 FuncPtr,所以是一个函数 - -## 什么是Block 调用 -Block 的调用就是函数的调用 -在终端使用 clang 编译之后查看文件内容可得出以下结论: - -- 对其进行一个强制转换,转换之后取出成员变量 FuncPtr - -## 截获变量 - -**以下代码运行结果是 12** - -![](https://ws4.sinaimg.cn/large/006tNc79ly1g1xvmfjgo8j30uu0jmgnl.jpg) - - 原因:block 对基本数据类型的局部变量会截获其值,在定义 block 时以值的方式传到了block 对应的结构体当中,而调用block 时直接使用的是block对应结构体当中已经传过来的那个值,所以值为12 - - -- 局部变量截获 - - 基本数据类型的局部变量截获其值 - - 对象类型的局部变量连同所有权修饰符一起截获 - -- 局部静态变量: 已指针形式截获 - -- 全局变量:不截获 - -- 静态全局变量: 不截获 - - -**以下代码运行结果是 8** - -![](https://ws1.sinaimg.cn/large/006tNc79ly1g1xwb9qt9oj30n60bq75i.jpg) - - 原因:block 对局部静态变量会以指针形式截获,所以值为8 - -## __block 修饰符 - -- __block 修饰后的变量最后变成了对象,底层c++ 结构体中存在isa指针 -- 一般情况下,对被截获变量进行赋值操作需要添加__block 修饰符,而操作是不需要添加__block 修饰符的 -- 不需要__block 修饰符,全局变量和静态全局变量 block 是不截获的,所以不需要修饰,静态局部变量是通过指针截获的,修改外部的变量,也不需要添加 - - 静态局部变量 - - 全局变量 - - 静态全局变量 - -**不需要添加__Block 修饰符** - -``` -- (void)test { - NSMutableArray *array = [NSMutableArray array]; - void(^block)(void) = ^{ - [array addObject:@1234]; - }; - - block(); -} -``` - -**需要添加__Block 修饰符** - -``` -- (void)test { - __block NSMutableArray *tempArray = nil; - void(^block)(void) = ^{ - tempArray = [NSMutableArray array]; - }; - - block(); -} -``` - -**以下代码运行结果是 8** - -``` -- (void)test__Block3 { - __block int multiplier = 6; - int(^Block)(int) = ^int(int num){ - return multiplier * num; - }; - multiplier = 4; - NSLog(@"__block--test__Block3---result is %d", Block(2)); -} - -``` - -原因 - -![](https://ws4.sinaimg.cn/large/006tNc79ly1g1xxea9gckj31hi0l2ju3.jpg) - -![](https://ws1.sinaimg.cn/large/006tNc79ly1g1xxfpnz45j30zm0lstad.jpg) - - -**栈上Block 的__block 变量中的 __forwarding 指针指向的是自身** - - -## Block 的内存管理 - -**Block 的 内存分布** - -![](https://ws1.sinaimg.cn/large/006tNc79ly1g1xxnyye6kj31ag0ran0f.jpg) - -**Block 的 Copy 操作** - -![](https://ws1.sinaimg.cn/large/006tNc79ly1g1xxouef30j315a0d640x.jpg) - -- 全局block -- 堆block -- 栈block - -## Block 循环引用 - -- 如果当前block对当前对象的某一变量进行截获的话,block 会对对应变量有一个强引用,当前block因为当前的对象对其有个强引用,产生了自循环引用,通过声明为__weak变量来解决 -- 如果定义了__block 修饰符的话也会产生循环引用,在ARC 下会产生循环引用,而在MRC 下不会,在ARC 下通过打破环来消除,但是存在弊端时当block 没有机会执行时, - - - -循环引用1 -![](https://ws2.sinaimg.cn/large/006tNc79ly1g1xzmtsetqj31g20oewic.jpg) - -解决方法: - -![](https://ws2.sinaimg.cn/large/006tNc79ly1g1xzmtsetqj31g20oewic.jpg) - - -大环循环引用2 - -![](https://ws2.sinaimg.cn/large/006tNc79ly1g1xzqfwf1uj319c0la0vk.jpg) - -以上代码,在MRC 下,不会产生循环引用,在ARC 下,会产生循环应用,引起内存泄露 -![](https://ws3.sinaimg.cn/large/006tNc79ly1g1xzvs524zj31h80l20uo.jpg) - -解决方法 - -如果block 没有机会执行,那么循环引用将不会被打破 -![](https://ws2.sinaimg.cn/large/006tNc79ly1g1xzxf0iyxj31hp0u0aeq.jpg) - - - diff --git "a/source/_posts/\347\254\254\344\270\211\347\253\240UI\350\247\206\345\233\276\345\223\215\345\272\224\351\223\276\347\246\273\345\261\217\346\270\262\346\237\223\351\235\242\350\257\225.md" "b/source/_posts/\347\254\254\344\270\211\347\253\240UI\350\247\206\345\233\276\345\223\215\345\272\224\351\223\276\347\246\273\345\261\217\346\270\262\346\237\223\351\235\242\350\257\225.md" deleted file mode 100644 index 65ef6ef..0000000 --- "a/source/_posts/\347\254\254\344\270\211\347\253\240UI\350\247\206\345\233\276\345\223\215\345\272\224\351\223\276\347\246\273\345\261\217\346\270\262\346\237\223\351\235\242\350\257\225.md" +++ /dev/null @@ -1,150 +0,0 @@ - - - -## UITableView 相关 - -- 重用机制 -- 并发访问,数据拷贝问题:主线程删除了一条数据,子线程数据由删除之前的数据源copy而来,子线程网络请求,数据解析,预排版等操作后,回到主线程刷新时已经删除的数据又会重现 - - 主线程数据源删除时对数据进行记录,子线程的数据回来后再进行一次同步删除操作,然后再刷新数据 - - - 使用GCD中的串行队列,将数据删除和数据解析在此队列中进行 - - -## UIView 和CALayer 的关系 - -- UIView 为 CALayer 提供内容,负责处理触摸事件,参与响应链 -- CALayer 负责显示内容,分工明确,UIView 的Layer 实际上就是CALayer, backgroundColor 等属性本质是对CALayer 做了一层包装 - -## 事件传递 - -![](https://ws3.sinaimg.cn/large/006tKfTcly1g1omq41fi4j31hn0u0n3a.jpg) - -- hitTest 方法是返回响应时间的试图 -- pointInside withEvent 方法判断点击位置是否在当前视图范围内 -- 倒序遍历最终响应事件的试图,最后添加的试图会最优先被遍历到 - -**需求案例** - -只让正方形的view中圆形区域内可以响应事件,四个角的位置不响应事件 -![](https://ws1.sinaimg.cn/large/006tKfTcly1g1onklz0k8j309q0a674h.jpg) - -``` -class CircularAreaResponseButton: UIButton { - - override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { - if self.isHidden || !self.isUserInteractionEnabled || self.alpha <= 0.01 { - return nil - } - - var hitView: UIView? - if self.point(inside: point, with: event) { - // 遍历当前视图的子试图 - for subView in subviews { - // 坐标转换 - let convertPoint = self.convert(point, to: subView) - if let hitTestView = subView.hitTest(convertPoint, with: event) { - // 找到了最终响应事件的试图 - hitView = hitTestView - break; - } - } - - if hitView != nil { - return hitView - } - return self - } - return nil - } - - - override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { - let x1 = point.x - let y1 = point.y - - let x2 = self.frame.width / 2.0 - let y2 = self.frame.height / 2.0 - // 使用平面内两点见距离公式计算距离, 并且判断是否 <= 圆的半径 - return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)) <= x2 - } -} -``` - -## 视图响应 - -UIView 都继承于 UIResponder,都有以下方法 - -``` -func touchesBegan(_ touches: Set, with event: UIEvent?) -func touchesMoved(_ touches: Set, with event: UIEvent?) -func touchesEnded(_ touches: Set, with event: UIEvent?) -func touchesCancelled(_ touches: Set, with event: UIEvent?) -``` -如果事件沿着视图响应链一直传递到 UIApplicationDelegate 仍然没有任何一个视图处理事件,程序将会发生什么? - -- 忽略掉了这个事件当作什么都没有发生 - - -## 图像显示原理 - -CALayer 的 contents 是位图 - -![](https://ws1.sinaimg.cn/large/006tKfTcly1g1oqxcw9dvj32260n041w.jpg) - -- CPU的工作 - - Layout | Display | Prepare | Commite | - ---------|-----------|------------|---------| - UI布局 | 绘制 | 图片编解码 | 提交位图 | - 文本计算 | - -- GPU渲染管线 - - 定点着色 | 图元转配 | 光栅化 | 片段着色 | 片段处理 | - --------|--------|-------|---------|---------| - - - 提交到帧缓冲区(frameBuffer) - - -## UI卡顿掉帧的原因 - -1/60 ms 时间内, 由cpu 和GPU 协同产生最终的数据,当 CPU UI布局,文本计算所用的时间较长时,留给GPU渲染,提交到帧缓冲区的时间就会变短,这样以来就会发生掉帧情况 - - -### UIView 的绘制原理 - - - - -**CPU优化方案** - -- 对象的创建、调整、销毁放在子线程,节省cpu的一部分时间 -- 预排版(布局计算,文本计算)放在子线程中 -- 预渲染(文本等异步绘制,图片编解码)等 - -**GPU优化方案** - - -## 在屏渲染 -指GPU的渲染操作是在当前用于显示的屏幕缓冲区域中进行 - -## 离屏渲染 -指GPU在当前屏幕缓冲区域外新开劈了一个缓冲区域进行渲染操作 -指定了UI视图图层的某些属性,标记为视图在未预合成之前不能用于在当前屏幕上直接显示的时候就会发生离屏渲染,例如图层的圆角设置和蒙层设置 - -- 离屏渲染何时触发 - - - 设置layer 的圆角并且和 maskToBounds 一起使用时 - - 图层蒙版 - - 阴影 - - 光栅化 - -- 为何要避免离屏渲染 - - 离屏渲染,会增加 GPU 的工作量,GPU工作量的增加就很有可能导致CPU和GPU的总耗时超过1/60s,导致UI卡顿和掉帧 - -- 避免离屏渲染 - -###异步绘制 - -![](https://ws3.sinaimg.cn/large/006tKfTcly1g1osowi1dtj31l70u0ag5.jpg) diff --git "a/source/_posts/\347\254\254\344\271\235\347\253\240Runloop\351\235\242\350\257\225.md" "b/source/_posts/\347\254\254\344\271\235\347\253\240Runloop\351\235\242\350\257\225.md" deleted file mode 100644 index 11eaf22..0000000 --- "a/source/_posts/\347\254\254\344\271\235\347\253\240Runloop\351\235\242\350\257\225.md" +++ /dev/null @@ -1,5 +0,0 @@ -RunLop - - -![](https://ws4.sinaimg.cn/large/006tNc79ly1g21ckps89dj31vy03cq56.jpg) - diff --git "a/source/_posts/\347\254\254\344\272\224\347\253\240Runtime\347\233\270\345\205\263\351\235\242\350\257\225.md" "b/source/_posts/\347\254\254\344\272\224\347\253\240Runtime\347\233\270\345\205\263\351\235\242\350\257\225.md" deleted file mode 100644 index 9fd4ea8..0000000 --- "a/source/_posts/\347\254\254\344\272\224\347\253\240Runtime\347\233\270\345\205\263\351\235\242\350\257\225.md" +++ /dev/null @@ -1,234 +0,0 @@ -# Runtime 相关问题 - -## 数据结构 - -### objc_object 结构体 - -### objc_class - -#### 对象、类对象、元类对象的区别 -- 类对象存储实例方法列表等信息 -- 元类对象存锤类方法列表等信息 - -## 消息传递 - -`void objc_msgSend(void /* id self, SEL op, ... */)` - -经过编译器转变后 -`[self class]` <==> `objc_msgSend(self, @selector(class))` -第一个参数是消息传递的接收者self, 第二个参数是传递的消息名称(选择器) - -`void objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */)` -第一个参数是一个结构体指针 - -// -struuch objc_super { - __unsafe_unretained id receiver; // 就是当前的对象 -} - -`[superclass]` <==> `objc_msgSendSuper(super, @selector(class))` - - -无论调用[super class] 还是 [self class] 最终这条消息的接收者都是当前对象 - -图1 - -![](https://ws4.sinaimg.cn/large/006tNc79ly1g1viq97z3cj31k90u0qd3.jpg) - -## 消息传递过程 - -图2 - -![](https://ws3.sinaimg.cn/large/006tNc79ly1g1vigugmvmj31qu0u07a4.jpg) - -问题1 - -![](https://ws1.sinaimg.cn/large/006tNc79ly1g1vikx3a6wj31hp0u0dmw.jpg) - -`打印结果都是 Phone` - -原因: - -- 无论调用[super class] 还是 [self class] 最终这条消息的接收者都是当前对象 -- [self class] 在Phone的类对象中查找class 方法,没有去父类查找,父类也没有,如图1顺次往上查找到根类,调用根类中class 的具体实现 -- [super class]只是跨越了当前类,直接从当前类的父类中查找 class 方法,父类中没有class 方法,如图1所示只能继续往上查找到根类对象,在NSObject 中有class 方法实现,所以接受者任然是当前的对象 - -### 缓存查找 - -根据给定的SEL(方法选择器)通过一个函数来映射出bucket_t 在数组当中的位置,本质上是哈希查找 - -![](https://ws3.sinaimg.cn/large/006tNc79ly1g1vj2xha6gj318i0jc76s.jpg) - -### 当前类中查找 -当前类中存在着对应的方法列表 -- 对于已经排序好的列表,采用二分查找算法查找对应的执行函数 -- 对于没有排序的列表,采用一般遍历查找方法查找对应的执行函数 - - -### 父类逐级查找 - -图3 -![](https://ws1.sinaimg.cn/large/006tNc79ly1g1vkkjffglj31xm0ssgpn.jpg) - -- 最关键是通过当前类的 superclass 成员变量去查找父类,或者访问父类 -- curClass 的父类是否为 nil?如果是NSObject 类,他的父类为空,所以会结束查找 -- 如果当前类有父类就要去该父类的缓存中查找,如果在缓存中命中,那么就结束查找,如果缓存中没有,就需要遍历当前类的父类的方法列表,如果没有就遍历父类的父类查找,沿着superclass指针逐级向上查找,直到查找到NSObject,再去Superclass。如果还是没有为nil时就结束父类逐级查找 - - -## 消息转发 - -### resolveInstanceMethod: - -- 该方法是类方法不是实例方法,参数是SEL 方法选择器类型 -- 返回值是一个BOOL值 -- 告诉系统是否要解决当前实例方法的实现 -- 如果返回YES,结束消息转发 -- 如果返回NO,系统会给第二次机会处理这个消息,会回掉forwardingTargetForSelector: - -### forwardingTargetForSelector: -- 参数是 SEL 方法选择器 -- 返回值是 id 类型,说明有哪个对象来处理,转发的对象是谁,如果返回了转发目标就会结束当前调用 -- 如果返回为nil,系统会调用 methodSignatureForSelector:方法,最后一次机会处理这个消息 - -### methodSignatureForSelector: - -- 参数是 SEL 方法选择器 -- 返回值是一个对象,实际上这个对象是对`methodSignatureForSelector` 方法的参数,参数个数,参数类型和返回值的包装 -- 如果返回一个方法签名的话,就会调用 forwardInvocation: 方法 -- 如果返回为nil,标记为消息无法处理,程序crash - -### forwardInvocation: -- 如果此方法不能处理消息,程序crash - - -代码示例 - -.h 文件 - -``` -@interface RunTimeObject : NSObject -// 只声明,不实现,验证消息转发机制 -- (void)test; -@end -``` - -.m 文件 - -``` -@implementation RunTimeObject - -// 实现消息转发流程 中的方法 -/** - 是否要解决当前实例方法的实现 - */ -+ (BOOL)resolveInstanceMethod:(SEL)sel { - if (sel == @selector(test)) { - NSLog(@"resolveInstanceMethod:"); - // 如果返回YES,消息转发就会结束 -// return YES; - - return NO; - } else { - - NSLog(@"super resolveInstanceMethod:"); - // 返回父类的默认调用 - return [super resolveInstanceMethod:sel]; - } -} - - -/** - - 参数是 SEL 方法选择器 - - 返回值是 id 类型,说明有哪个对象来处理,转发的对象是谁,如果返回了转发目标就会结束当前调用 - - 如果返回为nil,系统会调用 methodSignatureForSelector:方法,最后一次机会处理这个消息 - */ -- (id)forwardingTargetForSelector:(SEL)aSelector { - NSLog(@" forwardingTargetForSelector: "); - return nil; -} - -/** - - 参数是 SEL 方法选择器 - - 返回值是一个对象,实际上这个对象是对`methodSignatureForSelector` 方法的参数,参数个数,参数类型和返回值的包装 - - 如果返回一个方法签名的话,就会调用 forwardInvocation: 方法 - - 如果返回为nil,标记为消息无法处理,程序crash - - */ -- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { - if (aSelector == @selector(test)) { - NSLog(@"methodSignatureForSelector:"); - /** - v: 表示这个方法返回值是 void - - 固定参数@: 表示参数类型是 id, 即self - 固定参数`:` : 表示参数类型是选择器类型,即 @selector(test) - */ - return [NSMethodSignature signatureWithObjCTypes:"v@:"]; - } else { - - NSLog(@"super methodSignatureForSelector:"); - // 返回父类的默认调用 - return [super methodSignatureForSelector:aSelector]; - } -} - - -- (void)forwardInvocation:(NSInvocation *)anInvocation { - NSLog(@"forwardInvocation:"); -} - -@end - -``` - -调用 - -``` -- (void)runTiemTest { - RunTimeObject *obj = [[RunTimeObject alloc] init]; - [obj test]; -} - -``` - -结果: - -``` -resolveInstanceMethod: -forwardingTargetForSelector: -methodSignatureForSelector: -super resolveInstanceMethod: -forwardInvocation: - -``` - -## 为类动态添加方法 - -methond Swizzling -### 使用场景 -- 页面中的进出添加统计信息,使用 methond Swizzling 替换viewillAppear 方法 - -### 动态添加方法 - -- perforSelector: 实际上是考察class_addMethod 的方法使用 - - -### 动态方法解析 -- @dynamic关键字 -- 动态运行时语言将函数决议推迟到运行时 -- 编译时不生成setter/getter 方法,而是在运行时才添加具体的执行函数 - -## 问题 -### [obj foo] 和 obj_msgSend()函数之间有什么关系? - -- `[obj foo]`经过编译器处理过后会变成 `obj_msgSend() `有两个参数,一个是obj,第二个参数是foo 选择器 - - -### runtime 如何通过Selector找到对应的IMP地址的? - -消息传递机制 - -### 能否向编译后的类增加实例变量 -编译之前就完成了实例变量的布局, -runtime_r_t 表示是readonly 的,编译后的类是不能添加实例变量的,可以向动态添加的类中添加实例变量 - diff --git "a/source/_posts/\347\254\254\345\205\253\347\253\240\345\244\232\347\272\277\347\250\213\351\235\242\350\257\225.md" "b/source/_posts/\347\254\254\345\205\253\347\253\240\345\244\232\347\272\277\347\250\213\351\235\242\350\257\225.md" deleted file mode 100644 index 12dfcab..0000000 --- "a/source/_posts/\347\254\254\345\205\253\347\253\240\345\244\232\347\272\277\347\250\213\351\235\242\350\257\225.md" +++ /dev/null @@ -1,2 +0,0 @@ -第三章多线程面试 - diff --git "a/source/_posts/\347\254\254\345\205\255\347\253\240\345\206\205\345\255\230\347\256\241\347\220\206\351\235\242\350\257\225.md" "b/source/_posts/\347\254\254\345\205\255\347\253\240\345\206\205\345\255\230\347\256\241\347\220\206\351\235\242\350\257\225.md" deleted file mode 100644 index e51a63d..0000000 --- "a/source/_posts/\347\254\254\345\205\255\347\253\240\345\206\205\345\255\230\347\256\241\347\220\206\351\235\242\350\257\225.md" +++ /dev/null @@ -1,130 +0,0 @@ -# 内存管理 - -## 内存布局 - -![](https://ws2.sinaimg.cn/large/006tKfTcly1g1mabhcs20j31cb0u0n2p.jpg) - -- stack 栈是从高地址向低地址扩展,所以栈是向下增长的,对象和block copy 后都会放在堆上 -- heap 堆区向上增长 - - -- 散列表方式 - - - 自旋锁 - - 自旋锁是“忙等” 的锁 - - 引用计数表 - - 弱应用表 - -## 内存管理方案 - -- 小对象使用 TaggedPointer -- 64 位架构下面使用的是 NONPONINT_ISA 内存管理方案,非指针型的 isa,内部存储了一些 -- 散列表结构 (Side Tables() 结构) - - 自旋锁 - - 引用计数表 - - 弱引用表 - - Side Tables() 结构 - - -### 为什么不是一个SideTable,而是多个组成了Side Tables -如果所有对象的存储都放在一张大表当中,因为每个对象是在不同的线程中创建的,要操作其中一个对象时,需要将表加锁处理保证数据安全,要等锁释放之后才能操作下一个对象,此时存在效率问题,引入分离锁技术方案 - -- 分离锁:把引用计数表分成多张表进行, -- Side Tables 本质是一张Hash 表 - -![](https://ws4.sinaimg.cn/large/006tNc79ly1g1wp3j6obfj310u07g0tn.jpg) - -### 自旋锁 -是“忙等”的锁,如果当前锁已被其他线程获取,当前线程会不断探测是否被释放,会第一时间获取,而其他锁比如信号量当它获取不到锁时,会把自己的线程阻塞休眠,等到其他线程释放这个锁时再唤醒这个锁 - -- 适用于轻量访问 - -### 引用计数表 -使用过Hash表来实现,hash查找提高了查找高效率,插入和获取是通过Hash 算法来是实现的,避免了for循环 - - -#### alloc 实现 -经过一系列调用,最终调用了C函数 calloc, 并没有引用计数+1 - -#### retain 实现 -底层经过了2次Hash表查找 - -![](https://ws4.sinaimg.cn/large/006tNc79ly1g1wq0qej61j31kg0dmtca.jpg) - -#### release 实现 -和 retain 实现相反 - -#### dealloc 实现原理 - -![](https://ws1.sinaimg.cn/large/006tNc79ly1g1wqa1mub5j31df0u0gqo.jpg) - - -![](https://ws4.sinaimg.cn/large/006tNc79ly1g1wqvau8guj316u0rwmz9.jpg) - - -#### objc_destructInstance() 实现 - -![](https://ws2.sinaimg.cn/large/006tNc79ly1g1wqvfecquj315t0u0n1e.jpg) - -#### clearDeallocating() 实现 - -![](https://ws2.sinaimg.cn/large/006tNc79ly1g1wqvivtonj31la0tqdke.jpg) - - -#### MRC -手动引用计数 - -#### ARC -- 编译器在对应位置自动插入retain 和 release 操作,并和runtime协作达到自动引用计数管理内存的效果 - -- ARC 中禁止手动调用 retain/release -- ARC 中新增 weeak、strong 等关键字 - -### 弱引用管理 -被声明为__weak 的对象指针经过编译后会进过调用以下方法,在最终的weak_register_no_lock() 方法中进行弱引用变量的添加。添加的位置是通过Hash算法查找的 - -![](https://ws1.sinaimg.cn/large/006tNc79ly1g1wr6cz3ctj31ac0hijtf.jpg) - -### weak 修饰的对象,释放时置为nil 如何实现的 -当一个对象被 dealloc 之后,在dealloc内部实现当中会调用弱引用清除的相关函数,在相关函数当中会根据当前对象指针查找弱引用表,把当前对象下对应的弱应用都取出,遍历弱应用指针并置为nil。 - - -### 自动释放池 - -是以栈为结点通过双向链表的形式组合而成,和线程一一对应 - -Autoreleasepool - -## 循环引用 - -### 自循环引用 - -一个对象中有拥有一个 强持有他的 obj,如果给 obj 赋值为原对象就会造成自循环引用 - -### 相互循环引用 - -- 代理 -- Block -- NSTimer -- 大环多循环 - -### 多循环引用 - -![](https://ws3.sinaimg.cn/large/006tNc79ly1g1ws3as0wdj31sa0rwae5.jpg) - -## 解决循环引用的方案 - -### __weak - -解决相互循环引用 - -### __block - -![](https://ws3.sinaimg.cn/large/006tNc79ly1g1wscj9qnij320o09igq4.jpg) - -### __unsafe_unretained - -一般不建议使用 - -![](https://ws3.sinaimg.cn/large/006tNc79ly1g1wsdln9jrj31iq0a041v.jpg) - diff --git "a/source/_posts/\347\254\254\345\215\201\344\270\200\347\253\240\350\256\276\350\256\241\346\250\241\345\274\217\351\235\242\350\257\225.md" "b/source/_posts/\347\254\254\345\215\201\344\270\200\347\253\240\350\256\276\350\256\241\346\250\241\345\274\217\351\235\242\350\257\225.md" deleted file mode 100644 index d7a7fba..0000000 --- "a/source/_posts/\347\254\254\345\215\201\344\270\200\347\253\240\350\256\276\350\256\241\346\250\241\345\274\217\351\235\242\350\257\225.md" +++ /dev/null @@ -1,60 +0,0 @@ -第十一章 设计模式 - -## 六大设计原则 - -单一职责原则:一个类只负责一件事 - -开闭原则: 对修改关闭,对扩展开放 - -接口隔离原则:使用多个专门的协议,而不是一个庞大臃肿的协议,iOS系统的UITableView的数据和代理协议分开 - -依赖倒置原则:抽象不应该依赖于具体实现,具体实现可以依赖于抽象 - -里氏替换原则:父类可以被子类无缝替换,且原有功能不受任何影响,iOS系统 的 KVO 机制遵守了这一原则,当调用 addObserver 方法时,因为系统在动态运行时为我们创建了一个子类,直观感受是仍然使用的是父类,实际上系统已经替换为创建的子类,所以KVO 机制 不但使用了管擦这模式,并且遵从了 里氏替换原则 - -迪米特法则: 一个对象应对其他对象有尽可能少的了解,平时所说的高内聚,低耦合 - - -## 常用设计模式 - -### 责任链 - -有三个业务分别为A/B/C,以前的调用顺序为 A -> B -> C ,现在顺序有调整, - -定义某一个类,有一个成员变量他的类型跟原有类类型一致,组成一个责任链,让处理具体业务的责任者去处理 - -![](https://i.loli.net/2019/04/18/5cb85694cd028.jpg) - -### 桥接模式 - -业务解耦的问题:同一个列表和多套数据显示,多套数据通过后端来控制是共存, - -![](https://i.loli.net/2019/04/18/5cb857a83803d.jpg) - -类构成 - -抽象类A ——> class B - -![](https://i.loli.net/2019/04/18/5cb85da6d46b6.jpg) - -### 适配器模式 - -一个现有类需要适应变化的问题, 年代比较久远的类或者对象 - -类构成 -![](https://i.loli.net/2019/04/18/5cb8600aa067b.jpg) - - -- 对象适配 -- 类适配 - -### 单例模式 - - - -### 命令模式 - -行为参数化 - -降低代码重合度 - diff --git "a/source/_posts/\347\254\254\345\215\201\344\270\211\347\253\240\347\256\227\346\263\225\351\235\242\350\257\225.md" "b/source/_posts/\347\254\254\345\215\201\344\270\211\347\253\240\347\256\227\346\263\225\351\235\242\350\257\225.md" deleted file mode 100644 index 576fc6b..0000000 --- "a/source/_posts/\347\254\254\345\215\201\344\270\211\347\253\240\347\256\227\346\263\225\351\235\242\350\257\225.md" +++ /dev/null @@ -1 +0,0 @@ -第十三章算法 \ No newline at end of file diff --git "a/source/_posts/\347\254\254\345\215\201\344\272\214\347\253\240\346\236\266\346\236\204\345\222\214\346\241\206\346\236\266\351\235\242\350\257\225.md" "b/source/_posts/\347\254\254\345\215\201\344\272\214\347\253\240\346\236\266\346\236\204\345\222\214\346\241\206\346\236\266\351\235\242\350\257\225.md" deleted file mode 100644 index 563a35c..0000000 --- "a/source/_posts/\347\254\254\345\215\201\344\272\214\347\253\240\346\236\266\346\236\204\345\222\214\346\241\206\346\236\266\351\235\242\350\257\225.md" +++ /dev/null @@ -1,128 +0,0 @@ -第十二章 架构和框架 - -目的 - -- 实现模块化 -- 分层 -- 解耦 - - - -## 图片缓存 - -### 图片缓存框架设计方案? - -![](https://i.loli.net/2019/04/18/5cb8873419c28.jpg) - -### 图片缓存时内存设计上需要考虑的问题 - -- 内存存储的size - - - 10kb的图片开辟的内存空间 50 张 - - 100kb图片的 20 张 - - 100kb 以上的 10 张 -通过队列的方式存储,先进先出的方式淘汰 - -- 淘汰策略 - - 以队列先进先出的方式淘汰 - - 已LRU 算法 (如 30 min 之内是否使用过) - -### 磁盘设计考虑的问题 - -- 存储方式 -- 大小限制 -- 淘汰策略(超过 7 天) - -### 网络部分的设计需要考虑的问题 - -- 图片请求最大并发量 -- 请求超时策略,一旦超时了,可以重试,如果重试2次失败,是否还下载 -- 请求优先级,下载或者缓存的图片是否紧急需要 - -### 图片通过什么方式进行读写,过程是怎样的? - -- 以图片的url 的单向哈希值作为key 存储 -- 读取过程 - -![](https://i.loli.net/2019/04/18/5cb887ab505f3.jpg) - - -### 图片解码 - -- 对于不同格式的图片,解码采用什么方式来做? - - - 应用策略模式对于不同图片格式进行解码 - -- 在那个阶段做图片解码处理? - - - 再磁盘读取后未解码或者网络请求返回后,解码后再放到内存中,可以减轻主线程的压力,解码在主线程中 - -### 线程处理 - - ![](https://i.loli.net/2019/04/18/5cb8903ae7004.jpg) - - -## 阅读时长统计 - -### 怎样设计一个时长统计框架? - -![](https://i.loli.net/2019/04/18/5cb8915823482.jpg) - -### 为何要有不同类型的记录器,处于什么考虑? - -基于不同分类场景提供的关于记录的封装、适配 - -### 记录的数据由于程序杀死或者断电,关机等丢失,你是怎样处理的? - -- 定时写磁盘 -- 限定内存缓存条数,超过该条数就写如磁盘 - -### 记录器上传 中延时上传的具体场景有哪些?上传时机如何把握? - -上传时机 - -- 立刻上传 -- 延时上传 -- 定时上传 - -延时上传场景 - -统计结果立即上传会重复调用接口导致资源浪费 - -- 前后台切换 -- 从无网到有网的变化时 -- 通过其他的接口捎带着上传 - - -## 复杂页面的架构 - -### MVVM 框架思想 - -![](https://i.loli.net/2019/04/18/5cb894992c97c.jpg) - -### RN 的数据流思想 - -![](https://i.loli.net/2019/04/18/5cb895a29c8e3.jpg) - -任何一个子节点是没有权力做自己的变化更新的,它必须把这个消息传递给根结点,根结点通过自顶向下的遍历查找需要更新的节点 - -### 系统UIView 更新机制的思想 - - -### AsyncDisplayKit 关于预排版的设计思想 - - -## 客户端整体架构 - -需要独立于APP 的通用层 -通用的业务层 -中间层 -业务层 - -![](https://i.loli.net/2019/04/18/5cb8962545202.jpg) - - -业务之间的解耦通信方式 - -- OpenURL -- 依赖注入方式, 在中间层通过代理 的方式实现业务A 和业务B之间的通信 \ No newline at end of file diff --git "a/source/_posts/\347\254\254\345\215\201\345\233\233\347\253\240\347\254\254\344\270\211\346\226\271\345\272\223\351\235\242\350\257\225.md" "b/source/_posts/\347\254\254\345\215\201\345\233\233\347\253\240\347\254\254\344\270\211\346\226\271\345\272\223\351\235\242\350\257\225.md" deleted file mode 100644 index dc7f31a..0000000 --- "a/source/_posts/\347\254\254\345\215\201\345\233\233\347\253\240\347\254\254\344\270\211\346\226\271\345\272\223\351\235\242\350\257\225.md" +++ /dev/null @@ -1,50 +0,0 @@ -第十四章 第三方库 - -### AFNetworking - -#### 框架图 - -#### 会话NSURSession - -![](https://i.loli.net/2019/04/19/5cb92763a70c9.jpg) - -#### 主要类关系图 - -![](https://i.loli.net/2019/04/19/5cb927e446880.jpg) - - -#### AFURLSessionManager - -- 创建和管理 NSURLSession, NSURLSessionTask -- 实现NSURLSessionDelegate 等协议的代理方法 -- 引入AFSecurityPolicy 保证请求安全,https 请求时 证书检验,公钥验证 -- 引入AFNetworkReachabilityManager 监控网络状态 - - -### SDWebImage - -架构图 - -![](https://i.loli.net/2019/04/19/5cb92fed65b5f.jpg) - -加载图片流程 -![](https://i.loli.net/2019/04/19/5cb93011b9d52.jpg) - - -### Reactive Cocoa - -![](https://i.loli.net/2019/04/19/5cb9303d51275.jpg) - -RACStream - -![](https://i.loli.net/2019/04/19/5cb930588621e.jpg) - -### AsyncDisplayKit - -主要解决的问题 - -![](https://i.loli.net/2019/04/19/5cb93096d7ecd.jpg) - -基本原理 - -![](https://i.loli.net/2019/04/19/5cb930c6d0bff.jpg) \ No newline at end of file diff --git "a/source/_posts/\347\254\254\345\215\201\347\253\240\347\275\221\347\273\234\347\233\270\345\205\263.md" "b/source/_posts/\347\254\254\345\215\201\347\253\240\347\275\221\347\273\234\347\233\270\345\205\263.md" deleted file mode 100644 index e2326d5..0000000 --- "a/source/_posts/\347\254\254\345\215\201\347\253\240\347\275\221\347\273\234\347\233\270\345\205\263.md" +++ /dev/null @@ -1,204 +0,0 @@ -第十章 网络请求相关 - -### HTTP协议 - -超文本传输协议 - - - -#### 请求/响应报文 - -请求报文: - -- 请求行 - 方法 URL 协议版本(1.1版本) CRLF(回车) - -- 首部字段区域 - 首部字段名(key) :值(value )CRLF - 首部字段名(key) :值(value )CRLF - -- 实体主体 - get 请求没有 - post 有实体主体 - - -响应报文: - -- 响应行 - 版本 状态码 状态码描述(短语) CRLF - -- 首部字段区 -- -- 响应实体主体 - -#### 连接建立流程 - -三次握手和四次挥手 - -三次握手 - -- 客户端 发送 syn 同步报文到Server, -- server 端收到syn报文后会,链接建立,server 端会返回一个 syn,ack 报文给客户端, -- 客户端收到 server 端的ask 报文后,会再次回应一个确认ack 的报文 - -数据请求 - -- 接下来在已经建立的tcp 通道上进行http 的请求报文 -- server 端回复给客户端一个http响应报文 - -四次挥手 - -- 客户端发送 fin 终止报文到server 端 -- server 端收到报文后会回回复一个ack 报文到客户端 -- 客户端到server 端的tcp的链接已经断开,但是 server 端到客户端的链接还会传输数据 -- server 端向客户端发送一个fin ack 报文 ,然后客户端回应给server 一个ack 报文,释放链接 - - -#### HTTP 的特点 - -- 无连接 - 非持久链接 需要多次断开/建立链接,就会经历 三次握手,四次挥手 - - HTTP 的持久链接: 多个http请求在同一个tcp 通道上, 一定时间内不会断开tcp通道,提高了网络请求的效率 - - 头部字段: - coonection : keep-alive 客户端是否采用持久链接 - time :20 多长时间有效 - max:10 最多发生多少个http请求和响应对 - -- 无状态 - 同一个用户多次发送http请求,server 端是不知道是同一个用户的,server 端通过 Cookie/Session 规避这个问题 - - cookie - cookie 主要是用来记录用户状态,区分用户,cookie由server端返回给客户端,cookie 状态保存在客户端 - - 保证cookie 的安全 - - cookie 加密处理 - - 只在https 上携带Cookie - - 设置Cookie为 httpOnly,防止跨站脚本攻击 - - - session 用来记录用户的状态,区分用户,状态存放在服务端 - - - -### HTTPS 与网络安全 - - -HTTPS 都使用了那些加密手段,为什么? - -- 建立链接过程使用非对称加密,因为耗时 -- 后续通信数据传输过程中使用对称性加密 - -对称性加密 - -### TCP: 传输控制协议 - -- 面向链接 - TCP 是全双工的所以数据传输之前需要三次握手,传输完毕需要四次挥手, -- 可靠传输 - - - 无差错 - - 按序到达 - - - -- 面向字节流 - - 发送方发送数据时不管一次性提交给TCP的缓冲是多大的数据,TCP本身会根据实际情况来划分数据包再发送给接受数据方 - -- 流量控制 - - 滑动窗口协议 - - -### UDP : 用户数据协议 - -- 无连接 - 发送数据时不需要建立链接,传输完毕也不需要释放链接 - -- 尽最大努力交付 - 是不保证可靠传输的, - -- 面向报文 - 既不合并,也不拆分 - -### 问题 - -#### GET 和post 请求的区别 - -- GET 获取资源的 - - - 安全的: 不因该引起Server 端的任何状态变化的 - - 幂等的:同一个请求方法执行多次和执行一次的效果完全相同 - - 可缓存的:请求是可以缓存的,代理服务器可以缓存 - - 请求参数以? 分割拼接到URL 后面 - -- post 请求处理资源 - - 不安全的: - - 非幂等的 - - 不可缓存的 - -GET 请求参数有长度限制,是2048 个字符,post 一般没有限制 - -#### HTTPS 链接建立流程 - -客户端会发送服务端一个支持的加密算法列表 -包括 TLS 的版本号 + 随机数C, -服务端再回给客户端一个证书 -包括 商定的加密算法 后续首先通过非对称加密进行对称性加密的密钥传输,http的网络就通过被非对称密钥保护的对称密钥访问和传输数据 - -### TCP 和 UDP的区别 - -tcp是面向链接的,并且支持可靠的传输,支持面向字节流, tcp 提供了流浪上的控制和拥塞的控制 -UDP 提供了简单的复用/分用及差错检测的传输层的功能, - - -#### 状态吗有哪些 - -- 1XX -- 2XX:成功 -- 3XX:重定向 -- 4XX:客户端请求错误 -- 5XX: 服务器端请求错误 - -#### 为什么需要三次握手,两次行不行? - -解决同步请求链接建立时超时的情况,如果只有两次握手, - -#### 持久链接怎么判断一个请求是否结束 - -- 请求/响应报文的头部字段 - -响应报文 content-length: 1024 头部字段是由server 端返回数据大小,客户端根据所接收的数据的字节数是否达到了头部字段 cotent-length 的大小判断 - -- 通过post请求时server 端返回数据有可能需要多次响应才能返回,此时需要根据chunked 字段名 是否为空来判断 - - -#### Charels 抓包原理是什么? - -中间人攻击 - -正常步骤是 客户端 ---- server - -抓包:发送请求和响应请求都可以通过中间人修改再返回给某一端 - -客户端 ---中间人---- server - - -#### DNS 解析 - -域名到IP地址的映射,DNS解析请求采用UDP数据报,且明文 - -#### NDS 解析存在的常见问题 - -- DNS 劫持问题 - 钓鱼网站 -- DNS 解析转发问题 - -#### DNS 劫持和HTTP 的关系时怎么样的 - -没有关系, -- DNS 解析发在HTTP 建立连接之前 -- DNS 解析请求使用UDP数据报, 端口是53,跟HTTP没有任何关系 - -解决DNS 劫持问题 -- httpDNS -- 长连接 \ No newline at end of file diff --git "a/source/_posts/\347\254\254\345\233\233\347\253\240OC\350\257\255\350\250\200\347\211\271\346\200\247\351\235\242\350\257\225.md" "b/source/_posts/\347\254\254\345\233\233\347\253\240OC\350\257\255\350\250\200\347\211\271\346\200\247\351\235\242\350\257\225.md" deleted file mode 100644 index c0ef7fc..0000000 --- "a/source/_posts/\347\254\254\345\233\233\347\253\240OC\350\257\255\350\250\200\347\211\271\346\200\247\351\235\242\350\257\225.md" +++ /dev/null @@ -1,152 +0,0 @@ -# OC 语言特性 - -## 分类 - -### 原理 - -由运行时来决议的,不同分类当中含有同名方法 谁最终生效取决于谁最终参与编译,最后参编译的同名分类方法会最终生效,假如分类方法中添加的方法和宿主类中的某一个方法名相同,分类中的方法会覆盖宿主类中的同名方法,覆盖是指在消息传递过程中优先查找数组靠前的元素,如果查找到了同名方法就直接调用,实际上宿主类的同名方法实现仍然是存在的,我们可可以通过一些手段调用到原有类的同名方法的实现 - -### 使用场景 - -- 申明私有方法,分类的 .m 文件中申明私有方法啊,对外不暴露 -- 对类中的代码进行抽取分解 -- 把Framework的私有方法公开化 - -### 特点 -- 运行时决议,运行时通过runtime 才把分类中的方法添加到了宿主类上 -- 可以为系统类添加方法 -- 有多个分类中存在同名方法时,最后编译的方法会最先生效 -- 分类中有和宿主类同名方法时,底层中通过内存copy将分类方法“覆盖”宿主类方法 -- 名字相同的分类编译会报错 - -### 分类中可以添加的内容 - -- 实例方法 -- 类方法 -- 协议 -- 属性,实际上只是声明了 getter / setter 方法,并没有添加成员变量 - -### 关联对象技术 - -关联对象由 AssocicationsManager 管理并在 AssociationsHashMap 上存储 -所有对象的关联内容都在同一个全局容器中 - -- 给分类添加成员变量,通过关联对象的方式 - - -## 扩展 - -### 使用场景 -- 声明私有属性 -- 声明私有方法 -- 声明私有成员变量 - -### 特点 -- 编译时决议 -- 只以声明的形式存在,多数情况下寄生在宿主类的.m中 -- 不能为系统添加扩展 - -## 代理 -- 代理设计模式,传递方式是一对一 -- 使用weak 避免循环引用 - -## 通知 - -使用观察者模式来实现的用于跨层传递消息的机制 - - -## KVO - -KVO 是系统对观察者模式的又一实现,使用isa 混写技术(isa - swizzling)来动态运行时为某一个类添加一个子类重写了它的setter 方法,同时将原有类的isa指针指向了新创建的类上 - - -- KVO 是OC对观察者模式的又一实现 -- apple 使用isa 混写技术(isa - swizzling)来实现KVO - - 给我A类注册一个观察者时本质上是调用系统的 Observer for keyPath 方法,系统会创建一个NSKVONotifiying_A 的类 -- 使用 setter 方法设置值KVO才能生效 -- 使用setValue:forKey: 改变值KVO才能生效 -- 使用下划线的成员变量直接修改值需要手动添加KVO才能生效(增加 willChangeValueForKey 和 didChangeValueForKey 两个方法) - - - -## KVC -apple提供的键值编码技术 - -- (nullable id)valueForKey:(NSString *)key; -- (void)setValue:(nullable id)value forKey:(NSString *)key; - - 以上两个方法中的key是没有任何限制的,只要知道对应的成员变量名称,就可以对私有的成员变量设置和操作,这违背了面向对象的编程思想 - -**(void)setValue:(nullable id)value forKey:(NSString *)key; 方法调用流程** - -- 先判断是否有跟key 相关的setter 方法,如果有就直接调用,结束调用 -- 如果没有再判断是否存在实例变量,如果存在直接给赋值,结束调用 -- 如果说实例变量不存在,会去调用 `- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;` 抛出异常 - - -## 属性关键字 - -### 读写权限 - -- readonly - -- readwrite 系统默认 - -### 原子性 -- atomic (系统默认)保证赋值和获取(对成员属性的直接获取和赋值)时线程安全,并不代表操作和访问,如果atomic修饰的一个数组,只能保证独具改数组的读取和赋值时线程安全,并不能保证对数据操作(增加/删除数组元素)时是安全的 -- nonatomic - -### 引用技术 - -- assign - - 修饰基本数据类型, - - 修饰对象时不改变其引用计数, - - 会产生悬垂指针 -- weak - - 不改变被修饰对象的引用计数, - - 所指对象被释放之后指针自动置为nil - -**两者区别:** -- weak可以修饰对象,而assign 既可以修饰对象也可以修饰基本数据类型 -- assign 修饰的对象,对象释放后指针仍然指向原对象的内存地址,而weak 修饰的对象,释放后会自动置为nil - -**问题1** - -weak 修饰的对象为什么释放后会自动置为nil - -**问题2** - -`@property (copy) NSMutableArray *array;` 有什么问题 - -- 被copy修饰后,如果赋值过来的是NSMutableArray,copy之后就是NSArray,如果赋值过来的是NSAarray,copy 之后仍然是NSArray,有可能会调用array的添加元素方法会导致crash - -### copy 关键词 - - ![](https://ws2.sinaimg.cn/large/006tNc79ly1g1uh6lwwe2j320s0lm7bm.jpg) - - - 浅拷贝:内存地址的复制,让目标对象指针和源对象指向同一块内存空间, - - 会增加对象的引用计数 - - 并没有一个新的内存分配 - - - - 深拷贝:目标对象指针和源对象指针分别指向两块内容相同的内存空间 - - 不会增加源对象的引用计数 - - 有新对象的内存分配 - -## MRC 重写 retain修饰的变量的setter 方法 - -``` - -- (void)setObj:(id)obj { - if (_obj != obj) { - [_obj release]; - } - _obj = [obj retain]; - -} -``` - -判断是为了防止异常处理,如果不做 if 判断,当传入的 obj 对象正好是原来的_obj 对象,对原对象尽行releas 操作,实际上也会对传入的对象进行releas 操作进行释放,此时如果再通过obj指针访问废弃的对象时就会导致carsh - - - diff --git "a/source/_posts/\347\275\221\347\273\234\344\270\203\345\261\202\345\215\217\350\256\256.md" "b/source/_posts/\347\275\221\347\273\234\344\270\203\345\261\202\345\215\217\350\256\256.md" deleted file mode 100644 index 0159144..0000000 --- "a/source/_posts/\347\275\221\347\273\234\344\270\203\345\261\202\345\215\217\350\256\256.md" +++ /dev/null @@ -1,58 +0,0 @@ ---- -title: iOS 即使通讯网络知识篇 -date: 2018-01-10 23:26:18 -tags: ---- - -## 网络七层协议 - -读《图解TCP/IP》第5版以及看过的关于网络文章之后自己的理解 - -OSI (Open System Interconnec) 译为 开发式系统互联 七层模型从下到上一次是:物理层 -> 数据链路层 —> 网络层 -> 传输层 -> 会话层 -> 表示层 -》 应用层 - -数据每次经过一层之后就会在数据头部增加一个首部信息 -##### 物理层:网卡,网线,集线器,中继器,调制解调器 - -- 定义设备标准,如网线,光纤接口类型,将0和1转化为电信号的强和弱,达到传输**比特流**目的,这层数据叫比特 - -##### 数据链路层:网桥,交换机 - -- 传输的地址帧,并且有检测错误功能,保证数据可靠传输, 这层数据叫帧 - -##### 网络层:路由器 - -- 两个计算机通信时可能会经过多个数据链路或者通信子网,网络层将数据链路层的帧组成数据包,包中有封装好的包头,其中含有逻辑地址信息(源站点和目的站点),此层为数据包选择合适的路由和交换结点,IP协议产生,这层数据叫着数据包 - -##### 传输层: - -- 解决数据如何在网络中传输,这个层负责获取全部信息,因此,它必须跟踪数据单元碎片、乱序到达的 数据包和其它在传输过程中可能发生的危险。第4层为上层提供端到端(最终用户到最终用户)的透明的、可靠的数据传输服务。所为透明的传输是指在通信过程中 传输层对上层屏蔽了通信传输系统的具体细节。传输层协议的代表包括:TCP、UDP、SPX等 - -##### 会话层: -- 这一层也可以称为会晤层或对话层,在会话层及以上的高层次中,数据传送的单位不再另外命名,而是统称为报文。会话层不参与具体的传输,它提供包括访问验证和会话管理在内的建立和维护应用之间通信的机制。如服务器验证用户登录便是由会话层完成的 - -##### 表示层: -- 这一层主要解决拥护信息的语法表示问题。它将欲交换的数据从适合于某一用户的抽象语法,转换为适合于OSI系统内部使用的传送语法。即提供格式化的表示和转换数据服务。数据的压缩和解压缩, 加密和解密等工作都由表示层负责。 - -##### 应用层:HTTP 协议 -- 如何包装数据 - - -**通常从上到下会分成四层** - -TCP/IP 通常是指TCP/IP协议族,是一组不同协议组合在一起构成的协议族 - -- 应用层:FTP 应用层协议 -- 传输层:TCP,UDP 传输层协议,TCP 提供了一个可靠的服务 -- 网络层:IP,ICMP 网络层协议,传输时不可靠的 -- 链路层:以太网协议 - -## Socket -**为了解决不同计算机上进程间通信问题** -服务器在指定的端口上监听,然后生成一对儿新的socket 进行通讯,一个线程对应一个socket - -英文翻译为“插座”通常称为"套接字", 本质是对于TCP/IP的封装,有客户端 socket 和 服务端socket - -- 流式 Socket (STREAM): 是一种面向连接的socket,针对于面向连接的TCP服务应用,因为安全所以效率低 -- 数据报式Socket (DATAGRAM): 是一种无连接的SOcket,正对于无连接的UDP服务应用,无序,不安全,需要在接收端分析重排或者要求重发,所以效率高 - - diff --git "a/source/_posts/\351\235\242\350\257\225\344\270\255\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223.md" "b/source/_posts/\351\235\242\350\257\225\344\270\255\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223.md" deleted file mode 100644 index 3b38f10..0000000 --- "a/source/_posts/\351\235\242\350\257\225\344\270\255\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223.md" +++ /dev/null @@ -1,102 +0,0 @@ ---- -title: 面试中常见问题总结 -date: 2018-01-17 22:54:12 -tags: ---- - - - -### OC 的动态性 - -OC是基于C语言的,C语言在编译阶段就会确定了调用具体的那个函数,OC它的底层是通过runtime 运行时机制来调用函数,在OC中成为消息发送,具体来说就是在编译阶段OC可以调用任何函数,即时这个函数是没有实现的,只有在运行的时候才会根据isa指针和已经注册的方法列表中找到 IMP(函数指针)真正要调用的那个函数,我们平时写的OC代码,在运行的时候也都是转换成了runtime 的方式进行的,不管是什么方法本质都是发送一个消息 - - -### runtime - -- 消息机制原理: 根据方法编号列表 SEL 去映射表中查找对应方法的实现 - -``` -Person *p = [Person allo] init]; - -// 底层的写法 -// alloc -Person *p = objc_msgSend(objc_getClass("Person"), sel_registerName("alloc"),); - -// init -p = objc_msgSend(p, sel-registerName("init")); - -// 调用对象方法本质就是:让对象发消息 -objc_msgSend(p, @selector(eat)); - -// 调用类方法本质: 让类发消息 -objc_msgSend([Person class], @selector(run:), 20); - -// alloc init 也可以理解为: - -id objc = objc_msgSend([NSObject class], @selector(alloc)); - -objc = objc_msgSend(objc, @selector(alloc)); - -``` - - -- runtime 消息机制调用流程 - - 每个对象内部都有一个isa指针,指向他自己真实的类来确定调用的是那个类的方法; 每个方法sel都有一个IMP指针(指向现实函数的指针),不管是对象方法还是类方法,都会有一个方法列表 - - 1. 发消息时根据对象内部的isa 指针去该类所对应的方法类表中查找方法,如果没有去父类中查找 - 2. 注册每个方法编号,为了以后快速查找调用 - 3. 根据方法编号查找对应方法 - 4. 通过isa指针和IMP指针确定最终调用的函数 - -- runtime 常见使用 - - 1. 运行是动态添加方法 - 2. 给分类添加属性,(关联属性) - 3. 字典转模型中从先遍历模型属性再从字典中查找相关值实现模型属性赋值 - 4. NSCoding 自动归档解档时对象属性过多时 `encodeWithCoder:` 和 `initWithCoder ` 两个方法实现 - - -### Runloop - -- 概念 - - NSRunloop 是封装 CFRunloopRef 一套纯`C`的函数, - -- 作用 - - 1. 保持程序持续运行 - 2. 处理App中各种事件,具体的有:toches事件, NSTimer 事件, Selector 事件, PerformSelector 事件,source 源等等 - 3. 有事件触发时就运行,没有就休眠 - 4. 渲染app 中唯一一个主线程上所有UI界面的更新 - -- 使用和要点 - - 1. Runloop 在程序入口 main 函数中就会开启,并且开启的是主线程不会销毁除非退出程序,保证了程序一直运行并能响应事件和交互 - 2. 每一条线程都是唯一对应一个Runloop - 3. 主线程不需要创建,因为在程序启动的时候就已经创建了,而子线程的需要手动创建 直接获取currentRunloop 方式(本质是使用了懒加载) - 4. Runloop 对象是利用字典进行存储,因为一一对应,所有key 和value 就是线程和 他所对应的runloop - - 5. Runloop 中 NSDefaultRunLoopMode 一般情况下主线程运行的 Mode - 6. UIInitializationRunLoopMode 初始化mode,通常不用 - 7. UITrackingRunLoopMode 界面跟踪, 用于scrollView 追踪触摸滑动,保证界面不受其他mode影响 - 8. GSEventReceiveRunLoopMode 用于接收系统事件内部 mode, 通常不用 - - 9. NSRunLoopCommonModes, 并不是某种具体的 mode ,开发中使用最多,他是一个组合 mode,组合了 NSDefaultRunLoopMode和 NSRunLoopCommonModes - -- Runloop 相关类 - - 1. Source 事件源/输入源可以理解为 激发源 - 2. Timer 基于事件触发,CADisplayLink,NSTimer 都是加到了 Runloop,受 Mode 的影响,GCD 定时器不受Runloop 影响 - 3. Observer 相当于runloop 循环中的监听器,时刻监听当前Runloop的运行状态 - 4. 休眠没事情做的时候可以停下来,处于休眠状态 - -- 使用场景 - - 1. 创建NSTimer - 2. PersforSelector方法 - 3. 常驻线程:(创建后处于等待状态,有事件时响应事件) - - 实现后台收集用户停留时间或者是某个按钮的点击次数,如果使用主线程会不方便,使用runloop解决 - 4. AutoreleasePool 自动释放池 - 5. UI更新 - diff --git a/tags/AES-Base64-MD5/index.html b/tags/AES-Base64-MD5/index.html new file mode 100644 index 0000000..9ee46dc --- /dev/null +++ b/tags/AES-Base64-MD5/index.html @@ -0,0 +1,200 @@ + + + + + + + + Tag: AES, Base64, MD5 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/tags/Alamofire/index.html b/tags/Alamofire/index.html new file mode 100644 index 0000000..c92b00e --- /dev/null +++ b/tags/Alamofire/index.html @@ -0,0 +1,200 @@ + + + + + + + + Tag: Alamofire | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/tags/AlertView/index.html b/tags/AlertView/index.html new file mode 100644 index 0000000..2f2aba3 --- /dev/null +++ b/tags/AlertView/index.html @@ -0,0 +1,200 @@ + + + + + + + + Tag: AlertView | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/tags/Flutter-GridView/index.html b/tags/Flutter-GridView/index.html new file mode 100644 index 0000000..9986914 --- /dev/null +++ b/tags/Flutter-GridView/index.html @@ -0,0 +1,200 @@ + + + + + + + + Tag: Flutter GridView | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/tags/Flutter/index.html b/tags/Flutter/index.html new file mode 100644 index 0000000..a6c973c --- /dev/null +++ b/tags/Flutter/index.html @@ -0,0 +1,200 @@ + + + + + + + + Tag: Flutter | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/tags/GCD/index.html b/tags/GCD/index.html new file mode 100644 index 0000000..6201484 --- /dev/null +++ b/tags/GCD/index.html @@ -0,0 +1,200 @@ + + + + + + + + Tag: GCD | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/tags/Hexo/index.html b/tags/Hexo/index.html new file mode 100644 index 0000000..c5dc101 --- /dev/null +++ b/tags/Hexo/index.html @@ -0,0 +1,200 @@ + + + + + + + + Tag: Hexo | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/tags/OC-JS/index.html b/tags/OC-JS/index.html new file mode 100644 index 0000000..23696f5 --- /dev/null +++ b/tags/OC-JS/index.html @@ -0,0 +1,200 @@ + + + + + + + + Tag: OC, JS | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/tags/Quartz-2D/index.html b/tags/Quartz-2D/index.html new file mode 100644 index 0000000..8cff448 --- /dev/null +++ b/tags/Quartz-2D/index.html @@ -0,0 +1,200 @@ + + + + + + + + Tag: Quartz 2D | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/tags/Quartz2D/index.html b/tags/Quartz2D/index.html new file mode 100644 index 0000000..c192b19 --- /dev/null +++ b/tags/Quartz2D/index.html @@ -0,0 +1,219 @@ + + + + + + + + Tag: Quartz2D | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/tags/RunTime/index.html b/tags/RunTime/index.html new file mode 100644 index 0000000..9fcb79a --- /dev/null +++ b/tags/RunTime/index.html @@ -0,0 +1,200 @@ + + + + + + + + Tag: RunTime | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/tags/SDWebImage/index.html b/tags/SDWebImage/index.html new file mode 100644 index 0000000..3ac60c6 --- /dev/null +++ b/tags/SDWebImage/index.html @@ -0,0 +1,200 @@ + + + + + + + + Tag: SDWebImage | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/tags/SQL/index.html b/tags/SQL/index.html new file mode 100644 index 0000000..e3575eb --- /dev/null +++ b/tags/SQL/index.html @@ -0,0 +1,200 @@ + + + + + + + + Tag: SQL | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/tags/Swift/index.html b/tags/Swift/index.html new file mode 100644 index 0000000..0063989 --- /dev/null +++ b/tags/Swift/index.html @@ -0,0 +1,219 @@ + + + + + + + + Tag: swift | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/tags/TableView/index.html b/tags/TableView/index.html new file mode 100644 index 0000000..08e25b7 --- /dev/null +++ b/tags/TableView/index.html @@ -0,0 +1,200 @@ + + + + + + + + Tag: TableView | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/tags/WKWebView/index.html b/tags/WKWebView/index.html new file mode 100644 index 0000000..7d04844 --- /dev/null +++ b/tags/WKWebView/index.html @@ -0,0 +1,286 @@ + + + + + + + + Tag: WKWebView | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/tags/git/index.html b/tags/git/index.html new file mode 100644 index 0000000..fe82bd3 --- /dev/null +++ b/tags/git/index.html @@ -0,0 +1,200 @@ + + + + + + + + Tag: git | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/tags/\345\274\202\346\255\245/index.html" "b/tags/\345\274\202\346\255\245/index.html" new file mode 100644 index 0000000..e2d7f99 --- /dev/null +++ "b/tags/\345\274\202\346\255\245/index.html" @@ -0,0 +1,200 @@ + + + + + + + + Tag: 异步 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git "a/tags/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" "b/tags/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" new file mode 100644 index 0000000..25c6afc --- /dev/null +++ "b/tags/\346\234\254\345\234\260\347\274\223\345\255\230/index.html" @@ -0,0 +1,200 @@ + + + + + + + + Tag: 本地缓存 | aTreey's Blog + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/themes/landscape/.gitignore b/themes/landscape/.gitignore deleted file mode 100644 index 6e3a08a..0000000 --- a/themes/landscape/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -.DS_Store -node_modules -tmp \ No newline at end of file diff --git a/themes/landscape/Gruntfile.js b/themes/landscape/Gruntfile.js deleted file mode 100644 index 59fd5df..0000000 --- a/themes/landscape/Gruntfile.js +++ /dev/null @@ -1,46 +0,0 @@ -module.exports = function(grunt){ - grunt.initConfig({ - gitclone: { - fontawesome: { - options: { - repository: 'https://github.com/FortAwesome/Font-Awesome.git', - directory: 'tmp/fontawesome' - }, - }, - fancybox: { - options: { - repository: 'https://github.com/fancyapps/fancyBox.git', - directory: 'tmp/fancybox' - } - } - }, - copy: { - fontawesome: { - expand: true, - cwd: 'tmp/fontawesome/fonts/', - src: ['**'], - dest: 'source/css/fonts/' - }, - fancybox: { - expand: true, - cwd: 'tmp/fancybox/source/', - src: ['**'], - dest: 'source/fancybox/' - } - }, - _clean: { - tmp: ['tmp'], - fontawesome: ['source/css/fonts'], - fancybox: ['source/fancybox'] - } - }); - - require('load-grunt-tasks')(grunt); - - grunt.renameTask('clean', '_clean'); - - grunt.registerTask('fontawesome', ['gitclone:fontawesome', 'copy:fontawesome', '_clean:tmp']); - grunt.registerTask('fancybox', ['gitclone:fancybox', 'copy:fancybox', '_clean:tmp']); - grunt.registerTask('default', ['gitclone', 'copy', '_clean:tmp']); - grunt.registerTask('clean', ['_clean']); -}; \ No newline at end of file diff --git a/themes/landscape/LICENSE b/themes/landscape/LICENSE deleted file mode 100644 index 9ce4d32..0000000 --- a/themes/landscape/LICENSE +++ /dev/null @@ -1,7 +0,0 @@ -Copyright (c) 2013 Tommy Chen - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/themes/landscape/README.md b/themes/landscape/README.md deleted file mode 100644 index 90ecccd..0000000 --- a/themes/landscape/README.md +++ /dev/null @@ -1,112 +0,0 @@ -# Landscape - -A brand new default theme for [Hexo]. - -- [Preview](http://hexo.io/hexo-theme-landscape/) - -## Installation - -### Install - -``` bash -$ git clone https://github.com/hexojs/hexo-theme-landscape.git themes/landscape -``` - -**Landscape requires Hexo 2.4 and above.** If you would like to enable the RSS, the [hexo-generate-feed] plugin is also required. - -### Enable - -Modify `theme` setting in `_config.yml` to `landscape`. - -### Update - -``` bash -cd themes/landscape -git pull -``` - -## Configuration - -``` yml -# Header -menu: - Home: / - Archives: /archives -rss: /atom.xml - -# Content -excerpt_link: Read More -fancybox: true - -# Sidebar -sidebar: right -widgets: -- category -- tag -- tagcloud -- archives -- recent_posts - -# Miscellaneous -google_analytics: -favicon: /favicon.png -twitter: -google_plus: -``` - -- **menu** - Navigation menu -- **rss** - RSS link -- **excerpt_link** - "Read More" link at the bottom of excerpted articles. `false` to hide the link. -- **fancybox** - Enable [Fancybox] -- **sidebar** - Sidebar style. You can choose `left`, `right`, `bottom` or `false`. -- **widgets** - Widgets displaying in sidebar -- **google_analytics** - Google Analytics ID -- **favicon** - Favicon path -- **twitter** - Twiiter ID -- **google_plus** - Google+ ID - -## Features - -### Fancybox - -Landscape uses [Fancybox] to showcase your photos. You can use Markdown syntax or fancybox tag plugin to add your photos. - -``` -![img caption](img url) - -{% fancybox img_url [img_thumbnail] [img_caption] %} -``` - -### Sidebar - -You can put your sidebar in left side, right side or bottom of your site by editing `sidebar` setting. - -Landscape provides 5 built-in widgets: - -- category -- tag -- tagcloud -- archives -- recent_posts - -All of them are enabled by default. You can edit them in `widget` setting. - -## Development - -### Requirements - -- [Grunt] 0.4+ -- Hexo 2.4+ - -### Grunt tasks - -- **default** - Download [Fancybox] and [Font Awesome]. -- **fontawesome** - Only download [Font Awesome]. -- **fancybox** - Only download [Fancybox]. -- **clean** - Clean temporarily files and downloaded files. - -[Hexo]: https://hexo.io/ -[Fancybox]: http://fancyapps.com/fancybox/ -[Font Awesome]: http://fontawesome.io/ -[Grunt]: http://gruntjs.com/ -[hexo-generate-feed]: https://github.com/hexojs/hexo-generator-feed diff --git a/themes/landscape/_config.yml b/themes/landscape/_config.yml deleted file mode 100644 index ca22374..0000000 --- a/themes/landscape/_config.yml +++ /dev/null @@ -1,37 +0,0 @@ -# Header -menu: - Home: / - Archives: /archives -rss: /atom.xml - -# Content -excerpt_link: Read More -fancybox: true - -# Sidebar -sidebar: right -widgets: -- category -- tag -- tagcloud -- archive -- recent_posts - -# display widgets at the bottom of index pages (pagination == 2) -index_widgets: -# - category -# - tagcloud -# - archive - -# widget behavior -archive_type: 'monthly' -show_count: false - -# Miscellaneous -google_analytics: -gauges_analytics: -favicon: /favicon.png -twitter: -google_plus: -fb_admins: -fb_app_id: diff --git a/themes/landscape/languages/de.yml b/themes/landscape/languages/de.yml deleted file mode 100644 index 630055f..0000000 --- a/themes/landscape/languages/de.yml +++ /dev/null @@ -1,19 +0,0 @@ -categories: Kategorien -search: Suche -tags: Tags -tagcloud: Tag Cloud -tweets: Tweets -prev: zurück -next: weiter -comment: Kommentare -archive_a: Archiv -archive_b: "Archive: %s" -page: Seite %d -recent_posts: letzter Beitrag -newer: Neuer -older: Älter -share: Teilen -powered_by: Powered by -rss_feed: RSS Feed -category: Kategorie -tag: Tag diff --git a/themes/landscape/languages/default.yml b/themes/landscape/languages/default.yml deleted file mode 100644 index 3ef7e92..0000000 --- a/themes/landscape/languages/default.yml +++ /dev/null @@ -1,19 +0,0 @@ -categories: Categories -search: Search -tags: Tags -tagcloud: Tag Cloud -tweets: Tweets -prev: Prev -next: Next -comment: Comments -archive_a: Archives -archive_b: "Archives: %s" -page: Page %d -recent_posts: Recent Posts -newer: Newer -older: Older -share: Share -powered_by: Powered by -rss_feed: RSS Feed -category: Category -tag: Tag \ No newline at end of file diff --git a/themes/landscape/languages/es.yml b/themes/landscape/languages/es.yml deleted file mode 100644 index d862e87..0000000 --- a/themes/landscape/languages/es.yml +++ /dev/null @@ -1,19 +0,0 @@ -categories: Categorías -search: Buscar -tags: Tags -tagcloud: Nube de Tags -tweets: Tweets -prev: Previo -next: Siguiente -comment: Comentarios -archive_a: Archivos -archive_b: "Archivos: %s" -page: Página %d -recent_posts: Posts recientes -newer: Nuevo -older: Viejo -share: Compartir -powered_by: Construido por -rss_feed: RSS -category: Categoría -tag: Tag \ No newline at end of file diff --git a/themes/landscape/languages/fr.yml b/themes/landscape/languages/fr.yml deleted file mode 100644 index c84f51b..0000000 --- a/themes/landscape/languages/fr.yml +++ /dev/null @@ -1,19 +0,0 @@ -categories: Catégories -search: Rechercher -tags: Mot-clés -tagcloud: Nuage de mot-clés -tweets: Tweets -prev: Précédent -next: Suivant -comment: Commentaires -archive_a: Archives -archive_b: "Archives: %s" -page: Page %d -recent_posts: Articles récents -newer: Récent -older: Ancien -share: Partager -powered_by: Propulsé par -rss_feed: Flux RSS -category: Catégorie -tag: Mot-clé diff --git a/themes/landscape/languages/ja.yml b/themes/landscape/languages/ja.yml deleted file mode 100644 index af0f7fe..0000000 --- a/themes/landscape/languages/ja.yml +++ /dev/null @@ -1,19 +0,0 @@ -categories: カテゴリ -search: 検索 -tags: タグ -tagcloud: タグクラウド -tweets: ツイート -prev: 戻る -next: 次へ -comment: コメント -archive_a: アーカイブ -archive_b: "アーカイブ: %s" -page: ページ %d -recent_posts: 最近の投稿 -newer: 次の記事 -older: 前の記事 -share: 共有 -powered_by: Powered by -rss_feed: RSSフィード -category: カテゴリ -tag: タグ diff --git a/themes/landscape/languages/ko.yml b/themes/landscape/languages/ko.yml deleted file mode 100644 index 1d27b43..0000000 --- a/themes/landscape/languages/ko.yml +++ /dev/null @@ -1,19 +0,0 @@ -categories: 카테고리 -search: 검색 -tags: 태그 -tagcloud: 태그 클라우드 -tweets: 트윗 -prev: 이전 -next: 다음 -comment: 댓글 -archive_a: 아카이브 -archive_b: "아카이브: %s" -page: 페이지 %d -recent_posts: 최근 포스트 -newer: 최신 -older: 이전 -share: 공유 -powered_by: Powered by -rss_feed: RSS Feed -category: 카테고리 -tag: 태그 diff --git a/themes/landscape/languages/nl.yml b/themes/landscape/languages/nl.yml deleted file mode 100644 index 568d33e..0000000 --- a/themes/landscape/languages/nl.yml +++ /dev/null @@ -1,20 +0,0 @@ - -categories: Categorieën -search: Zoeken -tags: Labels -tagcloud: Tag Cloud -tweets: Tweets -prev: Vorige -next: Volgende -comment: Commentaren -archive_a: Archieven -archive_b: "Archieven: %s" -page: Pagina %d -recent_posts: Recente berichten -newer: Nieuwer -older: Ouder -share: Delen -powered_by: Powered by -rss_feed: RSS Feed -category: Categorie -tag: Label diff --git a/themes/landscape/languages/no.yml b/themes/landscape/languages/no.yml deleted file mode 100644 index b997691..0000000 --- a/themes/landscape/languages/no.yml +++ /dev/null @@ -1,19 +0,0 @@ -categories: Kategorier -search: Søk -tags: Tags -tagcloud: Tag Cloud -tweets: Tweets -prev: Forrige -next: Neste -comment: Kommentarer -archive_a: Arkiv -archive_b: "Arkiv: %s" -page: Side %d -recent_posts: Siste innlegg -newer: Newer -older: Older -share: Share -powered_by: Powered by -rss_feed: RSS Feed -category: Category -tag: Tag \ No newline at end of file diff --git a/themes/landscape/languages/pt.yml b/themes/landscape/languages/pt.yml deleted file mode 100644 index 3d74af3..0000000 --- a/themes/landscape/languages/pt.yml +++ /dev/null @@ -1,19 +0,0 @@ -categories: Categorias -search: Buscar -tags: Tags -tagcloud: Nuvem de Tags -tweets: Tweets -prev: Anterior -next: Próximo -comment: Comentários -archive_a: Arquivos -archive_b: "Arquivos: %s" -page: Página %d -recent_posts: Postagens Recentes -newer: Mais Recente -older: Mais Antigo -share: Compartilhar -powered_by: Desenvolvido por -rss_feed: Feed RSS -category: Categoria -tag: Tag diff --git a/themes/landscape/languages/ru.yml b/themes/landscape/languages/ru.yml deleted file mode 100644 index 625a83c..0000000 --- a/themes/landscape/languages/ru.yml +++ /dev/null @@ -1,19 +0,0 @@ -categories: Категории -search: Поиск -tags: Метки -tagcloud: Облако меток -tweets: Твиты -prev: Назад -next: Вперед -comment: Комментарии -archive_a: Архив -archive_b: "Архив: %s" -page: Страница %d -recent_posts: Недавние записи -newer: Следующий -older: Предыдущий -share: Поделиться -powered_by: Создано с помощью -rss_feed: RSS-каналы -category: Категория -tag: Метка \ No newline at end of file diff --git a/themes/landscape/languages/zh-CN.yml b/themes/landscape/languages/zh-CN.yml deleted file mode 100644 index 51e1321..0000000 --- a/themes/landscape/languages/zh-CN.yml +++ /dev/null @@ -1,19 +0,0 @@ -categories: 分类 -search: 搜索 -tags: 标签 -tagcloud: 标签云 -tweets: 推文 -prev: 上一页 -next: 下一页 -comment: 留言 -archive_a: 归档 -archive_b: 归档:%s -page: 第 %d 页 -recent_posts: 最新文章 -newer: Newer -older: Older -share: Share -powered_by: Powered by -rss_feed: RSS Feed -category: Category -tag: Tag \ No newline at end of file diff --git a/themes/landscape/languages/zh-TW.yml b/themes/landscape/languages/zh-TW.yml deleted file mode 100644 index 76d2916..0000000 --- a/themes/landscape/languages/zh-TW.yml +++ /dev/null @@ -1,19 +0,0 @@ -categories: 分類 -search: 搜尋 -tags: 標籤 -tagcloud: 標籤雲 -tweets: 推文 -prev: 上一頁 -next: 下一頁 -comment: 留言 -archive_a: 彙整 -archive_b: 彙整:%s -page: 第 %d 頁 -recent_posts: 最新文章 -newer: Newer -older: Older -share: Share -powered_by: Powered by -rss_feed: RSS Feed -category: Category -tag: Tag \ No newline at end of file diff --git a/themes/landscape/layout/_partial/after-footer.ejs b/themes/landscape/layout/_partial/after-footer.ejs deleted file mode 100644 index ff2d509..0000000 --- a/themes/landscape/layout/_partial/after-footer.ejs +++ /dev/null @@ -1,25 +0,0 @@ -<% if (config.disqus_shortname){ %> - -<% } %> - - - -<% if (theme.fancybox){ %> - <%- css('fancybox/jquery.fancybox') %> - <%- js('fancybox/jquery.fancybox.pack') %> -<% } %> - -<%- js('js/script') %> -<%- partial('gauges-analytics') %> diff --git a/themes/landscape/layout/_partial/archive-post.ejs b/themes/landscape/layout/_partial/archive-post.ejs deleted file mode 100644 index 36f2cc3..0000000 --- a/themes/landscape/layout/_partial/archive-post.ejs +++ /dev/null @@ -1,8 +0,0 @@ -
-
-
- <%- partial('post/date', {class_name: 'archive-article-date', date_format: 'MMM D'}) %> - <%- partial('post/title', {class_name: 'archive-article-title'}) %> -
-
-
\ No newline at end of file diff --git a/themes/landscape/layout/_partial/archive.ejs b/themes/landscape/layout/_partial/archive.ejs deleted file mode 100644 index 9da934a..0000000 --- a/themes/landscape/layout/_partial/archive.ejs +++ /dev/null @@ -1,34 +0,0 @@ -<% if (pagination == 2){ %> - <% page.posts.each(function(post){ %> - <%- partial('article', {post: post, index: true}) %> - <% }) %> -<% } else { %> - <% var last; %> - <% page.posts.each(function(post, i){ %> - <% var year = post.date.year(); %> - <% if (last != year){ %> - <% if (last != null){ %> - - <% } %> - <% last = year; %> -
- -
- <% } %> - <%- partial('archive-post', {post: post, even: i % 2 == 0}) %> - <% }) %> - <% if (page.posts.length){ %> -
- <% } %> -<% } %> -<% if (page.total > 1){ %> - -<% } %> diff --git a/themes/landscape/layout/_partial/article.ejs b/themes/landscape/layout/_partial/article.ejs deleted file mode 100644 index 0f951a9..0000000 --- a/themes/landscape/layout/_partial/article.ejs +++ /dev/null @@ -1,44 +0,0 @@ -
- -
- <%- partial('post/gallery') %> - <% if (post.link || post.title){ %> -
- <%- partial('post/title', {class_name: 'article-title'}) %> -
- <% } %> -
- <% if (post.excerpt && index){ %> - <%- post.excerpt %> - <% if (theme.excerpt_link){ %> -

- <%= theme.excerpt_link %> -

- <% } %> - <% } else { %> - <%- post.content %> - <% } %> -
- -
- <% if (!index){ %> - <%- partial('post/nav') %> - <% } %> -
- -<% if (!index && post.comments && config.disqus_shortname){ %> -
-
- -
-
-<% } %> \ No newline at end of file diff --git a/themes/landscape/layout/_partial/footer.ejs b/themes/landscape/layout/_partial/footer.ejs deleted file mode 100644 index 3aca618..0000000 --- a/themes/landscape/layout/_partial/footer.ejs +++ /dev/null @@ -1,11 +0,0 @@ -
- <% if (theme.sidebar === 'bottom'){ %> - <%- partial('_partial/sidebar') %> - <% } %> -
- -
-
\ No newline at end of file diff --git a/themes/landscape/layout/_partial/gauges-analytics.ejs b/themes/landscape/layout/_partial/gauges-analytics.ejs deleted file mode 100644 index d64be38..0000000 --- a/themes/landscape/layout/_partial/gauges-analytics.ejs +++ /dev/null @@ -1,18 +0,0 @@ -<% if (theme.gauges_analytics){ %> - - - -<% } %> diff --git a/themes/landscape/layout/_partial/google-analytics.ejs b/themes/landscape/layout/_partial/google-analytics.ejs deleted file mode 100644 index 84e75f0..0000000 --- a/themes/landscape/layout/_partial/google-analytics.ejs +++ /dev/null @@ -1,14 +0,0 @@ -<% if (theme.google_analytics){ %> - - - -<% } %> diff --git a/themes/landscape/layout/_partial/head.ejs b/themes/landscape/layout/_partial/head.ejs deleted file mode 100644 index 43d5f93..0000000 --- a/themes/landscape/layout/_partial/head.ejs +++ /dev/null @@ -1,36 +0,0 @@ - - - - - <%- partial('google-analytics') %> - <% - var title = page.title; - - if (is_archive()){ - title = __('archive_a'); - - if (is_month()){ - title += ': ' + page.year + '/' + page.month; - } else if (is_year()){ - title += ': ' + page.year; - } - } else if (is_category()){ - title = __('category') + ': ' + page.category; - } else if (is_tag()){ - title = __('tag') + ': ' + page.tag; - } - %> - <% if (title){ %><%= title %> | <% } %><%= config.title %> - - <%- open_graph({twitter_id: theme.twitter, google_plus: theme.google_plus, fb_admins: theme.fb_admins, fb_app_id: theme.fb_app_id}) %> - <% if (theme.rss){ %> - - <% } %> - <% if (theme.favicon){ %> - - <% } %> - <% if (config.highlight.enable){ %> - - <% } %> - <%- css('css/style') %> - diff --git a/themes/landscape/layout/_partial/header.ejs b/themes/landscape/layout/_partial/header.ejs deleted file mode 100644 index e8a305e..0000000 --- a/themes/landscape/layout/_partial/header.ejs +++ /dev/null @@ -1,32 +0,0 @@ - \ No newline at end of file diff --git a/themes/landscape/layout/_partial/mobile-nav.ejs b/themes/landscape/layout/_partial/mobile-nav.ejs deleted file mode 100644 index 7c1d2af..0000000 --- a/themes/landscape/layout/_partial/mobile-nav.ejs +++ /dev/null @@ -1,5 +0,0 @@ - \ No newline at end of file diff --git a/themes/landscape/layout/_partial/post/category.ejs b/themes/landscape/layout/_partial/post/category.ejs deleted file mode 100644 index db2ed48..0000000 --- a/themes/landscape/layout/_partial/post/category.ejs +++ /dev/null @@ -1,10 +0,0 @@ -<% if (post.categories && post.categories.length){ %> - -<% } %> \ No newline at end of file diff --git a/themes/landscape/layout/_partial/post/date.ejs b/themes/landscape/layout/_partial/post/date.ejs deleted file mode 100644 index 3f49613..0000000 --- a/themes/landscape/layout/_partial/post/date.ejs +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/themes/landscape/layout/_partial/post/gallery.ejs b/themes/landscape/layout/_partial/post/gallery.ejs deleted file mode 100644 index 886c8ec..0000000 --- a/themes/landscape/layout/_partial/post/gallery.ejs +++ /dev/null @@ -1,11 +0,0 @@ -<% if (post.photos && post.photos.length){ %> -
-
- <% post.photos.forEach(function(photo, i){ %> - - - - <% }) %> -
-
-<% } %> \ No newline at end of file diff --git a/themes/landscape/layout/_partial/post/nav.ejs b/themes/landscape/layout/_partial/post/nav.ejs deleted file mode 100644 index 720798a..0000000 --- a/themes/landscape/layout/_partial/post/nav.ejs +++ /dev/null @@ -1,22 +0,0 @@ -<% if (post.prev || post.next){ %> - -<% } %> \ No newline at end of file diff --git a/themes/landscape/layout/_partial/post/tag.ejs b/themes/landscape/layout/_partial/post/tag.ejs deleted file mode 100644 index e0f327f..0000000 --- a/themes/landscape/layout/_partial/post/tag.ejs +++ /dev/null @@ -1,6 +0,0 @@ -<% if (post.tags && post.tags.length){ %> - <%- list_tags(post.tags, { - show_count: false, - class: 'article-tag' - }) %> -<% } %> \ No newline at end of file diff --git a/themes/landscape/layout/_partial/post/title.ejs b/themes/landscape/layout/_partial/post/title.ejs deleted file mode 100644 index 69d646f..0000000 --- a/themes/landscape/layout/_partial/post/title.ejs +++ /dev/null @@ -1,15 +0,0 @@ -<% if (post.link){ %> -

- -

-<% } else if (post.title){ %> - <% if (index){ %> -

- <%= post.title %> -

- <% } else { %> -

- <%= post.title %> -

- <% } %> -<% } %> \ No newline at end of file diff --git a/themes/landscape/layout/_partial/sidebar.ejs b/themes/landscape/layout/_partial/sidebar.ejs deleted file mode 100644 index c1e48e5..0000000 --- a/themes/landscape/layout/_partial/sidebar.ejs +++ /dev/null @@ -1,5 +0,0 @@ - \ No newline at end of file diff --git a/themes/landscape/layout/_widget/archive.ejs b/themes/landscape/layout/_widget/archive.ejs deleted file mode 100644 index a20c58c..0000000 --- a/themes/landscape/layout/_widget/archive.ejs +++ /dev/null @@ -1,8 +0,0 @@ -<% if (site.posts.length){ %> -
-

<%= __('archive_a') %>

-
- <%- list_archives({show_count: theme.show_count, type: theme.archive_type}) %> -
-
-<% } %> diff --git a/themes/landscape/layout/_widget/category.ejs b/themes/landscape/layout/_widget/category.ejs deleted file mode 100644 index 8d9e5e9..0000000 --- a/themes/landscape/layout/_widget/category.ejs +++ /dev/null @@ -1,8 +0,0 @@ -<% if (site.categories.length){ %> -
-

<%= __('categories') %>

-
- <%- list_categories({show_count: theme.show_count}) %> -
-
-<% } %> diff --git a/themes/landscape/layout/_widget/recent_posts.ejs b/themes/landscape/layout/_widget/recent_posts.ejs deleted file mode 100644 index 7a38547..0000000 --- a/themes/landscape/layout/_widget/recent_posts.ejs +++ /dev/null @@ -1,14 +0,0 @@ -<% if (site.posts.length){ %> -
-

<%= __('recent_posts') %>

-
- -
-
-<% } %> \ No newline at end of file diff --git a/themes/landscape/layout/_widget/tag.ejs b/themes/landscape/layout/_widget/tag.ejs deleted file mode 100644 index ea5fb2c..0000000 --- a/themes/landscape/layout/_widget/tag.ejs +++ /dev/null @@ -1,8 +0,0 @@ -<% if (site.tags.length){ %> -
-

<%= __('tags') %>

-
- <%- list_tags({show_count: theme.show_count}) %> -
-
-<% } %> diff --git a/themes/landscape/layout/_widget/tagcloud.ejs b/themes/landscape/layout/_widget/tagcloud.ejs deleted file mode 100644 index 5feb435..0000000 --- a/themes/landscape/layout/_widget/tagcloud.ejs +++ /dev/null @@ -1,8 +0,0 @@ -<% if (site.tags.length){ %> -
-

<%= __('tagcloud') %>

-
- <%- tagcloud() %> -
-
-<% } %> \ No newline at end of file diff --git a/themes/landscape/layout/archive.ejs b/themes/landscape/layout/archive.ejs deleted file mode 100644 index 52f9b21..0000000 --- a/themes/landscape/layout/archive.ejs +++ /dev/null @@ -1 +0,0 @@ -<%- partial('_partial/archive', {pagination: config.archive, index: true}) %> \ No newline at end of file diff --git a/themes/landscape/layout/category.ejs b/themes/landscape/layout/category.ejs deleted file mode 100644 index 3ffe252..0000000 --- a/themes/landscape/layout/category.ejs +++ /dev/null @@ -1 +0,0 @@ -<%- partial('_partial/archive', {pagination: config.category, index: true}) %> \ No newline at end of file diff --git a/themes/landscape/layout/index.ejs b/themes/landscape/layout/index.ejs deleted file mode 100644 index 60a2c68..0000000 --- a/themes/landscape/layout/index.ejs +++ /dev/null @@ -1 +0,0 @@ -<%- partial('_partial/archive', {pagination: 2, index: true}) %> \ No newline at end of file diff --git a/themes/landscape/layout/layout.ejs b/themes/landscape/layout/layout.ejs deleted file mode 100644 index cf88daf..0000000 --- a/themes/landscape/layout/layout.ejs +++ /dev/null @@ -1,18 +0,0 @@ -<%- partial('_partial/head') %> - -
-
- <%- partial('_partial/header', null, {cache: !config.relative_link}) %> -
-
<%- body %>
- <% if (theme.sidebar && theme.sidebar !== 'bottom'){ %> - <%- partial('_partial/sidebar', null, {cache: !config.relative_link}) %> - <% } %> -
- <%- partial('_partial/footer', null, {cache: !config.relative_link}) %> -
- <%- partial('_partial/mobile-nav', null, {cache: !config.relative_link}) %> - <%- partial('_partial/after-footer') %> -
- - \ No newline at end of file diff --git a/themes/landscape/layout/page.ejs b/themes/landscape/layout/page.ejs deleted file mode 100644 index bea6318..0000000 --- a/themes/landscape/layout/page.ejs +++ /dev/null @@ -1 +0,0 @@ -<%- partial('_partial/article', {post: page, index: false}) %> \ No newline at end of file diff --git a/themes/landscape/layout/post.ejs b/themes/landscape/layout/post.ejs deleted file mode 100644 index bea6318..0000000 --- a/themes/landscape/layout/post.ejs +++ /dev/null @@ -1 +0,0 @@ -<%- partial('_partial/article', {post: page, index: false}) %> \ No newline at end of file diff --git a/themes/landscape/layout/tag.ejs b/themes/landscape/layout/tag.ejs deleted file mode 100644 index 048cdb0..0000000 --- a/themes/landscape/layout/tag.ejs +++ /dev/null @@ -1 +0,0 @@ -<%- partial('_partial/archive', {pagination: config.tag, index: true}) %> \ No newline at end of file diff --git a/themes/landscape/package.json b/themes/landscape/package.json deleted file mode 100644 index ac0df3d..0000000 --- a/themes/landscape/package.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "hexo-theme-landscape", - "version": "0.0.2", - "private": true, - "devDependencies": { - "grunt": "~0.4.2", - "load-grunt-tasks": "~0.2.0", - "grunt-git": "~0.2.2", - "grunt-contrib-clean": "~0.5.0", - "grunt-contrib-copy": "~0.4.1" - } -} diff --git a/themes/landscape/scripts/fancybox.js b/themes/landscape/scripts/fancybox.js deleted file mode 100644 index 83f1fdc..0000000 --- a/themes/landscape/scripts/fancybox.js +++ /dev/null @@ -1,24 +0,0 @@ -var rUrl = /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[.\!\/\\w]*))?)/; - -/** -* Fancybox tag -* -* Syntax: -* {% fancybox /path/to/image [/path/to/thumbnail] [title] %} -*/ - -hexo.extend.tag.register('fancybox', function(args){ - var original = args.shift(), - thumbnail = ''; - - if (args.length && rUrl.test(args[0])){ - thumbnail = args.shift(); - } - - var title = args.join(' '); - - return '' + - '' + title + '' - '' + - (title ? '' + title + '' : ''); -}); \ No newline at end of file diff --git a/themes/landscape/source/css/_extend.styl b/themes/landscape/source/css/_extend.styl deleted file mode 100644 index 96a1817..0000000 --- a/themes/landscape/source/css/_extend.styl +++ /dev/null @@ -1,63 +0,0 @@ -$block-caption - text-decoration: none - text-transform: uppercase - letter-spacing: 2px - color: color-grey - margin-bottom: 1em - margin-left: 5px - line-height: 1em - text-shadow: 0 1px #fff - font-weight: bold - -$block - background: #fff - box-shadow: 1px 2px 3px #ddd - border: 1px solid color-border - border-radius: 3px - -$base-style - h1 - font-size: 2em - h2 - font-size: 1.5em - h3 - font-size: 1.3em - h4 - font-size: 1.2em - h5 - font-size: 1em - h6 - font-size: 1em - color: color-grey - hr - border: 1px dashed color-border - strong - font-weight: bold - em, cite - font-style: italic - sup, sub - font-size: 0.75em - line-height: 0 - position: relative - vertical-align: baseline - sup - top: -0.5em - sub - bottom: -0.2em - small - font-size: 0.85em - acronym, abbr - border-bottom: 1px dotted - ul, ol, dl - margin: 0 20px - line-height: line-height - ul, ol - ul, ol - margin-top: 0 - margin-bottom: 0 - ul - list-style: disc - ol - list-style: decimal - dt - font-weight: bold \ No newline at end of file diff --git a/themes/landscape/source/css/_partial/archive.styl b/themes/landscape/source/css/_partial/archive.styl deleted file mode 100644 index 90ef053..0000000 --- a/themes/landscape/source/css/_partial/archive.styl +++ /dev/null @@ -1,80 +0,0 @@ -.archives-wrap - margin: block-margin 0 - -.archives - clearfix() - -.archive-year-wrap - margin-bottom: 1em - -.archive-year - @extend $block-caption - -.archives - column-gap: 10px - @media mq-tablet - column-count: 2 - @media mq-normal - column-count: 3 - -.archive-article - avoid-column-break() - -.archive-article-inner - @extend $block - padding: 10px - margin-bottom: 15px - -.archive-article-title - text-decoration: none - font-weight: bold - color: color-default - transition: color 0.2s - line-height: line-height - &:hover - color: color-link - -.archive-article-footer - margin-top: 1em - -.archive-article-date - color: color-grey - text-decoration: none - font-size: 0.85em - line-height: 1em - margin-bottom: 0.5em - display: block - -#page-nav - clearfix() - margin: block-margin auto - background: #fff - box-shadow: 1px 2px 3px #ddd - border: 1px solid color-border - border-radius: 3px - text-align: center - color: color-grey - overflow: hidden - a, span - padding: 10px 20px - line-height: 1 - height: 2ex - a - color: color-grey - text-decoration: none - &:hover - background: color-grey - color: #fff - .prev - float: left - .next - float: right - .page-number - display: inline-block - @media mq-mobile - display: none - .current - color: color-default - font-weight: bold - .space - color: color-border \ No newline at end of file diff --git a/themes/landscape/source/css/_partial/article.styl b/themes/landscape/source/css/_partial/article.styl deleted file mode 100644 index 46094f9..0000000 --- a/themes/landscape/source/css/_partial/article.styl +++ /dev/null @@ -1,357 +0,0 @@ -.article - margin: block-margin 0 - -.article-inner - @extend $block - overflow: hidden - -.article-meta - clearfix() - -.article-date - @extend $block-caption - float: left - -.article-category - float: left - line-height: 1em - color: #ccc - text-shadow: 0 1px #fff - margin-left: 8px - &:before - content: "\2022" - -.article-category-link - @extend $block-caption - margin: 0 12px 1em - -.article-header - padding: article-padding article-padding 0 - -.article-title - text-decoration: none - font-size: 2em - font-weight: bold - color: color-default - line-height: line-height-title - transition: color 0.2s - a&:hover - color: color-link - -.article-entry - @extend $base-style - clearfix() - color: color-default - padding: 0 article-padding - p, table - line-height: line-height - margin: line-height 0 - h1, h2, h3, h4, h5, h6 - font-weight: bold - h1, h2, h3, h4, h5, h6 - line-height: line-height-title - margin: line-height-title 0 - a - color: color-link - text-decoration: none - &:hover - text-decoration: underline - ul, ol, dl - margin-top: line-height - margin-bottom: line-height - img, video - max-width: 100% - height: auto - display: block - margin: auto - iframe - border: none - table - width: 100% - border-collapse: collapse - border-spacing: 0 - th - font-weight: bold - border-bottom: 3px solid color-border - padding-bottom: 0.5em - td - border-bottom: 1px solid color-border - padding: 10px 0 - blockquote - font-family: font-serif - font-size: 1.4em - margin: line-height 20px - text-align: center - footer - font-size: font-size - margin: line-height 0 - font-family: font-sans - cite - &:before - content: "—" - padding: 0 0.5em - .pullquote - text-align: left - width: 45% - margin: 0 - &.left - margin-left: 0.5em - margin-right: 1em - &.right - margin-right: 0.5em - margin-left: 1em - .caption - color: color-grey - display: block - font-size: 0.9em - margin-top: 0.5em - position: relative - text-align: center - // http://webdesignerwall.com/tutorials/css-elastic-videos - .video-container - position: relative - padding-top: (9 / 16 * 100)% // 16:9 ratio - height: 0 - overflow: hidden - iframe, object, embed - position: absolute - top: 0 - left: 0 - width: 100% - height: 100% - margin-top: 0 - -.article-more-link a - display: inline-block - line-height: 1em - padding: 6px 15px - border-radius: 15px - background: color-background - color: color-grey - text-shadow: 0 1px #fff - text-decoration: none - &:hover - background: color-link - color: #fff - text-decoration: none - text-shadow: 0 1px darken(color-link, 20%) - -.article-footer - clearfix() - font-size: 0.85em - line-height: line-height - border-top: 1px solid color-border - padding-top: line-height - margin: 0 article-padding article-padding - a - color: color-grey - text-decoration: none - &:hover - color: color-default - -.article-tag-list-item - float: left - margin-right: 10px - -.article-tag-list-link - &:before - content: "#" - -.article-comment-link - float: right - &:before - content: "\f075" - font-family: font-icon - padding-right: 8px - -.article-share-link - cursor: pointer - float: right - margin-left: 20px - &:before - content: "\f064" - font-family: font-icon - padding-right: 6px - -#article-nav - clearfix() - position: relative - @media mq-normal - margin: block-margin 0 - &:before - absolute-center(8px) - content: "" - border-radius: 50% - background: color-border - box-shadow: 0 1px 2px #fff - -.article-nav-link-wrap - text-decoration: none - text-shadow: 0 1px #fff - color: color-grey - box-sizing: border-box - margin-top: block-margin - text-align: center - display: block - &:hover - color: color-default - @media mq-normal - width: 50% - margin-top: 0 - -#article-nav-newer - @media mq-normal - float: left - text-align: right - padding-right: 20px - -#article-nav-older - @media mq-normal - float: right - text-align: left - padding-left: 20px - -.article-nav-caption - text-transform: uppercase - letter-spacing: 2px - color: color-border - line-height: 1em - font-weight: bold - #article-nav-newer & - margin-right: -2px - -.article-nav-title - font-size: 0.85em - line-height: line-height - margin-top: 0.5em - -.article-share-box - position: absolute - display: none - background: #fff - box-shadow: 1px 2px 10px rgba(0, 0, 0, 0.2) - border-radius: 3px - margin-left: -145px - overflow: hidden - z-index: 1 - &.on - display: block - -.article-share-input - width: 100% - background: none - box-sizing: border-box - font: 14px font-sans - padding: 0 15px - color: color-default - outline: none - border: 1px solid color-border - border-radius: 3px 3px 0 0 - height: 36px - line-height: 36px - -.article-share-links - clearfix() - background: color-background - -$article-share-link - width: 50px - height: 36px - display: block - float: left - position: relative - color: #999 - text-shadow: 0 1px #fff - &:before - font-size: 20px - font-family: font-icon - absolute-center(@font-size) - text-align: center - &:hover - color: #fff - -.article-share-twitter - @extend $article-share-link - &:before - content: "\f099" - &:hover - background: color-twitter - text-shadow: 0 1px darken(color-twitter, 20%) - -.article-share-facebook - @extend $article-share-link - &:before - content: "\f09a" - &:hover - background: color-facebook - text-shadow: 0 1px darken(color-facebook, 20%) - -.article-share-pinterest - @extend $article-share-link - &:before - content: "\f0d2" - &:hover - background: color-pinterest - text-shadow: 0 1px darken(color-pinterest, 20%) - -.article-share-google - @extend $article-share-link - &:before - content: "\f0d5" - &:hover - background: color-google - text-shadow: 0 1px darken(color-google, 20%) - -.article-gallery - background: #000 - position: relative - -.article-gallery-photos - position: relative - overflow: hidden - -.article-gallery-img - display: none - max-width: 100% - &:first-child - display: block - &.loaded - position: absolute - display: block - img - display: block - max-width: 100% - margin: 0 auto -/* -$article-gallery-ctrl - position: absolute - top: 0 - height: 100% - width: 60px - color: #fff - text-shadow: 0 0 3px rgba(0, 0, 0, 0.3) - opacity: 0.3 - transition: opacity 0.2s - cursor: pointer - &:hover - opacity: 0.8 - &:before - font-size: 30px - font-family: font-icon - position: absolute - top: 50% - margin-top: @font-size * -0.5 - -.article-gallery-prev - @extend $article-gallery-ctrl - left: 0 - &:before - content: "\f053" - left: 15px - -.article-gallery-next - @extend $article-gallery-ctrl - right: 0 - &:before - content: "\f054" - right: 15px*/ \ No newline at end of file diff --git a/themes/landscape/source/css/_partial/comment.styl b/themes/landscape/source/css/_partial/comment.styl deleted file mode 100644 index 296b7dd..0000000 --- a/themes/landscape/source/css/_partial/comment.styl +++ /dev/null @@ -1,9 +0,0 @@ -#comments - background: #fff - box-shadow: 1px 2px 3px #ddd - padding: article-padding - border: 1px solid color-border - border-radius: 3px - margin: block-margin 0 - a - color: color-link \ No newline at end of file diff --git a/themes/landscape/source/css/_partial/footer.styl b/themes/landscape/source/css/_partial/footer.styl deleted file mode 100644 index fe2fd24..0000000 --- a/themes/landscape/source/css/_partial/footer.styl +++ /dev/null @@ -1,14 +0,0 @@ -#footer - background: color-footer-background - padding: 50px 0 - border-top: 1px solid color-border - color: color-grey - a - color: color-link - text-decoration: none - &:hover - text-decoration: underline - -#footer-info - line-height: line-height - font-size: 0.85em \ No newline at end of file diff --git a/themes/landscape/source/css/_partial/header.styl b/themes/landscape/source/css/_partial/header.styl deleted file mode 100644 index d18ebc8..0000000 --- a/themes/landscape/source/css/_partial/header.styl +++ /dev/null @@ -1,165 +0,0 @@ -#header - height: banner-height - position: relative - border-bottom: 1px solid color-border - &:before, &:after - content: "" - position: absolute - left: 0 - right: 0 - height: 40px - &:before - top: 0 - background: linear-gradient(rgba(0, 0, 0, 0.2), transparent) - &:after - bottom: 0 - background: linear-gradient(transparent, rgba(0, 0, 0, 0.2)) - -#header-outer - height: 100% - position: relative - -#header-inner - position: relative - overflow: hidden - -#banner - position: absolute - top: 0 - left: 0 - width: 100% - height: 100% - background: url(banner-url) center #000 - background-size: cover - z-index: -1 - -#header-title - text-align: center - height: logo-size - position: absolute - top: 50% - left: 0 - margin-top: logo-size * -0.5 - -$logo-text - text-decoration: none - color: #fff - font-weight: 300 - text-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) - -#logo - @extend $logo-text - font-size: logo-size - line-height: logo-size - letter-spacing: 2px - -#subtitle - @extend $logo-text - font-size: subtitle-size - line-height: subtitle-size - letter-spacing: 1px - -#subtitle-wrap - margin-top: subtitle-size - -#main-nav - float: left - margin-left: -15px - -$nav-link - float: left - color: #fff - opacity: 0.6 - text-decoration: none - text-shadow: 0 1px rgba(0, 0, 0, 0.2) - transition: opacity 0.2s - display: block - padding: 20px 15px - &:hover - opacity: 1 - -.nav-icon - @extend $nav-link - font-family: font-icon - text-align: center - font-size: font-size - width: font-size - height: font-size - padding: 20px 15px - position: relative - cursor: pointer - -.main-nav-link - @extend $nav-link - font-weight: 300 - letter-spacing: 1px - @media mq-mobile - display: none - -#main-nav-toggle - display: none - &:before - content: "\f0c9" - @media mq-mobile - display: block - -#sub-nav - float: right - margin-right: -15px - -#nav-rss-link - &:before - content: "\f09e" - -#nav-search-btn - &:before - content: "\f002" - -#search-form-wrap - position: absolute - top: 15px - width: 150px - height: 30px - right: -150px - opacity: 0 - transition: 0.2s ease-out - &.on - opacity: 1 - right: 0 - @media mq-mobile - width: 100% - right: -100% - -.search-form - position: absolute - top: 0 - left: 0 - right: 0 - background: #fff - padding: 5px 15px - border-radius: 15px - box-shadow: 0 0 10px rgba(0, 0, 0, 0.3) - -.search-form-input - border: none - background: none - color: color-default - width: 100% - font: 13px font-sans - outline: none - &::-webkit-search-results-decoration - &::-webkit-search-cancel-button - -webkit-appearance: none - -.search-form-submit - position: absolute - top: 50% - right: 10px - margin-top: -7px - font: 13px font-icon - border: none - background: none - color: #bbb - cursor: pointer - &:hover, &:focus - color: #777 \ No newline at end of file diff --git a/themes/landscape/source/css/_partial/highlight.styl b/themes/landscape/source/css/_partial/highlight.styl deleted file mode 100644 index c932ec3..0000000 --- a/themes/landscape/source/css/_partial/highlight.styl +++ /dev/null @@ -1,158 +0,0 @@ -// https://github.com/chriskempson/tomorrow-theme -highlight-background = #2d2d2d -highlight-current-line = #393939 -highlight-selection = #515151 -highlight-foreground = #cccccc -highlight-comment = #999999 -highlight-red = #f2777a -highlight-orange = #f99157 -highlight-yellow = #ffcc66 -highlight-green = #99cc99 -highlight-aqua = #66cccc -highlight-blue = #6699cc -highlight-purple = #cc99cc - -$code-block - background: highlight-background - margin: 0 article-padding * -1 - padding: 15px article-padding - border-style: solid - border-color: color-border - border-width: 1px 0 - overflow: auto - color: highlight-foreground - line-height: font-size * line-height - -$line-numbers - color: #666 - font-size: 0.85em - -.article-entry - pre, code - font-family: font-mono - code - background: color-background - text-shadow: 0 1px #fff - padding: 0 0.3em - pre - @extend $code-block - code - background: none - text-shadow: none - padding: 0 - .highlight - @extend $code-block - pre - border: none - margin: 0 - padding: 0 - table - margin: 0 - width: auto - td - border: none - padding: 0 - figcaption - clearfix() - font-size: 0.85em - color: highlight-comment - line-height: 1em - margin-bottom: 1em - a - float: right - .gutter pre - @extend $line-numbers - text-align: right - padding-right: 20px - .line - height: font-size * line-height - .line.marked - background: highlight-selection - .gist - margin: 0 article-padding * -1 - border-style: solid - border-color: color-border - border-width: 1px 0 - background: highlight-background - padding: 15px article-padding 15px 0 - .gist-file - border: none - font-family: font-mono - margin: 0 - .gist-data - background: none - border: none - .line-numbers - @extend $line-numbers - background: none - border: none - padding: 0 20px 0 0 - .line-data - padding: 0 !important - .highlight - margin: 0 - padding: 0 - border: none - .gist-meta - background: highlight-background - color: highlight-comment - font: 0.85em font-sans - text-shadow: 0 0 - padding: 0 - margin-top: 1em - margin-left: article-padding - a - color: color-link - font-weight: normal - &:hover - text-decoration: underline - -pre - .comment - .title - color: highlight-comment - .variable - .attribute - .tag - .regexp - .ruby .constant - .xml .tag .title - .xml .pi - .xml .doctype - .html .doctype - .css .id - .css .class - .css .pseudo - color: highlight-red - .number - .preprocessor - .built_in - .literal - .params - .constant - color: highlight-orange - .class - .ruby .class .title - .css .rules .attribute - color: highlight-green - .string - .value - .inheritance - .header - .ruby .symbol - .xml .cdata - color: highlight-green - .css .hexcolor - color: highlight-aqua - .function - .python .decorator - .python .title - .ruby .function .title - .ruby .title .keyword - .perl .sub - .javascript .title - .coffeescript .title - color: highlight-blue - .keyword - .javascript .function - color: highlight-purple diff --git a/themes/landscape/source/css/_partial/mobile.styl b/themes/landscape/source/css/_partial/mobile.styl deleted file mode 100644 index eb68b3a..0000000 --- a/themes/landscape/source/css/_partial/mobile.styl +++ /dev/null @@ -1,19 +0,0 @@ -@media mq-mobile - #mobile-nav - position: absolute - top: 0 - left: 0 - width: mobile-nav-width - height: 100% - background: color-mobile-nav-background - border-right: 1px solid #fff - -@media mq-mobile - .mobile-nav-link - display: block - color: color-grey - text-decoration: none - padding: 15px 20px - font-weight: bold - &:hover - color: #fff diff --git a/themes/landscape/source/css/_partial/sidebar-aside.styl b/themes/landscape/source/css/_partial/sidebar-aside.styl deleted file mode 100644 index 838b167..0000000 --- a/themes/landscape/source/css/_partial/sidebar-aside.styl +++ /dev/null @@ -1,27 +0,0 @@ -#sidebar - @media mq-normal - column(sidebar-column) - -.widget-wrap - margin: block-margin 0 - -.widget-title - @extend $block-caption - -.widget - color: color-sidebar-text - text-shadow: 0 1px #fff - background: color-widget-background - box-shadow: 0 -1px 4px color-widget-border inset - border: 1px solid color-widget-border - padding: 15px - border-radius: 3px - a - color: color-link - text-decoration: none - &:hover - text-decoration: underline - ul, ol, dl - ul, ol, dl - margin-left: 15px - list-style: disc \ No newline at end of file diff --git a/themes/landscape/source/css/_partial/sidebar-bottom.styl b/themes/landscape/source/css/_partial/sidebar-bottom.styl deleted file mode 100644 index e2403fd..0000000 --- a/themes/landscape/source/css/_partial/sidebar-bottom.styl +++ /dev/null @@ -1,27 +0,0 @@ -.widget-wrap - margin-bottom: block-margin !important - @media mq-normal - column(main-column) - -.widget-title - color: #ccc - text-transform: uppercase - letter-spacing: 2px - margin-bottom: .5em - line-height: 1em - font-weight: bold - -.widget - color: color-grey - ul, ol - li - display: inline-block - zoom:1 - *display:inline - padding-right: .75em -/* Having problems getting balanced white space between items - li:before - content: " | " - li:first-child:before - content: none - */ diff --git a/themes/landscape/source/css/_partial/sidebar.styl b/themes/landscape/source/css/_partial/sidebar.styl deleted file mode 100644 index e43d66a..0000000 --- a/themes/landscape/source/css/_partial/sidebar.styl +++ /dev/null @@ -1,35 +0,0 @@ -if sidebar is bottom - @import "sidebar-bottom" -else - @import "sidebar-aside" - -.widget - @extend $base-style - line-height: line-height - word-wrap: break-word - font-size: 0.9em - ul, ol - list-style: none - margin: 0 - ul, ol - margin: 0 20px - ul - list-style: disc - ol - list-style: decimal - -.category-list-count -.tag-list-count -.archive-list-count - padding-left: 5px - color: color-grey - font-size: 0.85em - &:before - content: "(" - &:after - content: ")" - -.tagcloud - a - margin-right: 5px - display: inline-block diff --git a/themes/landscape/source/css/_util/grid.styl b/themes/landscape/source/css/_util/grid.styl deleted file mode 100644 index 2a14dd2..0000000 --- a/themes/landscape/source/css/_util/grid.styl +++ /dev/null @@ -1,38 +0,0 @@ -///////////////// -// Semantic.gs // for Stylus: http://learnboost.github.com/stylus/ -///////////////// - -// Utility function — you should never need to modify this -// _gridsystem-width = (column-width + gutter-width) * columns -gridsystem-width(_columns = columns) - (column-width + gutter-width) * _columns - -// Set @total-width to 100% for a fluid layout -// total-width = gridsystem-width(columns) -total-width = 100% - -////////// -// GRID // -////////// - -body - clearfix() - width: 100% - -row(_columns = columns) - clearfix() - display: block - width: total-width * ((gutter-width + gridsystem-width(_columns)) / gridsystem-width(_columns)) - margin: 0 total-width * (((gutter-width * .5) / gridsystem-width(_columns)) * -1) - -column(x, _columns = columns) - display: inline - float: left - width: total-width * ((((gutter-width + column-width) * x) - gutter-width) / gridsystem-width(_columns)) - margin: 0 total-width * ((gutter-width * .5) / gridsystem-width(_columns)) - -push(offset = 1) - margin-left: total-width * (((gutter-width + column-width) * offset) / gridsystem-width(columns)) - -pull(offset = 1) - margin-right: total-width * (((gutter-width + column-width) * offset) / gridsystem-width(columns)) \ No newline at end of file diff --git a/themes/landscape/source/css/_util/mixin.styl b/themes/landscape/source/css/_util/mixin.styl deleted file mode 100644 index b56f037..0000000 --- a/themes/landscape/source/css/_util/mixin.styl +++ /dev/null @@ -1,31 +0,0 @@ -// http://www.zeldman.com/2012/03/01/replacing-the-9999px-hack-new-image-replacement/ -hide-text() - text-indent: 100% - white-space: nowrap - overflow: hidden - -// http://codepen.io/shshaw/full/gEiDt -absolute-center(width, height = width) - // margin: auto - // position: absolute - // top: 50% - // top: 0 - // left: 0 - // bottom: 0 - // right: 0 - // width: width - // height: height - // overflow: auto - width: width - height: height - position: absolute - top: 50% - left: 50% - margin-top: width * -0.5 - margin-left: height * -0.5 - -avoid-column-break() - vendor("column-break-inside", avoid, only: webkit) - page-break-inside: avoid // for firefox - overflow: hidden // fix for firefox - break-inside: avoid-column diff --git a/themes/landscape/source/css/_variables.styl b/themes/landscape/source/css/_variables.styl deleted file mode 100644 index 4562911..0000000 --- a/themes/landscape/source/css/_variables.styl +++ /dev/null @@ -1,63 +0,0 @@ -// Config -support-for-ie = false -vendor-prefixes = webkit moz ms official - -// Colors -color-default = #555 -color-grey = #999 -color-border = #ddd -color-link = #258fb8 -color-background = #eee -color-sidebar-text = #777 -color-widget-background = #ddd -color-widget-border = #ccc -color-footer-background = #262a30 -color-mobile-nav-background = #191919 -color-twitter = #00aced -color-facebook = #3b5998 -color-pinterest = #cb2027 -color-google = #dd4b39 - -// Fonts -font-sans = -apple-system, BlinkMacSystemFont, - "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", - "Fira Sans", "Droid Sans", "Helvetica Neue", - sans-serif -font-serif = Georgia, "Times New Roman", serif -font-mono = "Source Code Pro", Consolas, Monaco, Menlo, Consolas, monospace -font-icon = FontAwesome -font-icon-path = "fonts/fontawesome-webfont" -font-icon-version = "4.0.3" -font-size = 14px -line-height = 1.6em -line-height-title = 1.1em - -// Header -logo-size = 40px -subtitle-size = 16px -banner-height = 300px -banner-url = "images/banner.jpg" - -sidebar = hexo-config("sidebar") - -// Layout -block-margin = 50px -article-padding = 20px -mobile-nav-width = 280px -main-column = 9 -sidebar-column = 3 - -if sidebar and sidebar isnt bottom - _sidebar-column = sidebar-column -else - _sidebar-column = 0 - -// Grids -column-width = 80px -gutter-width = 20px -columns = main-column + _sidebar-column - -// Media queries -mq-mobile = "screen and (max-width: 479px)" -mq-tablet = "screen and (min-width: 480px) and (max-width: 767px)" -mq-normal = "screen and (min-width: 768px)" \ No newline at end of file diff --git a/themes/landscape/source/css/style.styl b/themes/landscape/source/css/style.styl deleted file mode 100644 index c51f8e4..0000000 --- a/themes/landscape/source/css/style.styl +++ /dev/null @@ -1,89 +0,0 @@ -@import "nib" -@import "_variables" -@import "_util/mixin" -@import "_util/grid" - -global-reset() - -input, button - margin: 0 - padding: 0 - &::-moz-focus-inner - border: 0 - padding: 0 - -@font-face - font-family: FontAwesome - font-style: normal - font-weight: normal - src: url(font-icon-path + ".eot?v=#" + font-icon-version) - src: url(font-icon-path + ".eot?#iefix&v=#" + font-icon-version) format("embedded-opentype"), - url(font-icon-path + ".woff?v=#" + font-icon-version) format("woff"), - url(font-icon-path + ".ttf?v=#" + font-icon-version) format("truetype"), - url(font-icon-path + ".svg#fontawesomeregular?v=#" + font-icon-version) format("svg") - -html, body, #container - height: 100% - -body - background: color-background - font: font-size font-sans - -webkit-text-size-adjust: 100% - -.outer - clearfix() - max-width: (column-width + gutter-width) * columns + gutter-width - margin: 0 auto - padding: 0 gutter-width - -.inner - column(columns) - -.left, .alignleft - float: left - -.right, .alignright - float: right - -.clear - clear: both - -#container - position: relative - -.mobile-nav-on - overflow: hidden - -#wrap - height: 100% - width: 100% - position: absolute - top: 0 - left: 0 - transition: 0.2s ease-out - z-index: 1 - background: color-background - .mobile-nav-on & - left: mobile-nav-width - -if sidebar and sidebar isnt bottom - #main - @media mq-normal - column(main-column) - -if sidebar is left - @media mq-normal - #main - float: right - -@import "_extend" -@import "_partial/header" -@import "_partial/article" -@import "_partial/comment" -@import "_partial/archive" -@import "_partial/footer" -@import "_partial/highlight" -@import "_partial/mobile" - -if sidebar - @import "_partial/sidebar" \ No newline at end of file diff --git a/yarn.lock b/yarn.lock deleted file mode 100644 index f5e7098..0000000 --- a/yarn.lock +++ /dev/null @@ -1,2218 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -JSONStream@^1.0.7: - version "1.3.2" - resolved "http://registry.npm.taobao.org/JSONStream/download/JSONStream-1.3.2.tgz#c102371b6ec3a7cf3b847ca00c20bb0fce4c6dea" - dependencies: - jsonparse "^1.2.0" - through ">=2.2.7 <3" - -a-sync-waterfall@^1.0.0: - version "1.0.0" - resolved "http://registry.npm.taobao.org/a-sync-waterfall/download/a-sync-waterfall-1.0.0.tgz#38e8319d79379e24628845b53b96722b29e0e47c" - -abab@^1.0.0: - version "1.0.4" - resolved "http://registry.npm.taobao.org/abab/download/abab-1.0.4.tgz#5faad9c2c07f60dd76770f71cf025b62a63cfd4e" - -abbrev@1, abbrev@^1.0.7: - version "1.1.1" - resolved "http://registry.npm.taobao.org/abbrev/download/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" - -accepts@~1.3.4: - version "1.3.5" - resolved "http://registry.npm.taobao.org/accepts/download/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2" - dependencies: - mime-types "~2.1.18" - negotiator "0.6.1" - -acorn-globals@^1.0.4: - version "1.0.9" - resolved "http://registry.npm.taobao.org/acorn-globals/download/acorn-globals-1.0.9.tgz#55bb5e98691507b74579d0513413217c380c54cf" - dependencies: - acorn "^2.1.0" - -acorn@^2.1.0, acorn@^2.4.0: - version "2.7.0" - resolved "http://registry.npm.taobao.org/acorn/download/acorn-2.7.0.tgz#ab6e7d9d886aaca8b085bc3312b79a198433f0e7" - -ajv@^4.9.1: - version "4.11.8" - resolved "http://registry.npm.taobao.org/ajv/download/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" - dependencies: - co "^4.6.0" - json-stable-stringify "^1.0.1" - -ajv@^5.1.0: - version "5.5.2" - resolved "http://registry.npm.taobao.org/ajv/download/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" - dependencies: - co "^4.6.0" - fast-deep-equal "^1.0.0" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.3.0" - -align-text@^0.1.1, align-text@^0.1.3: - version "0.1.4" - resolved "http://registry.npm.taobao.org/align-text/download/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" - dependencies: - kind-of "^3.0.2" - longest "^1.0.1" - repeat-string "^1.5.2" - -amdefine@>=0.0.4: - version "1.0.1" - resolved "http://registry.npm.taobao.org/amdefine/download/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" - -ansi-regex@^2.0.0: - version "2.1.1" - resolved "http://registry.npm.taobao.org/ansi-regex/download/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - -ansi-regex@^3.0.0: - version "3.0.0" - resolved "http://registry.npm.taobao.org/ansi-regex/download/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" - -ansi-styles@^2.2.1: - version "2.2.1" - resolved "http://registry.npm.taobao.org/ansi-styles/download/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - -ansi-styles@^3.2.1: - version "3.2.1" - resolved "http://registry.npm.taobao.org/ansi-styles/download/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - dependencies: - color-convert "^1.9.0" - -anymatch@^1.3.0: - version "1.3.2" - resolved "http://registry.npm.taobao.org/anymatch/download/anymatch-1.3.2.tgz#553dcb8f91e3c889845dfdba34c77721b90b9d7a" - dependencies: - micromatch "^2.1.5" - normalize-path "^2.0.0" - -aproba@^1.0.3: - version "1.2.0" - resolved "http://registry.npm.taobao.org/aproba/download/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" - -archy@^1.0.0: - version "1.0.0" - resolved "http://registry.npm.taobao.org/archy/download/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" - -are-we-there-yet@~1.1.2: - version "1.1.4" - resolved "http://registry.npm.taobao.org/are-we-there-yet/download/are-we-there-yet-1.1.4.tgz#bb5dca382bb94f05e15194373d16fd3ba1ca110d" - dependencies: - delegates "^1.0.0" - readable-stream "^2.0.6" - -argparse@^1.0.7: - version "1.0.10" - resolved "http://registry.npm.taobao.org/argparse/download/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" - dependencies: - sprintf-js "~1.0.2" - -arr-diff@^2.0.0: - version "2.0.0" - resolved "http://registry.npm.taobao.org/arr-diff/download/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" - dependencies: - arr-flatten "^1.0.1" - -arr-flatten@^1.0.1: - version "1.1.0" - resolved "http://registry.npm.taobao.org/arr-flatten/download/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" - -array-unique@^0.2.1: - version "0.2.1" - resolved "http://registry.npm.taobao.org/array-unique/download/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" - -asap@^2.0.3: - version "2.0.6" - resolved "http://registry.npm.taobao.org/asap/download/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" - -asn1@~0.2.3: - version "0.2.3" - resolved "http://registry.npm.taobao.org/asn1/download/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" - -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "http://registry.npm.taobao.org/assert-plus/download/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - -assert-plus@^0.2.0: - version "0.2.0" - resolved "http://registry.npm.taobao.org/assert-plus/download/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" - -async-each@^1.0.0: - version "1.0.1" - resolved "http://registry.npm.taobao.org/async-each/download/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" - -async@~0.2.6: - version "0.2.10" - resolved "http://registry.npm.taobao.org/async/download/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1" - -asynckit@^0.4.0: - version "0.4.0" - resolved "http://registry.npm.taobao.org/asynckit/download/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - -aws-sign2@~0.6.0: - version "0.6.0" - resolved "http://registry.npm.taobao.org/aws-sign2/download/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" - -aws-sign2@~0.7.0: - version "0.7.0" - resolved "http://registry.npm.taobao.org/aws-sign2/download/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - -aws4@^1.2.1, aws4@^1.6.0: - version "1.6.0" - resolved "http://registry.npm.taobao.org/aws4/download/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" - -balanced-match@^1.0.0: - version "1.0.0" - resolved "http://registry.npm.taobao.org/balanced-match/download/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" - -basic-auth@~2.0.0: - version "2.0.0" - resolved "http://registry.npm.taobao.org/basic-auth/download/basic-auth-2.0.0.tgz#015db3f353e02e56377755f962742e8981e7bbba" - dependencies: - safe-buffer "5.1.1" - -bcrypt-pbkdf@^1.0.0: - version "1.0.1" - resolved "http://registry.npm.taobao.org/bcrypt-pbkdf/download/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" - dependencies: - tweetnacl "^0.14.3" - -binary-extensions@^1.0.0: - version "1.11.0" - resolved "http://registry.npm.taobao.org/binary-extensions/download/binary-extensions-1.11.0.tgz#46aa1751fb6a2f93ee5e689bb1087d4b14c6c205" - -block-stream@*: - version "0.0.9" - resolved "http://registry.npm.taobao.org/block-stream/download/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" - dependencies: - inherits "~2.0.0" - -bluebird@^3.0.6, bluebird@^3.2.2, bluebird@^3.4.0: - version "3.5.1" - resolved "http://registry.npm.taobao.org/bluebird/download/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" - -boolbase@~1.0.0: - version "1.0.0" - resolved "http://registry.npm.taobao.org/boolbase/download/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" - -boom@2.x.x: - version "2.10.1" - resolved "http://registry.npm.taobao.org/boom/download/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" - dependencies: - hoek "2.x.x" - -boom@4.x.x: - version "4.3.1" - resolved "http://registry.npm.taobao.org/boom/download/boom-4.3.1.tgz#4f8a3005cb4a7e3889f749030fd25b96e01d2e31" - dependencies: - hoek "4.x.x" - -boom@5.x.x: - version "5.2.0" - resolved "http://registry.npm.taobao.org/boom/download/boom-5.2.0.tgz#5dd9da6ee3a5f302077436290cb717d3f4a54e02" - dependencies: - hoek "4.x.x" - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "http://registry.npm.taobao.org/brace-expansion/download/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -braces@^1.8.2: - version "1.8.5" - resolved "http://registry.npm.taobao.org/braces/download/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" - dependencies: - expand-range "^1.8.1" - preserve "^0.2.0" - repeat-element "^1.1.2" - -browser-fingerprint@0.0.1: - version "0.0.1" - resolved "http://registry.npm.taobao.org/browser-fingerprint/download/browser-fingerprint-0.0.1.tgz#8df3cdca25bf7d5b3542d61545d730053fce604a" - -bytes@3.0.0: - version "3.0.0" - resolved "http://registry.npm.taobao.org/bytes/download/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" - -camel-case@^3.0.0: - version "3.0.0" - resolved "http://registry.npm.taobao.org/camel-case/download/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73" - dependencies: - no-case "^2.2.0" - upper-case "^1.1.1" - -camelcase@^1.0.2: - version "1.2.1" - resolved "http://registry.npm.taobao.org/camelcase/download/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" - -camelcase@^2.0.1: - version "2.1.1" - resolved "http://registry.npm.taobao.org/camelcase/download/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" - -caseless@~0.12.0: - version "0.12.0" - resolved "http://registry.npm.taobao.org/caseless/download/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - -center-align@^0.1.1: - version "0.1.3" - resolved "http://registry.npm.taobao.org/center-align/download/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" - dependencies: - align-text "^0.1.3" - lazy-cache "^1.0.3" - -chalk@^1.1.1, chalk@^1.1.3: - version "1.1.3" - resolved "http://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - -chalk@^2.3.1: - version "2.3.2" - resolved "http://registry.npm.taobao.org/chalk/download/chalk-2.3.2.tgz#250dc96b07491bfd601e648d66ddf5f60c7a5c65" - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -cheerio@^0.20.0: - version "0.20.0" - resolved "http://registry.npm.taobao.org/cheerio/download/cheerio-0.20.0.tgz#5c710f2bab95653272842ba01c6ea61b3545ec35" - dependencies: - css-select "~1.2.0" - dom-serializer "~0.1.0" - entities "~1.1.1" - htmlparser2 "~3.8.1" - lodash "^4.1.0" - optionalDependencies: - jsdom "^7.0.2" - -chokidar@^1.5.2, chokidar@^1.6.0: - version "1.7.0" - resolved "http://registry.npm.taobao.org/chokidar/download/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" - dependencies: - anymatch "^1.3.0" - async-each "^1.0.0" - glob-parent "^2.0.0" - inherits "^2.0.1" - is-binary-path "^1.0.0" - is-glob "^2.0.0" - path-is-absolute "^1.0.0" - readdirp "^2.0.0" - optionalDependencies: - fsevents "^1.0.0" - -cliui@^2.1.0: - version "2.1.0" - resolved "http://registry.npm.taobao.org/cliui/download/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" - dependencies: - center-align "^0.1.1" - right-align "^0.1.1" - wordwrap "0.0.2" - -cliui@^3.0.3: - version "3.2.0" - resolved "http://registry.npm.taobao.org/cliui/download/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" - wrap-ansi "^2.0.0" - -co@^4.6.0: - version "4.6.0" - resolved "http://registry.npm.taobao.org/co/download/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - -code-point-at@^1.0.0: - version "1.1.0" - resolved "http://registry.npm.taobao.org/code-point-at/download/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - -color-convert@^1.9.0: - version "1.9.1" - resolved "http://registry.npm.taobao.org/color-convert/download/color-convert-1.9.1.tgz#c1261107aeb2f294ebffec9ed9ecad529a6097ed" - dependencies: - color-name "^1.1.1" - -color-name@^1.1.1: - version "1.1.3" - resolved "http://registry.npm.taobao.org/color-name/download/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - -combined-stream@1.0.6, combined-stream@^1.0.5, combined-stream@~1.0.5: - version "1.0.6" - resolved "http://registry.npm.taobao.org/combined-stream/download/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818" - dependencies: - delayed-stream "~1.0.0" - -command-exists@^1.2.0: - version "1.2.2" - resolved "http://registry.npm.taobao.org/command-exists/download/command-exists-1.2.2.tgz#12819c64faf95446ec0ae07fe6cafb6eb3708b22" - -compressible@~2.0.13: - version "2.0.13" - resolved "http://registry.npm.taobao.org/compressible/download/compressible-2.0.13.tgz#0d1020ab924b2fdb4d6279875c7d6daba6baa7a9" - dependencies: - mime-db ">= 1.33.0 < 2" - -compression@^1.6.0: - version "1.7.2" - resolved "http://registry.npm.taobao.org/compression/download/compression-1.7.2.tgz#aaffbcd6aaf854b44ebb280353d5ad1651f59a69" - dependencies: - accepts "~1.3.4" - bytes "3.0.0" - compressible "~2.0.13" - debug "2.6.9" - on-headers "~1.0.1" - safe-buffer "5.1.1" - vary "~1.1.2" - -concat-map@0.0.1: - version "0.0.1" - resolved "http://registry.npm.taobao.org/concat-map/download/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - -connect@3.x: - version "3.6.6" - resolved "http://registry.npm.taobao.org/connect/download/connect-3.6.6.tgz#09eff6c55af7236e137135a72574858b6786f524" - dependencies: - debug "2.6.9" - finalhandler "1.1.0" - parseurl "~1.3.2" - utils-merge "1.0.1" - -console-control-strings@^1.0.0, console-control-strings@~1.1.0: - version "1.1.0" - resolved "http://registry.npm.taobao.org/console-control-strings/download/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" - -core-js@^1.1.1: - version "1.2.7" - resolved "http://registry.npm.taobao.org/core-js/download/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" - -core-util-is@1.0.2, core-util-is@~1.0.0: - version "1.0.2" - resolved "http://registry.npm.taobao.org/core-util-is/download/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - -cross-spawn@^4.0.0: - version "4.0.2" - resolved "http://registry.npm.taobao.org/cross-spawn/download/cross-spawn-4.0.2.tgz#7b9247621c23adfdd3856004a823cbe397424d41" - dependencies: - lru-cache "^4.0.1" - which "^1.2.9" - -cryptiles@2.x.x: - version "2.0.5" - resolved "http://registry.npm.taobao.org/cryptiles/download/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" - dependencies: - boom "2.x.x" - -cryptiles@3.x.x: - version "3.1.2" - resolved "http://registry.npm.taobao.org/cryptiles/download/cryptiles-3.1.2.tgz#a89fbb220f5ce25ec56e8c4aa8a4fd7b5b0d29fe" - dependencies: - boom "5.x.x" - -css-parse@1.7.x: - version "1.7.0" - resolved "http://registry.npm.taobao.org/css-parse/download/css-parse-1.7.0.tgz#321f6cf73782a6ff751111390fc05e2c657d8c9b" - -css-select@~1.2.0: - version "1.2.0" - resolved "http://registry.npm.taobao.org/css-select/download/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" - dependencies: - boolbase "~1.0.0" - css-what "2.1" - domutils "1.5.1" - nth-check "~1.0.1" - -css-what@2.1: - version "2.1.0" - resolved "http://registry.npm.taobao.org/css-what/download/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd" - -cssom@0.3.x, "cssom@>= 0.3.0 < 0.4.0": - version "0.3.2" - resolved "http://registry.npm.taobao.org/cssom/download/cssom-0.3.2.tgz#b8036170c79f07a90ff2f16e22284027a243848b" - -"cssstyle@>= 0.2.29 < 0.3.0": - version "0.2.37" - resolved "http://registry.npm.taobao.org/cssstyle/download/cssstyle-0.2.37.tgz#541097234cb2513c83ceed3acddc27ff27987d54" - dependencies: - cssom "0.3.x" - -cuid@~1.3.8: - version "1.3.8" - resolved "http://registry.npm.taobao.org/cuid/download/cuid-1.3.8.tgz#4b875e0969bad764f7ec0706cf44f5fb0831f6b7" - dependencies: - browser-fingerprint "0.0.1" - core-js "^1.1.1" - node-fingerprint "0.0.2" - -dashdash@^1.12.0: - version "1.14.1" - resolved "http://registry.npm.taobao.org/dashdash/download/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - dependencies: - assert-plus "^1.0.0" - -debug@*: - version "3.1.0" - resolved "http://registry.npm.taobao.org/debug/download/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" - dependencies: - ms "2.0.0" - -debug@2.6.9, debug@^2.2.0: - version "2.6.9" - resolved "http://registry.npm.taobao.org/debug/download/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - dependencies: - ms "2.0.0" - -decamelize@^1.0.0, decamelize@^1.1.1: - version "1.2.0" - resolved "http://registry.npm.taobao.org/decamelize/download/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" - -deep-extend@~0.4.0: - version "0.4.2" - resolved "http://registry.npm.taobao.org/deep-extend/download/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f" - -deep-is@~0.1.3: - version "0.1.3" - resolved "http://registry.npm.taobao.org/deep-is/download/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "http://registry.npm.taobao.org/delayed-stream/download/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - -delegates@^1.0.0: - version "1.0.0" - resolved "http://registry.npm.taobao.org/delegates/download/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" - -depd@1.1.1: - version "1.1.1" - resolved "http://registry.npm.taobao.org/depd/download/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359" - -depd@~1.1.1, depd@~1.1.2: - version "1.1.2" - resolved "http://registry.npm.taobao.org/depd/download/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - -destroy@~1.0.4: - version "1.0.4" - resolved "http://registry.npm.taobao.org/destroy/download/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" - -detect-libc@^1.0.2: - version "1.0.3" - resolved "http://registry.npm.taobao.org/detect-libc/download/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" - -dom-serializer@0, dom-serializer@~0.1.0: - version "0.1.0" - resolved "http://registry.npm.taobao.org/dom-serializer/download/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" - dependencies: - domelementtype "~1.1.1" - entities "~1.1.1" - -domelementtype@1: - version "1.3.0" - resolved "http://registry.npm.taobao.org/domelementtype/download/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2" - -domelementtype@~1.1.1: - version "1.1.3" - resolved "http://registry.npm.taobao.org/domelementtype/download/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b" - -domhandler@2.3: - version "2.3.0" - resolved "http://registry.npm.taobao.org/domhandler/download/domhandler-2.3.0.tgz#2de59a0822d5027fabff6f032c2b25a2a8abe738" - dependencies: - domelementtype "1" - -domutils@1.5, domutils@1.5.1: - version "1.5.1" - resolved "http://registry.npm.taobao.org/domutils/download/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" - dependencies: - dom-serializer "0" - domelementtype "1" - -ecc-jsbn@~0.1.1: - version "0.1.1" - resolved "http://registry.npm.taobao.org/ecc-jsbn/download/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" - dependencies: - jsbn "~0.1.0" - -ee-first@1.1.1: - version "1.1.1" - resolved "http://registry.npm.taobao.org/ee-first/download/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" - -ejs@^2.3.4: - version "2.5.7" - resolved "http://registry.npm.taobao.org/ejs/download/ejs-2.5.7.tgz#cc872c168880ae3c7189762fd5ffc00896c9518a" - -encodeurl@~1.0.1, encodeurl@~1.0.2: - version "1.0.2" - resolved "http://registry.npm.taobao.org/encodeurl/download/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" - -entities@1.0: - version "1.0.0" - resolved "http://registry.npm.taobao.org/entities/download/entities-1.0.0.tgz#b2987aa3821347fcde642b24fdfc9e4fb712bf26" - -entities@~1.1.1: - version "1.1.1" - resolved "http://registry.npm.taobao.org/entities/download/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" - -escape-html@~1.0.3: - version "1.0.3" - resolved "http://registry.npm.taobao.org/escape-html/download/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "http://registry.npm.taobao.org/escape-string-regexp/download/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - -escodegen@^1.6.1: - version "1.9.1" - resolved "http://registry.npm.taobao.org/escodegen/download/escodegen-1.9.1.tgz#dbae17ef96c8e4bedb1356f4504fa4cc2f7cb7e2" - dependencies: - esprima "^3.1.3" - estraverse "^4.2.0" - esutils "^2.0.2" - optionator "^0.8.1" - optionalDependencies: - source-map "~0.6.1" - -esprima@^3.1.3: - version "3.1.3" - resolved "http://registry.npm.taobao.org/esprima/download/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" - -esprima@^4.0.0: - version "4.0.0" - resolved "http://registry.npm.taobao.org/esprima/download/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804" - -estraverse@^4.2.0: - version "4.2.0" - resolved "http://registry.npm.taobao.org/estraverse/download/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" - -esutils@^2.0.2: - version "2.0.2" - resolved "http://registry.npm.taobao.org/esutils/download/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" - -etag@~1.8.1: - version "1.8.1" - resolved "http://registry.npm.taobao.org/etag/download/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" - -expand-brackets@^0.1.4: - version "0.1.5" - resolved "http://registry.npm.taobao.org/expand-brackets/download/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" - dependencies: - is-posix-bracket "^0.1.0" - -expand-range@^1.8.1: - version "1.8.2" - resolved "http://registry.npm.taobao.org/expand-range/download/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" - dependencies: - fill-range "^2.1.0" - -extend@~3.0.0, extend@~3.0.1: - version "3.0.1" - resolved "http://registry.npm.taobao.org/extend/download/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" - -extglob@^0.3.1: - version "0.3.2" - resolved "http://registry.npm.taobao.org/extglob/download/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" - dependencies: - is-extglob "^1.0.0" - -extsprintf@1.3.0: - version "1.3.0" - resolved "http://registry.npm.taobao.org/extsprintf/download/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - -extsprintf@^1.2.0: - version "1.4.0" - resolved "http://registry.npm.taobao.org/extsprintf/download/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" - -fast-deep-equal@^1.0.0: - version "1.1.0" - resolved "http://registry.npm.taobao.org/fast-deep-equal/download/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614" - -fast-json-stable-stringify@^2.0.0: - version "2.0.0" - resolved "http://registry.npm.taobao.org/fast-json-stable-stringify/download/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" - -fast-levenshtein@~2.0.4: - version "2.0.6" - resolved "http://registry.npm.taobao.org/fast-levenshtein/download/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - -filename-regex@^2.0.0: - version "2.0.1" - resolved "http://registry.npm.taobao.org/filename-regex/download/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" - -fill-range@^2.1.0: - version "2.2.3" - resolved "http://registry.npm.taobao.org/fill-range/download/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723" - dependencies: - is-number "^2.1.0" - isobject "^2.0.0" - randomatic "^1.1.3" - repeat-element "^1.1.2" - repeat-string "^1.5.2" - -finalhandler@1.1.0: - version "1.1.0" - resolved "http://registry.npm.taobao.org/finalhandler/download/finalhandler-1.1.0.tgz#ce0b6855b45853e791b2fcc680046d88253dd7f5" - dependencies: - debug "2.6.9" - encodeurl "~1.0.1" - escape-html "~1.0.3" - on-finished "~2.3.0" - parseurl "~1.3.2" - statuses "~1.3.1" - unpipe "~1.0.0" - -for-in@^1.0.1: - version "1.0.2" - resolved "http://registry.npm.taobao.org/for-in/download/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" - -for-own@^0.1.4: - version "0.1.5" - resolved "http://registry.npm.taobao.org/for-own/download/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" - dependencies: - for-in "^1.0.1" - -forever-agent@~0.6.1: - version "0.6.1" - resolved "http://registry.npm.taobao.org/forever-agent/download/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - -form-data@~2.1.1: - version "2.1.4" - resolved "http://registry.npm.taobao.org/form-data/download/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1" - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.5" - mime-types "^2.1.12" - -form-data@~2.3.1: - version "2.3.2" - resolved "http://registry.npm.taobao.org/form-data/download/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099" - dependencies: - asynckit "^0.4.0" - combined-stream "1.0.6" - mime-types "^2.1.12" - -fresh@0.5.2: - version "0.5.2" - resolved "http://registry.npm.taobao.org/fresh/download/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "http://registry.npm.taobao.org/fs.realpath/download/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - -fsevents@^1.0.0: - version "1.1.3" - resolved "http://registry.npm.taobao.org/fsevents/download/fsevents-1.1.3.tgz#11f82318f5fe7bb2cd22965a108e9306208216d8" - dependencies: - nan "^2.3.0" - node-pre-gyp "^0.6.39" - -fstream-ignore@^1.0.5: - version "1.0.5" - resolved "http://registry.npm.taobao.org/fstream-ignore/download/fstream-ignore-1.0.5.tgz#9c31dae34767018fe1d249b24dada67d092da105" - dependencies: - fstream "^1.0.0" - inherits "2" - minimatch "^3.0.0" - -fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2: - version "1.0.11" - resolved "http://registry.npm.taobao.org/fstream/download/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171" - dependencies: - graceful-fs "^4.1.2" - inherits "~2.0.0" - mkdirp ">=0.5 0" - rimraf "2" - -gauge@~2.7.3: - version "2.7.4" - resolved "http://registry.npm.taobao.org/gauge/download/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" - dependencies: - aproba "^1.0.3" - console-control-strings "^1.0.0" - has-unicode "^2.0.0" - object-assign "^4.1.0" - signal-exit "^3.0.0" - string-width "^1.0.1" - strip-ansi "^3.0.1" - wide-align "^1.1.0" - -getpass@^0.1.1: - version "0.1.7" - resolved "http://registry.npm.taobao.org/getpass/download/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - dependencies: - assert-plus "^1.0.0" - -glob-base@^0.3.0: - version "0.3.0" - resolved "http://registry.npm.taobao.org/glob-base/download/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" - dependencies: - glob-parent "^2.0.0" - is-glob "^2.0.0" - -glob-parent@^2.0.0: - version "2.0.0" - resolved "http://registry.npm.taobao.org/glob-parent/download/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" - dependencies: - is-glob "^2.0.0" - -glob@7.0.x: - version "7.0.6" - resolved "http://registry.npm.taobao.org/glob/download/glob-7.0.6.tgz#211bafaf49e525b8cd93260d14ab136152b3f57a" - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.2" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@^6.0.1: - version "6.0.4" - resolved "http://registry.npm.taobao.org/glob/download/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" - dependencies: - inflight "^1.0.4" - inherits "2" - minimatch "2 || 3" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@^7.0.5: - version "7.1.2" - resolved "http://registry.npm.taobao.org/glob/download/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.4: - version "4.1.11" - resolved "http://registry.npm.taobao.org/graceful-fs/download/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" - -har-schema@^1.0.5: - version "1.0.5" - resolved "http://registry.npm.taobao.org/har-schema/download/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e" - -har-schema@^2.0.0: - version "2.0.0" - resolved "http://registry.npm.taobao.org/har-schema/download/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" - -har-validator@~4.2.1: - version "4.2.1" - resolved "http://registry.npm.taobao.org/har-validator/download/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a" - dependencies: - ajv "^4.9.1" - har-schema "^1.0.5" - -har-validator@~5.0.3: - version "5.0.3" - resolved "http://registry.npm.taobao.org/har-validator/download/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd" - dependencies: - ajv "^5.1.0" - har-schema "^2.0.0" - -has-ansi@^2.0.0: - version "2.0.0" - resolved "http://registry.npm.taobao.org/has-ansi/download/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - dependencies: - ansi-regex "^2.0.0" - -has-flag@^3.0.0: - version "3.0.0" - resolved "http://registry.npm.taobao.org/has-flag/download/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - -has-unicode@^2.0.0: - version "2.0.1" - resolved "http://registry.npm.taobao.org/has-unicode/download/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" - -hawk@3.1.3, hawk@~3.1.3: - version "3.1.3" - resolved "http://registry.npm.taobao.org/hawk/download/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" - dependencies: - boom "2.x.x" - cryptiles "2.x.x" - hoek "2.x.x" - sntp "1.x.x" - -hawk@~6.0.2: - version "6.0.2" - resolved "http://registry.npm.taobao.org/hawk/download/hawk-6.0.2.tgz#af4d914eb065f9b5ce4d9d11c1cb2126eecc3038" - dependencies: - boom "4.x.x" - cryptiles "3.x.x" - hoek "4.x.x" - sntp "2.x.x" - -hexo-bunyan@^1.0.0: - version "1.0.0" - resolved "http://registry.npm.taobao.org/hexo-bunyan/download/hexo-bunyan-1.0.0.tgz#b2106b26547b232f0195db863cb5d5ff8527fd36" - optionalDependencies: - moment "^2.10.6" - mv "~2" - safe-json-stringify "~1" - -hexo-cli@^1.1.0: - version "1.1.0" - resolved "http://registry.npm.taobao.org/hexo-cli/download/hexo-cli-1.1.0.tgz#496d238d4646dbfd1cf047b6dc5271bfb5cb798f" - dependencies: - abbrev "^1.0.7" - bluebird "^3.4.0" - chalk "^1.1.3" - command-exists "^1.2.0" - hexo-fs "^0.2.0" - hexo-log "^0.2.0" - hexo-util "^0.6.0" - minimist "^1.2.0" - object-assign "^4.1.0" - resolve "^1.5.0" - tildify "^1.2.0" - -hexo-front-matter@^0.2.2: - version "0.2.3" - resolved "http://registry.npm.taobao.org/hexo-front-matter/download/hexo-front-matter-0.2.3.tgz#c7ca8ef420ea36bd85e8408a2e8c9bf49efa605e" - dependencies: - js-yaml "^3.6.1" - -hexo-fs@^0.2.0: - version "0.2.2" - resolved "http://registry.npm.taobao.org/hexo-fs/download/hexo-fs-0.2.2.tgz#0b97b79f68df121f765d1291b8e90fae326d000e" - dependencies: - bluebird "^3.4.0" - chokidar "^1.5.2" - escape-string-regexp "^1.0.5" - graceful-fs "^4.1.4" - -hexo-generator-archive@^0.1.4: - version "0.1.5" - resolved "http://registry.npm.taobao.org/hexo-generator-archive/download/hexo-generator-archive-0.1.5.tgz#a979214cdddee2693e0551809c294bedadbb69b3" - dependencies: - hexo-pagination "0.0.2" - object-assign "^2.0.0" - -hexo-generator-category@^0.1.3: - version "0.1.3" - resolved "http://registry.npm.taobao.org/hexo-generator-category/download/hexo-generator-category-0.1.3.tgz#b9e6a5862530a83bdd7da4c819c1b9f3e4ccb4b2" - dependencies: - hexo-pagination "0.0.2" - object-assign "^2.0.0" - -hexo-generator-index@^0.2.0: - version "0.2.1" - resolved "http://registry.npm.taobao.org/hexo-generator-index/download/hexo-generator-index-0.2.1.tgz#9042229fcac79aaf700575da19332bf3f7ee5c5d" - dependencies: - hexo-pagination "0.0.2" - object-assign "^4.0.1" - -hexo-generator-tag@^0.2.0: - version "0.2.0" - resolved "http://registry.npm.taobao.org/hexo-generator-tag/download/hexo-generator-tag-0.2.0.tgz#c5715846bb41e57d9c20c1d66d7db21a1abf7a62" - dependencies: - hexo-pagination "0.0.2" - object-assign "^4.0.1" - -hexo-i18n@^0.2.1: - version "0.2.1" - resolved "http://registry.npm.taobao.org/hexo-i18n/download/hexo-i18n-0.2.1.tgz#84f141432bf09d8b558ed878c728164b6d1cd6de" - dependencies: - sprintf-js "^1.0.2" - -hexo-log@^0.2.0: - version "0.2.0" - resolved "http://registry.npm.taobao.org/hexo-log/download/hexo-log-0.2.0.tgz#d30fd45e1a12a83c88033586640485efc5df5a6f" - dependencies: - chalk "^1.1.1" - hexo-bunyan "^1.0.0" - -hexo-pagination@0.0.2: - version "0.0.2" - resolved "http://registry.npm.taobao.org/hexo-pagination/download/hexo-pagination-0.0.2.tgz#8cf470c7db0de5b18a3926a76deb194015df7f2b" - dependencies: - utils-merge "^1.0.0" - -hexo-renderer-ejs@^0.3.0: - version "0.3.1" - resolved "http://registry.npm.taobao.org/hexo-renderer-ejs/download/hexo-renderer-ejs-0.3.1.tgz#c0c1a3757532d47e5b7d9dc908b5dfd98c94be2c" - dependencies: - ejs "^2.3.4" - object-assign "^4.0.1" - -hexo-renderer-marked@^0.3.0: - version "0.3.2" - resolved "http://registry.npm.taobao.org/hexo-renderer-marked/download/hexo-renderer-marked-0.3.2.tgz#d6a37af9ff195e30f9ef6ede1a06ea1fe4322966" - dependencies: - hexo-util "^0.6.2" - marked "^0.3.9" - object-assign "^4.1.1" - strip-indent "^2.0.0" - -hexo-renderer-stylus@^0.3.1: - version "0.3.3" - resolved "http://registry.npm.taobao.org/hexo-renderer-stylus/download/hexo-renderer-stylus-0.3.3.tgz#c54ea27e1fd8e3c8a9a7a84cfba8ad354122ca7f" - dependencies: - nib "^1.1.2" - stylus "^0.54.5" - -hexo-server@^0.2.0: - version "0.2.2" - resolved "http://registry.npm.taobao.org/hexo-server/download/hexo-server-0.2.2.tgz#592686b554b8bfe09a19bc86c0f003ac3e8c19b9" - dependencies: - bluebird "^3.0.6" - chalk "^1.1.1" - compression "^1.6.0" - connect "3.x" - mime "^1.3.4" - morgan "^1.6.1" - object-assign "^4.0.1" - opn "^4.0.0" - serve-static "^1.10.0" - -hexo-util@^0.6.0, hexo-util@^0.6.2: - version "0.6.3" - resolved "http://registry.npm.taobao.org/hexo-util/download/hexo-util-0.6.3.tgz#16a2ade457bef955af0dfd22a3fe6f0a49a9137c" - dependencies: - bluebird "^3.4.0" - camel-case "^3.0.0" - cross-spawn "^4.0.0" - highlight.js "^9.4.0" - html-entities "^1.2.0" - striptags "^2.1.1" - -hexo@^3.2.0: - version "3.6.0" - resolved "http://registry.npm.taobao.org/hexo/download/hexo-3.6.0.tgz#f978263eb889655269902bc60cd0dfaa29d63c0f" - dependencies: - abbrev "^1.0.7" - archy "^1.0.0" - bluebird "^3.4.0" - chalk "^2.3.1" - cheerio "^0.20.0" - hexo-cli "^1.1.0" - hexo-front-matter "^0.2.2" - hexo-fs "^0.2.0" - hexo-i18n "^0.2.1" - hexo-log "^0.2.0" - hexo-util "^0.6.0" - js-yaml "^3.6.1" - lodash "^4.17.5" - minimatch "^3.0.4" - moment "^2.19.4" - moment-timezone "^0.5.14" - nunjucks "^3.1.2" - pretty-hrtime "^1.0.2" - resolve "^1.5.0" - strip-ansi "^4.0.0" - strip-indent "^2.0.0" - swig-extras "0.0.1" - swig-templates "^2.0.2" - text-table "^0.2.0" - tildify "^1.2.0" - titlecase "^1.1.2" - warehouse "^2.2.0" - -highlight.js@^9.4.0: - version "9.12.0" - resolved "http://registry.npm.taobao.org/highlight.js/download/highlight.js-9.12.0.tgz#e6d9dbe57cbefe60751f02af336195870c90c01e" - -hoek@2.x.x: - version "2.16.3" - resolved "http://registry.npm.taobao.org/hoek/download/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" - -hoek@4.x.x: - version "4.2.1" - resolved "http://registry.npm.taobao.org/hoek/download/hoek-4.2.1.tgz#9634502aa12c445dd5a7c5734b572bb8738aacbb" - -html-entities@^1.2.0: - version "1.2.1" - resolved "http://registry.npm.taobao.org/html-entities/download/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f" - -htmlparser2@~3.8.1: - version "3.8.3" - resolved "http://registry.npm.taobao.org/htmlparser2/download/htmlparser2-3.8.3.tgz#996c28b191516a8be86501a7d79757e5c70c1068" - dependencies: - domelementtype "1" - domhandler "2.3" - domutils "1.5" - entities "1.0" - readable-stream "1.1" - -http-errors@~1.6.2: - version "1.6.2" - resolved "http://registry.npm.taobao.org/http-errors/download/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736" - dependencies: - depd "1.1.1" - inherits "2.0.3" - setprototypeof "1.0.3" - statuses ">= 1.3.1 < 2" - -http-signature@~1.1.0: - version "1.1.1" - resolved "http://registry.npm.taobao.org/http-signature/download/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" - dependencies: - assert-plus "^0.2.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - -http-signature@~1.2.0: - version "1.2.0" - resolved "http://registry.npm.taobao.org/http-signature/download/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" - dependencies: - assert-plus "^1.0.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - -inflight@^1.0.4: - version "1.0.6" - resolved "http://registry.npm.taobao.org/inflight/download/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: - version "2.0.3" - resolved "http://registry.npm.taobao.org/inherits/download/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - -ini@~1.3.0: - version "1.3.5" - resolved "http://registry.npm.taobao.org/ini/download/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" - -invert-kv@^1.0.0: - version "1.0.0" - resolved "http://registry.npm.taobao.org/invert-kv/download/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" - -is-binary-path@^1.0.0: - version "1.0.1" - resolved "http://registry.npm.taobao.org/is-binary-path/download/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" - dependencies: - binary-extensions "^1.0.0" - -is-buffer@^1.1.5: - version "1.1.6" - resolved "http://registry.npm.taobao.org/is-buffer/download/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" - -is-dotfile@^1.0.0: - version "1.0.3" - resolved "http://registry.npm.taobao.org/is-dotfile/download/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" - -is-equal-shallow@^0.1.3: - version "0.1.3" - resolved "http://registry.npm.taobao.org/is-equal-shallow/download/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" - dependencies: - is-primitive "^2.0.0" - -is-extendable@^0.1.1: - version "0.1.1" - resolved "http://registry.npm.taobao.org/is-extendable/download/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" - -is-extglob@^1.0.0: - version "1.0.0" - resolved "http://registry.npm.taobao.org/is-extglob/download/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" - -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "http://registry.npm.taobao.org/is-fullwidth-code-point/download/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - dependencies: - number-is-nan "^1.0.0" - -is-glob@^2.0.0, is-glob@^2.0.1: - version "2.0.1" - resolved "http://registry.npm.taobao.org/is-glob/download/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" - dependencies: - is-extglob "^1.0.0" - -is-number@^2.1.0: - version "2.1.0" - resolved "http://registry.npm.taobao.org/is-number/download/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" - dependencies: - kind-of "^3.0.2" - -is-number@^3.0.0: - version "3.0.0" - resolved "http://registry.npm.taobao.org/is-number/download/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" - dependencies: - kind-of "^3.0.2" - -is-plain-object@^2.0.1: - version "2.0.4" - resolved "http://registry.npm.taobao.org/is-plain-object/download/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" - dependencies: - isobject "^3.0.1" - -is-posix-bracket@^0.1.0: - version "0.1.1" - resolved "http://registry.npm.taobao.org/is-posix-bracket/download/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" - -is-primitive@^2.0.0: - version "2.0.0" - resolved "http://registry.npm.taobao.org/is-primitive/download/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" - -is-typedarray@~1.0.0: - version "1.0.0" - resolved "http://registry.npm.taobao.org/is-typedarray/download/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - -isarray@0.0.1: - version "0.0.1" - resolved "http://registry.npm.taobao.org/isarray/download/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - -isarray@1.0.0, isarray@~1.0.0: - version "1.0.0" - resolved "http://registry.npm.taobao.org/isarray/download/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - -isexe@^2.0.0: - version "2.0.0" - resolved "http://registry.npm.taobao.org/isexe/download/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - -isobject@^2.0.0: - version "2.1.0" - resolved "http://registry.npm.taobao.org/isobject/download/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" - dependencies: - isarray "1.0.0" - -isobject@^3.0.1: - version "3.0.1" - resolved "http://registry.npm.taobao.org/isobject/download/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" - -isstream@~0.1.2: - version "0.1.2" - resolved "http://registry.npm.taobao.org/isstream/download/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - -js-yaml@^3.6.1: - version "3.11.0" - resolved "http://registry.npm.taobao.org/js-yaml/download/js-yaml-3.11.0.tgz#597c1a8bd57152f26d622ce4117851a51f5ebaef" - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - -jsbn@~0.1.0: - version "0.1.1" - resolved "http://registry.npm.taobao.org/jsbn/download/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - -jsdom@^7.0.2: - version "7.2.2" - resolved "http://registry.npm.taobao.org/jsdom/download/jsdom-7.2.2.tgz#40b402770c2bda23469096bee91ab675e3b1fc6e" - dependencies: - abab "^1.0.0" - acorn "^2.4.0" - acorn-globals "^1.0.4" - cssom ">= 0.3.0 < 0.4.0" - cssstyle ">= 0.2.29 < 0.3.0" - escodegen "^1.6.1" - nwmatcher ">= 1.3.7 < 2.0.0" - parse5 "^1.5.1" - request "^2.55.0" - sax "^1.1.4" - symbol-tree ">= 3.1.0 < 4.0.0" - tough-cookie "^2.2.0" - webidl-conversions "^2.0.0" - whatwg-url-compat "~0.6.5" - xml-name-validator ">= 2.0.1 < 3.0.0" - -json-schema-traverse@^0.3.0: - version "0.3.1" - resolved "http://registry.npm.taobao.org/json-schema-traverse/download/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" - -json-schema@0.2.3: - version "0.2.3" - resolved "http://registry.npm.taobao.org/json-schema/download/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - -json-stable-stringify@^1.0.1: - version "1.0.1" - resolved "http://registry.npm.taobao.org/json-stable-stringify/download/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" - dependencies: - jsonify "~0.0.0" - -json-stringify-safe@~5.0.1: - version "5.0.1" - resolved "http://registry.npm.taobao.org/json-stringify-safe/download/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - -jsonify@~0.0.0: - version "0.0.0" - resolved "http://registry.npm.taobao.org/jsonify/download/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" - -jsonparse@^1.2.0: - version "1.3.1" - resolved "http://registry.npm.taobao.org/jsonparse/download/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" - -jsprim@^1.2.2: - version "1.4.1" - resolved "http://registry.npm.taobao.org/jsprim/download/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.2.3" - verror "1.10.0" - -kind-of@^3.0.2: - version "3.2.2" - resolved "http://registry.npm.taobao.org/kind-of/download/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" - dependencies: - is-buffer "^1.1.5" - -kind-of@^4.0.0: - version "4.0.0" - resolved "http://registry.npm.taobao.org/kind-of/download/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" - dependencies: - is-buffer "^1.1.5" - -lazy-cache@^1.0.3: - version "1.0.4" - resolved "http://registry.npm.taobao.org/lazy-cache/download/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" - -lcid@^1.0.0: - version "1.0.0" - resolved "http://registry.npm.taobao.org/lcid/download/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" - dependencies: - invert-kv "^1.0.0" - -levn@~0.3.0: - version "0.3.0" - resolved "http://registry.npm.taobao.org/levn/download/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" - -lodash@^4.1.0, lodash@^4.17.5, lodash@^4.2.1: - version "4.17.5" - resolved "http://registry.npm.taobao.org/lodash/download/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511" - -longest@^1.0.1: - version "1.0.1" - resolved "http://registry.npm.taobao.org/longest/download/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" - -lower-case@^1.1.1: - version "1.1.4" - resolved "http://registry.npm.taobao.org/lower-case/download/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" - -lru-cache@^4.0.1: - version "4.1.2" - resolved "http://registry.npm.taobao.org/lru-cache/download/lru-cache-4.1.2.tgz#45234b2e6e2f2b33da125624c4664929a0224c3f" - dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" - -markdown@~0.5.0: - version "0.5.0" - resolved "http://registry.npm.taobao.org/markdown/download/markdown-0.5.0.tgz#28205b565a8ae7592de207463d6637dc182722b2" - dependencies: - nopt "~2.1.1" - -marked@^0.3.9: - version "0.3.17" - resolved "http://registry.npm.taobao.org/marked/download/marked-0.3.17.tgz#607f06668b3c6b1246b28f13da76116ac1aa2d2b" - -micromatch@^2.1.5: - version "2.3.11" - resolved "http://registry.npm.taobao.org/micromatch/download/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" - dependencies: - arr-diff "^2.0.0" - array-unique "^0.2.1" - braces "^1.8.2" - expand-brackets "^0.1.4" - extglob "^0.3.1" - filename-regex "^2.0.0" - is-extglob "^1.0.0" - is-glob "^2.0.1" - kind-of "^3.0.2" - normalize-path "^2.0.1" - object.omit "^2.0.0" - parse-glob "^3.0.4" - regex-cache "^0.4.2" - -"mime-db@>= 1.33.0 < 2", mime-db@~1.33.0: - version "1.33.0" - resolved "http://registry.npm.taobao.org/mime-db/download/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" - -mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.18, mime-types@~2.1.7: - version "2.1.18" - resolved "http://registry.npm.taobao.org/mime-types/download/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" - dependencies: - mime-db "~1.33.0" - -mime@1.4.1: - version "1.4.1" - resolved "http://registry.npm.taobao.org/mime/download/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" - -mime@^1.3.4: - version "1.6.0" - resolved "http://registry.npm.taobao.org/mime/download/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" - -"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4: - version "3.0.4" - resolved "http://registry.npm.taobao.org/minimatch/download/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - dependencies: - brace-expansion "^1.1.7" - -minimist@0.0.8: - version "0.0.8" - resolved "http://registry.npm.taobao.org/minimist/download/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" - -minimist@^1.2.0: - version "1.2.0" - resolved "http://registry.npm.taobao.org/minimist/download/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" - -minimist@~0.0.1: - version "0.0.10" - resolved "http://registry.npm.taobao.org/minimist/download/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" - -mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.1, mkdirp@~0.5.1: - version "0.5.1" - resolved "http://registry.npm.taobao.org/mkdirp/download/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" - dependencies: - minimist "0.0.8" - -moment-timezone@^0.5.14: - version "0.5.14" - resolved "http://registry.npm.taobao.org/moment-timezone/download/moment-timezone-0.5.14.tgz#4eb38ff9538b80108ba467a458f3ed4268ccfcb1" - dependencies: - moment ">= 2.9.0" - -"moment@>= 2.9.0", moment@^2.10.6, moment@^2.19.4: - version "2.21.0" - resolved "http://registry.npm.taobao.org/moment/download/moment-2.21.0.tgz#2a114b51d2a6ec9e6d83cf803f838a878d8a023a" - -morgan@^1.6.1: - version "1.9.0" - resolved "http://registry.npm.taobao.org/morgan/download/morgan-1.9.0.tgz#d01fa6c65859b76fcf31b3cb53a3821a311d8051" - dependencies: - basic-auth "~2.0.0" - debug "2.6.9" - depd "~1.1.1" - on-finished "~2.3.0" - on-headers "~1.0.1" - -ms@2.0.0: - version "2.0.0" - resolved "http://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - -mv@~2: - version "2.1.1" - resolved "http://registry.npm.taobao.org/mv/download/mv-2.1.1.tgz#ae6ce0d6f6d5e0a4f7d893798d03c1ea9559b6a2" - dependencies: - mkdirp "~0.5.1" - ncp "~2.0.0" - rimraf "~2.4.0" - -nan@^2.3.0: - version "2.10.0" - resolved "http://registry.npm.taobao.org/nan/download/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f" - -ncp@~2.0.0: - version "2.0.0" - resolved "http://registry.npm.taobao.org/ncp/download/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3" - -negotiator@0.6.1: - version "0.6.1" - resolved "http://registry.npm.taobao.org/negotiator/download/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" - -nib@^1.1.2: - version "1.1.2" - resolved "http://registry.npm.taobao.org/nib/download/nib-1.1.2.tgz#6a69ede4081b95c0def8be024a4c8ae0c2cbb6c7" - dependencies: - stylus "0.54.5" - -no-case@^2.2.0: - version "2.3.2" - resolved "http://registry.npm.taobao.org/no-case/download/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac" - dependencies: - lower-case "^1.1.1" - -node-fingerprint@0.0.2: - version "0.0.2" - resolved "http://registry.npm.taobao.org/node-fingerprint/download/node-fingerprint-0.0.2.tgz#31cbabeb71a67ae7dd5a7dc042e51c3c75868501" - -node-pre-gyp@^0.6.39: - version "0.6.39" - resolved "http://registry.npm.taobao.org/node-pre-gyp/download/node-pre-gyp-0.6.39.tgz#c00e96860b23c0e1420ac7befc5044e1d78d8649" - dependencies: - detect-libc "^1.0.2" - hawk "3.1.3" - mkdirp "^0.5.1" - nopt "^4.0.1" - npmlog "^4.0.2" - rc "^1.1.7" - request "2.81.0" - rimraf "^2.6.1" - semver "^5.3.0" - tar "^2.2.1" - tar-pack "^3.4.0" - -nopt@^4.0.1: - version "4.0.1" - resolved "http://registry.npm.taobao.org/nopt/download/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" - dependencies: - abbrev "1" - osenv "^0.1.4" - -nopt@~2.1.1: - version "2.1.2" - resolved "http://registry.npm.taobao.org/nopt/download/nopt-2.1.2.tgz#6cccd977b80132a07731d6e8ce58c2c8303cf9af" - dependencies: - abbrev "1" - -normalize-path@^2.0.0, normalize-path@^2.0.1: - version "2.1.1" - resolved "http://registry.npm.taobao.org/normalize-path/download/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" - dependencies: - remove-trailing-separator "^1.0.1" - -npmlog@^4.0.2: - version "4.1.2" - resolved "http://registry.npm.taobao.org/npmlog/download/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" - dependencies: - are-we-there-yet "~1.1.2" - console-control-strings "~1.1.0" - gauge "~2.7.3" - set-blocking "~2.0.0" - -nth-check@~1.0.1: - version "1.0.1" - resolved "http://registry.npm.taobao.org/nth-check/download/nth-check-1.0.1.tgz#9929acdf628fc2c41098deab82ac580cf149aae4" - dependencies: - boolbase "~1.0.0" - -number-is-nan@^1.0.0: - version "1.0.1" - resolved "http://registry.npm.taobao.org/number-is-nan/download/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - -nunjucks@^3.1.2: - version "3.1.2" - resolved "http://registry.npm.taobao.org/nunjucks/download/nunjucks-3.1.2.tgz#85945a66bb8239bb37ecef83dab4cc1f152aabb9" - dependencies: - a-sync-waterfall "^1.0.0" - asap "^2.0.3" - postinstall-build "^5.0.1" - yargs "^3.32.0" - optionalDependencies: - chokidar "^1.6.0" - -"nwmatcher@>= 1.3.7 < 2.0.0": - version "1.4.4" - resolved "http://registry.npm.taobao.org/nwmatcher/download/nwmatcher-1.4.4.tgz#2285631f34a95f0d0395cd900c96ed39b58f346e" - -oauth-sign@~0.8.1, oauth-sign@~0.8.2: - version "0.8.2" - resolved "http://registry.npm.taobao.org/oauth-sign/download/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" - -object-assign@^2.0.0: - version "2.1.1" - resolved "http://registry.npm.taobao.org/object-assign/download/object-assign-2.1.1.tgz#43c36e5d569ff8e4816c4efa8be02d26967c18aa" - -object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: - version "4.1.1" - resolved "http://registry.npm.taobao.org/object-assign/download/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - -object.omit@^2.0.0: - version "2.0.1" - resolved "http://registry.npm.taobao.org/object.omit/download/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" - dependencies: - for-own "^0.1.4" - is-extendable "^0.1.1" - -on-finished@~2.3.0: - version "2.3.0" - resolved "http://registry.npm.taobao.org/on-finished/download/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" - dependencies: - ee-first "1.1.1" - -on-headers@~1.0.1: - version "1.0.1" - resolved "http://registry.npm.taobao.org/on-headers/download/on-headers-1.0.1.tgz#928f5d0f470d49342651ea6794b0857c100693f7" - -once@^1.3.0, once@^1.3.3: - version "1.4.0" - resolved "http://registry.npm.taobao.org/once/download/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - dependencies: - wrappy "1" - -opn@^4.0.0: - version "4.0.2" - resolved "http://registry.npm.taobao.org/opn/download/opn-4.0.2.tgz#7abc22e644dff63b0a96d5ab7f2790c0f01abc95" - dependencies: - object-assign "^4.0.1" - pinkie-promise "^2.0.0" - -optimist@~0.6: - version "0.6.1" - resolved "http://registry.npm.taobao.org/optimist/download/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" - dependencies: - minimist "~0.0.1" - wordwrap "~0.0.2" - -optionator@^0.8.1: - version "0.8.2" - resolved "http://registry.npm.taobao.org/optionator/download/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" - dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.4" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - wordwrap "~1.0.0" - -os-homedir@^1.0.0: - version "1.0.2" - resolved "http://registry.npm.taobao.org/os-homedir/download/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" - -os-locale@^1.4.0: - version "1.4.0" - resolved "http://registry.npm.taobao.org/os-locale/download/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" - dependencies: - lcid "^1.0.0" - -os-tmpdir@^1.0.0: - version "1.0.2" - resolved "http://registry.npm.taobao.org/os-tmpdir/download/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" - -osenv@^0.1.4: - version "0.1.5" - resolved "http://registry.npm.taobao.org/osenv/download/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" - dependencies: - os-homedir "^1.0.0" - os-tmpdir "^1.0.0" - -parse-glob@^3.0.4: - version "3.0.4" - resolved "http://registry.npm.taobao.org/parse-glob/download/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" - dependencies: - glob-base "^0.3.0" - is-dotfile "^1.0.0" - is-extglob "^1.0.0" - is-glob "^2.0.0" - -parse5@^1.5.1: - version "1.5.1" - resolved "http://registry.npm.taobao.org/parse5/download/parse5-1.5.1.tgz#9b7f3b0de32be78dc2401b17573ccaf0f6f59d94" - -parseurl@~1.3.2: - version "1.3.2" - resolved "http://registry.npm.taobao.org/parseurl/download/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "http://registry.npm.taobao.org/path-is-absolute/download/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - -path-parse@^1.0.5: - version "1.0.5" - resolved "http://registry.npm.taobao.org/path-parse/download/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" - -performance-now@^0.2.0: - version "0.2.0" - resolved "http://registry.npm.taobao.org/performance-now/download/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" - -performance-now@^2.1.0: - version "2.1.0" - resolved "http://registry.npm.taobao.org/performance-now/download/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - -pinkie-promise@^2.0.0: - version "2.0.1" - resolved "http://registry.npm.taobao.org/pinkie-promise/download/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" - dependencies: - pinkie "^2.0.0" - -pinkie@^2.0.0: - version "2.0.4" - resolved "http://registry.npm.taobao.org/pinkie/download/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" - -postinstall-build@^5.0.1: - version "5.0.1" - resolved "http://registry.npm.taobao.org/postinstall-build/download/postinstall-build-5.0.1.tgz#b917a9079b26178d9a24af5a5cd8cb4a991d11b9" - -prelude-ls@~1.1.2: - version "1.1.2" - resolved "http://registry.npm.taobao.org/prelude-ls/download/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - -preserve@^0.2.0: - version "0.2.0" - resolved "http://registry.npm.taobao.org/preserve/download/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" - -pretty-hrtime@^1.0.2: - version "1.0.3" - resolved "http://registry.npm.taobao.org/pretty-hrtime/download/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" - -process-nextick-args@~2.0.0: - version "2.0.0" - resolved "http://registry.npm.taobao.org/process-nextick-args/download/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" - -pseudomap@^1.0.2: - version "1.0.2" - resolved "http://registry.npm.taobao.org/pseudomap/download/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" - -punycode@^1.4.1: - version "1.4.1" - resolved "http://registry.npm.taobao.org/punycode/download/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - -qs@~6.4.0: - version "6.4.0" - resolved "http://registry.npm.taobao.org/qs/download/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" - -qs@~6.5.1: - version "6.5.1" - resolved "http://registry.npm.taobao.org/qs/download/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" - -randomatic@^1.1.3: - version "1.1.7" - resolved "http://registry.npm.taobao.org/randomatic/download/randomatic-1.1.7.tgz#c7abe9cc8b87c0baa876b19fde83fd464797e38c" - dependencies: - is-number "^3.0.0" - kind-of "^4.0.0" - -range-parser@~1.2.0: - version "1.2.0" - resolved "http://registry.npm.taobao.org/range-parser/download/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" - -rc@^1.1.7: - version "1.2.6" - resolved "http://registry.npm.taobao.org/rc/download/rc-1.2.6.tgz#eb18989c6d4f4f162c399f79ddd29f3835568092" - dependencies: - deep-extend "~0.4.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" - -readable-stream@1.1: - version "1.1.13" - resolved "http://registry.npm.taobao.org/readable-stream/download/readable-stream-1.1.13.tgz#f6eef764f514c89e2b9e23146a75ba106756d23e" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - -readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.4: - version "2.3.5" - resolved "http://registry.npm.taobao.org/readable-stream/download/readable-stream-2.3.5.tgz#b4f85003a938cbb6ecbce2a124fb1012bd1a838d" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.0.3" - util-deprecate "~1.0.1" - -readdirp@^2.0.0: - version "2.1.0" - resolved "http://registry.npm.taobao.org/readdirp/download/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" - dependencies: - graceful-fs "^4.1.2" - minimatch "^3.0.2" - readable-stream "^2.0.2" - set-immediate-shim "^1.0.1" - -regex-cache@^0.4.2: - version "0.4.4" - resolved "http://registry.npm.taobao.org/regex-cache/download/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd" - dependencies: - is-equal-shallow "^0.1.3" - -remove-trailing-separator@^1.0.1: - version "1.1.0" - resolved "http://registry.npm.taobao.org/remove-trailing-separator/download/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" - -repeat-element@^1.1.2: - version "1.1.2" - resolved "http://registry.npm.taobao.org/repeat-element/download/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" - -repeat-string@^1.5.2: - version "1.6.1" - resolved "http://registry.npm.taobao.org/repeat-string/download/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" - -request@2.81.0: - version "2.81.0" - resolved "http://registry.npm.taobao.org/request/download/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" - dependencies: - aws-sign2 "~0.6.0" - aws4 "^1.2.1" - caseless "~0.12.0" - combined-stream "~1.0.5" - extend "~3.0.0" - forever-agent "~0.6.1" - form-data "~2.1.1" - har-validator "~4.2.1" - hawk "~3.1.3" - http-signature "~1.1.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.7" - oauth-sign "~0.8.1" - performance-now "^0.2.0" - qs "~6.4.0" - safe-buffer "^5.0.1" - stringstream "~0.0.4" - tough-cookie "~2.3.0" - tunnel-agent "^0.6.0" - uuid "^3.0.0" - -request@^2.55.0: - version "2.85.0" - resolved "http://registry.npm.taobao.org/request/download/request-2.85.0.tgz#5a03615a47c61420b3eb99b7dba204f83603e1fa" - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.6.0" - caseless "~0.12.0" - combined-stream "~1.0.5" - extend "~3.0.1" - forever-agent "~0.6.1" - form-data "~2.3.1" - har-validator "~5.0.3" - hawk "~6.0.2" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.17" - oauth-sign "~0.8.2" - performance-now "^2.1.0" - qs "~6.5.1" - safe-buffer "^5.1.1" - stringstream "~0.0.5" - tough-cookie "~2.3.3" - tunnel-agent "^0.6.0" - uuid "^3.1.0" - -resolve@^1.5.0: - version "1.5.0" - resolved "http://registry.npm.taobao.org/resolve/download/resolve-1.5.0.tgz#1f09acce796c9a762579f31b2c1cc4c3cddf9f36" - dependencies: - path-parse "^1.0.5" - -right-align@^0.1.1: - version "0.1.3" - resolved "http://registry.npm.taobao.org/right-align/download/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" - dependencies: - align-text "^0.1.1" - -rimraf@2, rimraf@^2.5.1, rimraf@^2.6.1: - version "2.6.2" - resolved "http://registry.npm.taobao.org/rimraf/download/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" - dependencies: - glob "^7.0.5" - -rimraf@~2.4.0: - version "2.4.5" - resolved "http://registry.npm.taobao.org/rimraf/download/rimraf-2.4.5.tgz#ee710ce5d93a8fdb856fb5ea8ff0e2d75934b2da" - dependencies: - glob "^6.0.1" - -safe-buffer@5.1.1, safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.1" - resolved "http://registry.npm.taobao.org/safe-buffer/download/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" - -safe-json-stringify@~1: - version "1.1.0" - resolved "http://registry.npm.taobao.org/safe-json-stringify/download/safe-json-stringify-1.1.0.tgz#bd2b6dad1ebafab3c24672a395527f01804b7e19" - -sax@0.5.x: - version "0.5.8" - resolved "http://registry.npm.taobao.org/sax/download/sax-0.5.8.tgz#d472db228eb331c2506b0e8c15524adb939d12c1" - -sax@^1.1.4: - version "1.2.4" - resolved "http://registry.npm.taobao.org/sax/download/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" - -semver@^5.3.0: - version "5.5.0" - resolved "http://registry.npm.taobao.org/semver/download/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" - -send@0.16.2: - version "0.16.2" - resolved "http://registry.npm.taobao.org/send/download/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1" - dependencies: - debug "2.6.9" - depd "~1.1.2" - destroy "~1.0.4" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "~1.6.2" - mime "1.4.1" - ms "2.0.0" - on-finished "~2.3.0" - range-parser "~1.2.0" - statuses "~1.4.0" - -serve-static@^1.10.0: - version "1.13.2" - resolved "http://registry.npm.taobao.org/serve-static/download/serve-static-1.13.2.tgz#095e8472fd5b46237db50ce486a43f4b86c6cec1" - dependencies: - encodeurl "~1.0.2" - escape-html "~1.0.3" - parseurl "~1.3.2" - send "0.16.2" - -set-blocking@~2.0.0: - version "2.0.0" - resolved "http://registry.npm.taobao.org/set-blocking/download/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - -set-immediate-shim@^1.0.1: - version "1.0.1" - resolved "http://registry.npm.taobao.org/set-immediate-shim/download/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" - -setprototypeof@1.0.3: - version "1.0.3" - resolved "http://registry.npm.taobao.org/setprototypeof/download/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04" - -signal-exit@^3.0.0: - version "3.0.2" - resolved "http://registry.npm.taobao.org/signal-exit/download/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" - -sntp@1.x.x: - version "1.0.9" - resolved "http://registry.npm.taobao.org/sntp/download/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" - dependencies: - hoek "2.x.x" - -sntp@2.x.x: - version "2.1.0" - resolved "http://registry.npm.taobao.org/sntp/download/sntp-2.1.0.tgz#2c6cec14fedc2222739caf9b5c3d85d1cc5a2cc8" - dependencies: - hoek "4.x.x" - -source-map@0.1.x: - version "0.1.43" - resolved "http://registry.npm.taobao.org/source-map/download/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346" - dependencies: - amdefine ">=0.0.4" - -source-map@~0.5.1: - version "0.5.7" - resolved "http://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" - -source-map@~0.6.1: - version "0.6.1" - resolved "http://registry.npm.taobao.org/source-map/download/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - -sprintf-js@^1.0.2: - version "1.1.1" - resolved "http://registry.npm.taobao.org/sprintf-js/download/sprintf-js-1.1.1.tgz#36be78320afe5801f6cea3ee78b6e5aab940ea0c" - -sprintf-js@~1.0.2: - version "1.0.3" - resolved "http://registry.npm.taobao.org/sprintf-js/download/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - -sshpk@^1.7.0: - version "1.14.1" - resolved "http://registry.npm.taobao.org/sshpk/download/sshpk-1.14.1.tgz#130f5975eddad963f1d56f92b9ac6c51fa9f83eb" - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - dashdash "^1.12.0" - getpass "^0.1.1" - optionalDependencies: - bcrypt-pbkdf "^1.0.0" - ecc-jsbn "~0.1.1" - jsbn "~0.1.0" - tweetnacl "~0.14.0" - -"statuses@>= 1.3.1 < 2", statuses@~1.4.0: - version "1.4.0" - resolved "http://registry.npm.taobao.org/statuses/download/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" - -statuses@~1.3.1: - version "1.3.1" - resolved "http://registry.npm.taobao.org/statuses/download/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e" - -string-width@^1.0.1, string-width@^1.0.2: - version "1.0.2" - resolved "http://registry.npm.taobao.org/string-width/download/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - -string_decoder@~0.10.x: - version "0.10.31" - resolved "http://registry.npm.taobao.org/string_decoder/download/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - -string_decoder@~1.0.3: - version "1.0.3" - resolved "http://registry.npm.taobao.org/string_decoder/download/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" - dependencies: - safe-buffer "~5.1.0" - -stringstream@~0.0.4, stringstream@~0.0.5: - version "0.0.5" - resolved "http://registry.npm.taobao.org/stringstream/download/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" - -strip-ansi@^3.0.0, strip-ansi@^3.0.1: - version "3.0.1" - resolved "http://registry.npm.taobao.org/strip-ansi/download/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - dependencies: - ansi-regex "^2.0.0" - -strip-ansi@^4.0.0: - version "4.0.0" - resolved "http://registry.npm.taobao.org/strip-ansi/download/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - dependencies: - ansi-regex "^3.0.0" - -strip-indent@^2.0.0: - version "2.0.0" - resolved "http://registry.npm.taobao.org/strip-indent/download/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" - -strip-json-comments@~2.0.1: - version "2.0.1" - resolved "http://registry.npm.taobao.org/strip-json-comments/download/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - -striptags@^2.1.1: - version "2.2.1" - resolved "http://registry.npm.taobao.org/striptags/download/striptags-2.2.1.tgz#4c450b708d41b8bf39cf24c49ff234fc6aabfd32" - -stylus@0.54.5, stylus@^0.54.5: - version "0.54.5" - resolved "http://registry.npm.taobao.org/stylus/download/stylus-0.54.5.tgz#42b9560931ca7090ce8515a798ba9e6aa3d6dc79" - dependencies: - css-parse "1.7.x" - debug "*" - glob "7.0.x" - mkdirp "0.5.x" - sax "0.5.x" - source-map "0.1.x" - -supports-color@^2.0.0: - version "2.0.0" - resolved "http://registry.npm.taobao.org/supports-color/download/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - -supports-color@^5.3.0: - version "5.3.0" - resolved "http://registry.npm.taobao.org/supports-color/download/supports-color-5.3.0.tgz#5b24ac15db80fa927cf5227a4a33fd3c4c7676c0" - dependencies: - has-flag "^3.0.0" - -swig-extras@0.0.1: - version "0.0.1" - resolved "http://registry.npm.taobao.org/swig-extras/download/swig-extras-0.0.1.tgz#b503fede372ab9c24c6ac68caf656bcef1872328" - dependencies: - markdown "~0.5.0" - -swig-templates@^2.0.2: - version "2.0.2" - resolved "http://registry.npm.taobao.org/swig-templates/download/swig-templates-2.0.2.tgz#d2502a7303019356f4ea76ea9065d4f58af6ab75" - dependencies: - optimist "~0.6" - uglify-js "2.6.0" - -"symbol-tree@>= 3.1.0 < 4.0.0": - version "3.2.2" - resolved "http://registry.npm.taobao.org/symbol-tree/download/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6" - -tar-pack@^3.4.0: - version "3.4.1" - resolved "http://registry.npm.taobao.org/tar-pack/download/tar-pack-3.4.1.tgz#e1dbc03a9b9d3ba07e896ad027317eb679a10a1f" - dependencies: - debug "^2.2.0" - fstream "^1.0.10" - fstream-ignore "^1.0.5" - once "^1.3.3" - readable-stream "^2.1.4" - rimraf "^2.5.1" - tar "^2.2.1" - uid-number "^0.0.6" - -tar@^2.2.1: - version "2.2.1" - resolved "http://registry.npm.taobao.org/tar/download/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" - dependencies: - block-stream "*" - fstream "^1.0.2" - inherits "2" - -text-table@^0.2.0: - version "0.2.0" - resolved "http://registry.npm.taobao.org/text-table/download/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - -"through@>=2.2.7 <3": - version "2.3.8" - resolved "http://registry.npm.taobao.org/through/download/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - -tildify@^1.2.0: - version "1.2.0" - resolved "http://registry.npm.taobao.org/tildify/download/tildify-1.2.0.tgz#dcec03f55dca9b7aa3e5b04f21817eb56e63588a" - dependencies: - os-homedir "^1.0.0" - -titlecase@^1.1.2: - version "1.1.2" - resolved "http://registry.npm.taobao.org/titlecase/download/titlecase-1.1.2.tgz#78113d1108086b8326331a3247dea8f5a49ea853" - -tough-cookie@^2.2.0, tough-cookie@~2.3.0, tough-cookie@~2.3.3: - version "2.3.4" - resolved "http://registry.npm.taobao.org/tough-cookie/download/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655" - dependencies: - punycode "^1.4.1" - -tr46@~0.0.1: - version "0.0.3" - resolved "http://registry.npm.taobao.org/tr46/download/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "http://registry.npm.taobao.org/tunnel-agent/download/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - dependencies: - safe-buffer "^5.0.1" - -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "http://registry.npm.taobao.org/tweetnacl/download/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - -type-check@~0.3.2: - version "0.3.2" - resolved "http://registry.npm.taobao.org/type-check/download/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - dependencies: - prelude-ls "~1.1.2" - -uglify-js@2.6.0: - version "2.6.0" - resolved "http://registry.npm.taobao.org/uglify-js/download/uglify-js-2.6.0.tgz#25eaa1cc3550e39410ceefafd1cfbb6b6d15f001" - dependencies: - async "~0.2.6" - source-map "~0.5.1" - uglify-to-browserify "~1.0.0" - yargs "~3.10.0" - -uglify-to-browserify@~1.0.0: - version "1.0.2" - resolved "http://registry.npm.taobao.org/uglify-to-browserify/download/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" - -uid-number@^0.0.6: - version "0.0.6" - resolved "http://registry.npm.taobao.org/uid-number/download/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" - -unpipe@~1.0.0: - version "1.0.0" - resolved "http://registry.npm.taobao.org/unpipe/download/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" - -upper-case@^1.1.1: - version "1.1.3" - resolved "http://registry.npm.taobao.org/upper-case/download/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" - -util-deprecate@~1.0.1: - version "1.0.2" - resolved "http://registry.npm.taobao.org/util-deprecate/download/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - -utils-merge@1.0.1, utils-merge@^1.0.0: - version "1.0.1" - resolved "http://registry.npm.taobao.org/utils-merge/download/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" - -uuid@^3.0.0, uuid@^3.1.0: - version "3.2.1" - resolved "http://registry.npm.taobao.org/uuid/download/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14" - -vary@~1.1.2: - version "1.1.2" - resolved "http://registry.npm.taobao.org/vary/download/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - -verror@1.10.0: - version "1.10.0" - resolved "http://registry.npm.taobao.org/verror/download/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - -warehouse@^2.2.0: - version "2.2.0" - resolved "http://registry.npm.taobao.org/warehouse/download/warehouse-2.2.0.tgz#5d09d64942992be667d8f7c86a09c2b8aea04062" - dependencies: - JSONStream "^1.0.7" - bluebird "^3.2.2" - cuid "~1.3.8" - graceful-fs "^4.1.3" - is-plain-object "^2.0.1" - lodash "^4.2.1" - -webidl-conversions@^2.0.0: - version "2.0.1" - resolved "http://registry.npm.taobao.org/webidl-conversions/download/webidl-conversions-2.0.1.tgz#3bf8258f7d318c7443c36f2e169402a1a6703506" - -whatwg-url-compat@~0.6.5: - version "0.6.5" - resolved "http://registry.npm.taobao.org/whatwg-url-compat/download/whatwg-url-compat-0.6.5.tgz#00898111af689bb097541cd5a45ca6c8798445bf" - dependencies: - tr46 "~0.0.1" - -which@^1.2.9: - version "1.3.0" - resolved "http://registry.npm.taobao.org/which/download/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a" - dependencies: - isexe "^2.0.0" - -wide-align@^1.1.0: - version "1.1.2" - resolved "http://registry.npm.taobao.org/wide-align/download/wide-align-1.1.2.tgz#571e0f1b0604636ebc0dfc21b0339bbe31341710" - dependencies: - string-width "^1.0.2" - -window-size@0.1.0: - version "0.1.0" - resolved "http://registry.npm.taobao.org/window-size/download/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" - -window-size@^0.1.4: - version "0.1.4" - resolved "http://registry.npm.taobao.org/window-size/download/window-size-0.1.4.tgz#f8e1aa1ee5a53ec5bf151ffa09742a6ad7697876" - -wordwrap@0.0.2: - version "0.0.2" - resolved "http://registry.npm.taobao.org/wordwrap/download/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" - -wordwrap@~0.0.2: - version "0.0.3" - resolved "http://registry.npm.taobao.org/wordwrap/download/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" - -wordwrap@~1.0.0: - version "1.0.0" - resolved "http://registry.npm.taobao.org/wordwrap/download/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" - -wrap-ansi@^2.0.0: - version "2.1.0" - resolved "http://registry.npm.taobao.org/wrap-ansi/download/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" - -wrappy@1: - version "1.0.2" - resolved "http://registry.npm.taobao.org/wrappy/download/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - -"xml-name-validator@>= 2.0.1 < 3.0.0": - version "2.0.1" - resolved "http://registry.npm.taobao.org/xml-name-validator/download/xml-name-validator-2.0.1.tgz#4d8b8f1eccd3419aa362061becef515e1e559635" - -y18n@^3.2.0: - version "3.2.1" - resolved "http://registry.npm.taobao.org/y18n/download/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" - -yallist@^2.1.2: - version "2.1.2" - resolved "http://registry.npm.taobao.org/yallist/download/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" - -yargs@^3.32.0: - version "3.32.0" - resolved "http://registry.npm.taobao.org/yargs/download/yargs-3.32.0.tgz#03088e9ebf9e756b69751611d2a5ef591482c995" - dependencies: - camelcase "^2.0.1" - cliui "^3.0.3" - decamelize "^1.1.1" - os-locale "^1.4.0" - string-width "^1.0.1" - window-size "^0.1.4" - y18n "^3.2.0" - -yargs@~3.10.0: - version "3.10.0" - resolved "http://registry.npm.taobao.org/yargs/download/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" - dependencies: - camelcase "^1.0.2" - cliui "^2.1.0" - decamelize "^1.0.0" - window-size "0.1.0"