有两年没有开发iOS了,最近又开始接触iOS开发,发现好多旧知识忘记了,好多新知识还不知道。
最近正在学习中,一些心得做下memo,,也可以和大家分享一下。
初始化方法的返回值类型(类名,id,instancetype)
初始化方法的返回值一般设成id。为什么呢?为什么不设成类名呢?如果你设成类名,子类就不好处理了。比如说子类想覆盖父类的初始化方法,但想返回自己的类型,就不好处理了。所以一般返回id类型。
但是id类型不是type safe的。比如说,NSString *str = [NSArray array]; 编译器是检查不是来的,等到运行的时候就会崩溃的。
后来LLVM编译器出来之后,建议使用instancetype来代替id。凡是返回值是instancetype的方法,编译器都会检查返回值,如果没有返回本类或者子类,都会报编译错误。
NSInteger和int,long
NSInteger类型可以代表一个int类型或者一个long类型,我们推荐使用NSInteger,是因为它会根据手机的处理器来决定到底用int还是long,如果处理器是32位的,那么就用32位的int,如果处理器是64位的,那么就用64位的long。从A7处理器(iPhone 5S)开始,苹果开始采用64位的处理器。
iOS中的单例模式
从iOS4.0开始,GCD横空出世,不仅方便了多线程开发,也引入了一个适合实现单例模式的函数dispatch_once。一下是具体代码:
+ (instancetype)sharedInstance {static MyClass *shared = nil;static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{shared = [[self alloc] init];});return shared;}onceToken是一个检查代码块是否调用过的一个谓词。
dispatch_once不仅能保证代码只调用一次,还是线程安全的,所以就不需要用@synchronized了。确实很方便。
关于self.name和_name的区别
name是属性,通过self.name来调用,调用的是get方法,可以在类外使用。
_name是成员变量,只能在.m文件中使用。
一般来说,.m文件中推荐使用_name。因为使用self.name有时候会带来不必要的麻烦。举个例子:
代码1
self.name = [[NSString alloc] init];代码2
_name = [[NSString alloc] init];self.name = @"name";关于_name的引用计数,代码2是正常的,引用计数为1,代码1时不正常的,引用计数是2。
我们来分析一下:
self.name = 会调用set方法,set方法如下:
– (void)setName:(NSString *)name {if (_name != name) {[_name release];_name = [name retain];}}代码1,alloc了引用计数+1,调用set方法,[_name release]的时候_name还是nil,所以不起作用,最后有retain了,引用计数变为2。
代码2,alloc了引用计数+1,调用set方法,[_name release]的时候_name还是不为nil,所以起了作用,所以,最后引用计数还是为1。
所以,代码1是会造成内存泄漏的。
关于ARC中的strong和copy
@property (copy, nonatomic) NSString *copyName;@property (strong, nonatomic) NSString *strongName;上面的NSString到底用stong还是copy呢?
ARC中的stong其实就是retain,就是引用计数+1。而copy会拷贝一个副本出来。
但是,具体的区别是什么呢?
NSMutableString *name = [NSMutableString stringWithFormat:@"name"];self.copyName = name;self.strongName = name;@"name"存在于堆上面的某个地址中,假设它的地址是0xB1。name存在于栈上面的某个地址中,假设它的地址是0xA1。
同样,我们假设strongName的栈地址是0xA2,copyName的栈地址是0xA3。
strong只是引用计数+1,不会重新分配内存。copy会拷贝副本,所以会重新分配内存,假设分配到0xB2。所以存在以下指向关系:
由于NSMutableString是NSString的子类,子类可以直接赋给父类。
当NSMutableString类型的name在另外一个类里面,通过stringName或copyName所在类的实例来赋值的时候,如果是strongName,会有潜在的风险。因为name和strongName是指向同一块内存的,而name又是可变类型的,所以当外面的name的值变化了,里面的strongName会跟着变化,而我们往往是不希望strongName变化的。要解决此问题,copy就起到了作用,因为copyName又拷贝了一份内存地址,所以和原来的name是相互独立的,不管name改成什么值,copyName永远是@“name”。
所以,如果一个类它的子类有可变类型的,那么我们推荐用copy而不是strong。这样的类有NSArray, NSDictionary, NSSet, NSString, NSData等等。
有人担心从外部传进来的如果不是可变类型的,那么岂不是每次都copy,会存在性能问题?其实ARC会进行判断,如果赋的值时可变类型的,那么就会执行copy,如果赋的值时不可变类型的,那么就不会执行copy,而是让引用计数+1,就和strong一样了。
我知道我不是一个很好的记录者,但我比任何人都喜欢回首自己来时的路,