Alicia's Blog

[LEAF Photo]作者


  • Home

  • Tags

  • Archives

Clang Attributes in Open Source

Posted on 2016-09-23

这篇文章应该叫做那些开源项目中用到过的 Clang Attributes,会列出开源项目的名字和使用方式以及用例

visibility

解释

__attribute__ ((visibility("default")))

visibility 中的值可以为 default | internal | hidden | protected

该属性用于设置动态链接库中函数或者变量的可见性

我们在 C 语言中,如果想要将函数或变量限制在当前文件中,可以使用 static 关键字,但是在复杂的项目中,如果想要限制某个符号被共享库内部某些文件可以访问,而外部不可以访问泽有些困难,于是 GCC 中引入了 visibility 属性,它可以设置某个函数或变量是否可访问,例如这个变量在多个文件中被定义,又同时有多个链接库需要引用它时,如果不设置可见性,那么到底引用为那个值或者函数就要看加载顺序了。

用例

UIKIT_EXTERN const UIWindowLevel UIWindowLevelNormal;
UIKIT_EXTERN const UIWindowLevel UIWindowLevelAlert;
UIKIT_EXTERN const UIWindowLevel UIWindowLevelStatusBar __TVOS_PROHIBITED;

#ifdef __cplusplus
#define UIKIT_EXTERN        extern "C" __attribute__((visibility ("default")))
#else
#define UIKIT_EXTERN            extern __attribute__((visibility ("default")))
#endif

项目

JDStatusBarNotification

参考文献

Visibility Pragmas
Controlling Symbol Visibility

objc_subclassing_restricted

解释

__attribute__((objc_subclassing_restricted))

用于限制某个类是不可继承的,但是这个限制只是编译时的限制,不直接写 @interface SubClass : ParentClass,而是使用 Runtime 的方式仍然是可以创建继承关系的。

void run(id self, SEL _cmd) {}
class subClass = objc_allocateClassPair([ParentClass class], "SubClass", 0);
class_addMethod(subClass, @selector(run),(IMP) run, "v@:");
objc_registerClassPair(subClass);

用例

__attribute__((objc_subclassing_restricted))
@interface ParentClass : NSObject
@end
@interface SubClass : ParentClass // <--- Compile Error
@end

参考文献

Clang Attributes 黑魔法小记

deprecated

解释

__attribute__((deprecated("弃用解释说明")));

用于说明某个属性已经弃用

@property (assign, nonatomic) CGFloat opacity __attribute__((deprecated("Customize bezelView properties instead.")));

用例

另外,忽略弃用警告可以使用

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
self.bezelView.alpha = self.opacity;
#pragma clang diagnostic pop

项目

MBProgress

objc_requires_super

解释

子类可以覆盖父类方法,如果子类需要调用父类方法使用 super 父类方法名,如果需要强制子类调用父类的方法,可以使用 objc_requires_super,当子类没有调用父类方法时则会给出警告

示例

@interface Father : NSObject
- (void)payMoney __attribute__((objc_requires_super));
@end
@implementation Father
- (void)payMoney {
    NSLog(@"I paid");    
}
@end
@interface Son : Father
@end
@implementation Son
- (void)payMoney {
} // <--- Compile Error Missing call [super payMoney]
@end

// .. 待追加

名称

解释

用例

项目

示例

参考文献

Writing Crash-Free Code

Posted on 2016-09-05

面试的时候经常会被问到“程序崩溃都有哪些原因造成”,突然想的话想不到那么全面, 总结一下以备不时之需,也可以在写代码时注意下。另外,“如何处理崩溃”也是面试官喜欢问的问题,也做个总结。

程序崩溃都有哪些原因造成

  • 可变数组的操作
    1. 使用 (for in) 或迭代器遍历可变数组时删除数组元素,改为逆序遍历或者添加个数组副本
    2. 可变数组声明属性为 copy
      // .h 文件
      @property (nonatomic, copy) NSMutableArray mutableArray;
      // .m 文件
      NSMutableArray
      array = @[@1,@2];
      self.mutableArray = array;
      // 下句崩溃,因为 NSArray 中找不到 removeObjectAtIndex 方法
      [self.mutableArray removeObjectAtIndex:0];
  • 多线程中访问同一块资源时,没有为数据加锁,数据错误可能会导致崩溃。
  • 在多线程中,UI 等操作需要放到主线程操作,否则会导致崩溃
  • 操作野指针。
  • 数组越界,像 NSDictionary 或者 NSArray 中插入数组等
  • 调用对象不存在的方法,unrecognized selector
  • 低内存终止,看门狗结束程序,大多是内存泄漏等导致。
  • 内存泄漏详情见之前的文章

发生 EXC_BAD_ACCESS 的错误,通过全局断点进行定位。可以使用 NSZombies 可以定位野指针。资源竞争的定位可以通过 Thread Sanitizer 查找。

修复线上崩溃的方法

使用 symbolicatecrash 工具将崩溃堆栈符号化

创建新的文件夹,将 symbolicatecrash、.crash 和 .dSYM 文件放到该文件夹下执行下面命令,生成新的 symbol.crash 文件,该文件中可以显示崩溃函数名及位置。

Read more »

Reading The Solution Tango

Posted on 2016-08-31

最近读了一本不错的管理类书籍,《The Solution Tango: Solving any problem in senven easy steps》 译者可能也知道这个英文名字估计不能吸引中国读者,所以他的中文译名是意译的 《不懂带人,你就自己干到死:把身边的庸才变干将》。

做技术的人大多跟机器打交道,并不擅长处理人的关系。当从做技术升级为管理层时,很少和下属沟通,或者弄得下属觉得整天被指手画脚工作地并不舒服;又或是一些下属主动沟通的能力基本没有,被动沟通时又表现出一副不屑或者不愿搭理的样子。前两天还看到有人说,技术很厉害的人凑在一起创业的多了,但是成功的就没有几个了,技术上的问题很容易解决,但是人的问题就没那么容易处理了。

这本书就是解决人的问题的,它提出了一些事例和七个步骤来说明如何带人:

别再给员工讲道理了,没人听

当出现问题时,首要需要想想如何解决问题,而不是陷入指责谁是导致问题的原因。

大部分情况下领导在出现问题时都是先追责,追责可以让导致出现问题的人记住教训避免其再犯,或者找出一个人来承担责任。但是书中指出这对问题的解决一点用处没有,而且很容易使人陷入一种负面的情绪中,大家变得沮丧、压抑、毫无斗志,我们应该在出现问题时,及时想办法补救。忘掉为什么,多问怎么办。

如何听,员工才会说;如何说,员工才会听

听,不要让自己的先入之见扭曲别人传递的信息。做到这点其实还挺不容易的,我们总是习惯在听人说话时,把自己放在一个更高的位置,认为自己有多么聪明,可以想到别人要表达什么,但是很多时候那只是我们的想法不是别人要说的,倾听时要对他人有耐心。

说,要用宽松而不是指令性的语言,采取邀请的姿态,而不是摆出指挥的架势。想到最近的女排,每次比赛切到郎平指导时,她都是以一种商量的口吻、建议的语气指出队员的问题(“XXX 刚才是不是 XXX 那样更好些?”)。又想到羽毛球,总指导说林丹是那种比较抗压的,可以指出他的问题说他逼他,但是谌龙就不能用这种方式,要用安抚的方式。大家都能接受宽松式的语言,但却有很多人不能接受指令性语言。

书名中提到的解决方案的七个步骤是:

Read more »

What can make reading Github code easy

Posted on 2016-08-30

经常 Github 上逛,某些代码在下载前需要看下大概的逻辑,在网页里点好几层才能看到代码,怎么破?装个插件 Octotree,源码介绍看这里。

img

不想翻墙就点击这里下载,下载后在 Chrome 中打开一个新 Tab,输入chrome://extensions,把下载的 .crx 文件拖拽到浏览器安装,再打开 Github 页面会出现提示是否使用 Octotree。

GCD Timer and Leaks

Posted on 2016-08-24

GCD 的 Timer 相对于 NSTimer 更加灵活、高效(无需在主线程和后台线程之间进行切换),而且 NSTimer 需要在合适的地方调用 invalid 以避免内存泄漏,所以平时比较常用。但是发现 GCD Timer 随便调用 API 的话也会造成内存泄漏。

GCD Timer 通过 Dispatch Source 实现,调用方法如:

dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 
    2.0 * NSEC_PER_SEC, 0.1 * NSEC_PER_SEC);
dispatch_source_set_event_handler(timer, ^() {
    // do something
});
dispatch_resume(timer);

如果需要暂停定时器调用

dispatch_suspend(timer);

dispatch_suspend 和 dispatch_resume 应该是成对出现的。两者分别会减少和增加 dispatch 对象的挂起计数,但是没有 API 获取当前是挂起还是执行状态,所以需要自己记录。

dispatch_suspend 暂停队列并不意味着当前执行的 block 暂停

当暂停派发队列时需要注意,调用 dispatch_suspend 暂停一个队列,并不意味着暂停当前正在执行的 block,而是 block 可以执行完,但是接下来的 block 会被暂停,直到 dispatch_resume 被调用。

如果添加 dispatch_suspend 的调用后 timer 是无法被释放的。一般情况下会发生崩溃并报“EXC_BAD_INSTRUCTION”错误,看下 GCD 源码 Source 释放的处理 dispatch source release 的时候判断了当前是否是在暂停状态。

void _dispatch_source_xref_release(dispatch_source_t ds) {
    if (slowpath(DISPATCH_OBJECT_SUSPENDED(ds))) {
        // Arguments for and against this assert are within 6705399
        DISPATCH_CLIENT_CRASH("Release of a suspended object");
    }
    _dispatch_wakeup(ds);
    _dispatch_release(ds);
}

GCD 完整源码点击这里

dispatch_suspend 状态下无法释放

但是还有不一般的情况,如果暂停的代码加到 dispatch_source_set_event_handler 的 block 中,并不会发生崩溃,但是这个时候页面会无法释放造成内存泄漏!内存泄漏!内存泄漏!。

dispatch_source_set_event_handler(timer, ^() {
    // do something
    dispatch_suspend(timer);
});

Demo 代码点击这里,Demo 中进入“GCD Timer” 页面再退出,通过 Allocations 工具可以发现 GCDTimerViewController 没有释放,dealloc 无法执行。

怎么破?如果有需求按照上面这种方式写的话,添加个变量记录下 dispatch_suspend 是否被调用的状态,在 dealloc 时判断下,调用过 dispatch_suspend 则再调用下 dispatch_resume。或者暂停后不需要重新运行 timer 的话,取消 timer,一旦取消则不能再重新运行 timer,只能重建。

dispatch_source_cancel(timer);

参考内容

Mikeash GCD 介绍

AsyncDisplayKit

Posted on 2016-07-18

特性

Pinterest 为了 1 亿用户的目标对其 iOS app 进行了重构。这次重构进行的主要改变是使用 AsyncDisplayKit。

ASDK 是用来保证 UIs 的流畅性和可响应性的一个开源库。它可以将图片的解码、文字大小调整及渲染,和其他比较耗时的 UI 处理放到后台线程中进行。除了异步处理这个亮点,它还提供了一种类似 CSS Box Model 的系统,让你能够像使用 UIView 一样容易的使用 CALayers (无需注册触摸事件)。另外,它还提供了滚动视图移动差值的计算,它可以在获取网络数据后,计算出用户滚动位置的 Cell 区域,提前渲染出一小部分 Cell。

Node 介绍

UIView 和 CALayer 不是线程安全的,所以无法做到在非主线程中进行操作,所以 ASDK 创建了以 node 为基本单位的对象,需要使用各种 node 来代替之前的各种控件。

node 和 UIKit 的 View 对应关系(摘要)
ASDisplayNode 代替 UIKit 的 UIView (AsyncDisplayKit 的根 node)
ASCellNode 代替 UIKit 的 UITableViewCell 和 UICollectionViewCell

ASDK 中除了 node 外,还有很多 node containers,不建议单独使用 node,而是应当将其添加到 node containers 中。

node containers 和 UIKit 的 Controller 的对应关系(摘要)
ASViewController 代替 UIKit 的 UIViewController

智能预加载

要将 node 添加到 node containers 中主要是因为所有的 node 都有它们当前接口状态的概念,这个接口状态的属性名称是 interfaceState,由所有容器创建和内部维护的 ASRangeController 进行更新。未添加到容器中的 node 不会收到该状态的更新。

例如当 nodes 被添加到可滚动或者分页的页面中,nodes 通常会在以下的范围内:
Fetch Data Range:可见的最远范围。它的内容需要从 API 或者本地磁盘中获取。
Display Range:显示任务例如文本栅格化和图片解码。
Visible Range:Node 正在屏幕上被显示的

Read more »

Concurrent Programming and Synchronization

Posted on 2016-06-24

使用多线程时,要注意防止不同线程意外修改数据的问题,下面总结一下常用的同步方式。

原子操作

原子操作是同步的一个简单的形式,它处理简单的数据类型。其优势是不妨碍竞争的线程。对于简单操作,比如递增一个计数器,原子操作比使用锁具有更高的性能优势。

void retain() {
    // retainCount 为实例变量,相当于 retainCount++
    OSAtomicIncrement64(&retainCount);    
}
void release() {
    unit32_t originalValue;
    // 相当于 retainCount--
    orginalValue = OSAtomicDecrement64(&retainCount); 
    if (originalValue == 1) {
        this->free();
    }
}

查看支持原子操作的列表,参阅/user/include/libkern/OSAtomic.h头文件和参见 atomic主页。

顺带提一下 iOS 中常用的属性关键字 atomic,它是默认值,能够保证线程安全,同时我们为了提高效率,在单线程下都会给变量属性声明为 nonatomic。

Read more »

LEAF Photo

Posted on 2016-05-26

Download now!

LEAF Photo - Edit your photos easy.

Design Tools: Templates, Text, Sticker, Marker Pen, Background, all of these make your photos beautiful or fashion.

Not just collages. Do you wanna make scrapbook with an app? It can help you combine multiple photos into single one, and then print it, print one photo that contains multiple photos can save ink for you, at last paste photos on scrapbook. Or you can edit your photos, after done then print it, bind your photos to an album. Enjoy your D.I.Y album or scrapbook.

Have a question or suggestion? I am waiting for your feedback.
Email: leafsupport@163.com

iOS Localization

Posted on 2016-05-26

上一次写需要本地化的代码还是 iPhone 4 风靡的时代,现在 iPhone 7 都要出了。因为手机系统默认语言是英文,所以经常看到一些小公司写的 App 实现了本地化但是不够彻底,残留了一些文字还是中文,这也就罢了毕竟我还能认识,比较恶心的是,很多英文单词比中文长,不测试看看的话,很容易出现一个按钮上显示一半英文单词的现象。

其实 Xcode 上测试本地化的方式很简单,好久不用了找半天才找到,写到这里备忘。

Product -> Scheme -> Edit Scheme -> Options -> Application Language 中设置语言

img

另外,介绍个工具 Linguan,这个工具可以把本地化的 String 显示在一个画面上,这样编辑的时候省去切换的苦恼,如果有漏掉没有翻译的文字还会有提示。

img

iOS Memory Warning

Posted on 2016-05-20

[LEAF Photo] 中要处理图片,如果操作时间长的话会收到 Memory Warning,内存处理的不好的话就会崩溃,下面记录下处理内存警告的一些优化。

处理内存警告

处理所有 ViewController 中的 didReceiveMemoryWarning 方法
didReceiveMemoryWarning 方法中应当把缓存的变量都清空,这些变量最好都是通过懒加载的方式创建,这样收到内存警告后也会自动加载。

@interface TestViewController () 
@property (strong, nonatomic) NSMutableArray *dataArray;
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
    self.dataArray = nil;
}
- (NSMutableArray *)dataArray {
    if (!_dataArray) {
        _dataArray = [[NSMutableArray alloc] init];
    }
    // 赋值处理 ... 
    return _dataArray;
}

在处理图片的 Controller 中当用户点击某个按钮时会加载一些 View,例如处理文字、背景、绘图笔等是弹出的设置页面,这些 View 都封装成了自定义的类从 Controller 中分离出去,这些类的内存可以被处理,如果在 didReceiveMemoryWarning 中把这些视图清理时,需要判断当前视图是否是否正在使用,否则可能出现正在操作这些视图时,由于收到内存警告而被删除导致错误,在 iOS 6 以上的版本中可以用如下方式判断视图是否在使用

Read more »
12345
Alicia

Alicia

45 posts
23 tags
GitHub E-Mail Twitter weibo
© 2020 Alicia
Powered by Hexo
|
Theme — NexT.Mist v5.1.4