新年之际,盘点一些APP开发技巧
(原文:Reader Submissions - New Year's 2015 作者:Mattt Thompson 译者:培子 校对:蓝魂)
回顾过去一年发生在我们身边的事情时,有一点不得不提:对苹果开发者来讲,2014年是令人难以置信的一年。在这短短的一年中(有关APP的开发)发生了如此多的变化:在充满吸引力的Swift面前,我们几乎忘了之前是如何痴迷于Objective-C;以及充满想象力的iOS 8和WatchKit,难以想象还有什么API能与之相比。
NSHipster的惯例:请可爱的童鞋们,在新年的第一天,为大家展示你们(在开发中)常使用的技巧和方法。如今,随着来自库比蒂诺(Cupertino,苹果总部,位于旧金山)和众多开源社区的一系列API的涌现,妈妈再也不用担心我们找不到有趣的东西来分享啦!
在此,感谢以下童鞋们所做的贡献:
Colin Rofls, Cédric Luthi, Florent Pillet, Heath Borders, Joe Zobkiw, Jon Friskics, Justin Miller, Marcin Matczuk, Mikael Konradsson, Nolan O'Brien, Robert Widmann, Sachin Palewar,Samuel Defago, Sebastian Wittenkamp, Vadim Shpakovski, and Zak
成员函数的使用技巧
(来自 Robert Widmann)
在用静态方式调用Swift类和结构中的成员函数时,通常使用以下格式:
Object->(参数)->Things
比如,你可以用以下两种方式调用reverse():
1
2
|
[1,2,3,4].reverse( ) Array.reverse([1,2,3,4]) |
用@()来封装C字符串
(来自 Samuel Defago)
事实上文字大部分时候是数字和字母的集合,使用C字符串,尤其当我在使用运行时编码的时候,我常常会忘记用UTF8编码、以NULL结束:Objective-C字符串封装:
1
2
3
|
NSString *propertyAttributesString = @(property_getAttributes(class_getProperty([NSObject class], "description" ))); // T@"NSString",R,C |
AmIBeingDebugged
Nolan O'Brien在这篇Q&A技术文档中让我们注意到了AmIBeingDebugged函数方法:
使用延迟存储属性
(来自 Colin Rofls)
在开发过程中,应该避免使用Optionals类型,更不应该使用隐式解包optionals类型。你想声明一个var变量却不想给一个初始值?使用“lazy”吧,唯一要注意的就是:在你的属性被赋值之前不要调用getter方法即可(童叟无欺!)
1
|
lazy var someModelStructure = ExpensiveClass() |
假如你仅仅对这var变量调用set方法,而没有调用getter方法的话,这个被lazy修饰的var变量不会被赋值。例如,用lazy修饰那些直到viewDidLoad时才需要初始化的views变量就会非常合适。
获取Storyboard视图容器里的子视图控制器
(来自 Vadim Shpakovski)
有一个比较方便的方法来获取故事板视图容器里的子视图控制器:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
// 1. A property has the same name as a segue identifier in XIB @property (nonatomic) ChildViewController1 *childController1; @property (nonatomic) ChildViewController2 *childController2; // #pragma mark - UIViewController - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { [ super prepareForSegue:segue sender:sender]; // 2. All known destination controllers assigned to properties if ([self respondsToSelector:NSSelectorFromString(segue.identifier)]) { [self setValue:segue.destinationViewController forKey:segue.identifier]; } } - (void)viewDidLoad { [ super viewDidLoad]; // 3. Controllers already available bc viewDidLoad is called after prepareForSegue self.childController1.view.backgroundColor = [UIColor redColor]; self.childController2.view.backgroundColor = [UIColor blueColor]; } |
重复运行项目,不重复构建项目
(来自 Heath Borders)
假如你一直在不停地调试同一个问题,你可以在不重复构建的情况下运行你的APP,这样:“Product>Perform Action>Run without Building” (快捷键??R: Command + R)
快速获取Playground资源
(来自 Jon Friskics)
Swift里的所有Playground共享相同的数据目录:/Users/HOME/Documents/Shared Playground Data
如果你喜欢使用很多Playgrounds,你将需要在上述共享目录下为每个Playground新建对应的子目录,来存储每个Playground用到的数据;但是那之后你需要告诉每个Playground在哪儿可以获取其对应的数据。下面是我常用的一个辅助解决方法:
1
2
3
|
func pathToFileInSharedSubfolder(file: String) -> String { return XCPSharedDataDirectoryPath + "/" + NSProcessInfo.processInfo().processName + "/" + file } |
processName属性是Playground文件的名字,因此只要你已经在Playground数据共享文件目录下以相同的名字新建了一个子目录,那么你可以很容易访问这些数据,和读取本地JSON数据一样:
1
2
3
|
var jsonReadError:NSError? let jsonData = NSFileManager.defaultManager().contentsAtPath(pathToFileInSharedSubfolder( "data.json" ))! let jsonArray = NSJSONSerialization.JSONObjectWithData(jsonData, options: nil, error: &jsonReadError) as [AnyObject] |
....或者 访问本地图片
1
2
|
let imageView = UIImageView() imageView.image = UIImage(contentsOfFile: pathToFileInSharedSubfolder( "image.png" )) |
-----------------------------------------------------------------------
Please attention!本篇文章剩余的部分来自Cédric Luthi大神的贡献,他分享了一些比较有用的开发技巧和技术,这些内容足够自成一篇,值得细细品读。这里再次感谢Cédric!
CocoaPods大揭秘
这儿有一个快速的方法来检查APP里用到的所有pods:
1
|
$ class-dump -C Pods_ /Applications/Squire.app | grep -o "Pods_\w+" |
CREATE_INFOPLIST_SECTION_IN_BINARY
注意Xcode中为命令模式APP(command-line apps)设置的CREATE_INFOLIST_SECTION_IN_BINARY属性。这比使用-sectcreate__TEXT__info_plist链接标志位更加容易,前者还把已经编译好的Info.plist文件嵌入在二进制编码中。
关于如何向苹果提需求,它也给我们上了一课,这个特性需求早在2006年的 rdar://4722772 被提出,但直到7年后才被满足。
(译者注:言外之意是它是反面教材,应该更有技巧的提需求)
禁用 dylib钩子
(来自 Sam Marshall)
Sam Marshall这个技巧可谓是走自己的路,让黑客无路可走。
在你的“Other Linker Flags”里加上下面这行:
1
|
-Wl,-sectcreate,__RESTRICT,__restrict,/dev/ null |
NSBundle -preferredLocalizations
某些时候,你需要知道APP当前使用的是什么语言。通常,大家会使用NSLocal+preferredLanguages. 可惜的是这个方法不会告诉你APP实际呈现的文字语种。你仅仅会得到iOS系统里“Settings->General->Language&Region->Preferred Language”列表中的选项,或者OSX系统里“System Preferences->Language & Region->Preferred Languages”列表中的选项。想象一下:优先语言列表中只有{英语,法语},但你的APP仅使用德语;调用[[NSLocal preferredLanguages] firstObject]返回给你的是英语,而不是德语。
正确的方法是用[[NSBundle mainBundle] preferredLocalizations]方法。
苹果的开发文档是这样说的:
一个包含了在bundle中本地化的语言ID的NSString对象的数组,里面的字符串排序是根据用户的语言偏好设置和可使用的地理位置而来的。
NSBundle.h里的备注:
一个bundle中本地化的子集,重新排序到当前执行坏境的优先序列里,main bundle的语言顺序中最前面的是用户希望在UI界面上看到的语种。
当然你也许需要调用这个方法:
1
|
NSLocal+canonicalLanguageIdentifierFromString: |
来确保你使用的文字语种是规范的语种。
保护SDK头文件
如果你用dmg安装Xcode,那么看看这篇Joar Wingfors的文章,它讲述了如何通过保留所有权来避免SDK头文件被意外修改:
1
|
$ sudo ditto /Volumes/Xcode/Xcode.app /Applications/Xcode.app |
任意类型的实例变量检测
为了达到逆向处理的目的,查询对象的实例变量是一个常见可靠的途径。通常调用对象valueForKey:方法就能达到这一目的,除了少数重写了类方法+accessInstanceVariablesDirectly的类屏蔽了该操作。
下面是一个例子:当实例变量有一个为任意类型的属性时,上述提到的操作无效
这是iOS6.1 SDK中MediaPlayer 框架的一段引用:
1
2
3
4
|
@interface MPMoviePlayerController : NSObject { void *_internal; // 4 = 0x4 BOOL _readyForDisplay; // 8 = 0x8 } |
因为 id internal=[moviePlayerController valueForKey:@”internal”] 无效,下面有一个笨办法来取得这个变量:
1
|
id internal = *((const id*)(void*)((uintptr_t)moviePlayerController + sizeof(Class))); |
注意!不要随意调用这段代码,因为ivar的布局可能改变(指针偏移量计算可能出错)。仅在逆向工程中使用!
NSDateFormatter +dateFormatFromTemplate:options:locale:
友情提示:假如你调用[NSDateFormatter setDateFormat],而没有调用[NSDateFormatter dateFormatFromTemplate:options:local:],n那么很可能出错。
苹果文档:
1
2
3
|
+ (NSString *)dateFormatFromTemplate:(NSString *)template options:(NSUInteger)opts locale:(NSLocale *)locale |
不同地区有不同的日期格式。使用这个方法的目的:得到指定地区指定日期字段的一个合适的格式(通常你可以通过currentLocal查看当前所属地区)
下面这个例子给我们表现了英式英语和美式英语不同的日期格式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
NSLocale *usLocale = [[NSLocale alloc] initWithLocaleIdentifier:@ "en_US" ]; NSLocale *gbLocale = [[NSLocale alloc] initWithLocaleIdentifier:@ "en_GB" ]; NSString *dateFormat; NSString *dateComponents = @ "yMMMMd" ; dateFormat = [NSDateFormatter dateFormatFromTemplate:dateComponents options:0 locale:usLocale]; NSLog(@ "Date format for %@: %@" , [usLocale displayNameForKey:NSLocaleIdentifier value:[usLocale localeIdentifier]], dateFormat); dateFormat = [NSDateFormatter dateFormatFromTemplate:dateComponents options:0 locale:gbLocale]; NSLog(@ "Date format for %@: %@" , [gbLocale displayNameForKey:NSLocaleIdentifier value:[gbLocale localeIdentifier]], dateFormat); // Output: // Date format for English (United States): MMMM d, y // Date format for English (United Kingdom): d MMMM y |
通过调试获取内部常量
近期, Matthias Tretter在Twitter上问到:
有人知道在iOS8里modal viewController presentation的默认动画时间和跳转方式吗?
我们在UIKit的类库中发现了这样一个函数:[UITransitionView defaultDurationForTransition:],并在这个方法的位置加一个断点:
1
|
(lldb) br set -n "+[UITransitionView defaultDurationForTransition:]" |
模态显示一个viewController,就会停在这个断点,输入finish执行该方法:
1
|
(lldb)finish |
在defaultDurationForTransition:被执行时,你就能读到结果(在xmm0寄存器里)
1
2
|
(lldb) register read xmm0 --format float64 xmm0 = {0.4 0} |
回复:默认动画时间0.4s
DIY 弱关联对象
不幸的是,关联对象OBJC_ASSOCIATION_ASSIGN策略不支持引用计数为0的弱引用。幸运的是,你可以很容易实现它,你仅仅需要一个简单的类,并在这个类里弱引用一个对象:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
@interface WeakObjectContainter : NSObject @property (nonatomic, readonly, weak) id object; @end @implementation WeakObjectContainter - (instancetype)initWithObject:(id)object { self = [ super init]; if (!self) { return nil; } self.object = object; return self; } @end |
然后,通过OBJC_ASSOCIATION_RETAIN(_NONATOMIC)关联WeakObjectContainter:
1
|
objc_setAssociatedObject(self, &MyKey, [[WeakObjectContainter alloc] initWithObject:object], OBJC_ASSOCIATION_RETAIN_NONATOMIC); |
用object属性指向这个所需的引用计数为0的弱引用对象。
1
|
id object = [objc_getAssociatedObject(self, &MyKey) object]; |
在这么多日新月异的新技术推动下,我们将迎来一个充满无限可能和机会的一年。在此,祝大家2015年新年快乐!
愿编程路上,与君共勉。
(本文为CocoaChina组织翻译,本译文权利归译者所有,未经允许禁止转载。)
新年之际,盘点一些APP开发技巧的更多相关文章
- 这些APP开发技巧可少花60万!
用户需求——我偏不用干嘛要装? 随着手机的普及,大众流量的端口从电脑转移到手机,传统的商业平台从线下到电脑再到手机进行了转换.手机APP作为移动互联网的入口,众多创业者凭借一个手机APP成就了亿万财富 ...
- web app开发技巧总结 (share)
(转自http://hi.baidu.com/kuntakinte/item/ca92d6e5edae9fc0bbf37d08) 自Iphone和Android这两个牛逼的手机操作系统发布以来,在互联 ...
- Web APP开发技巧总结(转)
一.META/LINK相关: 1.百度禁止转码 通过百度手机打开网页时,百度可能会对你的网页进行转码,往你页面贴上它的广告,非常之恶心.不过我们可以通过这个meta标签来禁止它: <meta h ...
- Web APP开发技巧总结
http://www.admin10000.com/document/6304.html 1. 当网站添加到主屏幕后再点击进行启动时,可隐藏地址栏(从浏览器跳转或输入链接进入并没有此效果) <m ...
- 前端读者 | Web App开发入门
本文来自互联网 自Iphone和Android这两个牛逼的手机操作系统发布以来,在互联网界从此就多了一个新的名词 - Web App(意为基于WEB形式的应用程序).业界关于Web App与Nativ ...
- 十大技巧快速提升原生APP开发性能
移动应用市场用户争夺战日益激烈,原来做APP拼想法拼创意拼是否抓住用户痛点.现在,精细化用户体验成为了一个APP能否留存用户的关键问题,一旦用户觉得体验不畅,马上就有竞品APP后补,如何开发高性能的移 ...
- AppCan移动开发技巧:3步走,获取移动APP签名信息
大家知道,在移动APP开发里,与应用包名一样,应用的签名信息需是唯一的,否则将会出现应用冒领.重复安装等问题.之前分享过安卓应用的签名如何获取(点击查看),这里将继续以AppCan平台为例,分享如何获 ...
- 移动 Web 开发技巧之(后续)
昨天的<移动 Web 开发技巧>的这篇文章,大家反响不错,因为这些问题在大家日常写移动端的页面时经常遇到的.所以那个文章还是超级实用的,那么我们今天继续来分享一下移动端的web开发技巧吧, ...
- 微信公众平台开发:Web App开发入门
WebApp与Native App有何区别呢?Native App:1.开发成本非常大.一般使用的开发语言为JAVA.C++.Objective-C.2.更新体验较差.同时也比较麻烦.每一次发布新的版 ...
随机推荐
- JSP版(utf8编码)的Ueditor百度文章编辑器配置以及使用说明
二话不说,先上图: 我配置好的效果大致是这些功能:基本的文字编辑功能.图片上传功能.附件上传功能.百度/谷歌地图搜索截图.视/音频发布功能.这个插件是现今我用过觉得最舒服的编辑器,功能齐全强大,稍微修 ...
- Office激活密钥
Retail密钥: PHX9Q-N9GKW-CG4VF-MHCWR-367TX PB44J-GNX2R-BJJYX-HJW6R-Q9JP9 6PVPD-CNWDQ-G734C-DG7BM-VQTXK ...
- cocos2dx 以子弹飞行为例解说拖尾效果类CCMotionStreak
在游戏开发中,有时会须要在某个游戏对象上的运动轨迹上实现渐隐效果.比方子弹的运动轨迹,假设不借助引擎的帮助,这样的效果则须要通过大量的图片来实现.而Cocos2D-x的拖动渐隐效果类CCMotionS ...
- QT皮肤系统的动态切换
应用需求: 提供皮肤切换选项,在不重启应用程序的情况下实现皮肤的动态切换. 理论基础: 1) 图片资源是如何被利用的 这里先简要说明一下实现原理,皮肤的动态切换其关键在于图片资源的加载方式.QT中每个 ...
- Setup Tensorflow with GPU on Mac OSX 10.11
Setup Tensorflow with GPU on OSX 10.11 环境描述 电脑:MacBook Pro 15.6 CPU: 2.7GHz 显卡: GT 650m 系统:OSX 10.11 ...
- IHttpModule接口
IHttpModule向实现类提供模块初始化和处置事件. IHttpModule包含兩個方法: public void Init(HttpApplication context);public voi ...
- jwPlayer实现支持IE8及以下版本避免出错的方法
jwplayer在支持Html5的情况下会自动使用html5的video和audio标签进行播放视频和音频.但是在IE中版本低于IE9时 <script src="jwplayer.h ...
- 用标准版的Eclipse搭建PHP环境
用标准版的Eclipse搭建PHP环境 ——@梁WP 摘要:用标准版的Eclipse搭建PHP环境. 一.下载Eclipse的PHP插件 百度搜索phpeclipse,看到某条结果是带有SourceF ...
- android java获取当前时间的总结
import java.text.SimpleDateFormat; SimpleDateFormat formatter = new SimpleDateFormat (&q ...
- CentOS 6.4 图文安装教…
点评:CentOS 6.4是最新的出的系统,这里分享下安装教程,有些设置大部分教程没出现过,特分享下,方便需要的朋友 CentOS 6.4 下载地址: http://www.jb51.net/soft ...