OC学习笔记
属性(property)和成员变量
- 属性为了让类外可以访问成员变量
- 属性就是成员变量的外部接口
- 在类内调用成员变量而不是属性,属性是给类外使用的
- 在新版本的iOS SDK中,只要声明了属性系统就会自动生成成员变量。
例如:
@property(nonatomic,strong)NSString *people;
则在类内可以直接调用成员变量_people。
- 属性可以用点(.)语法调用
方法
- []调用方法,详情如下
@interface People : NSObject
/*
声明方法
- 、+ 是方法的类型,(-代表对象方法(用对象名来调用),+代表类方法(用类名来调用)),
(加号方法和减号方法可以互相调用,但是需要类名和实例化变量,加号方法不能调用成员变量。)
*/
- (void)report;
+ (void)report;
@end
在对象方法中调用类方法,和在类方法中调用对象方法
/* .m文件对方法的实现 */
- (void)report
{
NSLog(@"- 号: report");
[People report1];
}
+ (void)report1
{
NSLog(@"+ 号:report1");
[[People alloc] report];
}
- 对象方法中可以调用成员变量
- 初始化方法
//初始化方法
- (id)init;
/* id类型是万能类型,可以返回各种类型对象 */
-(instancetype)init;
/* instancetype代表当前类的类型 */
对于选择哪个,在初始化方法中都行,对于其他的一些方法,填id会导致错误,一般填写instancetype。
关于id和instancetype
如下代码
@interface NSArray
+ (id) creatAnArray;
@end
当我们用如下方式初始化NSArray时:
[NSArray creatAnArray];
得到的返回类型将和方法声明的返回类型一样,是id;
但当我们将id改为instancetype时:
@interface NSArray
+ (instancetype) creatAnArray;
@end
再用刚同样的方式初始化时:
[NSArray creatAnArray];
得到的返回类型将会和方法所在类的类型相同,是NSArray*;
id和instancetype的区别
- id在编译的时候不能判断对象的真实类型
instancetype在编译的时候可以判断对象的真实类型
- 如果init方法的返回值是instancetype,那么将返回值赋值给一个其它的对象会报一个警告
如果是在以前, init的返回值是id,那么将init返回的对象地址赋值给其它对象是不会报错的
id可以用来定义变量, 可以作为返回值, 可以作为形参
instancetype只能用于作为返回值,例如:
//err,expected a type
{
//do something
}
就是错的,应该写成:
- (void)setValue:(id)value
{
//do something
}
**参考自[iOS instancetype 和 id 区别详解](https://juejin.im/entry/588022572f301e00697c8756)**
## 封装
- 修饰符的问题
@interface MyClass : NSObject
{
//成员变量访问修饰符的问题
//默认 - 受保护
//公有 - 在类内类外都可以使用
@public
int _classInt; /*声明为公有成员类型,则在类外也可以被调用,但是要用指向(->)调用*/
//私有 - 在类内可以使用,类外无法调用并且不能被继承
@private
//受保护 - 在类内可以使用,类外无法调用并且可以被继承
@protected
//框架权限 - 在框架内相当于受保护,在框架外相当于私有
@package
}
@property(nonatomic,strong)NSString *className;
//方法是没有访问修饰符的同C语言一样
- (void)report;
@end
## 继承
- OC中没有多继承,要实现多继承要通过协议来实现。
- 父类中的私有成员变量是无法继承使用的,而如果子类继承了父类的方法,方法中有队私有变量的操作以及打印,那我们是可以看到的,但是我们不可以在子类中直接调用私有变量。
- 如果父类中的方法没有写声明则子类无法继承父类中对应的方法。
## 多态
- OC中不支持方法的重载
## 注释
- 使用 /\*\* 文本 **/ 的注释格式(快捷键cmd+alt+/)可以对方法等进行快速注释。
## 修饰符
在苹果引入ARC之后,修饰符有所增加
### 存取类型
1. 任何属性都可以声明为readwrite或readonly,且默认设置为readwrite
2. readwrite:程序自动创建setter/getter方法。
3. readonly:程序之创建getter方法。
4. 自定义setter/getter方法 : @propery(setter=setId,getter=getId) int id;
### 原子性
1. atomic:生成的setter/getter操作为原子性的操作,执行性能较低(系统默认)。
2. noatomic:生成的setter/getter操作为非原子性的操作,执行性能较高。一般推荐手动设置为该属性。
### 生命周期
#### MRC
1. assign: 简单赋值,不更改引用计数。一般用于基础类型的数据(NSInteger)和C语言类型数(int,float,double,char,bool)。是MRC模式下的默认值。
2. copy: 会拷贝传入的对象(即创建一个引用计数为1的新对象,但是内容与传入对象相同),并把新对象赋值给实例变量。常用与NSString,NSArray,NSDictionary,NSSet等。
3. retain: 释放旧对象,并使传入的新对象引用计数+1。此属性只能用于NSObject及其子类,而不能用于Core Foundation(因为其没有使用引用计数,需要另外使用CFRetain和CFRelease爱进行CF的内存管理)。
#### ARC
ARC中加入以下修饰符
1. strong: 强引用,类似于retain。要求保留传入的对象,并放弃原有对象。一个对象只要被至少一个强引用指向,则其不会被释放,而当没有强引用指向时则会被释放。在ARC下是对象类型的默认值。
2. weak: 弱引用,要求不保留传入的属性(既不会使传入的对象引用计数+1)。类似于assign,但与assign不同的是,当它们指向的对象被释放后,weak会被自动置为nil,而assign则不会,所以assign会导致“野指针”的出现,weak可以避免悬空指针。
3. unsafe_unretained: 其实质等同于assign。与weak的区别就是指向的对象如果被释放,其不会被置为nil,而导致悬空指针的出现。它是ARC模式下非对象属性的默认值。
#### 属性的默认值
- MRC:(atomic, readwrite, assign)
- ARC下对象类型属性:(atomic, readwrite, strong)
- ARC下非对象类型:(atomic, readwrite, unsafe_unretained)
### 关于copy和strong
- **使用`copy`修饰immutable类型,使用`strong`修饰mutable类型**
引用一下一句话:
> For attributes whose type is an immutable value class that conforms
>
> to the `NSCopying` protocol, you almost always should specify `copy`
>
> in your `@property` declaration. Specifying retain is something you
>
> almost never want in such a situation.
举个例子:
@interface UserInfo : NSObject <NSCopying>
@property (nonatomic, copy) NSString *firstName;
@property (nonatomic, strong) NSString *lastName;
@end
//main 函数中
NSMutableString *mutableFirstName = [NSMutableString stringWithFormat:@”张”];
NSMutableString *mutableLastName = [NSMutableString stringWithFormat:@”全蛋”];
UserInfo *my = [[UserInfo alloc] init];
my.firstName = mutableFirstName;
my.lastName = mutableLastName;
NSLog(@”全名:%@%@”, my.firstName, my.lastName);
// print: 全名:张全蛋
// 改mutableFirstName 张 为 李
[mutableFirstName deleteCharactersInRange:NSMakeRange(0, 1)];
[mutableFirstName appendString:@”李”];
// 改mutableLastName 全蛋 为 没蛋
[mutableLastName appendString:@”没蛋”];
NSLog(@”全名:%@%@”, my.firstName, my.lastName);
// print: 全名:张没蛋
对于immutable对象类型属性,假设该类型存在mutable版本,若使用`strong`修饰该属性,则将会是不安全的。比如在上述例子中,`my.firstName`被`copy`修饰,而`my.lastName`被`strong`修饰,当把mutable类型赋给了immutable类型(即`NSMutableString`赋给`NSString`),之后又修改mutable类型变量的值(将张改为李,全蛋改为没蛋),`copy`修饰的`firstName`不会改变,而`strong`修饰的`lastName`会随之改变。这是不希望发生的。
对于两者的setter方法的定义如下:
- (void) setFirstName:(NSString*)firstName{
_firstName = [firstName copy];
}
- (void) setLastName:(NSString*)lastName{
_lastName = lastName;
}
如果去掉copy,写成下面这个样子,则copy的好处将不复存在。
- (void) setFirstName:(NSString*)firstName{
_firstName = [firstName copy];
}
- 不是所有遵循`NSCopying`类型属性都应该使用`copy`修饰,而是对于`NSString`、`NSDictionary`等属性才需要使用`copy`修饰,因为它们存在mutable版本,在为属性赋值时,右值很可能是它们的mutable类型对象,若使用`strong`修饰则会带来不稳定因子;另外一个方面,如果属性类型不存在对应的mutable版本,则完全不用担心这点,反正你也无法在外部修改它,不稳定因子自然不存在了。(参考[Objective-C copy那些事儿](http://zhangbuhuai.com/copy-in-objective-c/))
- mutable属性类型不能用`copy`修饰,被修饰符`copy`修饰的属性,默认的setter赋值方式是`_iVar = [var copy];`而`copy`方法返回的是immutable类型,将immutable对象赋值给mutable类型指针显然是不对的。
## 内存管理法则