Posts Tagged "Objective-C"

所谓,引用计数

简介 在大部分关于Objective-C的书中,一般对于引用计数的讲解基本类似于下面(以 Objective-C基础教程 为例): Cocoa采用了一种称为引用计数的技术。每个对象有一个与之相关联的整数,称作它的引用计数器。当某段代码需要访问一个对象时,该代码将该对象的引用计数器值加1。当该代码结束访问时,将该对象的引用计数器值减1。当引用计数器值为0时,表示不再有代码访问该对象,因此对象将被销毁,其占用的内存被系统回收以便重用。 概括一下就是,每个对象都会有个引用计数器,当且仅当引用计数器的值大于0时,该对象才可能是存活的。 引用计数的内存回收是分布于整个运行期的,基本类似于下图。图中红色表示引用计数的活动。(图片来自于https://github.com/kenfox/gc-viz) 从图中我们可以很直接的看出一些优点,比如: 不需要等到内存不够才回收。 不需要挂起应用程序才回收,回收分布于整个运行期。 当然,引用计数也有一些缺点: 无法完全解决循环引用导致的内存泄露问题。 即使只读操作,也会引起内存写操作(引用计数的修改)。 引用计数读写操作要原子化。 retain release 在苹果开源的 runtime 中,在objc-object.h中有部分关于retain和release的实现代码,具体如下: Retain…

Objective-C runtime常见用法

runtime是Objective-C上一个非常强大的屠龙刀,提供了很多奇幻的魔法,当然,如果过度滥用的话,维护上的代价也是显而易见的。 我们这里只讨论一下我们平常工作中常用的特性,当然,它有大量功能,只是我们并不一定用的到,类似objc_msgSend这种的我们也不作介绍。 Objective-C runtime已经开源了,有阅读源码习惯的程序员可以前往官网下载阅读。 下面是下载地址: http://www.opensource.apple.com/tarballs/objc4/ 添加、获取属性 以开源库SVPullToRefresh(SVPullToRefresh是一个提供上下拉刷新的库)举例。 在UIScrollView+SVPullToRefresh这个Category上,SVPullToRefresh 给UIScrollView动态添加了一个属性,我们以SVPullToRefreshView *pullToRefreshView这个属性举例。 在UIScrollView+SVPullToRefresh.h上先申明了这个属性 @property (nonatomic, strong, readonly) SVPullToRefreshView *pullToRefreshView; 之后,在UIScrollView+SVPullToRefresh.m中重写了它的Setter和Getter方法,分别如下: Setter: - (void)…

[译文]如何才能不崩溃 #9: Mindset

原文地址; http://inessential.com/2015/06/10/how_not_to_crash_9_mindset 你知道不写代码的保守派和你一样聪明(You know the old line about not writing code that’s as clever as you are),因为这可能需要一些更聪明的人来调试它? 我曾经认为我应该写和我一样聪明的人的80%左右的代码。留一点时间去调试。 但是在许多年之后,我想,我应该写和我一样聪明的人的10%左右的代码。我开始相信,真正聪明的是使代码非常清晰,并且使得代码明显看起来没有问题。 这就是为什么我有这样的规则:在主线程上做所有事情,除了可以被完美独立起来的和永远不要使用 unsafe_unretained。 这意味着我没有成为代码魔术师的加分点。我不会从帽子里面变出兔子,我也确定我不会走高空绳索。我甚至看都不看绳子。…

[译文]如何才能不崩溃 #8: Infrastructure

原文地址: http://inessential.com/2015/06/10/how_not_to_crash_8_infrastructure 即使你认为你的app不会崩溃,你依然需要收集崩溃日志 - 因为没有绝对的不崩溃:你只能避免已知的崩溃错误。 有很多不同的服务:我尝试过一些后觉得都不错,所以我不会做特殊建议。 但有几件事情应该要做: 崩溃日志应该不需要用户去找到并发送给你。它应该是自动的(如果在OS X系统上,用户可能应该被提示;但是在iOS上,没人希望看到提示。) 应该有一个聚集崩溃日志的方法,你应该能获取到所有聚集起来的崩溃日志组,那样你就能知道哪些是频繁的,而哪些不是。 你应该能够将某些组标记为已解决。 这不够,当然,只是为了收集崩溃日志。你应该定期查阅。(我每天早上都看一下崩溃日志。) 错误追踪 我有一套。 对于我个人的项目,我使用  Lighthouse 、OmniOutliner和笔和纸的组合 - 但是你应该使用任何适合你的工具,只要你进入你的错误追踪工具而不迷路。 (Lighthouse是一个很好的错误追踪工具。对于叙述整个app的未来,…

[译文]如何才能不崩溃 #7: Dealing with Nothing

原文地址: http://inessential.com/2015/05/29/how_not_to_crash_7_dealing_with_nothin 考虑一下这行代码: [thing doStuff]; 如果thing为空(nil),没有问题。不会崩溃。什么都不会发生。 但是你不能以此推断nil在任何情况下都OK: [self doStuff:thing]; 如果thing是nil,那么会发生什么?如果依赖于doStuff:的实现 - 那么,它可能会崩溃。考虑下面这行代码: menuItem.title = thing; 如果menuItem是一个NSMenuItem,那么当thing为nil的时候,它会崩溃。NSMenuItem的头文件上并没有说这些,文档也只提示了一下(“如果你不写title,请使用空字符串(@“”),不是nil。”) 这意味着你需要去确认thing不能为nil。你可能非常确信,thing不是nil。thing是一个字体的名字,…

[译文]如何才能不崩溃 #6: Properties and Accessors

原文地址: http://inessential.com/2015/05/27/how_not_to_crash_6_properties_and_acce 这让我毛骨悚然: - (void)someRandomMethod { some stuff… _thing = otherThing; other stuff… } 你可以证明这是正确的。你正在使用ARC,它会被适当的保持(retain)、释放(release)和添加(add)。没有人观察 _thing。 好吧。这合法并且它工作了。 你意识thing应该是可以被观察的。所以,每一个你设置thing的地方,你可以调用: [self willChangeValueForKey:kThingKey]; _thing = otherThing; [self didChangeValueForKey:kThingKey]; 依然合法,…

[译文]如何才能不崩溃 #5: Threading, part 2

原文地址: http://inessential.com/2015/05/26/how_not_to_crash_5_threading_part_2 我 前面发布的关于线程的文章 留下了一个开发的问题,关于怎样让代码运行在主线程之外进行沟通 - 安全的 - 返回到主线程。 对象创建的后台任务掌握着任务的结果。这是一个硬性的规则。 通常创建任务的对象在整个app的生命周期中一直存活着。图片缓存可能是个例子 - 缓存可能会在app的生命周期中被清空,但是缓存对象在这段时间内一直存活着。 另一个例子是类似于 Vesper 的 VSAccount 对象。总是有一个 VSAccount 实例。用户也许有也许没有一个服务端账户。用户可能会改变他们的账户。但是一直会有一个 VSAccount 对象贯穿于整个app的生命周期。 (注意:显然,一个app管理多个账户会做不同的事情。但是 Vesper 管理最多一个服务端账户,所以,…

[译文]如何才能不崩溃 #4: Threading

原文地址: http://inessential.com/2015/05/22/how_not_to_crash_4_threading 这有一个简单的规则:做任何事情都在主线程上。随着机器和设备的日益发展,你可以在主线程中做比你想象中更多的事情。 当你不需要考虑任何并发的时候,这就太美好了。 但是... 我是一个性能狂。或者,更重要的是,我是一个用户体验狂。唯一一个比缓慢更糟的是明显的阻塞主线程。所以不要这么做。 好,我们先从主线程开始。 主线程规则 所有我期望运行在主线程上的代码只在主线程上运行,除少数之外。(我们在一分钟内获得例外。) 这解决了一串问题。例如,我以前写了一个文章关于在dealloc中注销通知。很多人指出,你不能保证dealloc将会在哪一个线程中被调用 - 但是你能,实际上,对于任何在主线程上运行的对象,都只能在主线程上被引用。 这意味着,任何KVO的改变都被发送到主线程上,所有观察者也依然运行在主线程上,并在主线程上期待通知。 不处理并发的好处是极大的。我强烈建议你使用这种方式去编写你的app,然后去测试是否会有东西阻塞主线程。如果没有,…

[译文]如何才能不崩溃 #3: NSNotification

原文地址: http://inessential.com/2015/05/21/how_not_to_crash_3_nsnotification 实际上,相比起KVO我更喜欢NSNotification(特别是)在绑定上。我有的时候会使用KVO - 当有些时候,这是最明智的。但是NSNotification,像很多更老的API一样,在不引起崩溃的情况下,更容易使用。 但是你依然需要很小心。 崩溃的一种方法 当一个对象注册了一个通知,然后对象在释放的时候没有注销这个通知,然后在发送通知的时候,app就会崩溃。 大规则 我有一个简单的、硬性并快速的规则:通知只在主线程发送。没有例外。如果某些代码在另外一个线程上跑,并且它需要去发送一个通知,那它只能在主线程上这么做。 这能避免通知进入一个你不期望的线程所导致的所有问题。在注销通知后,这可以避免资源竞争。 一个app几乎所有的代码都应该运行在主线程上。运行在通知或者GCD队列上的代码应该从其他代码上独立出来,并且当多个对象一起工作的时候,应该使用代理模式(使用或不使用block)。 确保通知应该一直很容易的在主线程上发送。(我将在另一篇 如何才能不崩溃…

[译文]如何才能不崩溃 #2: Mutation Exceptions

原文地址: http://inessential.com/2015/05/16/hownottocrash2mutationexceptions 你从某处获得了一个集合并且枚举它 - 然后你得到了一个关于集合在枚举中被改变的错误。然后app崩溃了。 你可以用一种简单的方法避免这种不幸的命运:不枚举可变集合。 不同意我 你可能会认为真正正确的答案是枚举中不去改变一个可变的集合。你应该对你的app有足够的了解,来编写安全的可枚举可变集合的代码。 是的,你应该,你绝对应该。 然而,编写不崩溃的代码就是在移除疑惑的问题。将发生错误的机率降到最低,将未来引入改变而导致崩溃的机率降到最低(通过你或其他人)。 可变集合不应该为公共API 这个应该非常罕见 - 或者,更加,绝不 - 一个对象拥有一个公共的可变集合的属性。可变集合应该放在对象的内部。 (此外,尽可能的让公共的集合都是只读属性。当然,这并不一直都可能。) 现在,一个拥有公共集合属性的对象完全有可能在内部是一个可变集合。追踪一个operations集合,它的公共部分可能像这样: @property (nonatomic, readonly) NSArray *operations; 它的内部是这样: @property…