参考:https://blog.csdn.net/fww330666557/article/details/43232593
一、概述
Object-C是OS
X和iOS上的主要程序开发语言。它是C语言的超集,提供了面向对象和动态运行时(多态,调用都是消息机制进行的)的功能。Object-C继承了C语言的语法、基本类型和流程控制语句,同时添加了定义类和方法的语法。当进行动态类型识别和绑定的时候,Object-C还添加了对象结构管理和对象语法的语言层面的支持,将很多工作延迟到了运行时进行处理。
二、类的声明与实现
Object-C类声明,一般在一个独立的头文件中进行,比如声明一个名称为yourClass的类的一般格式如下:
[objc]
@Interface yourClass :NSObject
...
@end
NSObject是Object-C中所有类的根类,也就是说,所有类要么从它派生,要么从它的子类派生。但你一次只能从一个类派生,因为Object-C是单继承的。
yourClass类的实现一般在yourClass.m文件中,其一般格式为:
[objc]
#import “yourClass.h“
@implementation yourClass
...
@end
#import,它类似C语言的#include。只不过,在Object-C中,它比#include更好用,它可以防止重复包含。也就说#inlcude依然可以使用,但#import更好。
三、属性
1、属性的声明与修饰
属性就是对象的数据。属性使用@property关键字声明。一般格式如下:
[objc]
@property NSString *firstName;
@property NSString *lastName;
多个属性之间,可以使用逗号分隔:
[objc]
@property (strong, nonatomic) UIWindow *window;
属性通常还会加一些修饰,来控制数据的访问和存储等:
[objc]
@property (readonly) NSString *firstName;
@property (readonly) NSString *lastName;
readonly与readwrite:
readonly表明该属性是只读的,外部不能修改其值。与readonly相对的修饰是readwrite,但你不必把它写出来,因为它是默认的。
atomic与nonatomic:
(atomic、nonatomic)属性用于多线程编程,属性默认是atomic的。在多线程环境下设置为atomic可以保证数据读取的一致性(因为,它将保证数据仅仅被一个线程独占。也就是说一个线程进行写操作时,将锁定该属性,不允许其他的线程进行写操作。)由于该操作会对数据进行锁操作,故会消耗较多的资源。所以在不需要进行多线程操作时建议将该属性设置为nonatomic,设置为该参数时程序在任何情况都不会锁定该属性。
****************************************
引用自:https://blog.csdn.net/sinat_30162391/article/details/50667087
【atomic/nonatomic】
(1)atomic[默认属性]:OC使用的一种线程保护技术,是防止在写未完成的时候被另外一个线程读取,造成数据错误。而这种机制是耗费系统资源的,所以在iPhone这种小型设备上,如果没有使用多线程间的通讯编程,那么nonatomic是一个非常好的选择。
(2)nonatomic:非原子性访问,属性赋值的时候不加锁,多线程并发访问会提高性能。但可能会造成不安全。如果不加该属性,则默认setter/getter两个方法都是原子性事务访问。
所以,atomic和nonatomic用来决定编译器生成的getter,setter是否为原子操作。
******************************************
strong与weak:
其中strong是默认的。strong表示该属性对其相应的对象是强引用。一个变量保持对一个对象的强引用,只要该变量在其作用域范围内或者直到它被赋给另一个对象或者nil为止。weak表示对属性对应的对象的弱引用。
*******************************************
Strong 类似深拷贝,另开辟内存,拷贝对象
Weak 类似浅拷贝,浅拷贝直接引用统一地址
【strong和weak】/
ARC下使用
(1)strong[默认值]:表明这是一个强引用。相当于retain属性。只要引用存在,对象就不能被销毁。当所有强引用消除时,对象才能被释放。
(2)weak:弱引用。相当于assign属性。一般为了避免retain
cycles(就是父类中含有子类,父类retain了子类;子类中又调用了父类,子类又retain了父类),导致无法release,才会使用weak。
小结:(ARC下)你是不能使用retain,release,autorelease这些方法的,因为ARC会在必要的地方自动插入这些语句。所以我们需要在对象属性加上strong或者weak。
*********************************************
copy、assign、retain:
copy修饰表示该属性将使用强引用,因为它必须保持它创建的新对象。
assign指定setter方法用简单的赋值,不更改索引计数(Reference
Counting),一般对简单数据类型 使用assign。
retain,对象引用计数加一。
*******************************************
【assign/copy/retain】
(1)assign[默认值]:简单赋值,不更改引用计数,不考虑内存管理;常常用于基础数据类型(NSInteger)和C语言数据类型(int
,float,double,char等)。
(2)copy:建立一个引用计数为1的对象,然后释放原有对象;常常用于NSString/NSArray/NSDictionary(不可变)。在赋值时传入值的一份拷贝,拷贝工作由copy方法执行/block中使用copy。
(3)retain:
释放旧的对象,将旧对象的值赋给输入对象,再把输入对象的引用计数+1.
;常常用于NSObject和其子类。需要对变量release,再retain新值。指定retain会在赋值时唤醒传入值的retain消息,此属性只能用于OC对象类型,而不能用于Core
Foundation(retain会增加对象的引用计数,而基本数据类型或者Foundation对象都没有引用计数的概念)。
小结:assign和retain的区别,就是使用引用计数,可以对一个内存的释放方便很多。copy就是把原来的内存复制一遍,使各自都拥有一个内存,这样释放的时候也不会出错。
【copy和retain的区别】
copy是创建一个新对象。retain是创建一个指针,引用计数+1.
如:一个NSString对象,内存地址为:0x1111,内容为@“Hello”。
(1)copy到另外一个NSString后,地址为0x2222,内容相同(新建一个内容,内容拷贝),新的对象引用计数为1,旧的对象没有改变。
(2)retain到另外一个NSString后,地址相同(新建一个指针,指针拷贝),内容相同,对象的引用计数+1.
【assign和retain的区别】
(1)接触过C,那么假设你用malloc分配了一块内存,并且把它的地址赋值给了指针a,后来你希望指针b也共享这块内存,于是你又把a赋值给(assign)了b。此时a和b指向同一块内存,请问当a不再需要这块内存,能否直接释放它?答案是否定的。因为a并不知道b是否还在使用这块内存,如果a释放了,那么b在使用这块内存的时候会引起程序crash掉。
(2)知道了上述assign的问题,那么如何解决呢?最简单的一个方法就是使用引用计数。我们给那块内存设一个引用计数,当内存被分配并且赋值给a时,引用计数是1。当把a赋值给b时,引用计数增加到2.这时如果a不再使用这块内存,他只需要引用计数减1,表明自己不再拥有这块内存。b不再使用这块内存时也把引用计数-1.当引用计数变为0的时候,代表该内存不再被任何指针所引用,系统可以把它直接释放掉。
小结:assign就是直接赋值,从而可能引起1中的问题,当数据为int,float等原生类型时,可以使用assign。retain使用了引用计数,retain引起引用计数+1,release引起引用计数-1.当引用计数为0时,dealloc函数被调用,内存被回收。
********************************************
https://www.cnblogs.com/Jenaral/p/5970393.html
retain:他指的是将某个内存区域的指针赋值给变量,同时把该内存区域的引用计数器加1.每执行一次,该内存区域的引用计数器就要加1,当该区域的引用计数器变为0的时候内存区域被释放!
copy:它指的是将目标内存区域的值复制一份,然后开辟新的内存区域(新的指针)粘贴这个值。同时变量被赋值为新内存区域的指针!
assign:它指的是,仅只把目标内存区域的指针赋值给变量,该内存区域的引用计数器不发生变化!
1、2两点不做解释,3中的retain、copy、assign都是指的,在自动生成setter函数的时候,编译器需要识别个描述词来生成对应的setter函数!需要注意的是,如果没有加上该类的描述词,系统默认该变量的setter方法采取assign的方式。
*******************************************
2、属性的存取方法
一般情况下,一旦你使用@property声明了一个属性,编译器就会自动为该属性生成一对存取方法。编译器生成的存取方法遵守以下命名约定:
1)用来访问属性值的方法,getter方法,其名字与属性的名字是相同的;还可以把存取设置为想要名字;
2)用来设置属性值的方法,setter方法,其名字以“set”开始,其后是属性名,属性名首字母大写;
属性的修饰也会告诉编译器如何生成相关的存取方法,对于readonly属性,编译器会生成getter方法,但不会生成setter方法。
你也可以为存取方法取一个你自己想要的名字:同上1)
[objc]
@property (getter=isFinished) BOOL finished;
[objc]
@property (readonly, getter=isFinished)BOOL finished;
除了明确调用存取方法之外,OC还提供了点语法,来访问对象的属性:
[objc]
NSString *firstName = somePerson.firstName;
[objc]
somePerson.firstName = @"Johnny";
点语法只是存取方法调用的一个便捷方式。当你使用点语法的时候,属性依然是通过getter或者setter方法来访问或者改变的。
点语法访问属性,同样要受到属性修饰的约束。
3、属性与实例变量
默认地,一个可读写的属性是基于一个实例变量的,这个实例变量也是编译器自动生成的。
自动生成的实例变量与属性的名字是相同的,但是多了一个下划线前缀。
尽管一个对象通过存取方法或者点语法访问自己的属性是最佳做法,但是它也可以在类实现中的,任何实例方法中,直接访问该属性对应的实例变量。下划线前缀清晰地表明你正在访问一个实例变量,而不是一个局部变量:
[objc]
- (void)someMethod {
NSString *myString = @"An interesting string";
_someString = myString;
}
一般而言,你应该使用存取方法或者点语法来访问属性,即使你是在它自己的类实现中访问其属性,在这种情况下,你需要使用self:
[objc]
- (void)someMethod {
NSString *myString = @"An interesting string";
self.someString = myString;
// or
[self setSomeString:myString];
}
self在object-c中代表当前实例。这个规则的例外会出现在当你写初始化、释放内存或自定义存取方法的时候。
四、对象与方法
1、方法
在Object-C中,有两种类型的方法,类方法和实例方法。
类方法以+开头,其基本形式如下:
+ (id)someMethod;
类方法一般都是工厂方法,返回一个实例。
实例方法以-开头,其基本形式如下:
- (void)someMethod;
方法可以不带参数,也可以带一个或多个参数,也可以有返回值:
[objc]
- (int)someMethod;
- (void)someMethodWithValue:(SomeType)value;
- (void)someMethodWithFirstValue:(SomeType)value1 secondValue:(AnotherType)value2;
2、对象的创建与初始化
Object-C的对象的内存都是动态分配的,所以要使用指针来记录一个对象。
NSObject根类提供了一个类方法,alloc,用来为对象动态分配内存:
+ (id)alloc;
注意该方法的返回值是一个id类型,它表示指向一个对象的指针。
alloc方法还有一个重要的任务,就是清理分配给对象属性的内存,通过将其置为0。也就是说会它会帮你初始化对象的属性。
与+
(id)alloc对应的就是- (void) dealloc,它用来释放对象的内存,自动引用计数ARC会自动调用该函数,而不用我们手动调用。
NSObject还提供了对象的初始化方法:
- (id)init;
这是一个实例方法。在init函数中你可以为对象的属性赋予合适的初值。
与-
(id)init对应的是- (void) release,调用该函数会使得对象的引用计数-1,引用计数为0的话呢,ARC就会调用-
(void) dealloc释放掉对象的内存。
3、消息
对象可以发送和接受消息,一个对象通过调用该对象的一个方法发送一个消息给另一个对象。这是object-C的说法,说白了就是函数调用。
对象发送消息的最常见形式如下:
[objc]
[someObject doSomething];
[someObject doSomething: someValue];
对象可以给自己发送消息:
[objc]
[self saySomething:@"Hello, world!"];
对象可以调用其父类实现的方法:
[objc]
[super saySomething:@"Hello, world!"];
五、类扩展
类扩展一般在实现文件的最上部实现,用于扩展类的内部实现。
你可以在类扩展中声明一个属性:
[objc]
@interface yourClass ()
@property someType someProperty;
@end
在类扩展中声明的属性,编译器同样会为其生成相关的存取方法和实例变量。但是它只能在类的实现内部进行访问。
你也可以声明一个实例变量(不是属性的实例变量):
[objc]
@interface yourClass ()
{
someType someValue;
}
@end
这个实例变量也只能在类的实现内部访问。
你还可以在类扩展中声明方法:
[objc]
@interface yourClass ()
-(void)someMethod;
@end
这个方法需要在类实现中进行实现,并且只能在类实现中进行调用。
综合起来,.m文件的基本格式如下:
[objc]
#import “yourClass.h“
@interface yourClass ()
{
someType someValue;
}
[objc]
@property someType someProperty;
[objc]
-(void)someMethod;
@end
一些需要注意的问题,有一些上面提到过,再过一遍吧:
1、程序的头文件和源文件的扩展名分别为.h 和.m;
2、注释:单行(//)和多行(/* … */);
3、Object_C 中的nil 相当于NULL。
4、Object_C 中的YES 和NO 相当于true 和false。
5、#import相当于#include
,导入头文件也有两种查找方式< … > 和" … ",但是#import 可自动防止同一个文件被导入多次。
6、Object_C中的所有类都必须继承自NSObject。
7、Object_C仅支持单一父类继承,不支持多重继承。
8、Object_C中所有对象都是指针的形式。
9、Object_C用self代替this。
10、Object_C使用id代替void*。
11、Object_C中用消息表示类的方法,并采用[aInstance
method:argv]调用形式。
12、Object_C支持反射机制。
13、Object_C支持Dynamic Typing,Dynamic
Binding和Dynamic Loading。
本篇知识点回顾:
1、了解OC语言的基本特性;
2、掌握类的声明、实现;
3、掌握属性的声明和修饰;
4、掌握对象的创建和方法的声明及实现。