爱运动的腿

生活不易,且行且珍惜

  • 首页
  • 归档
  • 关于

神奇的load方法

发表于 2016-11-08   |     |   阅读次数

load方法说明

在Objective-C中,绝大多数类都继承自NSObject这个根类,而该类有load方法,可以用来实现初始化操作。其原型如下:

1
+ (void)load

对于加入运行期系统中的每个类(class)及分类(category)来说,必定会调用此方法,而且仅调用一次。当包含类或分类的程序库载入系统时,就会执行此方法(通常指应用程序启动)。如程序是iOS平台设计的,则肯定会在此时执行。Mac OS X应用程序更自由一些,它们可以使用“动态加载”(dynamic loading)之类的特性,等应用程序启动好之后再去加载程序库。如果分类和其所属的类都定义了load方法,则先调用类里的,再调用分类里的。
执行load方法时,运行期系统处于“脆弱状态”。在执行子类的load方法之前,必定会先执行所有超类的load方法,而如果代码还依赖了其他程序库,那么程序库里相关类的load方法也必定会先执行。

load方法的妙用

简化AppDelegate类

随着项目功能的不断增加,我们有很多功能或者第三库需要启动项目时就加载,AppDelegate类就会越来越庞大。这样结构既不够清晰,而且耦合性比较强。

改进前:

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
//设置NUI配置
[self setNUIConfig];
//开启统计
[[KSStatisticalMgr sharedInstance] start];
//初始化数据库
[KSDBUtils startInitDB];
//注册统计平台
if (!TARGET_IPHONE_SIMULATOR)
{
[[SocialService sharedInstance] registerPlatforms];
}
//检测服务器状态
[[KSServerMgr sharedInstance] doGetServerStatus];
//获取用户数据
[USER_MGR updateUserAssets];
//启动图界面
KSLaunchVC *splashVC = [[KSLaunchVC alloc] initWithNibName:@"KSLaunchVC" bundle:nil];
UIWindow *keywindow = [UIApplication sharedApplication].keyWindow;
[keywindow addSubview:splashVC.view];
[keywindow bringSubviewToFront:splashVC.view];
[self.window makeKeyAndVisible];
//自适应屏幕键盘控件
IQKeyboardManager * manager = [IQKeyboardManager sharedManager];
manager.enable = YES;
manager.shouldResignOnTouchOutside = YES;
manager.shouldToolbarUsesTextFieldTintColor = YES;
manager.enableAutoToolbar = YES;
//设置首页
BYCircleListViewController *homePageVC = [[BYCircleListViewController alloc] init];
BYNavigationViewController *navVC = [[BYNavigationViewController alloc] initWithRootViewController:homePageVC];
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
self.window.rootViewController = navVC;
[self.window makeKeyAndVisible];
阅读全文 »

Xcode8和iOS10新特性

发表于 2016-11-04   |     |   阅读次数

Xcode 8和 iOS10 新特性

Interface Builder

在Xcode8中,苹果推出了更加强大的可视化编辑工具以及预览功能,可以在不运行App的情况下,预览当前XIB或SB在不同屏幕尺寸下的显示。选择一个XIB文件进去,点击下面红框的位置,会出现从3.5寸-5.5存一系列屏幕的选项。 直接点击不同屏幕尺寸,以及横竖屏,切换不同的屏幕显示。在iPad上还可以选择是否分屏。

Interface Builder

新创建的XIB控件尺寸,不再是之前600*600的方块了,而是默认是6s的长方形XIB文件

Swift 2 和 3

在Xcode 8之前,Xcode的每一个版本都是内置明确版本的swift编程语言。在Xcode 8中就不同了。因为Swift 3有了大量的改变,而且大多数改变是具有破坏性的。
使用Xcode 8创建默认使用的Swift 3。幸运的是Xcode 8中的building setting选项可以让开发者明确表明继续使用Swift 2和Swift 2.3。
Swift
如果不想立刻就迁移到Swift 3,可以在Builder Setting中进行设置,选择Use Legacy Swift Language Version设置为YES,就可以继续使用就版本的Swift2.3。

阅读全文 »

UITableView的优化技巧

发表于 2016-08-31   |     |   阅读次数

这段时间也看了很多关于tableview优化的文章,结合项目的一些实践 ,思考了一些关于UITableView化技巧。UITableView是iOS开发中最常用的控件之一。

UITableView的简单认识

1.重用机制

UITableView最核心的思想就是UITableViewCell的重用机制。UITableView只会创建一屏的UITableViewCell,其他都是从中取出来重用的。每当Cell滑出屏幕时,就会放入到一个集合中,当要显示某一位置的Cell时,会先去集合中取,如果有,就直接重用显示。如果没有,才会创建,这样能极大的减少内存开销。

2.代理方法

UITableView最主要的两个代理方法tableView:cellForRowAtIndexPath:和tableView:heightForRowAtIndexPath:。我最开始接触UITableView的时候,认为UITableView会先调用前者,再调用后者,因为这跟我们创建控件的思路是一样的,先创建它,然后设置它的布局。但实际上并非如此,UITableView是继承自UIScrollView的,需要先确定它的contentSize及每个Cell的位置,然后才会把重用的Cell放置到对应的位置。所以事实上,UITableView的回调顺序是先多次调用tableView:heightForRowAtIndexPath:以确定contentSize及Cell的位置,然后才会调用tableView:cellForRowAtIndexPath:,来显示在当前屏幕的cell。
举个例子来说:如果现在要显示100个Cell,当前屏幕显示5个。刷新(reload)UITableView时,UITableView会先调用100次tableView:heightForRowAtIndexPath:方法,然后调用5次tableView:cellForRowAtIndexPath:方法;滚动屏幕时,每当Cell滚入屏幕,都会调用一次tableView:heightForRowAtIndexPath:、tableView:cellForRowAtIndexPath:方法。通过上述的讲解后,首先想到的UITableView优化的方案是优化上面的UITableView两个代理方法。

优化技巧

1.将赋值和计算布局分离,并根据数据源计算出对应的布局,并缓存到数据源中。

这样能让tableView:cellForRowAtIndexPath:方法只负责赋值,tableView:heightForRowAtIndexPath:方法只负责计算高度。示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
VVeboTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
if (cell==nil) {
cell = [[VVeboTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:@"cell"];
}
[self drawCell:cell withIndexPath:indexPath];
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
NSDictionary *dict = datas[indexPath.row];
float height = [dict[@"frame"] CGRectValue].size.height;
return height;
}

2.预渲染

微博的头像在某次改版中换成了圆形,当头像下载下来后。利用后台线程将头像预先渲染为圆形并保存到一个ImageCache中去。示例代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
+ (YYWebImageManager *)avatarImageManager {
static YYWebImageManager *manager;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSString *path = [[UIApplication sharedApplication].cachesPath stringByAppendingPathComponent:@"weibo.avatar"];
YYImageCache *cache = [[YYImageCache alloc] initWithPath:path];
manager = [[YYWebImageManager alloc] initWithCache:cache queue:[YYWebImageManager sharedManager].queue];
manager.sharedTransformBlock = ^(UIImage *image, NSURL *url) {
if (!image) return image;
return [image imageByRoundCornerRadius:100]; // a large value
};
});
return manager;
}

对于TableView来说,Cell内容的离屏渲染会带来较大的GPU消耗。为了避免离屏渲染,你应当尽量避免使用layer的border、corner、shadow、mask 等技术,而尽量在后台线程预先绘制好对应内容。

阅读全文 »

ReactiveCocoa进阶篇

发表于 2016-05-05   |     |   阅读次数

1.ReactiveCocoa常见操作方法介绍。

1.1 ReactiveCocoa操作须知

所有的信号(RACSignal)都可以进行操作处理,因为所有操作方法都定义在RACStream.h中,因此只要继承RACStream就有了操作处理方法。

1.2 ReactiveCocoa操作思想

运用的是Hook(钩子)思想,Hook是一种用于改变API(应用程序编程接口:方法)执行结果的技术.
Hook用处:截获API调用的技术。
Hook原理:在每次调用一个API返回结果之前,先执行你自己的方法,改变结果的输出。

1.3 ReactiveCocoa核心方法bind

ReactiveCocoa操作的核心方法是bind(绑定),而且RAC中核心开发方式,也是绑定,之前的开发方式是赋值,而用RAC开发,应该把重心放在绑定,也就是可以在创建一个对象的时候,就绑定好以后想要做的事情,而不是等赋值之后在去做事情。

列如:把数据展示到控件上,之前都是重写控件的setModel方法,用RAC就可以在一开始创建控件的时候,就绑定好数据。

在开发中很少使用bind方法,bind属于RAC中的底层方法,RAC已经封装了很多好用的其他方法,底层都是调用bind,用法比bind简单.

bind方法简单介绍和使用。
// 假设想监听文本框的内容,并且在每次输出结果的时候,都在文本框的内容拼接一段文字“输出:”

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
// 方式一:在返回结果后,拼接。
[_textField.rac_textSignal subscribeNext:^(id x) {
NSLog(@"输出:%@",x);
}];
// 方式二:在返回结果前,拼接,使用RAC中bind方法做处理。
// bind方法参数:需要传入一个返回值是RACStreamBindBlock的block参数
// RACStreamBindBlock是一个block的类型,返回值是信号,参数(value,stop),因此参数的block返回值也是一个block。
// RACStreamBindBlock:
// 参数一(value):表示接收到信号的原始值,还没做处理
// 参数二(*stop):用来控制绑定Block,如果*stop = yes,那么就会结束绑定。
// 返回值:信号,做好处理,在通过这个信号返回出去,一般使用RACReturnSignal,需要手动导入头文件RACReturnSignal.h。
// bind方法使用步骤:
// 1.传入一个返回值RACStreamBindBlock的block。
// 2.描述一个RACStreamBindBlock类型的bindBlock作为block的返回值。
// 3.描述一个返回结果的信号,作为bindBlock的返回值。
// 注意:在bindBlock中做信号结果的处理。
// 底层实现:
// 1.源信号调用bind,会重新创建一个绑定信号。
// 2.当绑定信号被订阅,就会调用绑定信号中的didSubscribe,生成一个bindingBlock。
// 3.当源信号有内容发出,就会把内容传递到bindingBlock处理,调用bindingBlock(value,stop)
// 4.调用bindingBlock(value,stop),会返回一个内容处理完成的信号(RACReturnSignal)。
// 5.订阅RACReturnSignal,就会拿到绑定信号的订阅者,把处理完成的信号内容发送出来。
// 注意:不同订阅者,保存不同的nextBlock,看源码的时候,一定要看清楚订阅者是哪个。
// 这里需要手动导入#import <ReactiveCocoa/RACReturnSignal.h>,才能使用RACReturnSignal。
[[_textField.rac_textSignal bind:^RACStreamBindBlock{
// 什么时候调用:
// block作用:表示绑定了一个信号.
return ^RACStream *(id value, BOOL *stop){
// 什么时候调用block:当信号有新的值发出,就会来到这个block。
// block作用:做返回值的处理
// 做好处理,通过信号返回出去.
return [RACReturnSignal return:[NSString stringWithFormat:@"输出:%@",value]];
};
}] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
阅读全文 »

国内iOS牛人博客

发表于 2016-04-25   |     |   阅读次数

李忠(limboy):来自蘑菇街,RAC实践的国内先驱者

http://limboy.me/

Bang的博客:目前就职于腾讯,JSPatch作者

http://blog.cnbang.net/

王巍的博客:王巍目前在日本横滨任职于LINE。工作内容主要进行Unity3D开发和iOS开发。他维护Swift使用技巧分享网站Swifter.tips,他的陈列柜中已有多款应用,其中番茄工作法工具非常棒。

http://onevcat.com

池建强的博客: 池建强,70后程序员,Blogger。98年毕业,先后就职于洪恩软件、RocketSofeware和用友软件工程公司(后更名为瑞友科技),现任瑞友科技IT应用研究院副院长。该博客最初每天发送一条Mac技巧,不过目前已经形成了一种技术和人文结合的风格,时而随笔,时而技术。

http://macshuo.com/

唐巧的博客: 唐巧是前网易有道员工,现在在猿题库创业中,负责iOS端的开发,首届国内Swift开发者大会的组织者。

http://blog.devtang.com/


许小帅的博客:许小帅目前任职于腾讯,他还是iOS Feed站点的负责人。博客中对支付宝钱包插件分析的文章,引起了支付宝开发团队的反思,大家可以去感受一下。

http://imallen.com/

蓝晨钰的博客:晨钰就职于猿题库。

http://gracelancy.com/

萧宸宇的博客:萧宸宇目前任职于Sumi Interactive。他出生于西南一个边陲小镇,梦想着能走出家乡,看看祖国的大好河山。他拥有丰富的 iOS 开发经验,在他的博客中发表了许多 iOS 开发的文章。

http://iiiyu.com/

陶丰平的博客:陶丰平目前任职于花瓣网,喜欢技术,喜欢音乐。他在软件开发领域有丰富的开发经验。

http://www.taofengping.com/

ibireme的博客:YYKit作者 曾就职于优酷土豆,先就职于滴滴

http://blog.ibireme.com


乔学士的博客:乔学士目前任职于创业公司拓词,他正在为成为“代码手工艺人”而努力,目前主要进行 iOS 开发。最近他做得非常精彩的一件事就是汇总了WWDC 2013 视频英文字幕下载,大家可以前往他的博客感受一下。

http://joeyio.com/

破船的博客:破船在移动领域摸爬滚打多年,经历了Symbian、Windows Mobile、Windows Phone和 iOS 等的洗礼。他希望能充分利用业余时间,努力做一名伪步道师。

http://beyondvincent.com/

陈咏聪的博客:陈咏聪现任职于百度,从事 iOS 相关开发。他热爱Web事业,关注设计、产品、开发。沉迷代码,无法自拔。

http://vinqon.com/

骆仕恺的博客:骆仕恺目前任职于爱折客 / 爱美味。他主要从事 iOS 和Java开发。拥有丰富的 iOS 开发经验。

http://luosky.com/

耿健桓的博客:耿健桓目前在清华大学攻读硕士,之前他是著名站点知乎的 iOS 技术主管。

http://dlog.dismory.com/

阅读全文 »

caffe--学习笔记

发表于 2016-02-27   |     |   阅读次数

4个重要大类

Blob、Layer、Net、Solver自上而下,欢欢相扣,分别负责数据传输、网络层次、网络骨架与参数求解策略。

1. Blob:数据层的数据结构,数据传输的媒介,神经网络的输入输出数据,网络权重参数等等都是用Blob进行存储。

2. Layer:神经网络的基础单元,层与层间的数据节点、前后传递都在该数据结构中被实现。它包括卷积层、激励层、池化层、全连接层等。

3. Net:是网络的整体搭建骨架,整合Layer中的层级机构组成的网络。

4. Solver:网络求解优化策略,让你用各种“积木”搭建的网络能最适应当前的场景的样本。

阅读全文 »

(转载)快速上手ReactiveCocoa--基础篇

发表于 2016-02-05   |     |   阅读次数

ReactiveCocoa简介

ReactiveCocoa(简称为RAC),是由Github开源的一个应用于iOS和OS开发的新框架,Cocoa是苹果整套框架的简称,因此很多苹果框架喜欢以Cocoa结尾。

ReactiveCocoa作用

在我们iOS开发过程中,当某些事件响应的时候,需要处理某些业务逻辑,这些事件都用不同的方式来处理。
比如按钮的点击使用action,ScrollView滚动使用delegate,属性值改变使用KVO等系统提供的方式。
其实这些事件,都可以通过RAC处理
ReactiveCocoa为事件提供了很多处理方法,而且利用RAC处理事件很方便,可以把要处理的事情,和监听的事情的代码放在一起,这样非常方便我们管理,就不需要跳到对应的方法里。非常符合我们开发中高聚合,低耦合的思想。

编程思想

在开发中我们也不能太依赖于某个框架,否则这个框架不更新了,导致项目后期没办法维护,比如之前Facebook提供的Three20框架,在当时也是神器,但是后来不更新了,也就没什么人用了。因此我感觉学习一个框架,还是有必要了解它的编程思想。

先简单介绍下目前咱们已知的编程思想。

面向过程:处理事情以过程为核心,一步一步的实现。

面向对象:万物皆对象

链式编程思想:是将多个操作(多行代码)通过点号(.)链接在一起成为一句代码,使代码可读性好。a(1).b(2).c(3)

链式编程特点:方法的返回值是block,block必须有返回值(本身对象),block参数(需要操作的值)

代表:masonry框架。

模仿masonry,写一个加法计算器,练习链式编程思想。

1.png

2.png

3.png

阅读全文 »

iOS开发之单例模式

发表于 2015-10-25   |     |   阅读次数

iOS开发之单例

单例介绍

1.什么是单例

单例模式是一种常用的软件设计模式。在它的核心结构中包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果系统在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。

2.单例用处

应用场景:
确保程序运行期某个类,只有一份实例,用于进行资源共享控制。
优势:
使用简单,延时求值,易于跨模块
iOS的系统中用到的一些单例

1
2
3
[UIApplication sharedApplication];
[NSUserDefaults standardUserDefaults];
[NSURLCache sharedURLCache];

iOS单例的创建

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
+(instancetype)shareInstance
{
static Singleton *singleteon;
if (!singleteon ) {
singleteon = [[Singleton alloc] init];
}
return singleteon;
}
```
单线程单例存在一些弊端,在多线程的情况下,会产生线程不安全的情况。严格意义上来说,我们还需要把alloc方法变为私有方法才行,严格的单例是不允许再创建其他实例的,而alloc方法可以在外部任意生成实例。但是考虑到**alloc**属于NSObject,iOS中无法将**alloc**变成私有方法,最多只能覆盖**alloc**让其返回空。不过个人不建议这么做,一般情况下对**alloc**不做特殊处理。系统的单例也未对**alloc**做任何处理
<!--more-->
## 2.@synchronized单例
上面单线程单例,在多线程情况下,可能会出现一些问题。如果两个线程同时调用**shareInstanc**,可能会创建出2个singleton出来。所以在对多线程情况下,我们需要使用**@synchronize**来加锁
``` objc
+(instancetype)shareInstance
{
static Singleton *singleton;
@synchronized (self) {
if (!singleton) {
singleton = [[Singleton alloc] init];
}
}
return singleton;
}
```
加锁以后,当多个线程同时调用shareInstance时,由于**@synchronized**已经加锁,只能有一个线程创建singleton实例。这样就解决了多线程调用单例的问题。
## 3.dispatch_once单例
使用**@synchronized**虽然一定程度上解决了多线程的问题,但并不完美。因为只有在**singleton**未创建时,加锁才是必要的。如果**singleton**已经创建,这个时候还加锁的话,会影响性能。
在iOS中,GCD为我们提供方便又高效的方法---**dispatch_once**
``` objc
+(instancetype)shareInstance
{
static Singleton *singleton;
static dispatch_once_t onceToken; //1.onceToken = 0;
dispatch_once(&onceToken,^{
NSLog(@"%ld",onceToken); //2.onceToken = 140734537148864
singleton = [[Singleton alloc] init];
});
NSLog(@"%ld",onceToken); //3.onceToken = -1
return singleton;
}

dispatch_once为什么能做到既解决同步多线程问题

dispatch_once的原理:
dispatch_once主要是根据onceToken的值来决定怎么去执行代码。
1.当onceToken = 0时,线程执行dispatch_once的block中代码
2.当onceToken = -1时,线程跳过dispatch_once的block中代码不执行
3.当onceToken为其他值时,线程被阻塞,等待onceToken值改变
当线程调用shareInstance,此时onceToken = 0,调用block中的代码,此时onceToken的值变为140734537148864。当其他线程再调用shareInstance方法时,onceToken的值已经是140734537148864了,线程阻塞。当block线程执行完block之后,onceToken变为-1.其他线程不再阻塞,跳过block。下次再调用shareInstance时,block已经为-1.直接跳过block。
dispatch_once设计挺巧妙的。

jasonjwl

jasonjwl

8 日志
2 标签
RSS
Github CSDN
© 2016 jasonjwl
由 Hexo 强力驱动
主题 - NexT.Pisces