Objective-C知识点杂集一

第一部分

  • 类的@interface、@implementation描述
  • 类方法和实例方法
  • 初步理解alloc和init
  • 初步理解声明一个对象(实例)
  • setter方法和getter方法
  • 基本数据类型
  • 限定词:long、long long、short、unsigned、signed
  • #import <>和""的区别

类的@interface、@implementation描述

@interface接口部分用于描述类和类的方法

@implementation实现部分用于描述数据(类对象的实例变量存储的数据),并实现在接口中声明方法的实际代码。也可以在interface(接口)部分为类声明实例变量。


类方法和实例方法

开头的负号(-)表示该方法是一个实例方法;实例方法能够对类的实例执行一些操作,例如,设置值、检索值和显示值等。

开头的正号(+)表示这是一个类方法;类方法是对类本身执行某些操作的方法,例如,创建类的新实例。

实例方法总是可以直接访问它的实例变量。然而,类方法不能,因为它只处理本身,并不处理任何实例。


初步理解alloc和init

alloc是allocate(vt.分配,分派; 把…拨给)的缩写。作用是为新创建的对象(实例)分配内存alloc方法保证对象的所有实例变量都变成初始状态。然而,这并不意味着该对象已经进行了适当的初始化,从而可以使用。在创建对象之后,还必须对它初始化。

init方法用于初始化类的实例变量

理解:alloc可以使实例变量指定为0或者是nil,但是对象还是没有被正确的初始化,所以必须使用init来初始化新创建的对象。


初步理解声明一个对象(实例)

声明一个对象:Fraction *myFraction;

myFraction前的星号()表明myFraction是Fraction对象的引用(或指针)。变量myFraction实际上并不储存Fraction的数据,而是存储了一个引用(其实是内存地址),表明对象数据在内存中的位置。在声明myFraction时,它的值时未定义的,它没有被设定为任何值,并且没有默认值*。在概念上,我们可以认为myFraction是一个能够容纳值的盒子。在初始化盒子时包含了一些未定义的值,它并没有被指定任何值。

如果你创建一个新对象(例如,使用alloc),就会在内存中为它保留足够的空间用于存储对象数据,这包括它的实例变量空间,再另外预留了一些。通常,alloc会返回存储数据的位置(对数据的引用),并赋给变量myFraction。


setter方法和getter方法

设值方法和取值方法通常称为访问器(accessor)方法。


基本数据类型
  • oc中的基本数据类型有:int、float、double、char、long、double。

  • 在oc中,任何数字、单个字符或者字符串通常称为常量。例如,数字58是一个常量整数值。字符串@"aaaaaa"表示一个常量字符串对象。表达式 @5 表示一个常量数字对象。

  • 每个值无论是字符、整数还是浮点数字,都有其对应的值域。这个值域与系统为特定类型的值分配的内存量有关。一般来说,在语言中没有规定这个量,它通常依赖于所运行的计算机。因此,叫做设备或机器相关量。例如,一个整数可以在计算机上占用32位。

  • 要区分浮点常量,可以看它是否包含小数点。float类型和double类型(浮点常量),可以存储科学计数法数字。例如,1.7e4、17e-1就是使用这种计数法来表示的浮点数,它表示值1.710^4、1710^-1。

  • 在计算机内存中,不能精确的表示一些浮点值


限定词:long、long long、short、unsigned、signed
  • 如果直接把long放在int前面,那么所声明的整型变量在某些计算机上具有扩展的值域。long变量的具体范围也是有具体的计算机系统决定。

long int a;long long int;long double;

  • 把限定词short放在int前,表示要声明的整型变量用来存储相当小的整数。之所以使用short变量,主要是出于节约内存空间的考虑。

short int;

  • unsigned放在int前,表示变量只能存储正值。

unsigned int a;


#import <>和""的区别

双引号""适用于本地文件,即你自己创建的文件,不是系统文件;尖括号<>导入的是系统文件。使用双引号时,编译器一般会首先在项目目录中寻找指定文件,然后转到其它位置寻找。

  • 理解@property和@synthesize
  • iOS中属性与成员(实例)量的区别
  • 属性修饰符
  • 深拷贝和浅拷贝
  • 重写NSString的setter方法

第二部分

  • 理解@property和@synthesize
  • iOS中属性与成员(实例)量的区别
  • 属性修饰符
  • 深拷贝和浅拷贝
  • 重写NSString的setter方法

理解@property和@synthesize

@proterty @synthesize详解

@synthesize 的作用

  • @property,iOS6以后出来的关键字:编译器会自动给你生成setter(setter方法名即setName)和getter(而getter方法名即name)方法的声明,以及实现一个以_name 的成员变量(name是你属性定义的变量名字)。

  • @synthesize ,简单来说,就是为属性声明实例变量

    1. @synthesize name = _name;将实例变量name名称换成_name。这样写是为了区分成员变量(实例变量)_name和属性name,在.m文件里面使用的时候见到_name就知道是成员(实例)变量了,见到self.name就知道是属性了。另外,系统库中的所有类的声明部分都是这样写的, 总之一句话:区分成员变量名称和属性名称。使用@synthesize可以改变_name名称
    2. @synthesize name = custom_name:将实例变量_name名称换成custom_name
  • @synthesize 还有一个作用,可以指定与属性对应的实例变量,例如@synthesize myButton = xxx;那么self.myButton其实是操作的实例变量xxx,而不是_myButton了。

  • 使用@property,编译器会自动生成@synthesize name = _name;

  • 如果只写@synthesize name;那么成员变量的名称会是name,而不是_name.

  • 注意:@synthesize不会影响@property产生的setter和getter方法的名称,@property和@synthesize不必成对出现。

  • 禁止@synthesize:如果某属性已经在某处实现了自己的 setter/getter ,可以使用 @dynamic 来阻止 @synthesize 自动生成新的 setter/getter 覆盖。

注意:如果你setter和getter两个方法都重写了,那么@property是不会自动生成带有下划线的成员变量的,需要手动使用synthesize生成。 @synthesize name = _name


iOS中属性与成员(实例)量的区别

iOS中属性与成员变量的区别
解惑——iOS中成员变量和属性区别

MyViewController.h文件

@interface MyViewController :UIViewController
@property (nonatomic, retain) UIButton *myButton;
@end
  • 在MyViewController.m文件中,编译器也会自动的生成一个实例变量_myButton。那么在.m文件中可以直接的使用_myButton实例变量,也可以通过属性self.myButton.都是一样的。注意这里的self.myButton其实是调用的myButton属性的getter/setter方法。
@interface MyViewController :UIViewController
{
    NSString *name;
}
@end
  • .m文件中,self.name 这样的表达式是错误的。xcode会提示你使用->,改成self->name就可以了。因为oc中点表达式是表示调用方法,而上面的代码中没有name这个方法。
@interface MyViewController :UIViewController
@property (nonatomic, retain) UIButton *myButton;
@end
  • 因为编译器会自动为你生成以下划线开头的实例变量_myButton,不需要自己手动再去写实例变量。而且也不需要在.m文件中写@synthesize myButton;也会自动为你生成setter,getter方法。@synthesize的作用就是让编译器为你自动生成setter与getter方法。

  • @synthesize 还有一个作用,可以指定与属性对应的实例变量,例如@synthesize myButton = xxx;那么self.myButton其实是操作的实例变量xxx,而不是_myButton了。

  • 成员变量用于类内部,无需与外界接触的变量

  • 根据成员变量的私有性为了方便访问,所以就有了属性变量属性变量的好处就是允许让其他对象访问到该变量。当然,你可以设置只读或者可写等,设置方法也可自定义。所以,属性变量是用于与其他对象交互的变量


属性修饰符

@property后面的关键字

@property 后面可以有哪些修饰符

@property都有哪些修饰词?以及作用总结


  • 线程安全的: atomic(原子性), nonatomic(非原子性)
  • 指定方法名称: setter= getter=
  • 访问权限的: readonly(只读), readwrite (读写)
  • 内存管理(ARC) assign,strong,weak,copy
  • 内存管理(MRC)assign, retain,copy
1. 线程安全(系统默认是atomic)
  • nonatomic(英['nɒnə'tɒmɪk]): 禁止多线程,变量保护,提高性能。

    1. 不对set、get方法加同步锁
    2. 性能好
    3. 线程不安全
  • atomic(英[əˈtɒmɪk]): Objc使用的一种线程保护技术,基本上来讲,是防止在写未完成的时候被另外一个线程读取,造成数据错误。而这种机制是耗费系统资源的,所以在iPhone这种小型设备上,如果没有使用多线程间的通讯编程,那么nonatomic是一个非常好的选择。

    1. 原子属性就是对生成的set方法加互斥锁(互斥锁是一种同步锁,互斥锁:如果共享数据已经有其他线程加锁了,线程会进入休眠状态等待锁,一旦被访问的资源解锁,则等待访问资源的线程会被唤醒。自旋锁:如果共享的数据已经有其他线程加锁了,线程会以死循环的方式等待锁,一旦被访问的资源被解锁,则等待资源的线程会立即执行。自旋锁的效率高于互斥锁)
    2. 需要消耗系统资源
    3. 互斥锁是用线程同步实现的,意在保证同一时间只有一个线程调用get、set方法。

简单来讲:一般单线程声明nonatomic,考虑到速度问题。多线程程序就不要使用nonatomic。

OC在定义属性时有nonatomic和atomic两种选择。

atomic:原子属性,为setter方法加锁(默认就是atomic)。

nonatomic:非原子属性,不会为setter方法加锁。

atomic加锁原理:

 @property (assign, atomic) int age;
 
 - (void)setAge:(int)age
 { 
     @synchronized(self) { 
        _age = age;
     }
 }

互斥锁

// 注意:锁定1份代码只用1把锁,用多把锁是无效的
@synchronized(锁对象) { // 需要锁定的代码  }

使用互斥锁,在同一个时间,只允许一条线程执行锁中的代码。因为互斥锁的代价非常昂贵,所以锁定的代码范围应该尽可能小,只要锁住资源读写部分的代码即可。使用互斥锁也会影响并发的目的。

1. nonatomic和atomic的介绍和区别

当使用atomic时,虽然对属性的读和写是原子性的,但是仍然可能出现线程错误:当线程A进行写操作,这时其他线程的读或者写操作会因为等该操作而等待。当A线程的写操作结束后,B线程进行写操作,所有这些不同线程上的操作都将依次顺序执行——也就是说,如果一个线程正在执行 getter/setter,其他线程就得等待。如果有线程C在A线程读操作之前release了该属性,那么还会导致程序崩溃。所以仅仅使用atomic并不会使得线程安全,我们还要为线程添加lock来确保线程的安全

2. 访问权限(默认为readwrite)
  • readonly,只生成getter方法,没有setter方法
  • readwrite,是默认的
3. 指定方法名称: setter= getter=
  • getter=newGetterName,指定新的getter方法名,一般重新改写BOOL实例变量的getter名。例如
@property (getter=isFinished) BOOL finished;  
  • setter=,指定新的setter方法名。
4. **retain(MRC,对象)和strong(ARC,对象)

retain(MRC)

1.release旧对象(旧对象引用计数-1),retain新对象(新对象引用计数+1),然后指向新对象

2.在set方法里面是这样写的

if(_dog!=nil){
    [_dog release];//先release
}
_dog = [dog retain];//再retain新对象

strong(ARC)(对象)

1.直接赋值,并且对象的引用计数器加1;

2.在ARC中替代了retain的作用。

相同点 :

  • strong 和 retain 都是针对对象类型进行内存管理。如果去修饰基本数据类型,Xcode会直接报错。
  • 当给对象类型使用此修饰符时,setter方法会先将旧的对象属性release掉,再将新的对象赋值给属性并对该对象进行一次retain操作。
  • 两者都会增加对象的引用计数。

不同点: strong一般用于ARC环境, retain用于MRC环境.

5. assign(ARC/MRC,对象/基本数据类型)

1.这个修饰词是直接赋值的意思,整型、浮点型等基础数据类型都用该修饰词;

3.assign不会牵扯到内存管理,不会增加引用计数(与weak相同);

2.如果没有使用weak、strong、retain、copy等词修饰,默认就用assign修饰(它们之间是有你没我的关系,一般的指针类型都用strong修饰)

3.当然其实对象也可以用assign修饰,只是对象的计数器不会加1(与strong的区别)

4.如果assign用来修饰对象属性,那么当对象被销毁后指针是不会指向nil的,必须手动将其置为nil,否则会出现野指针错误(如果weak指向的对象消失了,那么它会自动置为nil,不会产生野指针;weak只能修饰对象),如果还通过此指针操作那块内存,便会导致EXC_BAD_ACCESS错误,调用了已经释放的内存空间。

6. weak(ARC,对象)

1.weak弱指针,修饰对象的修饰词,也就是说它不能修饰基本数据类型(int、float)

2.weak修饰的对象引用计数器不会加1,也就是直接赋值;(和assign相同,引用计数都不会增加)

3.弱引用是为打破引用循环而生的

4.它最被人所喜欢的原因是 它所指向的对象如果被销毁 , 它会指向nil . 从而不会出现野指针错误 。

7. weak和assign的区别

assign和weak,他们都是弱引用类型,但是他们有区别的:

1.用weak声明的变量在栈中会被自动清空,赋值为nil;

2.用assign声明的变量在栈中可能不会被赋值为nil,就会造成野指针错误;

以delegate为例,在MRC中delegate被声明为assign,这是为了不造成循环引用,这时我们需要在dealloc中写上self.delegate = nil;
以免造成delegate的野指针错误,当然在ARC中只需要用weak声明delegate就可以自动释放了。

8. copy(ARC/MRC)

必须要使用copy的情况:(1.属性是一个不可变对象,如NSString,NSArray,NSDictionry;2.需要把一个可变对象赋值给属性,如把一个NSMutableString赋值给属性NSString,除此之外的情况的使用copy和strong是没区别的);

1.copy在MRC中时是这样做的:release旧对象,旧对象引用计数器减1,copy新对象,新对象的引用计数器加1,然后指向新对象(新对象是最终指向的那个对象,不管是深拷贝还是浅拷贝)。

在set方法里这样写的

- (void)setName:(NSString *)name {
    if(_name){
        [_name release];
    }
    _name = [name copy];
}

2.copy在ARC中是这样干的,copy新对象,新对象的引用计数器加1,然后指向新对象。

在set方法里是这样写的

- (void)setName:(NSString *)theName {
    if(_name != theName){
       _name = [theName copy];
    }

}

3.使用注意:

  • 修饰的属性本身必须是不可变的。例如:NSMutableArray采用copy修饰,在addObject时会出现Crash,因为NSMutableArray的对象在copy时就会变成NSArray,如需要拷贝NSMutableArray对象用mutableCopy。
  • 遵守NSCopying协议的对象使用
  1. copy 和 strong 都可修饰不可变类型,但一般用copy
    一般用copy修饰不可变的, 因为安全, 可以保证其封闭性.
    因为用copy修饰,setter方法中会自动判断如果来源,如果是不可变的,那和strong一样,进行浅拷贝,会增加其引用计数,如果是可变的那么就深拷贝,不会增加其引用计数。 所以如果如果项目中这样的不可变对象(比如NSString)多的话,当一定数量if判断消耗的时间累加起来就会影响性能.
    所以,只需要记住一点,当你给你的不可变对象赋值时, 如果来源是可变的,那么就用copy,如果来源是不可变类型的,就用strong。(这句话存疑,因为我理解为:希望不跟随源对象变化时才用copy,希望跟随源对象变化而变化时用strong
    注:如果当strong修饰不可变的, 如果来源是不可变得,那么同上,没有问题。如果来源是可变的时, 那么当源对象变化时,我们的不可变属性也会跟着变化,那么就破坏了其封闭性, 就不安全了。

  2. 如果用 copy 修饰 可变类型 会出现什么问题?
    copy修饰可变的对象的话, 会生成一个不可变的NSCFConstantSting对象,赋值给可变属性,编译没问题,方法修改其内容时崩溃:unrecognized selector sent to instance。

总结

  1. copy 修饰不可变的 要看赋值来源
    • 来源是可变的话, 会自动进行深拷贝, 来源对象的变化不会影响我们的不可变属性
    • 来源是不可变的话,那么就和strong一样大胆的指针拷贝,反正都不会改变.
  2. copy 修饰可变的.
    • 那么会生成一个不可变对象,赋值给可变属性,编译没问题,调方法修改其内容时会崩溃unrecognized selector sent to instance
  3. strong修饰不可变的 也要看来源
    • 如果来源是不可变的, 那就没什么问题
    • 如果来源是可变的, 那么当源对象的内容发生改变时,我们的不可变属性的值也会发生改变,那么就破坏的其封闭性, 不安全.
  4. strong修饰可变的 也要看来源
    • 如果来源是不可变的, 那么会直接报警告运行出错 unrecognized selector sent to instance
    • 如果来源是可变的,那么没问题.

重写NSString的setter方法

重写NSString的setter方法

copy是复制一个对象,当我们不希望新对象随着旧对象变化而变化时,就要使用copy。
copy修饰的是不可变的对象。

因为NSString是一般用copy来修饰。

所以在MRC中,setter方法这样写

  • (void)setName:(NSString *)name {
    if(_name){
    [_name release];
    }
    _name = [name copy];
    }

在ARC中,不用release,直接copy。ARC会自动管理内存。

  • (void)setName:(NSString *)name {
    _name = [name copy];
    }

注意,不要去加什么判断比如下面这样的。
if(_name != name){
_name = [name copy];
}
每次赋值都去判断一下会更耗时间,这个东西纯粹是多余的。


深拷贝和浅拷贝

浅拷贝:指针拷贝
深拷贝:内容拷贝(开辟内存)

浅拷贝就是对内存地址的复制,让目标对象指针和源对象指向同一片内存空间

深拷贝是指拷贝对象的具体内容,而内存地址是自主分配的,拷贝结束之后,两个对象虽然存的值是相同的,但是内存地址不一样,两个对象也互不影响,互不干涉。

你可能感兴趣的:(Objective-C知识点杂集一)