浅析ios事件的响应及传递

响应者Responders

说到事件,不得不从UIResponder说起; UIResponder是用于响应和处理事件的抽象接口,UIResponder的实例构成了UIKit的事件处理主干,许多UIKit类也都是继承自UIResponder,包括UIApplication, UIViewController以及UIView(包括UIWindow),它们的实例都是响应者:(对用户交互动作事件进行响应的对象),当事件发生时,UIKit将它们分派到应用的responder对象中进行处理。

Read More

使用dSYMTools插件进行Crash分析

app难免会发生崩溃,debug时发生的崩溃还好说,我们只要设置了All Exceptions断点一般情况都会定位到具体的代码行。但对于发布的版本,用户使用时发生崩溃,问题就没那么简单了。这个时候就需要我们通过解析Crash文件来分析了。关于Crash文件来分析我们首先来看下获取崩溃信息的方式:

  • 使用友盟、云测、百度等第三方平台统计
  • 连接设备,通过Xcode直接查看设备的崩溃信息
  • 通过NSException类获取,上传至自己的服务器 我自己的项目里用的是第三种,这里就主要讲讲通过NSException类获取,并上传至自己的服务器的方式。

    获取crash信息

Read More

使用uicollectionview实现3d效果的轮播

在很多APP上都看到过带有3D效果的广告轮播,当时觉得很酷炫。后来我就尝试去写代码实现类似的效果,实现起来其实也比较简单,首先看下运行后的效果: 效果

下面主要分享下主要的实现过程

首先创建自定义的UICollectionView

Read More

Ios开发常用权限汇总

主要总结下以下常用权限的获取及请求授权用法等(均不考虑iOS8以下系统): 网络权限 推送权限 定位权限 通讯录权限 相机权限 相册权限

Read More

Uibutton的titleedgeinsets和imageedgeinsets

之前好不容易弄明白了UIButton的titleEdgeInsets和imageEdgeInsets属性,然而今天应用到的时候又磕磕绊绊的花了好长时间才理清楚。长痛不如短痛,今天就在这里花点时间做个笔记,同时希望可以方便不了解这两个属性用法的伙伴有所理解。

Read More

Objective C实现链式编程

首先,我们看下优秀的自动布局第三方框架Masonry/SnapKit的链式语法应用: ``` // Masonry oc [box mas_makeConstraints:^(MASConstraintMaker *make) { make.width.height.equalTo(@50); make.center.equalTo(self.view); }];

Read More

Nstimer、cadisplaylink那些事

NSTimer计时器对象,就是一个能在从现在开始的后面的某一个时刻或者周期性的执行我们指定的方法的对象。 CADisplayLink也是一个计时器,它的timeInterval和屏幕刷新频率一致(60帧/秒)。

Read More

Effective objective C 2.0笔记(四)

第五章 内存管理

第29条:理解引用计数

  • OC使用引用计数管理内存,引用计数机制通过递增递减的计数器来管理内存。对象创建后,保留计数为1。若保留计数为正,则对象继续存活,保留计数降为0时,对象被销毁
  • 对象有三个方法用于操作计数器:retain:递增保留计数,release:递减保留计数,autorelease:待自动释放池销毁时,再递减保留计数;
  • MRC下对象调用release,保留计数如果降为0,对象所占内存会被回收;再访问对象可能使程序崩溃;因为对象所占内存dealloc后,只是放回“可用内存池”中;若果对象内存未被覆写,那么该对象仍然有效,程序不会崩溃;反之才会造成崩溃;为了防止访问野指针造成的程序崩溃,在对象release后应手动将指针置nil
    [object release];
    object = nil;
    
  • 属性存取方法中的内存管理:若属性为“strong”类型,setter方法的处理方式为:保留新值再释放旧值,然后更新实例变量令其指向新值; ```
  • (void)setFoo:(Foo *)foo { [foo retain]; [_foo release]; _foo = foo; } ``` 这个顺序极其重要,假如还未保留新值就释放旧值,两个对象又指向了同一个对象,那么先执行的release操作可能导致系统将此对象永久回收,后续的retain操作则无法令这个被彻底回收的对象复生;
  • autorelease对象的释放时机:如果创建了自己的自动释放池,对象会在自动释放池销毁时释放(即出了@autoreleasepool { }作用域后释放);否则就是等到当前线程的下一次事件循环(Runloop)时释放,因为一次事件循环会开启新的自动释放池;由此,autorelease能延长对象生命周期;

    第30条:以ARC简化引用计数

  • ARC会自动执行retain, release,autorelease等内存管理操作,在ARC下调用remain,release,autorelease,dealloc,ratinCount等方法是非法的,编译会报错;
  • ARC在调用内存管理方法时,是直接调用底层C语言,这样性能更好,因为保留,释放操作比较频繁,直接调用底层函数能节省很多CPU周期;
  • ARC确定的硬性规定:方法名以alloc,new,copy,mutableCopy开头的方法创建对象,返回的对象归调用者所有(负责释放对象);若方法名不以这四个词语开头,则表示所返回的对象不归调用者所有,返回的对象会自动释放;实际上就是调用了autorelease方法;因为ARC的这个硬性规定,所以声明属性名称时不能以new等开头,因为这样属性setter方法的内存语意会分不清;
  • ARC也会执行手工操作无法完成的优化:在编译期,ARC会把能够相互抵消的retain,release,autorelease操作约简,如果发现同一个对象上执行了多次保留与释放操作,ARC有时可以成对移除这两个操作;
  • 应用程序中,可用下列修辞符来改变局部变量与实例变量的语义: __strong:默认语义,保留值; __unsafe_unretained:不保留值,变量不会自动清空,可能不安全,会发生野指针问题; __weak:不保留值,但变量会自动清空,是安全的; __autorelease:自动释放;
  • ARC只负责管理OC对象的内存,对于OC对象,ARC会自动生成回收对象所执行的代码,但是对于非OC对象,如CoreFoundation中的对象或由malloc()分配在堆中的内存,那么仍需手动清理。 ```
  • (void)dealloc { CFRelease(_coreFoundationObject); free(_heapAllocaatedMemoryBlob); } ```

    第31条:在dealloc方法中只释放引用并解除监听

    在dealloc方法中,应该做的是:释放指向其他对象的引用;移除KVO或NSNotificationCenter通知;

    第32条:编写“异常安全代码”时留意内存管理问题

    异常一般只应在严重错误后才抛出(第21条); 在@try块中,如果先保留了某个对象,然后在释放它之前又抛出异常,除非@catch块能处理该问题,否则就发生内存泄露了;

  • MRC模式下解决方法:在@finally块中调用release释放对象,因为@finally块,无论是否抛出异常,代码都会运行;
      Object *ojbect;
      @try {
          object = [[Object alloc] init];
          exception
          ....
      }
      @catch(NSExpression *expression) {
          ...
      }
      @finally {
          [object release];
      }
    
  • ARC模式,默认情况下ARC不会自动处理这种情况,又不能调用release。对于异常处理更加棘手;解决方法是:通过设置-fobjec-arc-exceptions编译标志来开启ARC生成安全处理异常代码的模式;这可以使ARC安全处理异常;

    第33条:以弱引用避免保留环(循环引用)

  • 循环引用会导致内存泄露,避免循环引用的最佳方式就是弱引用;这种引用表示“非拥有关系”;
  • “非拥有关系”的修辞符有weak,assign,unsafe_unretained;assign一般用于基本类型(int ,float,struct),unsafe_unretained一般用于对象类型,是不安全的;weak也用于对象类型,但是安全的;assign也可以用于对象类型,但也是不安全的;weak不能用于基本类型;
  • weak之所以安全,是因为变量指向的实例被释放回收时,weak属性会指向nil,而unsafe_unretained属性仍然指向已被回收的对象,这时在访问属性时会发生错误;
  • weak属性自动置为nil的原理:weak属性对象会被写入一张哈希表中,以weak属性指向的对象地址为key,weak指针为value;当指向的对象销毁时,会根据对象地址去表中查找weak指针并置为nil;

    第34条:以自动释放池降低内存峰值

    非alloc,new,copy,mutableCopy词开头方法创建的对象,都是autorelease对象,如果没有手动创建自动释放池,那么autorelease对象要等到下个Runloop后才释放;如果在下个Runloop之前,autorelease对象已经很多(比如for循环创建对象),那么内存峰值将会很高;那么,可以在适当的时机,手动创建autorelease pool使得对象及时释放以降低内存:

        for (int i = 0; i < 100000; i ++) {
              @autoreleasepool {
                  Object *obj = [Object ojbect];
              }
          }
    
Read More

Effective objective C 2.0笔记(二)

#第二章 对象 消息 运行期 ##第6条:理解“属性”这一概念 属性是OC的一项特性,用于封装对象中的数据(通常数据保存为各种实例变量)。实例变量一般通过存取方法getter和setter方法读取和写入,使用属性,编译器自动编写了相关的存取方法(自动合成),我们也可以自己编写存取方法,但一定要满足命名规范。使用属性,编译器还自动向类中添加了适当类型的实例变量,并在属性名前加_作为实例变量的名称。同时属性引入了“点语法”(点语法其实就是调用了getter和setter方法),我们可以容易的访问存放于属性中的数据。 属性有关的关键字:@synthesize @dynamic @synthesize可以指定实例变量的名称,@dynamic编译器不会创建实现属性的实例变量,也不会合成存取方法。 属性的特质会影响到存取方法,属性拥有四种特质:

  1. 原子性:atomic是“原子的”,合成的方法中会加同步锁。nonatomic是“非原子的”,合成方法不会加同步锁。一般情况下使用nonatomic,因为加同步锁意义不大,因为并不能确保线程安全,而且使用同步锁开销较大,会带来性能问题。
  2. 读写权限:readwrite,属性拥有setter和getter方法;readonly,属性仅拥有获取方法,外部不能更改数据。
  3. 内存管理语义:assign,setter方法只会执行针对“纯量类型”的简单赋值操作;strong,属性定义了一种拥有关系,setter方法会保留新值,释放旧值,再将新值设置上去;weak,属性定义了一种非拥有关系,setter方法不会保留新值,也不释放旧值,当属性指向的对象销毁时,属性值会自动清空;unsafe_unretained,和weak类似,区别是当属性指向的对象销毁时,属性值不会自动清空(不安全);copy,和strong类似,然而setter方法并不保留新值,而是将其拷贝。当类型有可变和不可变类型时,不可变的属性一般需要使用copy特质,因为设置的新值可能指向可变的类型,如果不用copy,属性值可能在不知情的情况下遭人修改。
  4. 方法名:getter= 指定获取方法的方法名;setter=指定设置方法的方法名。 ##第7条:在对象内部尽量直接访问实例变量 直接访问实例变量和通过属性访问实例变量区别:
  5. 直接访问实例变量速度相对较快,因为不需要经过“方法派发”。编译器所生成的代码会直接访问保存对象实例变量的那块内存。
  6. 直接访问实例变量,不会调用getter,setter方法,这就绕过了属性所定义的内存管理语义。(修饰属性的strong,copy等没用了)
  7. 直接访问实例变量,不会触发KVO。
  8. 通过属性访问实例变量,可以通过getter,setter方法监控属性的调用者和其访问时机,方便调试。
Read More

Effective objective C 2.0笔记(三)

第三章 接口与API设计

第15条:用前缀避免命名冲突

  • OC没有命名空间,为了避免潜在的命名冲突,可以使用加前缀的方式
  • 选择公司,应用程序或有关联的名称作为类名的前缀,并在所有代码中使用这一前缀

    第16条:提供“全能初始化方法”

  • 全能初始化方法:为对象提供必要信息以便其能完成工作的初始化方法,类似UITableViewCell初始化方法: ```
  • (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(nullable NSString *)reuseIdentifier ``` 这里必要信息是:style,reuseIdentifier样式及重用标识
  • 有多个初始化方法时,可以在其中选定一个作为全能初始化方法,令其他初始化方法都调用他。这样,这有全能初始化方法存储内部数据,需要改变内部结构时,只需要改全能初始化方法而不用改动其他初始化方法
  • 如果超类的初始化方法不适用于子类,那么应该覆写这个超类的方法,并在其中抛出异常 ##第 17条:实现description方法 通过NSLog打印并查看对象信息时,对象会收到description消息,返回对象相关的信息。但默认的description方法返回的信息,有时并不是我们想要的,通过覆写description可以实现输出我们在定义的信息。类似的,还有debugDescription方法,它和description区别:debugDescription方法是开发者在调试器以控制台命令打印对象时调用的(LLDB “po”命令)。当我们通过LLDB “po”命令打印对象信息时,就可以覆写debugDescription返回我们需要的信息。

    第18条:尽量使用不可变对象

  • 使用属性时,默认情况下属性是“可读可写”的,这样设计出来的类都是“可变的”。
  • 为了防止对象被更改,应该尽量把对外公布的属性设为只读,而只在必要的时候才对外公布。
  • 若某属性仅可于对象内部修改,可以在分类中将readonly属性扩展为readwrite属性。
  • 不要把可变的集合(collection)作为属性公开,而应提供方法,通过方法修改对象的可变collection。

    第19条:使用清晰而协调的命名方式

  • 方法命名:方法名使用“驼峰大小写命名法”;使用“in”,“for”,“with”等介词连接,使得代码读起来和句子一样;方法名要明确每个参数等含义,把表示参数类型的名词放在参数前面;方法有返回值时,方法名的首个词最好是返回值的类型;布尔属性应该根据其功能,选用类似has, is当前缀;不要使用类似str简称,应该使用全称;方法名也不能长得太过分,应尽量在用意表达清楚的基础上做到言简意赅;
  • 类和协议的命名:类和协议的名称要加上前缀,避免命名冲突;命名应该把词汇组织好,从左至右读起来通顺;定义委托协议时,把委托接口的类名放在前面,后面加Delegate一词。

    第20条:为私有方法名加前缀

    为私有方法名加前缀,很容易区分公共方法和私有方法,有助于调试。使用何种前缀,由个人喜好决定,一般用p_作为前缀,尽量不要单独使用_作为前缀只是预留给苹果爸爸的。

    第21条:理解错误模型

    1. 用异常机制处理错误
  • 通过@throw抛出异常(NSException)
  • 通过@try @catch捕获并处理异常
Read More

Effective objective C 2.0笔记(一)

第一章 熟悉Objective-C

之前看了《Effective Objective-C 2.0 编写高质量iOS与OS X代码的52个有效方法》这本五星好书,受益颇多,在一定程度上提高了编写的代码的质量。现在就对这52个有效方法做简单笔记。

第1条:了解OC语言的起源

OC为C语言添加了面向对象特性,是其超集。OC与其他面向对象的语言在很多方面都有差别。OC语言由Smalltalk(消息型语言鼻祖)演化而来。OC语言使用动态绑定的“消息结构”而非“函数调用”,也就是说在运行时才会检查对象类型,接收一条消息后,究竟执行何种代码,由运行时环境而非编译器来决定。

第2条:在类的头文件中尽量少引入其他头文件

在类的头文件中引入其他头文件可能会有如下问题:

  1. A类头文件引入了B类的头文件,B类头文件又引入了A类头文件,这两个类就相互引用了,编译不过。
  2. A类头文件引入了C类的头文件,当B类引入A类头文件时,不需要的C类也被引入了B类。而另一个类又引入B类时,没有用到的A类C类也被引入了。如此持续下去,则要引入许多根本用不到的内容,会增加编译时间。
Read More