Objective-c学习笔记

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的区别

  1. id在编译的时候不能判断对象的真实类型

    instancetype在编译的时候可以判断对象的真实类型

  2. 如果init方法的返回值是instancetype,那么将返回值赋值给一个其它的对象会报一个警告

    如果是在以前, init的返回值是id,那么将init返回的对象地址赋值给其它对象是不会报错的

  3. 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类型指针显然是不对的。

## 内存管理法则
    原文作者:GG_Bond
    原文地址: https://segmentfault.com/a/1190000012906050
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞