寒哥教你学 iOS - 经验漫谈(转)
转自http://www.cocoachina.com/ios/20150907/13339.html
本篇文章主要讲解 4个问题
load妙用
aop面向切面编程
NSNumber Or Int
@()适配64位
1 让appDelegate 减少负担
经过漫长时间的学习 你终于掌握了iOS大法 你找到了份iOS开发的工作 信誓旦旦的要开始你的coding生涯 老板对你非常器重 然后告诉你 我觉得你的技术 是非常刁的 那这个项目就你自己来搞吧 啊哦这就意味着这个项目你就从头到尾处理 从软件的架构 到页面的展示 都交给你喽 ??
用着自己的半吊子水平 papapa的 coding 决心一定要把代码封装好 写的漂亮 (其实是听大神说 封装 其实自己不太懂)
项目到了尾声 老板告诉你我们的app 我们的app 将来得来个分享到朋友圈的功能吧 不然怎么体现我产品的牛逼
然后你听说友盟比较好使(有广告的嫌疑) 你去友盟看了他们的文档 他告诉你你要在 appdelegate didFinishLaunch方法里面写了这个东西
1
2
3
4
5
6
7
|
[UMSocialData setAppKey:@ "XX" ]; // 注册微信 [UMSocialWechatHandler setWXAppId:@ "XXX" appSecret:@ "XX" url:@ "" ]; // 注册QQ [UMSocialQQHandler setQQWithAppId:@ "XXX" appKey:@ "XXX" url:@ "" ]; |
过了几天 老板又说 我们需要统计下我页面的信息 你接入了友盟的统计 在appdelegate didFinishLaunch又 多了行代码
需求是无穷无尽 我需要bug统计(fir hud) 提醒用户评分系统(iRate) 推送(jPush 信鸽 个推。。)
当初你决心一定要把代码封装的完美 写的漂亮的心早就被老板的需求彻底打败了
别担心 寒哥教你小技巧
不知道你们用过 IQKeyBoardManage和iRate这种智能库没
大牛的readme 写了这段话
1
2
3
4
5
6
7
|
Key Features 1) CODELESS, Zero Line Of Code 不需要写任何代码 2) Works Automatically //自动工作 3) No More UIScrollView //不需要scrollview 4) No More Subclasses //不需要继承父类 5) No More Manual Work //不需要配置 6) No More #imports //不需要导入 |
其实不神奇 只是大牛用了 + load这个方法
学习OC都知道这个代码会在一个类被加载到运行库中就会被自动调用 这不就实现了 自动调用
写一个类继承自NSObject
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
#import <foundation foundation.h="">@interface ThirdPartService : NSObject@end #import "ThirdPartService.h" #import "UMSocial.h" #import "UMSocialWechatHandler.h" #import "UMSocialQQHandler.h" #import <mobclick.h> #import <fir fir.h="">@implementation ThirdPartService + (void)load {static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{ // TODO 这里是我自己测试的 fir hud [FIR handleCrashWithKey:@ "XX" ]; // 友盟 [UMSocialData setAppKey:@ "XX" ]; // 隐藏未安装的平台 [UMSocialConfig hiddenNotInstallPlatforms:@[UMShareToQQ,UMShareToQzone,UMShareToWechatSession,UMShareToWechatTimeline]]; // 注册微信 [UMSocialWechatHandler setWXAppId:@ "XX" appSecret:@ "XX" url:@ "" ]; // 注册QQ // TODO QQ的不是真的 [UMSocialQQHandler setQQWithAppId:@ "XX" appKey:@ "XX" url:@ "" ]; // TODO UM统计 [MobClick startWithAppkey:@ "" ]; [MobClick setCrashReportEnabled:NO]; NSLog(@ "第三方服务注册完毕" ); }); }@end</fir></mobclick.h></foundation> |
类似于定位也可以这样写
模块和服务完全拆开
但是有的服务 如APNS需要LaunchOption 那就只能写在appdDelegate 不过这样的话已经摘除很多代码了 只剩下几个固定的 到时候再修改appDelegate就会感觉非常清晰 了
2 ViewController继承?
接着上面讲 我们接入了友盟统计 友盟统计最基本的东西就是 统计页面的pv
友盟的这样写 对于新手的我们就觉得这不就so easy吗
我打开了某个vc(HomeViewController)
在代码里面写上了这句
1
2
3
4
5
6
7
|
-(void)viewWillAppear:(BOOL)animated { [ super viewWillAppear:animated]; #ifndef DEBUG [MobClick beginLogPageView:NSStringFromClass([self class])]; #endif} -(void)viewWillDisappear:(BOOL)animated { [ super viewWillDisappear:animated]; #ifndef DEBUG [MobClick endLogPageView:NSStringFromClass([self class])]; #endif} |
然后我一个项目中可能有几十个 甚至上百个页面需要统计pv 我总不能每个节目都这样写吧
聪明的我们想到了继承
如MyBaseViewController:UIViewController
这样就要做一件事 把我们项目中所有继承自UIViewController的类全部改为继承自MyBaseViewController 然而你真的觉得这样好吗 我们一个项目中有几十个控制器 我就要把每个控制器改一遍
这种重复性的工作一是无聊 而是容易出错 你复制着复制者就会遗漏掉某个类 重要的是 我们项目中很多类并不是直接继承自UIViewController 有的可能是UITableViewController UICollectionViewContr0ller UINavigationController 甚至不常用的UISearchDisPlayController UIPopoverController ?UIPresentController 是不是突然觉得这么多啊啊 ??
这也不是坑的 坑的是将来你混成了大牛 招了个小弟 你告诉他你所有的类都要继承自我写的各种父类 新手总是会不经意见犯错误 有些类忘记继承了 后期查起来难度非常大 浪费时间 所以这种设计是不合理的
寒哥再次教你黑魔法 Method swizzling
关于这个是干嘛的 自行百度
这里有一篇来自NSHipster博主的文章 英文
还有一篇解释runtime的文章传送门
用方法交叉 我们就可以拦截吸引的方法了 上代码了
这样就做到了面向切面编程(AOP)的思想
上代码
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
|
#import <uikit uikit.h="">@interface UIViewController (AOP)#warning 运行时 改变一下方法 做一些切面编程比如 统计 等等 @end #import "UIViewController+AOP.h" #import <objc runtime.h="">#import <mobclick.h> @implementation UIViewController (AOP) + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ Class class = [self class]; // When swizzling a class method, use the following: // Class class = object_getClass((id)self); swizzleMethod(class, @selector(viewDidLoad), @selector(aop_viewDidLoad)); swizzleMethod(class, @selector(viewDidAppear:), @selector(aop_viewDidAppear:)); swizzleMethod(class, @selector(viewWillAppear:), @selector(aop_viewWillAppear:)); swizzleMethod(class, @selector(viewWillDisappear:), @selector(aop_viewWillDisappear:)); }); } void swizzleMethod(Class class, SEL originalSelector, SEL swizzledSelector) { Method originalMethod = class_getInstanceMethod(class, originalSelector); Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)); if (didAddMethod) { class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); } else { method_exchangeImplementations(originalMethod, swizzledMethod); } } - (void)aop_viewDidAppear:(BOOL)animated { [self aop_viewDidAppear:animated]; } -(void)aop_viewWillAppear:(BOOL)animated { [self aop_viewWillAppear:animated]; #ifndef DEBUG [MobClick beginLogPageView:NSStringFromClass([self class])]; #endif} -(void)aop_viewWillDisappear:(BOOL)animated { [self aop_viewWillDisappear:animated]; #ifndef DEBUG [MobClick endLogPageView:NSStringFromClass([self class])]; #endif} - (void)aop_viewDidLoad { [self aop_viewDidLoad]; if ([self isKindOfClass:[UINavigationController class]]) { UINavigationController *nav = (UINavigationController *)self; nav.navigationBar.translucent = NO; nav.navigationBar.barTintColor = GLOBAL_NAVIGATION_BAR_TIN_COLOR; nav.navigationBar.tintColor = [UIColor whiteColor]; NSDictionary *titleAtt = @{NSForegroundColorAttributeName:[UIColor whiteColor]}; [[UINavigationBar appearance] setTitleTextAttributes:titleAtt]; [[UIBarButtonItem appearance] setBackButtonTitlePositionAdjustment:UIOffsetMake(0, -60) forBarMetrics:UIBarMetricsDefault]; } // self.view.backgroundColor = [UIColor whiteColor];self.navigationController.interactivePopGestureRecognizer.delegate = (id<uigesturerecognizerdelegate>)self; } @end</uigesturerecognizerdelegate></mobclick.h></objc></uikit> |
图片代码一份 方便观看
我们充分利用了黑魔法达到了面向切面编程的好处
思想来源这里http://casatwy.com/iosying-yong-jia-gou-tan-viewceng-de-zu-zhi-he-diao-yong-fang-an.html
黑魔法非毒药 遵守一个规范写出来的代码是不会Crash的 只要能帮我们解决问题就是好东西
黑魔法性能 有瓶颈? 都到runtime的底层了 你还担心有瓶颈 ?少年安心使用就好了 ? 不服 可以用Time Profiel测试
黑魔法也非万能 ?像 我们在导航控制器要封装手势 统一管理左侧返回按钮 ?这些东西 还是继承来得好
技术就是工具 黑猫,白猫,抓住老鼠就是好猫
3 网络访问参数到底用基本数据类型还是对象
下面看两个方法
1
2
3
4
5
6
7
8
9
10
11
|
+ (void)getDataAtPageNo:(NSNumber *)pageNo PageSize:(NSNumber *)pageSize complete:(CompleteBlock)complete {NSMutableDictionary *param = [NSMutableDictionary dictionary]; if (pageSize) { [param setObject:pageSize forKey:@ "pageSize" ]; } [param setObject:pageNo forKey:@ "pageNo" ]; // SendRequest} + (void)getData2AtPageNo:(long )pageNo PageSize:(long )pageSize complete:(CompleteBlock)complete { NSMutableDictionary *param = [NSMutableDictionary dictionary]; [param setObject:@(pageSize) forKey:@ "pageSize" ]; [param setObject:@(pageNo) forKey:@ "pageNo" ]; // SendRequest } |
在访问网络请求时 对于有参数的请求 设计一个方法 主流为以上两种
使用对象当做参数
使用基本数据类型做参数
一般情况下 这并没有什么大的区别 但是寒哥给出的意见是Never出现基本数据类型
一般情况下 开发者可能觉得并没有什么区别 下面我给大家举个例子
在设计一个分页展示数据的时候: 在页面上的逻辑就是 默认加载第一页 每页长度为10 (Server端的同学一般都很友好 默认情况下 不传每页的长度就是10个) 但是传了就会覆盖掉后台写的默认参数 如传了20 Server就吐20条数据
在第一中设计方案中: 可能在某个控制器中保留一个PageNo PageSize 的对象的成员变量 ,在下拉刷新或者上拉加载的时候 会传递对应的参数给请求方法 , 如果没有特殊需求的话pageSize 对象可有可无 也就是有可能为nil ,那在对应的param可能就没有这个参数传递给Server 。
Server 就会交还给我们某页的20条数据
在 第二种设计方案中 : 可能也在某个控制器中保留一个PageNo pageSize的基本数据类型的成员变量, 在访问网络请求时交给对应的方法 , 一般没有特殊需求我们也不会对PageSize专门设值 但是 基本数据类型在OC 和C语言这种传统的编程语言中是有默认值的 为0,虽然我们没有给pageSize 赋值 但是默认系统默认给了0这个初始值 那么传递到Server的时候 就会覆盖掉Server 写的默认pageSize=10 这样的请求既不会报错 也不会返回数据
超级难调试
所以在网络访问中 寒哥给出的意见就是Never出现基本数据类型
4 用NSNumber比基本数据类型的好处 ? 64位适配问题
我们一般都用来当做网络请求的参数 缓存或者展示到页面
对于网络请求的参数 因为NSDictionary只能放对象 所以NSNumber最好的方式
缓存 无论缓存到plist 还是KeyArchive 都是需要对象的所以NSNumber也非常合适
3 展示到页面
我见过这样给页面上赋值的朋友
我们看到这样貌似并没有什么不妥
但是我们把设备切换到iPhone5S以下 也就是32位的设备
注意这里有Warning
为什么呢 我们来看下NSInteger的头文件
在32下设备是Int 在64位是long
我们都知道苹果不允许不支持64位的app上架 但是貌似我们从来没有为32位和64位做适配
其实不然 在printf 和NSLog 时 对应%d %zd %f 占位符是非常严格的 如果不对项目就会造成意外的结果
其实拿到一个NSNumber我们并不知道他到底是int long unsigned int Bool 直接针对某个类型转换是有风险的 但是其实Clang 给我们提供了个非常好用的Macro @()
NSNumber并不是一个简单的类 它是cocoa 中 类簇的实现参考资料
http://www.cocoachina.com/ios/20140109/7681.html
http://www.cocoachina.com/ios/20150106/10848.html
http://www.cocoachina.com/ios/20141218/10688.html
寒哥教你学 iOS - 经验漫谈(转)的更多相关文章
- 寒哥教你学iOS - 经验漫谈
http://www.jianshu.com/p/cb54054d3add 寒哥教你学iOS - 经验漫谈 字数2848 阅读1896 评论19 喜欢43 顺便来个广告 iOS开发者 群1734993 ...
- 从零开始学 ios 的一些建议 摘自http://www.cocoachina.com/ios/20150826/13151.html
我是一只小菜鸟,今天在cocoaChina 看到一篇关于初学者,也就是零基础的童鞋的一些建议,感觉写的好好. 我觉得,学习真的是很累,但是,你要记得一句话,,世界上最最可怕的两个字是认真.共勉! 事情 ...
- 寒哥细谈之AutoLayout全解
文/南栀倾寒(简书作者)原文链接:http://www.jianshu.com/p/683fbcbfb705著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”. 看到群中好多朋友还停留在Fr ...
- 从零开始学 iOS 开发的15条建议
事情困难是事实,再困难的事还是要每天努力去做是更大的事实. 因为我是一路自学过来的,并且公认没什么天赋的前提下,进步得不算太慢,所以有很多打算从零开始的朋友会问我,该怎么学iOS开发.跟粉丝群的朋友交 ...
- 教妹学 Java:晦涩难懂的泛型
00.故事的起源 “二哥,要不我上大学的时候也学习编程吧?”有一天,三妹突发奇想地问我. “你确定要做一名程序媛吗?” “我觉得女生做程序员,有着天大的优势,尤其是我这种长相甜美的.”三妹开始认真了起 ...
- 教妹学 Java:大有可为的集合
00.故事的起源 “二哥,上一篇<泛型>的反响效果怎么样啊?”三妹对她提议的<教妹学 Java>专栏很是关心. “有人评论说,‘二哥你敲代码都敲出幻想了啊.’” “呵呵,这句话 ...
- 教妹学 Java:动态伴侣 Groovy
00.故事的起源 “二哥,听说上一篇<多线程>被 CSDN 创始人蒋涛点赞了?”三妹对她提议的<教妹学 Java>专栏一直很关心. “嗯,有点激动.刚开始还以为是个马甲,没 ...
- 教妹学 Java:难以驾驭的多线程
00.故事的起源 “二哥,上一篇<集合>的反响效果怎么样啊?”三妹对她提议的<教妹学 Java>专栏很关心. “这篇文章的浏览量要比第一篇<泛型>好得多.” “这是 ...
- 一步一步教你实现iOS音频频谱动画(二)
如果你想先看看最终效果再决定看不看文章 -> bilibili 示例代码下载 第一篇:一步一步教你实现iOS音频频谱动画(一) 本文是系列文章中的第二篇,上篇讲述了音频播放和频谱数据计算,本篇讲 ...
随机推荐
- 利用merge优化
create table a1(TP_END_DATE NUMBER,WK_END_DATE NUMBER, MTH_START_DATE NUMBER, MTH_END_DATE NUMBER, T ...
- 【转】OpenGL相关函数库介绍
原文:http://blog.chinaunix.net/uid-20638550-id-1909182.html OpenGL 函数库相关的API有核心库(gl).实用库(glu).辅助库(aux) ...
- MIPI DSI协议介绍
此文根据网上的资料翻译和整理而来 一.MIPI MIPI(移动行业处理器接口)是Mobile Industry Processor Interface的缩写.MIPI(移动行业处理器接口)是MIPI联 ...
- [转]Ubuntu上的包管理:dpkg,apt和aptitude
一直以来对于ubuntu的包管理的概念就是apt-get,偶尔手动装个包就是dpkg -i,现在觉得是要系统地了解一下这几个包管理的命令. 原文转自: http://zhouliang.pro/201 ...
- socket编程五种模型
客户端:创建套接字,连接服务器,然后不停的发送和接收数据. 比较容易想到的一种服务器模型就是采用一个主线程,负责监听客户端的连接请求,当接收到某个客户端的连接请求后,创建一个专门用于和该客户端通信的套 ...
- codeforces 385C Bear and Prime Numbers 预处理DP
题目链接:http://codeforces.com/problemset/problem/385/C 题目大意:给定n个数与m个询问区间,问每个询问区间中的所有素数在这n个数中被能整除的次数之和 解 ...
- asterisk错误排查
1.查看某个模块是否被包含在编译选项里了: See menuselect.makeoptsIf you see chan_sip in the MENUSELECT_CHANNELS option, ...
- springMVC能做什么,做j2ee时候要考虑什么
转载: http://jinnianshilongnian.iteye.com/category/231099 [置顶] 跟我学SpringMVC目录汇总贴.PDF下载.源码下载 博客分类: 跟开涛学 ...
- JavaScript之this指针深入详解
javascript中的this含义非常丰富,它可以是全局对象,当前对象或者是任意对象,这都取决于函数的调用方式.函数有以下几种调用方式:作为对象方法调用.作为函数调用.作为构造函数调用.apply或 ...
- python google play
#!/usr/env python #-*- coding: utf-8 -*- import urllib import urllib2 import random import requests ...