iOS技巧、特性及规范

一、编译器特性

1、ARC。

ARC是编译器特性。项目中使用了ARC,编译器会在项目编译的时候自动添加OC对象计数器release代码。并且使用了ARC,项目中将不允许出现release、retain、retainCount和[super dealloc]代码。

ARC不同于Java和.Net中的垃圾回收,Java和.Net中的垃圾回收是运行时特性。ARC只是将OC对象的计数器自动减1。非OC对象,像常见的数据类型、enum枚举和struct结构体不参与ARC,系统会自动销毁回收。(注:OC对象计数器在一个对象中占4个字节,当计数器为0时,如果该OC对象被strong强指针引用,那么此OC对象将成为僵尸对象不可再次访问,当然也无法“复活”该对象,应当手动将strong强指针置为nil;如果该OC对象被weak弱指针引用,那么此OC对象变为僵尸对象的时候,weak弱指针将自动销毁。)

2、@class

@class是在类的头文件中出现的。使用@class主要解决两个问题:

①在非ARC的项目中,解决因类之间的相互引用,导致OC对象计数器无法减为0的问题。在其中一个类头文件中引用另一个类的头文件使用@class,并且将其中一个类的所有OC对象指针设为assign类型即可。

②提高编译器特性。使用@class就相当于一个声明,告诉另一个类包含哪些成员变量和方法,具体在.m使用的时候,只需再#import此类的.h文件即可。如果不是用@class声明类的引用,全都是在.h文件中直接#import另一个类的.h文件,那么当一个.h文件改变,编译器在编译的时候就会再复制一遍此.h头文件,假如有上万个这样的引用,编译器就得复制一万遍。使用@class就可以不用复制,这样就提高了编译器性能。

3、[]

对于NSArray和NSDictionary,当需要直接根据索引index或者关键字key取得相应的值的时候,就可以像直接通过[index]或者[key]获得。注意,通过此方法获取到的对象都是id类型,想要再访问指定对象类型的方法或者成员变量还需要自己转一下。(注:为什么通过[]获取到的对象是id类型?因为[index]相当于调用了NSArray对象的objectAtIndex方法,[key]相当于调用了objectForKey,这两个方法返回的都是id类型的对象。)

4、@()和@20

@()和@20是在int整型转换为OC对象NSNumber的时候用到的。比如,@20就是将整型20转换为NSNumber的OC对象类型,进而可以在NSArray或者NSDictionary中使用。(注:NSArray和NSDictionary只允许添加OC对象作为元素。)。@(),是将常见数据类型临时变量转换为指定OC类型。比如int a = 20;NSNumber *aa = @(a);这样就将临时变量a的整型值转换为了NSNumber的OC类型。不可以这样写@a,一写就报错。其实还有很多()取值的例子,比如@(-100)、@-100、@(NO)、@(M_PI_4)等等...

二、性能

1、SEL

SEL即Selector。SEL是指向类中方法的指针类型。通常情况下,当对象或者类调用对象方法或者类方法的时候,都先要将调用的方法包装成SEL类型,然后根据SEL类型数据找到对应类中所调用方法的地址,最后就是调用。这样一个对象或者类调用指定方法就经过了2步,如果涉及到同一个方法多次频繁的调用还是每次都要经过SEL这两步的话,无疑会耗费一定的性能。此时就可以将SEL单独保存起来,每次调用就可以直接根据SEL类型的指针地址找到对应方法进行执行。

2、nonatomic

在一个类的头文件中声明一个成员变量时,系统默认将此变量声明为atomic,atonmic就是原子的意思,是在多线程中使用,防止同一个成员或对象同时被多个线程同时访问造成死锁。默认是atomic,项目在编译的时候系统会自动在后台添加很多代码,平时不牵涉到多线程,此处就可以手动设置为nonatomic,提高性能。

3、[UIImage imageNamed:]

一个UIImageView使用此方法访问显示一个图片,系统会自动将此图片在内存中生成一份缓存,如果不从内存中清除此缓存,它将一直占用内存。如果使用imageWithContentsOfFile,当UIImageView由一张图片显示为另一张图片的时候,系统就会自动将上一张图片从内存中销毁移除。

4、自定义UITableViewCell,如果一张图片只是控制了其hidden属性,那么就可以在UITableViewCell初始化的时候将UIImageView的image设置为此图片,避免在UITableViewCell从缓存池中循环利用重复设置数据设置图片的时候造成的内存消耗。

5、自定义UITableViewCell出现高度不一致的时候,需要引入frame模型,在获取到数据以后马上计算每一个UITableViewCell的frame,使用的时候直接设置计算好的高度就可以了。避免了在UITableView上下滚动的时候造成的重复计算消耗性能造成的卡顿现象。

 

三、技巧

1、界面上显示一道线,在WPF中可以使用Border或者Line现成的控件来做,在OC里面苹果官方使用的是UIView。如果是横线,则UIView的height为1,同理竖线的情况with为1。

2、UITextField左右两侧有leftView和rightView。如果我们想在用户输入的时候自动在左侧留有空隙,那么就可以使用指定leftView为[[UIView alloc] initWithRect:CGRectMake(0, 0, 8, 0)];

3、给一个UIButton设置一个图标,且此图标不能伸缩变形显示,此时可以设置此UIButton的image来展示,而不是BackgroundImage。

4、图像平铺技术比如QQ聊天窗口,每个人发的消息。其实程序就是指定了在一张图片的上、下、左、右多少范围内当图片拉伸的时候不变形,范围内的区域自动平铺填充图形。此技术在Win8里面叫NineGrid(九切片)。

 

四、调试

1、错误提示关键字forUndefinedKey。一看就知道StoryBoard里面的线连错了,或者NSDictionary的key为NULL没找到

2、Unrecoganized seletor sent to instance一看就是到对象或类的方法为找到。

此种情况比较常见的原因有两点:

①、子类调用父类的自定义构造函数初始化,父类方法没有使用self,结果返回的还是父类对象,调用了子类的方法。

②、在定义对象方法的时候,返回值使用了id类型而不是instancetype。如上面所说,id类型是不能自动转换为指定对象,默认返回的是任意类型的对象,比如一个要返回一个Person对象,没有定义length成员,此时用NSString类型的指针是可以去指向该对象的,那访问NSString的length成员就会出现此错误。解决方法,可以通过强转为Person对象,或者将返回类型id换位instancetype。那是不是所有使用id的地方都可以换位instancetype呢?不是。只有在id作为返回类型的时候才可以替换为instancetype。

3、has been modified。一看就知道清下项目缓存就可以解决。

4、点击某个按钮跳到了Xcode,等一会儿才报错,不用想一定是死循环错误了。

5、出现 "duplicate" 是重复的意思,不用想,肯定某个文件引用了某个文件的.m文件,导致重复编译。

 

五、布局

当我们修改了界面运行的时候看不到变化,此时就可以试着去去掉autolayout。

 

六、.pch文件

1、尽量将我们自己定义的东西放在@ifdef __OBJC__ 里面。

举个例子:比如在.pch文件里导入了一个.h头文件,#import "Person.h"。并且没有写在@ifdef __OBJC__里面,此时我们在项目中写一个.c的文件,一运行就会报很多错误。为什么呢?因为在.pch导入#import的.h文件,会在项目所有文件中默认导入#import该.h头文件,但C语言的.c程序需要#include才可以导入.h头文件,使用#import显然不行,所以报错。

2、一定要使用自定义的日志打印策略,不然在项目发布的时候还会包含很多日志打印的代码,无疑会影响性能。自定义日志打印策略如下:

iOS技巧、特性及规范

所以,看一个项目代码,首先去看.pch文件。

 

七、规范

1、类名和函数名单词首字母都大写,方法名和变量名单词第一个首字母小写,其他单词首字母大写。

2、只要是自定义View,都要写下这三个方法,这是规范。

/**

 *  从文件中解析一个对象的时候就会调用这个方法

 */

- (id)initWithCoder:(NSCoder *)decoder

{

    if (self = [super initWithCoder:decoder]) {

        [self setup];

    }

    return self;

}



/**

 *  通过代码创建控件的时候就会调用

 */

- (id)initWithFrame:(CGRect)frame

{

    if (self = [super initWithFrame:frame]) {

        [self setup];

    }

    return self;

}



/**

 *  初始化

 */

- (void)setup

{

     //你的初始化代码    

}
View Code

 

八、理解

1、为什么UITableViewCell需要Identifier,而UIDatePick的自定义View不需要。

UITableViewCell使用Identifier主要考虑到以下两点:

①不同数据用不同cell模板,就可以根据Identifier不同从缓存池中循环利用指定的cell模板。

②、根据Identifier从缓存池中去除已存在的模板,循环利用,提高性能。

UIDatePick的View都是一样的模板,不存在同一数据源不同模板的情况,所以不需要Identifier直接去使用resuming就行了。

 

 此外还要注意awakeFromNib、layoutSubViews(layoutViews重写了这个方法后一定要调用[super layoutSubviews])以及didMoveToSuperView方法的用法。

 

iOS还在学习中,有理解不到位或者错误的地方,还请各路大神指点。

你可能感兴趣的:(ios)