iOS Dev Tips

1.多次autorelease问题

例子:

1
2
NSObject *object = [[NSObject alloc] init];
[[object autorelease] autorelease];

结果:

会抛出一个malloc异常,程序不会Crash。malloc: *** error for object 0x7594920: pointer being freed was not allocated *** set a breakpoint in malloc_error_break to debug

2.Property之atomicity(nonatomic)

指定属性的访问方法是不是原子性的,默认是原子性的,这样多线程访问是安全的,但会消耗一定的资源。所以一般不是多线程的程序最好指定不是原子性的。

3.UIView alpha 与 opaque区别

opaque默认为YES,为了系统在绘制界面的时候优化提高性能。尤其是scrollview和做动画的时候。

4.Objective-C实现变参函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- (void)method:(NSString *)value,...
{
    //指向变参的指针
    va_list list;
    //使用第一个参数来初使化list指针
    va_start(list, value);
    while (YES)
    {
        //返回可变参数,va_arg第二个参数为可变参数类型,如果有多个可变参数,依次调用可获取各个参数
        NSString *string = va_arg(list, NSString*);
        if (!string) {
            break;
        }
        NSLog(@"%@",string); 
    }
    //结束可变参数的获取
    va_end(list);
}

5.关闭键盘方法

1
2
3
(1)[self.view endEditing:YES];
(2)[[UIApplication sharedApplication] sendAction:@selector(resignFirstResponder) to:nil from:nil forEvent:nil];
(3)[[[UIApplication sharedApplication] keyWindow] endEditing:YES];

6.NSString属性标识

1
2
1.@property(nonatomic,retain)NSString *str;
2.@property(nonatomic,copy)NSString *str;

1和2两张写法你习惯使用哪种,为什么? 最好采用第二种方法,NSMutableString是NSString的子类,可以作为set方法的参数传递,so,如果是retain的话,指针实际指向的对象是NSMutableString,这将导致该属性有可变的可能

7.Objective-C BOOL理解

名字类型头文件
BOOLsigned charobjc.hYES/NO
bool_Bool (int)stdbool.htrue/false
Booleanunsigned charMacTypes.hTRUE/FALSE
NSNumber__NSCFBooleanFoundation.h@(YES)/@(NO)
CFBooleanRefstructCoreFoundation.hkCFBooleanTrue/kCFBooleanFalse
1
2
3
4
5
6
7
8
9
10
11
12
#define  false   0
#define   true    1
#define YES ((BOOL)1)
#define NO  ((BOOL)0)
//例:
BOOL myBool;  (unsigined char)8位中最低位为1就是YES,最低位为0就是NO
myBool = YES;      //True
myBool = TRUE    //True
myBool = 1          //True
myBool = NO      //False
myBool = FALSE  //False
myBool = 0         //False

8.iOS手势UIGestureRecognizer类型

1
2
3
4
5
6
1、拍击UITapGestureRecognizer (任意次数的拍击)  
2、向里或向外捏UIPinchGestureRecognizer (用于缩放)  
3、摇动或者拖拽UIPanGestureRecognizer  
4、擦碰UISwipeGestureRecognizer (以任意方向)  
5、旋转UIRotationGestureRecognizer (手指朝相反方向移动)  
6、长按UILongPressGestureRecognizer

9.APP崩溃调试总结

1.问题:崩溃在main.m里面

答案:可以设置全局异常断点(Exception Breakpoint)

2.问题:在异常断点开启的状态下,你也没有得到得到有用的信息。

答案:在这种情况下,多继续几次运行这个app,或者在调试提示后面输入“po $eax”命令。“$eax”是cup的一个寄存器。在一个异常的情况下,这个寄存器将会包含一个异常对象的指针。注意:$eax只会在模拟器里面工作,假如你在设备上调试,你将需要使用”$r0″寄存器。

3.问题:大多数崩溃的一般原因和一些bug都是在你的xib中或者storyboard中的连接丢失了或者是错误的连接。这些情况不会在编译错误里面显示,因此你一般不知道。

答案:(我不常用xib和storyboard,一般不会遇到这个问题)

4.问题:不要忽略编译警告。假如你有编译警告,就说明你有些东西可能会出错。

  假如你不知道为什么你会到一个编译警告,最好去搞明白它. 这些都是安全的做法!

答案:搞清楚编译警告!

10.iOS内存管理之Autorelease

原理: – 1.先建立一个autorelease pool – 2.对象从这个autorelease pool里面生成。 – 3.对象生成 之后调用autorelease函数,这个函数的作用仅仅是在autorelease pool中做个标记,让pool记得将来release一下这个对象。 – 4.程序结束时,pool本身也需要rerlease,此时pool会把每一个标记为autorelease的对象release一次。如果某个对象此时retain count大于1,这个对象还是没有被销毁。

例子:

1
2
3
ClassName *myName = [[[ClassName alloc] init] autorelease];//标记为autorelease
[classA setName:myName]; //retain count == 2
[myName release]; //retain count==1,注意,在ClassA的dealloc中不能release name,否则release pool时会release这个retain count为0的对象,这是不对的。

记住: 如果这个对象是你alloc或者new出来的,你就需要调用release。如果使用autorelease,那么仅在发生过retain的时候release一次(让retain count始终为1)。

11.iOS UIImage 的延迟加载

SDWebImage中图片的加载为啥需要Decoder?由于UIImage的imageWithData函数是每次画图的时候才将Data解压成ARGB的图像,所以在每次画图的时候,会有一个解压操作,这样效率很低,但是只有瞬时的内存需求。为了提高效率通过SDWebImageDecoder将包装在Data下的资源解压,然后画在另外一张图片上,这样这张新图片就不再需要重复解压了。这种做法是典型的空间换时间的做法。Decoder代码如下:

1
TODO:Decoder代码

参考链接:Avoiding Image Decompression Sickness

12.NSFileManager创建文件或者NSData写入失败

  • 原因:没有当前目录,下面这样没有目录直接存文件是不行的
1
2
3
NSString *umdFolder = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/umd"];
NSString *imagePath = [umdFolder stringByAppendingPathComponent:[umd.title md5]];//[umd.title md5]
[[NSFileManager defaultManager] createFileAtPath:imagePath contents:coverImageData attributes:nil];
  • 解决方案:正确做法是先创建目录,然后再写入文件
1
2
3
4
5
6
7
NSString *umdFolder = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/umd"];
if ([[NSFileManager defaultManager] fileExistsAtPath:umdFolder] == NO)
{
        [[NSFileManager defaultManager] createDirectoryAtPath:umdFolder withIntermediateDirectories:YES attributes:nil error:nil];
}
NSString *imagePath = [umdFolder stringByAppendingPathComponent:[umd.title md5]];//[umd.title md5]
[[NSFileManager defaultManager] createFileAtPath:imagePath contents:coverImageData attributes:nil];

13.定义协议的时候为什么要绑定协议

例如:

1
2
3
4
5
@protocol MyProtocol <NSObject>
//定义一些方法
@end
id <MyProtocol> foo;
//foo符合<MyProtocol,NSObject>

id除了指向了对象外,不包含对象的任何信息。id 说明了id指向的对象实现了MyProtocol定义的方法,当使用id发送相应消息的时候编译器不会给出警告,但是当我们想要使用id发送NSObject协议定义的消息(retain、release)的时候,如果MyProtocol协议没有绑定NSObject协议的话,编译器会认为id没有实现相关方法,会给出警告。一般我们使用的类都继承于NSObject,NSObject的方法主要定义在NSObject协议内。所以说要想调用NSObject协议的方法,必须要指明id对象包含NSObject协议方法,通过以上方法绑定可以做到。另外两种方法也可以:

1
2
3
4
5
6
@protocol MyProtocol
//定义一些方法
@end
id <MyProtocol,NSObject> foo;
//或者
NSObject <MyProtocol> * foo;//要保证foo赋值为NSObject的子类对象。

14.NSNotification对于多线程的理解

15.@Dynamic与@synthesize区别

@Synthesize 会自动生成属性对应的成员变量和get和set方法。而@Dynamic不会自动生成实例变量和相应的get和set方法。但是我们可以提供自己的实例变量和相应的get和set方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@interface DynamicTest : NSObject
{
        NSString *_str;
}
@property (nonatomic,strong) NSString *str;
@end
@implementation DynamicTest
@dynamic str;
- (void)setStr:(NSString *)tempStr
{
    if (tempStr != _str)
    {
        _str = tempStr;
    }
}
- (NSString *)str
{
    return  _str;
}
@end

@Dynamic主要用于的情况是:当属性的get和set方法是在运行时动态实现的,我们的代码又要使用get和set方法,@Dynamic可以避免编译时的警告。例如CoreDada中的NSManagedObject。

1
2
3
4
5
6
7
8
@interface Movie : NSManagedObject
{
}
@property (retain) NSString* title;
@end
@implementation Movie
@dynamic title;
@end

16.+initialize和+load

  • +initialize会在类的任何其他函数调用前被调用。
  • +load方法是在所在类加载到系统的时候被调用,这通常会比+initialized调用的时机要早

如果子类里没有实现+initialized而父类里面实现了+initialized,那么用到子类时,不是说一定要生成对象,+initialize是调用任何方法,包括类方法,例如[SubClass class],那么父类的+initialized就会被执行两次!解决办法也很简单,就像开头的写法if (self == [Manager class]),先判断下是不是当前类的类型。

如果你在类的实现中实现了+load,但是在这个类的Category中又实现了一个+load,那么这两个+load都会被调用

17.imageNamed和imageWithContentsOfFile的区别

imageNamed会在内存中缓存image数据,imageWithContentsOfFile不会缓存

18.nil/Nil/NULL/NSNull

符号意义
NULL(void*)0C指针的字面值为0
nil(id)0Objective-C对象的字面零值
Nil(Class)0Objective-C类的字面零值
NSNull[NSNull null]用来表示零值的单独的对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//注:id是对象结构体指针,Class是类结构指针
struct objc_class {
    Class isa;
    Class super_class
    const char *name                                         
    long version                                             
    long info                                                
    long instance_size
    struct objc_ivar_list *ivars                             
    struct objc_method_list **methodLists                    
    struct objc_cache *cache                                 
    struct objc_protocol_list *protocols                     
};
typedef struct objc_class *Class;
struct objc_object {
    Class isa;
};
typedef struct objc_object *id;

19.id和void *转换的问题

ARC不能管理C指针的对象,result默认是__strong类型,在result出了作用域后会自动release,但是getReturnValue的参数为void *类型,在ARCresult不会获取到返回对象的持有关系,所以不应该是释放result。 出错的问题

1
2
id result;
[invocation getReturnValue:&result];

正确的方案

1
2
3
void *pointer;
[invocation getReturnValue:&pointer];
id result = (__bridge id)pointer; 

通过显示转换在赋值后result就会持有赋值的对象。