头部背景图片
崇山峻岭 |
崇山峻岭 |

2018-06-12

多线程中的锁

今日学习笔记:

  1. atomic 作为iOS开发者对这个单次可能不算熟悉, 因为iOS定位于移动端, 为了降低APP的复杂度, 苹果规定UI操作必须要在主线程上操作, 所以我们所有的UI控件属性修饰符必须为nonatomic 这个单次恐怕没有不认识的吧! 那么atomic有啥作用呢, atomic的术语叫做原子的, 即当一个字段设置为atomic时, 这个字段同一时间只能被一个线程去修改, 也叫自旋锁. 下面为示例代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    //原子属性的name
    @property (atomic) NSString *name;

    //在多个线程中需要修改
    - (void)updateName {
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
    self.name = @"wangwang";
    }); //线程1

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
    self.name = @"liuliu";
    }); //线程2

    //当代码执行线程1时, name的产生自旋锁, 不允许其他线程来读取, 这时候就避免了在第一次修改的时候,同事进行第二次修改, 当然上面代码仅仅是为了说明问题, 才把两个线程写到一个方法中,实际应用中肯定不是这个样子的, 实际使用会更加复杂.

    }
  2. @synchronized() {}

    自旋锁能保证属性本身不被修改但是下面这种情况可能就不太好办了

    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
    @interface JLUser : NSObject
    //名字
    @property (atomic, strong) NSString *name;
    //职业
    @property (atomic, strong) NSString *profession;

    @end


    //调用此更新代码结果就可能和想的不太一样
    - (void)updateUser {
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
    [self updateUserWithName:@"张三" profession:@"老师"];
    });

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
    [self updateUserWithName:@"李四" profession:@"程序员"];
    });
    }


    - (void)updateUserWithName:(NSString *)name profession:(NSString *)profession {
    self.user.name = name;
    self.user.profession = profession;
    }

    //执行上面的更新代码后, 得到的user 可能是这样的, 张三 程序员, 李四 老师, 这是为啥呢, 主要是当第一个线程给name赋值写数据时, 第二个线程不能再去给name赋值, 很可能第二个线程就直接给age赋值了, 所以最后可能会导致name和profession对应不上为了解决这种问题我么引入 @synchronized () {}

    @synchronized() {} 是同步所, 小括号中传入一个固定不变的对象, 一般是要锁的对象, 也可以为其他对象, 花括号中为对应需要锁定的读写代码块, 这样当程序进入到花括号中是, 其他线程就无法再次对锁定的代码块进行调用, 上面的代码需要改动的地方如下:

    1
    2
    3
    4
    5
    6
    - (void)updateUserWithName:(NSString *)name profession:(NSString *)profession {
    @synchronized (self.user) {
    self.user.name = name;
    self.user.profession = profession;
    }
    }
  3. NSLock

    锁是进入临界区的基础构件。atomic 属性和 @synchronized 块是为了实现便捷实用的高级别抽象, 而NSLock是更低级别的锁

    使用如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @interface ThreadSafeClass () { 
    NSLock *_lock;
    }
    @end
    -(instancetype)init {
    if(self = [super init]) {
    _lock = [NSLock new];
    }
    return self;
    }
    -(void)safeMethod {
    [_lock lock];
    //进行线程安全的操作代码
    [_lock unlock];
    }
  4. NSRecursiveLock 安全锁, 用法和NSLock基本一样, 不一样的是在同一个类中, 有多个方法使用同一个锁进行同步, 并且一个方法中调用另一个方法, 这时候就需要使用这个锁;示例代码如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    @interface ThreadSafeClass () { 
    NSRecursiveLock *_lock;
    }
    @end
    -(instancetype)init {
    if(self = [super init]) {
    _lock = [NSRecursiveLock new];
    }
    return self;
    }
    -(void)safeMethod1 {
    [_lock lock];
    //进行线程安全的操作代码
    [self safeMethod2];
    [_lock unlock];
    }
    -(void)safeMethod2 {
    [_lock lock];
    //进行线程安全的操作代码
    [_lock unlock];
    }
  5. NSCondition 条件锁

    苹果官方文档解释如下:

    NSCondition类实现了一个条件变量,其语义遵循用于POSIX样式条件的语义。 条件对象在给定线程中充当锁和检查点。 锁在测试条件并执行由条件触发的任务时保护您的代码。 检查点行为要求在线程继续其任务之前条件为真。 当条件不成立时,线程阻塞。它一直处于阻塞状态,直到另一个线程发出信号通知条件对象;使用如下:

    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
    -(IBAction)conditionTest:(id)sender
    {
    NSLog(@"begin condition works!");

    //创建一个商品库
    self.products = [[NSMutableArray alloc] init];
    //初始化条件
    self.condition = [[NSCondition alloc] init];

    [NSThread detachNewThreadSelector:@selector(createProducter) toTarget:self withObject:nil];
    [NSThread detachNewThreadSelector:@selector(createConsumenr) toTarget:self withObject:nil];
    }

    //创建一个消费者
    - (void)createConsumenr {
    [self.condition lock];//进入锁定区域

    //判断库存, 如果为空 线程等待signal信号,
    while ([self.products count] == 0) {
    NSLog(@"wait for products");
    [self.condition wait];
    }
    //收到信号后开始消费商品,
    [self.products removeObjectAtIndex:0];
    NSLog(@"comsume a product");
    //离开锁定区
    [self.condition unlock];
    }
    //创建一个生产者
    - (void)createProducter {
    [self.condition lock];//进入锁定区域
    //开始生产
    [self.products addObject:[[NSObject alloc] init]];
    NSLog(@"produce a product");
    //完成生产,发出信号告知其他线程当前完成
    [self.condition signal];
    //跳出锁定区域
    [self.condition unlock];
    }
  1. GCD的线程安全

    暂时还没有研究, 等待补充, 今日有点晚了, 等有时间接着补充!

    ​ 2018-06-12 02:00:00记

2018-01-06

理财总结

​ 理财投资是件大事情,很多朋友觉得当下的自己没有足够的资金,所以想着先攒够一笔钱再去理财,实则不然,为什么?因为当你手里有几百万的资金的时候,你的投资策略根本就没有形成,怎么去驾驭这么多资金?理财不仅仅是去赚钱,而是去打造赚钱的机器,通俗的讲就是去建立一套理财体系,适合自己,并且自己能掌控的体系,当你发现你用1000块钱去理财的时候,搞了几个月亏了几百块,你应该庆幸,还好不是一千万,还有机会去慢慢调整策略,现在你能在一年内把1000块钱理成2000,两千变成4000,那么恭喜你你已经建立了一个很好的体系,y =200%*nm,n为年,m为本金,一旦有一个体系完成,请问赚钱难道不是躺着就能做到的吗?所以说赚钱和理财是一个可以并发执行的程序,不分先后,没有理财意识的你看到这,还没有冲动吗?动起自己银行卡里每年年华收益为负(据说年通货膨胀率8%,银行卡活期收益0.35%,余额宝最近4.x%)那笔小钱,为自己打造一台赚钱的机器,到时候只需把原料本金扔进去,时间动一下,你的资产就会加一点,除非时间倒流,否则你的收益将会源源不断的增加,人生第一个百万很快就会完成,很快王健林的小目标就变成了你的小目标。

​ 下面总结一下我的理财路程,2014年余额宝开启了我理财之路,很多人还不敢用支付宝的时候我已经把所有手里的钱全部放到了余额宝,那时候每天会来看看收益了一毛几,虽然明知道收益就是一毛几,后来接触了百度理财,年化6.+,15年11月接触了基金,那时候1000放进去一天涨了十几块,非常惊讶,然后就一股脑把手里仅有的五千多块钱全部放进去了,2016年一月份证监会实行熔断策略,开盘第一天大盘熔断,我的基金当天下跌八个点,意味着一天就跌掉了400多块,这样持续了几天,亏损了一千多块,这是我第一次真实感受到风险与收益并存,前面说过我应该庆幸当时只有五千块而不是五千万,17年5月接触p2p,这个相对股票稍微风险低一点的市场,开启了新的投资方式,2017半年多的时间,用20000多块资金撬动一切可以利用的资源,一共赚了5000多块,当然我目前的模式风险非常高但是对于这台机器的打造还在继续,相信这台机器必定能稳定运行,到时候我只需要往里面扔点钱,出来的都是白花花的银子,想想都很美,哈哈,以上纯属臆想。

​ 20180106总结理财

2017-08-24

iOS开发之VFL布局总结

VFL是苹果推出的用来AutoLayout布局的一门比较形象的语言, 本身为字符串,虽然用起来比较麻烦, 但是相比直接使用苹果的另一个套布局方案要少写一些代码, 那么问题来了, github上那么多自动布局的框架, 简单又好用, 为什么要用这么复杂的代码去布局呢, 之前我也这么想, 直到有一天自己想封装一个框架的的时候才发现, 不能处处依赖别人的框架去封装, 因为这样的话, 你封装的框架几乎没法用. 举个例子, 假设你的框架用了AFN的2.0版本, 那么对于使用框架的人来说, 他们项目中如果使用了AFN3.0, 那么肯定出出现一大堆兼容问题, 因此对于自己的封装的框架, 要以原生为主. 废话不多说 下面总结一下我半天的的学习成果: VFL布局

下面是VFL使用的一个核心方法

1
2
3
4
+ (NSArray<__kindof NSLayoutConstraint *> *)constraintsWithVisualFormat:(NSString *)format 
options:(NSLayoutFormatOptions)opts
metrics:(nullable NSDictionary<NSString *,id> *)metrics
views:(NSDictionary<NSString *, id> *)views;

该方法一共有四个参数, 接下来将一一讲解四个参数的含义及其使用:

  1. format参数: 这个是具体布局的字符串,形如 @”V:|-10-[view1(50.0)]-20-[view2(50.0)]” 稍后再讲解此字符串的意思;
  2. option参数: 这个参数是一个可复选的参数, 主要用来布局view的对齐方式;
  3. metrics参数: 这个参数是替换VFL字符串中的变量用的, 如果这么写: @{@”topMargin”:@10} 则第一条中的布局可以替换成下面的写法: @”V:|-10-[view1(50.0)]-20-[view2(50.0)]” 其实就是替换字符串中的变量使用;
  4. views参数: 这个参数是用来存储本条VFL语句中所有用到的View, 可以直接创建字典 @{@”view1”:view1, @”view2”:view2} 对于这个参数苹果有一个特定的宏, NSDictionaryOfVariableBindings() 因此也可以这样写 NSDictionaryOfVariableBindings(view1, view2) ;

该方法返回的是一个装有约束的数组集合;可以直接添加到设定约束view的父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
40
41
42
43
44
- (void)viewDidLoad {
[super viewDidLoad];
[self testConstraint];

}
- (void)testConstraint {//测试布局

UIView *supView1 = [[UIView alloc] init];
supView1.backgroundColor = [UIColor blueColor];

UIView *supView2 = [[UIView alloc] init];
supView2.backgroundColor = [UIColor blueColor];

[self.view addSubview:supView1];
[self.view addSubview:supView2];


//由于AutoLayout布局和Autoresizing布局是冲突的,因此要使用AutoLayout必须要关闭Autoresizing
self.view.translatesAutoresizingMaskIntoConstraints = NO;
supView2.translatesAutoresizingMaskIntoConstraints = NO;
supView1.translatesAutoresizingMaskIntoConstraints = NO;



//此语句的意思是 垂直方向: supView1距离父view的距离为 topMargin,
//supView1的垂直方向的宽度为50.0, supView2距离supView1为20, 并且高度也为50.0;
NSString *top = @"V:|-topMargin-[supView1(50.0)]-20-[supView2(50.0)]";
//解释方法同上
NSString *left = @"H:|-10-[supView1(50.0)]";
NSString *v2T = @"[supView2(60.0)]";


//此处需要对topMargin参数进行解释, 其实就是给topMargin赋值;
NSArray *s1T = [NSLayoutConstraint constraintsWithVisualFormat:top options:NSLayoutFormatAlignAllRight metrics:@{@"topMargin":@100} views:NSDictionaryOfVariableBindings(supView1, supView2)];

NSArray *s1L = [NSLayoutConstraint constraintsWithVisualFormat:left options:0 metrics:nil views:NSDictionaryOfVariableBindings(supView1)];
NSArray *v2Tc = [NSLayoutConstraint constraintsWithVisualFormat:v2T options:0 metrics:nil views:NSDictionaryOfVariableBindings(supView2)];

//一般需要把约束添加到父view上
[self.view addConstraints:v2Tc];
[self.view addConstraints:s1T];
[self.view addConstraints:s1L];

}

效果如下图:

40155A04-EA83-4B25-A802-B601F830EA29.png

上面的例子只是简单的使用了一下VFL语句, 其实还有更多的东西, 需要自己去慢慢的理解体会多使用, 熟练了就好了, 对于其他的VFL语句的用法请参考附录里面的详细使用

附录

  1. 符号含义

    1
    2
    3
    4
    5
    6
    7
    8
    |: 	表示父视图
    -: 表示距离
    V: 表示垂直
    H: 表示水平
    >=: 表示视图间距、宽度和高度必须大于或等于某个值
    <= :表示视图间距、宽度和高度必须小宇或等于某个值
    == :表示视图间距、宽度或者高度必须等于某个值
    @: 优先级 最大为 1000
  2. 一般用法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    |-[view]-|: 						    视图处在父视图的左右边缘内
    |-[view] : 视图处在父视图的左边缘
    |[view] : 视图和父视图左边对齐
    V:[view(100.)] : 设置视图的高度
    H:[view(100.)] : 设置视图的宽度
    |-30.0-[view]-30.0-|: 表示离父视图 左右间距 30
    |-[view(view1)]-[view1]-| : View和view1视图宽度一样,并且在父视图内
    V:|-padding-[imageView]->=0-[button]-padding| : 表示离父视图的距离为Padding,这两个视图间距必须大于或等于0并且距离底部父视图为padding。此时必须对 metrics参数赋值eg. metrics:@{@"topMargin":@100};

    [wideView(>=60@700)] :视图的宽度为至少为60 优先级为700

    最后要注意, H可以忽略, 默认为水平布局 , V必须要写!!!

好了码了半天终于大功告成, 希望此博客对你有帮助, 记得点赞哦~

2017-08-18

iOS开发 const 和 static 关键字的作用

1. static

1)在函数体内,一个被声明为静态的变量在这一函数被调用过程中维持其值不变(该变量存放在静态变量区)。

2) 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。

3) 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。

 

2.const

如果你读过Bjarne的《The C++ Programming Language》,就应该接触过他所提到的办法,把*读成”pointer to(指向)”,同时声明从右向左读。解释如下:

char const p; // p is a const pointer to char; p是指向char的常量指针(char不是常量)char const p; // p is a pointer to const char; p是指向常量char的指针(p不是常量)const char p; // 同上。因为C++标准规定,const关键字放在类型或变量名之前等价的。C++里面没有const的运算符

1) 关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理其它人留下的垃圾,你就会很快学会感谢这点多余的信息。(当然,懂得用const的程序员很少会留下的垃圾让别人来清理的。)

2) 通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。

3) 合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现。

const关键字至少有下列n个作用:

(1)欲阻止一个变量被改变,可以使用const关键字。在定义该const变量时,通常需要对它进行初始化,因为以后就没有机会再去改变它了;
(2)对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const;
(3)在一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;
(4)对于类的成员函数,若指定其为const类型,则表明其是一个常函数,不能修改类的成员变量;
(5)对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为“左值”。例如:
const classA operator(const classA& a1,const classA& a2);
  operator
的返回结果必须是一个const对象。如果不是,这样的变态代码也不会编译出错:
classA a, b, c;
(a b) = c; // 对ab的结果赋值
  操作(a * b) = c显然不符合编程者的初衷,也没有任何意义。

2017-06-16

iOS开发简单的绘图

1. 画线

1
2
3
4
5
6
7
8
9
- (void)test1 {//画一根线
//1, 获取上下文
CGContextRef contextRef = UIGraphicsGetCurrentContext();
//2. 起点和终点
CGContextMoveToPoint(contextRef, 0, 0);
CGContextAddLineToPoint(contextRef, 100, 100);
//3. 绘制
CGContextStrokePath(contextRef);
}

效果如下图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (void)test2 {//画多个线
//获取上下文
CGContextRef contextRef = UIGraphicsGetCurrentContext();
//设置线的路径, c语言数组
CGPoint points[] = {
CGPointMake(0, 0),
CGPointMake(50, 50),
CGPointMake(0, 50),
CGPointMake(50, 100),
CGPointMake(0, 0)
};
CGContextAddLines(contextRef, points, 5);
//绘制
CGContextStrokePath(contextRef);
}

效果如下图:

1
2
3
4
5
6
7
8
- (void)test3 {//画矩形

CGContextRef contextRef = UIGraphicsGetCurrentContext();
//添加路径
CGContextAddRect(contextRef, CGRectMake(20, 40, 100, 100));
// 绘制
CGContextStrokePath(contextRef);
}

效果如下图:

1
2
3
4
5
6
7
8
9
10
11
12
- (void)test4 {//画圆弧
//获取当前上下文
CGContextRef contextRef = UIGraphicsGetCurrentContext();
//设置起点
CGContextMoveToPoint(contextRef, 200, 50);
//添加路径 这个方法用起来有点复杂, 首先要有当前点假设为P0(200, 50),
//其次是第一个点P1(50, 50), 然后是第二个点P2(50, 200);, 最后一个参数是半径,
//绘制先从P0->P1, 画一条直线, 然后画一条弧线, 会显得要求是与直线P0P1, 和直线P1P2 相切,并且半径是50.
CGContextAddArcToPoint(contextRef, 50, 50, 50, 200, 50);
//绘制
CGContextStrokePath(contextRef);
}

效果如下图

1
2
3
4
5
6
7
8
9
10
- (void)test5 {//圆弧2 or圆
CGContextRef contextRef = UIGraphicsGetCurrentContext();
//设置路径
// CGContextAddArc(上下文, 圆心x, 圆心y, 半径, 其实弧度, 结束弧度, 方向(逆时针和顺时针))
// CGContextAddArc(contextRef, 50, 50, 50, 0, M_PI, 0);//圆弧
CGContextAddArc(contextRef, 50, 50, 50, 0, 2*M_PI, 0);//圆形

CGContextStrokePath(contextRef);

}

效果如下图:

1
2
3
4
5
6
7
8
9
10
- (void)test6 { //画一个同心圆
CGContextRef contextRef = UIGraphicsGetCurrentContext();
CGContextAddArc(contextRef, 50, 50, 50, 0, 2*M_PI, 0);//圆形
//画过一个之后要讲其实点移动到下一个圆上,
CGContextMoveToPoint(contextRef, 80, 50);


CGContextAddArc(contextRef, 50, 50, 30, 0, 2*M_PI, 0);//圆形
CGContextStrokePath(contextRef);
}

效果如下图:

由于时间原因从下面开始不再截图 , 想看效果的可以自己拷贝, 或者下载我的源码自己查看, 哈哈,

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
- (void)test10 { //关闭路径的设置
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGPoint points[4] = {
CGPointMake(50, 50),
CGPointMake(150, 50),
CGPointMake(150, 150),
CGPointMake(50, 150)
};
CGContextAddLines(ctx, points, 4);
CGContextClosePath(ctx);//连接起始点

// CGContextFillPath(ctx); //填充
// CGContextStrokePath(ctx); //划线,
// CGPathDrawingMode {
// kCGPathFill, //填充
// kCGPathEOFill, // eo填充, 即 奇偶填充
// kCGPathStroke,//划线
// kCGPathFillStroke,//填充划线
// kCGPathEOFillStroke//奇偶划线
// }
[[UIColor blueColor] setFill];
CGContextDrawPath(ctx, kCGPathFill);// 等同CGContextFillPath(ctx)
// CGContextDrawPath(ctx, kCGPathEOFill);
// CGContextDrawPath(ctx, kCGPathStroke);//等同 CGContextStrokePath(ctx);
// CGContextDrawPath(ctx, kCGPathFillStroke);
// CGContextDrawPath(ctx, kCGPathEOFillStroke);
}



- (void)test9 {//贝塞尔曲线
CGContextRef contextRef = UIGraphicsGetCurrentContext();
CGContextMoveToPoint(contextRef, 50, 50);
// CGContextAddQuadCurveToPoint(上下文, 控制点x, 控制点y, 结束点x, 结束点y)
CGContextAddQuadCurveToPoint(contextRef, 100, 0, 150, 50);
CGContextStrokePath(contextRef);

}


- (void)test8 { //虚线
CGContextRef contextRef = UIGraphicsGetCurrentContext();
CGContextMoveToPoint(contextRef, 0, 20);
CGContextAddLineToPoint(contextRef, 200, 20);
CGFloat lengths[] = {
10, 5, 20, 30
};
// CGContextSetLineDash(上下文, 偏移(第一段线段的长度为 lengths[0]-偏移的距离), 线段数组, 数量);

CGContextSetLineDash(contextRef, 5, lengths, 4);
CGContextStrokePath(contextRef);

}


- (void)test7 {//设置线的特性
CGContextRef contextRef = UIGraphicsGetCurrentContext();

CGContextAddRect(contextRef, CGRectMake(50, 50, 150, 150));
CGContextSetLineWidth(contextRef, 5);//线宽
// CGContextSetRGBStrokeColor(contextRef, 1, 0, 0, 1);//颜色
[[UIColor blueColor] setStroke];//同上,
CGContextSetLineJoin(contextRef, kCGLineJoinRound);

CGContextStrokePath(contextRef);

}

以上是绘图的入门, 绘制基本的几何图形, 时间允许的话, 我再总结一下稍微复杂点的绘图内容

上面的demo地址: https://github.com/wangjunling888/DemoOfDrawPicture.git

2017-06-08

数组中取出最大值

1. 项目需求

​ 有一个模型数组arr, 其中模型model中有个字段num, 我需要找出所有模型中最大的一个

2. 解决方案分析

​ 我们第一印象肯定是遍历数组, 取出模型, 然后在取出num字段, 然后进行比较, 取出最大值. 但是本文, 就要另辟蹊径, 用一个更优雅的方法两行代买搞定

3. 代码

1
2
3
4
// 通过kvc方式取出num
NSArray *temArr = [arr valueForKeyPath:@"num"];
// 这步是重点,直接取出最大值
CGFloat maxNum = [[temArr valueForKeyPath:@"@max.integerValue"] integerValue];

4. 注意事项

​ 如果你的模型中的num不是对象, 那么第一行代码就会直接崩掉, 因为基础类型的数是不能放到oc数组中的 , 所以, 建立模型的时候一定要用NSNmuber类型的num字段, 这样才能放入temArr数组, 当然如果你直接拥有一个装有NSNmuber类型的数组, 直接执行第二步, 就可以获得组大值.

5. 扩展

​ 仿照上面的用法还可以获得数组中的最小值, 平均值等的 key值如下,

1
2
3
4
@”@max.floatValue”//(获取最大值), 
@”@min.floatValue”//(获取最小值),
@”@avg.floatValue” //(获取平均值),
@”@count.floatValue”//(获取数组大小)

好了这次就总结到这里, 希望看到此页的你撸码愉快!!!!!!

2017-03-14

JPush的坑

​ 搞了一下午, 没有高出来, 最终在一个同事的提醒下终于弄好了, 废话不多说直接上代码

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
/**
初始化极光推送
*/
- (void)setJPush:(NSDictionary *)launchOptions {


//初始化APNs
JPUSHRegisterEntity * entity = [[JPUSHRegisterEntity alloc] init];
entity.types = JPAuthorizationOptionAlert|JPAuthorizationOptionBadge|JPAuthorizationOptionSound;
if ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0) {
// 可以添加自定义categories
// NSSet<UNNotificationCategory *> *categories for iOS10 or later
// NSSet<UIUserNotificationCategory *> *categories for iOS8 and iOS9
}
[JPUSHService registerForRemoteNotificationConfig:entity delegate:self];


//初始换JPush
[JPUSHService setupWithOption:launchOptions appKey:JPushAppKey channel:nil apsForProduction:NO];

//设置别名
[JPUSHService setTags:nil alias:@"abc" fetchCompletionHandle:^(int iResCode, NSSet *iTags, NSString *iAlias) {
NSLog(@"%@", iAlias);
}];

// [[NSNotificationCenter defaultCenter] addObserver:self
// selector:@selector(networkDidLogin:)
// name:kJPFNetworkDidLoginNotification
// object:nil];
// 关掉无法无天的log, 需要时再开启
[JPUSHService setLogOFF];

}

上面就是我的代码, 完全按照极光官方文档集成, 但是就是绑定不了别名。

下面我来说一下原因, 在JPush刚刚初始化后面直接调用设置标签很有可能极光那边还没有登录完成, 因为都是异步网络请求, 所以此时去绑定标签或者别名肯定是不行的, 解决方案就是 在初始化的地方设置登录极光成功的通知, 极光sdk中有一下几个通知, 并且还给了中文注释, 不愧为中国人写的哈,

1
2
3
4
5
6
7
8
extern NSString *const kJPFNetworkIsConnectingNotification; // 正在连接中
extern NSString *const kJPFNetworkDidSetupNotification; // 建立连接
extern NSString *const kJPFNetworkDidCloseNotification; // 关闭连接
extern NSString *const kJPFNetworkDidRegisterNotification; // 注册成功
extern NSString *const kJPFNetworkFailedRegisterNotification; //注册失败
extern NSString *const kJPFNetworkDidLoginNotification; // 登录成功
extern NSString *const kJPFNetworkDidReceiveMessageNotification; // 收到消息(非APNS)
extern NSString *const kJPFServiceErrorNotification; // 错误提示

所以, 正确的姿势应该这么写:

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

/**
初始化极光推送
*/
- (void)setJPush:(NSDictionary *)launchOptions {


//初始化APNs
JPUSHRegisterEntity * entity = [[JPUSHRegisterEntity alloc] init];
entity.types = JPAuthorizationOptionAlert|JPAuthorizationOptionBadge|JPAuthorizationOptionSound;
if ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0) {
// 可以添加自定义categories
// NSSet<UNNotificationCategory *> *categories for iOS10 or later
// NSSet<UIUserNotificationCategory *> *categories for iOS8 and iOS9
}
[JPUSHService registerForRemoteNotificationConfig:entity delegate:self];


//初始换JPush
[JPUSHService setupWithOption:launchOptions appKey:JPushAppKey channel:nil apsForProduction:NO];
//添加监听通知
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(networkDidLogin:)
name:kJPFNetworkDidLoginNotification
object:nil];
[JPUSHService setLogOFF];


}
- (void)networkDidLogin:(NSNotification *)notification {
[JPUSHService setTags:nil alias:@"abc" fetchCompletionHandle:^(int iResCode, NSSet *iTags, NSString *iAlias) {
NSLog(@"%@", iAlias);
}];
//移除通知
[[NSNotificationCenter defaultCenter] removeObserver:self
name:kJPFNetworkDidLoginNotification
object:nil];

}

经过上面的改造成功设置别名和标签, 希望做到这的小伙伴们别再才坑, 哦, 对了, 通知一定要记得移除哦!

2017-01-09

阿里百川反馈和支付宝sdk冲突解决方案

总结一下从昨天晚上到今天上午踩过的坑, 希望能对大家有所帮助!

很多时候很多大事件貌似和我们没有关系, 但是冥冥中可能某天, 你就会感受到某个事件的影响, 比如阿里巴友盟收购了, 之前还觉得只是一个大公司收购一个小公司, 和自己没有啥关系, 但是慢慢地, 有些问题就开始暴露了, 前段时间, 友盟用户反馈sdk 突然停止服务, 给了一个阿里百川的服务, 让去迁移, 然后作为开发者, 你可以选择不用这些第三方服务, 但是为了节约开发成本, 又不得不去使用. 无奈只有去迁移.

接第三方, 一个看起来类似搬运工的活, 但是有时候也会有很多坑要踩. 下面就说一下我遇到的坑吧!

1. cocoapods 导入YWFeedback的坑

​ 因为项目中大部分第三方库都是使用cocoapods管理, 那么要接入第三方第一件事情肯定是要看看是否支持cocoapods, 看官方接入文档说可以支持cocoapods, 然后就按照文档写的去接入, 文档如下:

1
2
3
4
5
6
source 'http://repo.baichuan-ios.taobao.com/baichuanSDK/AliBCSpecs.git'
source 'http://repo.baichuan-ios.taobao.com/baichuanSDK/AliBCSpecsMirror.git'

target 'YourTargetName' do
pod 'YWFeedbackFMWK', '~> 2.0.3.1'
end

看完果断Copy and paste, 然后通过终端执行命令 pod update –no-repo-update 然后就是静待, 这个过程非常慢, 然后我就一直盯着终端 , 突然就发现一个问题, 就是明明我是执行的更新, 不知道为啥所有文件都重新安装了一遍, 然后安装到baidumapsdk的时候突然报错, 一大堆红, 看了一下, 貌似说是远程端的错误, 好像是不存在这样的库, 反正没有懂, 但是据我猜测应该是 Copy过来的前两行 source的问题, 于是乎吧那两行注释掉重新更新, 这次倒是很干脆直接报 YWFeedbackFMWK 不存在, 然后就联想了一下, 难道这个东西必须得上这个source上下载? 但是使用这两行代码就会报错, 这可如何是好, 以前从来没有研究过 podfile文件, 这次实在搞不懂, 于是乎开始找podfile 语法, 百度关键字”podfile 语法” 找到了一个比较优秀的文章 Podfile语法参考(译) 里面写了这样一段内容, 直接解决了我的疑问

Sources

Podfile检索了所有sources(repos)中的specs

Sources是全局的,不存储在每个target定义里面

source

指定specs的位置

使用这个方法指定sources。sources的顺序是有关系的的。CocoaPods将使用pod第一次出现的source中的最高版本(后续的source中哪怕出现了更高的版本,也不予考虑)

cocoapods 官方source是隐式的需要的,一旦你指定了其他source 你就需要也把官方的指定上

例子:

指定Artsy库然后带上官方的

1
2
3
4
CocoaPods Master Repository

source 'https://github.com/artsy/Specs.git'
source 'https://github.com/CocoaPods/Specs.git'

也就是说我的podfile文件中没有指定 默认的两个source, 导致了百度地图等很多第三方库找不到, 所以报错 加入这两行(注意一定要注意顺序) , 然后就顺利解决了这个问题, 然后继续在终端中执行pod update –no-repo-update 本以为万事大吉, 但是事情总是出乎意料, 接下来就开始说重点了: 支付宝AlipaySDK与阿里百川用户反馈YWFeedbackFMWK的冲突!

pod库更新完成后, 编译,报一个error, 按理说通过pod导入的第三方库, 环境都是默认配置好的, 不应该报错的, 但总是用这么一个意外. 下面是是报错信息我直接拷贝出来吧,

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
ld: warning: object file (/Users/wangjunling/Desktop/AAA-iOS/khb/Vendor/UMSocial_Sdk_Extra_Frameworks/SinaSSO/libSocialSinaSSO.a(UMSocialSinaSSOHandler.o)) was built for newer iOS version (8.2) than being linked (7.0)
duplicate symbol _OBJC_METACLASS_$_UTDIDAES in:
/Users/wangjunling/Desktop/AAA-iOS/Pods/Pingpp/lib/Channels/Alipay/AlipaySDK.framework/AlipaySDK
/Users/wangjunling/Desktop/AAA-iOS/Pods/UTDID/UTDID.framework/UTDID(UTDIDAES.o)
duplicate symbol _OBJC_CLASS_$_UTDIDAES in:
/Users/wangjunling/Desktop/AAA-iOS/Pods/Pingpp/lib/Channels/Alipay/AlipaySDK.framework/AlipaySDK
/Users/wangjunling/Desktop/AAA-iOS/Pods/UTDID/UTDID.framework/UTDID(UTDIDAES.o)
duplicate symbol _OBJC_METACLASS_$_UTDIDBaseUtils in:
/Users/wangjunling/Desktop/AAA-iOS/Pods/Pingpp/lib/Channels/Alipay/AlipaySDK.framework/AlipaySDK
/Users/wangjunling/Desktop/AAA-iOS/Pods/UTDID/UTDID.framework/UTDID(UTDIDBaseUtils.o)
duplicate symbol _OBJC_CLASS_$_UTDIDBaseUtils in:
/Users/wangjunling/Desktop/AAA-iOS/Pods/Pingpp/lib/Channels/Alipay/AlipaySDK.framework/AlipaySDK
/Users/wangjunling/Desktop/AAA-iOS/Pods/UTDID/UTDID.framework/UTDID(UTDIDBaseUtils.o)
duplicate symbol _OBJC_CLASS_$_AidRequester in:
/Users/wangjunling/Desktop/AAA-iOS/Pods/Pingpp/lib/Channels/Alipay/AlipaySDK.framework/AlipaySDK
/Users/wangjunling/Desktop/AAA-iOS/Pods/UTDID/UTDID.framework/UTDID(AidRequester.o)
duplicate symbol _OBJC_METACLASS_$_AidRequester in:
/Users/wangjunling/Desktop/AAA-iOS/Pods/Pingpp/lib/Channels/Alipay/AlipaySDK.framework/AlipaySDK
/Users/wangjunling/Desktop/AAA-iOS/Pods/UTDID/UTDID.framework/UTDID(AidRequester.o)
duplicate symbol _OBJC_CLASS_$_AidManager in:
/Users/wangjunling/Desktop/AAA-iOS/Pods/Pingpp/lib/Channels/Alipay/AlipaySDK.framework/AlipaySDK
/Users/wangjunling/Desktop/AAA-iOS/Pods/UTDID/UTDID.framework/UTDID(AidManager.o)
duplicate symbol _OBJC_METACLASS_$_AidManager in:
/Users/wangjunling/Desktop/AAA-iOS/Pods/Pingpp/lib/Channels/Alipay/AlipaySDK.framework/AlipaySDK
/Users/wangjunling/Desktop/AAA-iOS/Pods/UTDID/UTDID.framework/UTDID(AidManager.o)
duplicate symbol _OBJC_METACLASS_$_UTDIDIntUtils in:
/Users/wangjunling/Desktop/AAA-iOS/Pods/Pingpp/lib/Channels/Alipay/AlipaySDK.framework/AlipaySDK
/Users/wangjunling/Desktop/AAA-iOS/Pods/UTDID/UTDID.framework/UTDID(UTDIDIntUtils.o)
duplicate symbol _OBJC_CLASS_$_UTDIDIntUtils in:
/Users/wangjunling/Desktop/AAA-iOS/Pods/Pingpp/lib/Channels/Alipay/AlipaySDK.framework/AlipaySDK
/Users/wangjunling/Desktop/AAA-iOS/Pods/UTDID/UTDID.framework/UTDID(UTDIDIntUtils.o)
duplicate symbol _OBJC_METACLASS_$_UTDIDStringUtils in:
/Users/wangjunling/Desktop/AAA-iOS/Pods/Pingpp/lib/Channels/Alipay/AlipaySDK.framework/AlipaySDK
/Users/wangjunling/Desktop/AAA-iOS/Pods/UTDID/UTDID.framework/UTDID(UTDIDStringUtils.o)
duplicate symbol _OBJC_CLASS_$_UTDIDStringUtils in:
/Users/wangjunling/Desktop/AAA-iOS/Pods/Pingpp/lib/Channels/Alipay/AlipaySDK.framework/AlipaySDK
/Users/wangjunling/Desktop/AAA-iOS/Pods/UTDID/UTDID.framework/UTDID(UTDIDStringUtils.o)
duplicate symbol _OBJC_CLASS_$_UTDIDTypeConvert in:
/Users/wangjunling/Desktop/AAA-iOS/Pods/Pingpp/lib/Channels/Alipay/AlipaySDK.framework/AlipaySDK
/Users/wangjunling/Desktop/AAA-iOS/Pods/UTDID/UTDID.framework/UTDID(UTDIDTypeConvert.o)
duplicate symbol _OBJC_METACLASS_$_UTDIDTypeConvert in:
/Users/wangjunling/Desktop/AAA-iOS/Pods/Pingpp/lib/Channels/Alipay/AlipaySDK.framework/AlipaySDK
/Users/wangjunling/Desktop/AAA-iOS/Pods/UTDID/UTDID.framework/UTDID(UTDIDTypeConvert.o)
duplicate symbol _OBJC_CLASS_$_UTDIDMain in:
/Users/wangjunling/Desktop/AAA-iOS/Pods/Pingpp/lib/Channels/Alipay/AlipaySDK.framework/AlipaySDK
/Users/wangjunling/Desktop/AAA-iOS/Pods/UTDID/UTDID.framework/UTDID(UTDIDMain.o)
duplicate symbol _OBJC_METACLASS_$_UTDIDMain in:
/Users/wangjunling/Desktop/AAA-iOS/Pods/Pingpp/lib/Channels/Alipay/AlipaySDK.framework/AlipaySDK
/Users/wangjunling/Desktop/AAA-iOS/Pods/UTDID/UTDID.framework/UTDID(UTDIDMain.o)
duplicate symbol _OBJC_CLASS_$_UTDIDOpenUDID in:
/Users/wangjunling/Desktop/AAA-iOS/Pods/Pingpp/lib/Channels/Alipay/AlipaySDK.framework/AlipaySDK
/Users/wangjunling/Desktop/AAA-iOS/Pods/UTDID/UTDID.framework/UTDID(UTDIDOpenUDID.o)
duplicate symbol _OBJC_METACLASS_$_UTDIDOpenUDID in:
/Users/wangjunling/Desktop/AAA-iOS/Pods/Pingpp/lib/Channels/Alipay/AlipaySDK.framework/AlipaySDK
/Users/wangjunling/Desktop/AAA-iOS/Pods/UTDID/UTDID.framework/UTDID(UTDIDOpenUDID.o)
duplicate symbol _OBJC_CLASS_$_UTDIDHelper in:
/Users/wangjunling/Desktop/AAA-iOS/Pods/Pingpp/lib/Channels/Alipay/AlipaySDK.framework/AlipaySDK
/Users/wangjunling/Desktop/AAA-iOS/Pods/UTDID/UTDID.framework/UTDID(UTDIDHelper.o)
duplicate symbol _OBJC_METACLASS_$_UTDIDHelper in:
/Users/wangjunling/Desktop/AAA-iOS/Pods/Pingpp/lib/Channels/Alipay/AlipaySDK.framework/AlipaySDK
/Users/wangjunling/Desktop/AAA-iOS/Pods/UTDID/UTDID.framework/UTDID(UTDIDHelper.o)
duplicate symbol _OBJC_CLASS_$_AidStorage in:
/Users/wangjunling/Desktop/AAA-iOS/Pods/Pingpp/lib/Channels/Alipay/AlipaySDK.framework/AlipaySDK
/Users/wangjunling/Desktop/AAA-iOS/Pods/UTDID/UTDID.framework/UTDID(AidStorage.o)
duplicate symbol _OBJC_METACLASS_$_AidStorage in:
/Users/wangjunling/Desktop/AAA-iOS/Pods/Pingpp/lib/Channels/Alipay/AlipaySDK.framework/AlipaySDK
/Users/wangjunling/Desktop/AAA-iOS/Pods/UTDID/UTDID.framework/UTDID(AidStorage.o)
duplicate symbol _OBJC_CLASS_$_UTDevice in:
/Users/wangjunling/Desktop/AAA-iOS/Pods/Pingpp/lib/Channels/Alipay/AlipaySDK.framework/AlipaySDK
/Users/wangjunling/Desktop/AAA-iOS/Pods/UTDID/UTDID.framework/UTDID(UTDevice.o)
duplicate symbol _OBJC_METACLASS_$_UTDevice in:
/Users/wangjunling/Desktop/AAA-iOS/Pods/Pingpp/lib/Channels/Alipay/AlipaySDK.framework/AlipaySDK
/Users/wangjunling/Desktop/AAA-iOS/Pods/UTDID/UTDID.framework/UTDID(UTDevice.o)
duplicate symbol _OBJC_IVAR_$_UTDIDKeychainItemWrapper.genericPasswordQuery in:
/Users/wangjunling/Desktop/AAA-iOS/Pods/Pingpp/lib/Channels/Alipay/AlipaySDK.framework/AlipaySDK
/Users/wangjunling/Desktop/AAA-iOS/Pods/UTDID/UTDID.framework/UTDID(UTDIDKeychainItemWrapper.o)
duplicate symbol _OBJC_IVAR_$_UTDIDKeychainItemWrapper.keychainItemData in:
/Users/wangjunling/Desktop/AAA-iOS/Pods/Pingpp/lib/Channels/Alipay/AlipaySDK.framework/AlipaySDK
/Users/wangjunling/Desktop/AAA-iOS/Pods/UTDID/UTDID.framework/UTDID(UTDIDKeychainItemWrapper.o)
duplicate symbol _OBJC_CLASS_$_UTDIDKeychainItemWrapper in:
/Users/wangjunling/Desktop/AAA-iOS/Pods/Pingpp/lib/Channels/Alipay/AlipaySDK.framework/AlipaySDK
/Users/wangjunling/Desktop/AAA-iOS/Pods/UTDID/UTDID.framework/UTDID(UTDIDKeychainItemWrapper.o)
duplicate symbol _OBJC_METACLASS_$_UTDIDKeychainItemWrapper in:
/Users/wangjunling/Desktop/AAA-iOS/Pods/Pingpp/lib/Channels/Alipay/AlipaySDK.framework/AlipaySDK
/Users/wangjunling/Desktop/AAA-iOS/Pods/UTDID/UTDID.framework/UTDID(UTDIDKeychainItemWrapper.o)
duplicate symbol _OBJC_CLASS_$_UTDIDPersistentConf in:
/Users/wangjunling/Desktop/AAA-iOS/Pods/Pingpp/lib/Channels/Alipay/AlipaySDK.framework/AlipaySDK
/Users/wangjunling/Desktop/AAA-iOS/Pods/UTDID/UTDID.framework/UTDID(UTDIDPersistentConf.o)
duplicate symbol _OBJC_METACLASS_$_UTDIDPersistentConf in:
/Users/wangjunling/Desktop/AAA-iOS/Pods/Pingpp/lib/Channels/Alipay/AlipaySDK.framework/AlipaySDK
/Users/wangjunling/Desktop/AAA-iOS/Pods/UTDID/UTDID.framework/UTDID(UTDIDPersistentConf.o)
duplicate symbol _OBJC_CLASS_$_UTDIDPersistentFile in:
/Users/wangjunling/Desktop/AAA-iOS/Pods/Pingpp/lib/Channels/Alipay/AlipaySDK.framework/AlipaySDK
/Users/wangjunling/Desktop/AAA-iOS/Pods/UTDID/UTDID.framework/UTDID(UTDIDPersistentFile.o)
duplicate symbol _OBJC_METACLASS_$_UTDIDPersistentFile in:
/Users/wangjunling/Desktop/AAA-iOS/Pods/Pingpp/lib/Channels/Alipay/AlipaySDK.framework/AlipaySDK
/Users/wangjunling/Desktop/AAA-iOS/Pods/UTDID/UTDID.framework/UTDID(UTDIDPersistentFile.o)
duplicate symbol _OBJC_METACLASS_$_UTDIDGTMBase64 in:
/Users/wangjunling/Desktop/AAA-iOS/Pods/Pingpp/lib/Channels/Alipay/AlipaySDK.framework/AlipaySDK
/Users/wangjunling/Desktop/AAA-iOS/Pods/UTDID/UTDID.framework/UTDID(UTDIDGTMBase64.o)
duplicate symbol _OBJC_CLASS_$_UTDIDGTMBase64 in:
/Users/wangjunling/Desktop/AAA-iOS/Pods/Pingpp/lib/Channels/Alipay/AlipaySDK.framework/AlipaySDK
/Users/wangjunling/Desktop/AAA-iOS/Pods/UTDID/UTDID.framework/UTDID(UTDIDGTMBase64.o)
ld: 34 duplicate symbols for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

对于经验丰富的老程序员来说一看可能就知道是什么意思了, 但是我还没有遇到过这样的问题, 第一件事当然是百度啦, 关键字”clang: error: linker command failed with exit code 1 (use -v to see invocation)” 找了一堆,没有找到问题是什么原因, 换关键字”ld: 34 duplicate symbols for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)” 终于看到一个比较好的描述的文章, 里面大概意思是说, 有文件冲突, 或者重名, 然后我才想起来看看上面的报错信息, 仔细分析发现, 原来是alipaysdk 和百川sdk里面的UTDID文件冲突, 也许是因为同一个公司的缘故吧, 在做封装的时候封装了相同的库, 造成了冲突. 算是找到了报错原因, 接下来就是慢慢的找解决方案路.

百度关键词”iOS 开发第三方库冲突”, 又是一堆烂帖子博客, 挨个去看, 其实最大的感触就是在中国的博客上都是相互抄袭, 甚至一个人写错了, 一堆人都跟着错, 真不明白那些人抄袭这样的博客有啥用, 就算你抄也抄经典的, 然后加入自己的思想, 这样也算是进步对吧! 有人会问我那你为啥不去google搜索, 其实我到时想用google, 但是英文不好, 找不到好的关键字搜索的话, 用google并不见得比百度好, 比如你用百度搜索”日历”, 然后日历就直接出来了, 但是你用google搜索calendar, 出来的就不是你想要的答案. 好了不扯淡, 下面是我找到的写的看起来靠谱点的博客, 关于解决多个第三方库冲突的文章, iOS 第三方库冲突的处理 , 摘抄一下这篇文章的内容:

  1. 创建临时文件夹,用于存放armv7平台解压后的.o文件:mkdir armv7
  2. 取出armv7平台的包:lipo libx.a -thin armv7 -output armv7/libx-armv7.a
  3. 查看库中所包含的文件列表:ar -t armv7/libx-armv7.a
  4. 解压出object file(即.o后缀文件):cd armv7 && ar xv libx-armv7.a
  5. 找到冲突的包(JSONKit),删除掉rm JSONKit.o
  6. 重新打包object file:cd .. && ar rcs libx-armv7.a armv7/*.o,可以再次使用[2]中命令确认是否已成功将文件去除
  7. 将其他几个平台(armv7s, i386)包逐一做上述[1-6]操作
  8. 重新合并为fat file的.a文件:
    lipo -create libx-armv7.a libx-armv7s.a libx-i386.a -output libMiPushSDK-new.a
  9. 拷贝到项目中覆盖源文件:
    cp libMiPushSDK-new.a /Users/tony/Desktop/XXXProject/Lib/libMiPushSDK.a

看完文章感觉比较适合我的问题, 然后就开始按照步骤操作, 在执行到第4步的时候就出问题了, 终端中报错

1
2
wangjunlingdeMacBook-Pro:librepack wangjunling$ ar -t armv7/libx-armv7.a
ar: armv7/libx-armv7.a: Inappropriate file type or format

然后我就怀疑是我自己的错, 重复n遍发现还是不行, 我就换个思路, 去打开那个utdid包, 然后打开后, 发现里面的全部内容, 都是冲突内容, 那么也就是说重新封装这个库是不可能的了, alipay打不开, 这个有不能重新封装, 貌似所有的解决方案都没有了, 绝望中, 只能找到他们的客服, 下载一个叮叮客服端, 注册, 登录, 加他们提供的服务群, 然后就没信了, 可能是太晚了, 此时已经是凌晨一点了, 今天肯定是解决不了了, 无奈睡觉!

第二天, 一上班就看叮叮有没有通过我的申请, 一看有个客服回复消息了, 然后赶紧去咨询, 很明显这个问题应该是有人遇到过了, 客服直接和我说, 把UTDID删除, 问题是我删除了还是报错, 客服的意思是说吧支付宝里的UTDID删除,问题是iOS 10的是封装的.framework, 不可能去删除里面的东西, 而且通过上面的终端命令也试过, 根本打不开alipaysdk, 可能是他们有特殊加密, 后来客服说他们有一个不包含UTDID的alipaysdk, 然后将信将疑的把, 之前的alipaysdk换掉, 到此, 问题解决. 虽然最终的解决方式很没有技术含量, 但是这两天我收获了很多东西, 所以记录一下, 希望遇到同样问题的你, 在没有方案时不要绝望, 办法总是会有的, 坚持下去, 总会有收获!

2017-01-06

字符串修剪

最近公司项目中需要字符串修剪, 而且项目中每个地方的需求还不太一样, 所以写了下面的一个分类, 感觉挺好用分享出来给大家, 希望遇到同样问题的同志少花时间, 注释比较清晰, 我就废话不多说了, 看代码

  1. 头文件

    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
    //
    // NSString+JLTrim.h

    //
    // Created by 王俊岭 on 16/8/17.
    //

    #import <Foundation/Foundation.h>

    @interface NSString (JLTrim)
    /**
    * 修剪字符串
    *
    * @param val 要修改的字符串
    * @param characterSet 要去除的类型
    *
    * @return 修剪后的字符串
    */
    + (NSString *)trim:(NSString *)val trimCharacterSet:(NSCharacterSet *)characterSet;
    /**
    * 删除字符串前后空格
    *
    * @param val 原始字符串
    *
    * @return 修剪后字符串
    */
    + (NSString *)trimWhitespace:(NSString *)val;
    /**
    * 删除字符串前后空行
    *
    * @param val 原始字符串
    *
    * @return 修改后字符串
    */
    + (NSString *)trimNewline:(NSString *)val;
    /**
    * 删除字符串前后空行和空格
    *
    * @param val 原始字符串
    *
    * @return 修改后字符串
    */
    + (NSString *)trimWhitespaceAndNewline:(NSString *)val;

    /**
    删除字符串中所有空格

    @param val 原始字符串
    @return 修改后的字符串
    */

    • (NSString )trimAllWhitespace:(NSString )val;

/**
删除字符创中所有回车

@param val 原始字符串
@return 修改后字符串
*/
  • (NSString )trimAllNewLine:(NSString )val;
    @end

    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

    2. 实现文件

    ```objc
    //
    // NSString+JLTrim.m
    //
    // Created by 王俊岭 on 16/8/17.
    //

    #import "NSString+JLTrim.h"

    @implementation NSString (JLTrim)
    + (NSString *)trim:(NSString *)val trimCharacterSet:(NSCharacterSet *)characterSet {
    NSString *returnVal = @"";
    if (val) {
    returnVal = [val stringByTrimmingCharactersInSet:characterSet];
    }
    return returnVal;
    }
    + (NSString *)trimWhitespace:(NSString *)val {
    return [self trim:val trimCharacterSet:[NSCharacterSet whitespaceCharacterSet]]; //去掉前后空格
    }
    + (NSString *)trimNewline:(NSString *)val {
    return [self trim:val trimCharacterSet:[NSCharacterSet newlineCharacterSet]]; //去掉前后回车符
    }
    + (NSString *)trimWhitespaceAndNewline:(NSString *)val {
    return [self trim:val trimCharacterSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; //去掉前后空格和回车符
    }

    + (NSString *)trimAllWhitespace:(NSString *)val {
    NSMutableString *mutStr = [NSMutableString stringWithString:val];
    NSRange range = {0,val.length};
    [mutStr replaceOccurrencesOfString:@" " withString:@"" options:NSLiteralSearch range:range];
    return mutStr.copy;
    }
    + (NSString *)trimAllNewLine:(NSString *)val {
    NSMutableString *mutStr = [NSMutableString stringWithString:val];
    NSRange range = {0,val.length};
    [mutStr replaceOccurrencesOfString:@"\n" withString:@"" options:NSLiteralSearch range:range];
    return mutStr.copy;
    }
    @end

    注意

    stringByTrimmingCharactersInSet 这个方法只能删除字符串两端的字符, 不能对字符串中间的字符进行操作, 所以想删除字符串中间的字符就需要字符串替换的方法replaceOccurrencesOfString

2016-12-28

mac支持NTFS

​ 所有的办法都是在没有办法的情况下才能找到, 之前一直用的 Paragon NTFS, 当然虽然是程序员有两种方式, 一种就是支持自己的事业,用收费的, 另一种就是发挥自己的特长, 去破解. 无奈承担不起Paragon NTFS每个版本更新都要重新购买的费用, 如果一次购买终身有效19.9刀结果面前可以接受. 但是破解版的终归不是最终的解决方案, 系统升级或软件升级之后就会一堆头疼的事, 最主要的是Paragon NTFS这个软件稳定性不是很好, 尤其是大批量大文件拷贝时, 有时候会在拷贝中途报错, 这是让人不能忍受的, 说这么多废话, 那么有没有一种更好的办法解决呢, 有, 当然有, 哈哈, 接下来就是重点: 挖掘mac系统的潜力.

​ 首先, 插上NTFS格式的硬盘, 打开终端(为了照顾非程序员, 解释一下什么是终端, 就是mac系统下类似Windows中的cmd命令窗口的一个东西, 在mac的launchpad中的搜索框中直接搜索”终端”即可找到). 输入一下命令:

1
diskutil list

​ 按回车键会得到如下图所示, 找到你的磁盘名称, 我的叫 HD-E1 , 上面的命令目的就是查看磁盘卷标名称;执行以下命令:

1
sudo vim /etc/fstab

按回车键后会让你输入密码, 因为这个文件为系统文件所以修改时需要密码, 终端中输入密码是光标不会变化, 只管输入就行. 输入完成后会打开这个文件, 如下图:

现在需要编辑这个文件, 按i键进入编辑状态吧下面的一行代码拷贝到文件中,

1
LABEL=FreeAgent\040GoFlex\040Drive none ntfs rw,auto,nobrowse

粘贴完成之后, 按下esc键结束编辑 然后按下 : 键, 在冒号后面输入 wq 回车, wq是保存并退出的意思! 到这基本就打工搞成, 拔掉硬盘重新插入, 你会发现竟然没有硬盘了, 哈哈, 是不是自己操作错了, 没关系, 其实你没有错, 只是你的磁盘挂在到Volumes, 如果你的电脑打开了隐藏文件显示功能, 你就能在Macintosh HD磁盘下看到这个文件夹, 打开之后, 就可以找到你爹磁盘, 显然这么做事很愚蠢的事情, 那么有什么好的替代方案呢? 嘿, 这个还用问, 必须有, 给这个文件夹设置个快捷方式到桌面不就成了吗, 说做就做, 依然是在终端中, 执行以下代码

1
sudo ln -s /Volumes ~/Desktop/Volumes

执行完你就会在桌面上看到一个Volumes的快捷方式了, 这次真正的打工搞成! 哈哈, 你真棒又一次完成了不可能完成的事, 让那些做ntfs驱动的公司都去倒闭吧[阴笑]

  • 1

  • 2

  • 3

  • 4

  • 5