Objective-C知识点杂集一

2018-09-06  本文已影响0人  拧发条鸟xds

第一部分


类的@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)方法。


基本数据类型

限定词:long、long long、short、unsigned、signed

long int a;long long int;long double;

short int;

unsigned int a;


#import <>和""的区别

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


第二部分


理解@property和@synthesize

@proterty @synthesize详解

@synthesize 的作用

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


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

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

MyViewController.h文件

@interface MyViewController :UIViewController
@property (nonatomic, retain) UIButton *myButton;
@end
@interface MyViewController :UIViewController
{
    NSString *name;
}
@end
@interface MyViewController :UIViewController
@property (nonatomic, retain) UIButton *myButton;
@end

属性修饰符

@property后面的关键字

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

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


1. 线程安全(系统默认是atomic)

简单来讲:一般单线程声明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)
3. 指定方法名称: setter= getter=
@property (getter=isFinished) BOOL finished;  
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一般用于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.使用注意:

  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方法这样写

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

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


深拷贝和浅拷贝

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

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

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

上一篇 下一篇

猜你喜欢

热点阅读