OC基础

作者: 开着保时捷堵你家门口 | 来源:发表于2018-11-13 22:16 被阅读6次

    Oc基础

    1、#import的用途

    1>#import与#include一样,拷贝文件的内容

    2>可以自动防止文件的内容被重复拷贝

    2、#import

            NSLog(<#NSString * _Nonnull format,…#>)声明的函数

    3、Foundation框架头文件路径

    1>、Xcode右击,显示报内容

    2>、/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks

    4、主头文件

    1>、最主要的头文件,名字一般跟框架名一样,包含框架中所有的其他文件

    2>、Foundation框架的主头文件名称就是Foundation.h

    3>、只要包含了Foundation的主头文件,就可以使用整个框架的东西

    5、运行过程

    1>编写代码  .m .c

    2>编译  xxx.m xxx.c

    3>链接  xxx.o xxx.o

    4>运行    ./a.out

    6、在使用类的时候,会将类加载到内存中

    没有对象内部都有一个isa指针,这个指针指向类的方法列表

    7、方法与函数的区别

    方法:

    1>对象方法以-号开头

    2>方法的声明必须写在@interface@end之间

           方法的实现必须写在@implementationend之间

    3>对象方法只能又对象来调用

    4>对象方法归对象和类拥有

    函数:

    1>函数可以写在文件的任意位置,函数归文件所有

    2>函数的调用不依赖于对象

    8、匿名对象

    就是创建对象的时候,没有指针去指向这个对象

    9、类方法和对象方法可以同名

    类方法的执行效率比较高,但是类方法不能访问成员变量

    10、工具类:基本没有任何成员变量的时候,里面的方法都是类方法。

    11、继承

    1>子类不能拥有和父类相同的成员变量

    2>父类必须声明在子类之前

    3>调用某个对象的方法,优先从该对象中查找,如果找不到再去父类中查找方法的实现

    每个类中都有一个superClass指针,指向自己的父类,进而才可以去

    super关键字的使用

    1>super直接调用父类的方法

    2>具体调用super的对象方法还是类方法,完全取决于当前的环境

    3>子类中想先调用父类的方法之后,才去调用子类的方法时候,可以调用下super的方法

    12、多态:父类的指针指向子类的对象 (狗 是DOG类型 ,它的父类是animation。所以一个狗的对象既可以用狗类的对象指针指向,也可以用动物类对象的指针指向)

    1>没有继承就没有多态

    2>如果函数的参数是父类,那么参数对象可以传子类、父类对象

    3>局限性:父类类型的变量不能直接调用子类类型特有的方法

    13、成员变量的作用域

    局部变量、全局变量有自己的作用域,成员变量也不例外。

    成员变量的作用域:

    @public:在任何地方都能访问对象的成员变量             

    @private:只有当前类的对象方法中访问    或者是用set/get方法 @implementation中声明的成员变量默认是private

    @protected:可以在当前类以及子类的对象中直接访问(默认就是@protected)或者是用set/get方法

    @package:只要处于同一框架,就可以直接访问对象的成员变量

    @implementation中也能定义一个成员属性,但是默认是私有的

    @interface@implementation不能声明同名的成员属性

    14、@property  int age;会自动生成setAgeage方法的声明

    @synthesizeage = _age;会自动声明setAgeage方法的实现,而且这句话会访问_age这个属性,如果不存在就会自动帮我们创建一个@private_age属性。

    @synthesizeage(如果不写=_age)会自动声明setAgeage方法的实现,而且这句话会访问_age这个属性,如果不存在就会自动帮我们创建一个@privateage属性。

    Xcode4.3之后@property  int age这一句代码就会帮我们生成 _age 以及setAgeage的方法声明与实现,但是这个方法生成的成员属性是私有的,不能让子类进行访问,如果想让子类进行访问,那么就需要在.h中声明声明一个protected_age属性

    如果手动实现了set方法,那么编译器就只会生成get方法和下划线属性

    如果手动实现了get方法,那么编译器就只会生成set方法和下划线属性

    如果手动生成了setget方法,那么编译器就不会帮你生成下划线属性

    15、构造方法

    用来初始化对象的方法,是一个对象方法

    重写init方法:

    1>调用【super init】方法初始化父类的属性,并赋值给self

    2>判断self是否为空,不为空则进行初始化属性操作

    3>返回self

    重写构造方法的目的:为了创建对象的时候,成员变量就有值

    -(instancetype)init{

        if(self=[superinit]){

            _age  =10;

        }

        returnself;

    }

    自定义构造方法:

    1>一定是对象方法,以 - 开头

    2>返回类型是 id类型

    3>方法名以initWith开头

    -(instancetype)initWithAge:(int)age{

        if(self=[superinit]){

            _age = age;

        }

        returnself;

    }

    16、修改Xcode创建文件的时候的一些配置属性

    /Applications/Xcode.app/Contents/Developer/Library/Xcode/Templates在这里面修改

    17、分类:可以给某一类扩展一些方法,在不改变原来类的基础上,为类增加一写方法。

    分类的使用注意:

    1>只能增加方法,不能只能成员变量

    2>分类方法的实现中可以访问原来类的成员变量

    3>分类可以重新实现原来类中的方法,但是会覆盖掉原来类中的方法,会导致原来类中的方法无法使用

    4>方法调用的优先级: 分类(最后编译的分类优先) —> 原类 —> 父类

    18、

    1>当程序启动时,就会加载项目中所有的类和分类,而且加载后会调用类和分类的+ (void)load{   

    }方法,只会调用一次

    2>当第一次使用某个类的时候,会调用当前类的+(void)initialize

    }方法

    3>先加载父类,再加载子类,最后加载分类的(先调用父类的load方法,再调用子类的load方法)

    先初始化父类,再初始化子类(先调用父类的initialize方法,再调用子类的initialize方法)

    19、类也是个对象

    类也是一个对象,是一个class类型的对象,简称类对象

    Class类型的定义

    typedefstructobjc_class * Class

    类名就代表类对象,每个类只有一个对象

    20、-(NSString *)description{}方法

    默认情况下,利用NSLog和%@输出对象的时候,结果是<类名:内存地址>

    1>会调用对象的description方法

    2>拿到description方法的返回值展示到屏幕上

    3>description默认返回的是“类名+内存地址”

    +(NSString *)description{}方法,类的-(NSString *)description{}方法,默认返回的是类名

    21、    NSLog(@"%s",__FILE__);

    NSLog输出c语言字符串的时候,不可以包含中文

    22、SEL

    typedefstructobjc_selectorSEL

    SEL s = NSSelectorFromString(@"test")

    NSStringFromSelector(@selector(test))

    _cmd代表当前方法

     NSString *s = NSStringFromSelector(_cmd);

    SEL其实是对方法的一个包装,去找到对应的方法地址就可以调用此方法

    内存管理   

    1、局部变量的对象是放在栈中的,系统会管理他们的内存

         一个oc对象的内存是放在堆中的,需要我们去手动管理他的内存

    2、每个对象的内部都有四个字节来存储对象的内存空间

    当一个对象alloc /new /copy的时候引用计数器会+1

    给对象发送 retain 消息的时候,对象的引用计数器+1

            给对象发送release消息的时候,对象的引用计数器-1

            给对象发送 retainCount 消息的时候,返回对象的引用计数器的个数

    3、对象销毁

           当一个对象的引用计数器为0的时候,系统会给对象发送一个dealloc消息,一般重写此方法在其中做一些释放性的操作

    4、野指针: 指针指向了一块已经释放的内存对象(这个对象成为僵尸对象)给野指针发送消息会报错 野指针经常报错为 EXC_BAD_ADRESS

          空指针:没有指向任何对象的指针(存储的为nil/NULL/0) oc中给nil 发送消息不会报错

          僵尸对象:所占用的内存已经被回收的对象,僵尸对象不能再继续使用

    5、retain方法会返回对象本身

          release方法不会有返回值

    6、delloc

          当对象要被回收的时候会调用、一定要调用【super delloc】,因为【super delloc】中也要做一些释放操作,而且这一句一定要写在最后

    7、alloc/new/mutableCopy来创建对象的时候,引用计数器会+1,不使用这个对象的时候,需要将引用计数器-1

    8、谁retain了,谁就要release

    9、当你想使用或者占有某个对象的时候,需要对对象进行一次retain操作

          当你不想使用或者占有某个对象的时候,需要对对象进行一个release操作

          谁retain谁release

          谁alloc谁release

    10、假如person  有一个car属性

    _car:直接访问成员属性

    self->_car:直接访问成员属性

    self.car  :get方法

    [self car]:get方法

    11、MRC中的set方法 

    @interfacePerson : NSObject

    {

        Car *_car;

    }

    -(void)setCar:(Car *)car;

    -(Car *)car;

    @end

    @implementationPerson

    -(void)setCar:(Car *)car{

        if(_car != car){        //这个判断是为了防止调用多次[self car]此时的car为同一个car,先给_car做一次release操作,此时car已经被释放了,然后再去调用【car retain】方法,这个相当于是给一个僵尸对象发送了一个retain消息。

            [_car release];    //这个是为了防止在你给对象赋值新的car的时候,原有的car没有做release操作,而导致的原有的car不会被释放

            _car =[car retain];//这段代码是为了谁拥有谁就要去retain

        }

    }

    -(Car *)car{

        return_car;

    }

    @end

    12、内存管理的原则

    1>只要调用了alloc,就必须调用release或者autorelease

    2>set方法的代码规范

       基本数据类型:

    -(void)setAge:(int)age{

    _age = age;  直接赋值

    }

        对象类型:

    -(void)setCar:(Car*)car{

    if(_car != car){

    [_car release];//对旧对象进行release操作

    _car =[car retain];//对新对象进行retain操作

    }

    }

    3>delloc //对self所拥有的所有对象进行一次release操作

    -(void)delloc{

    [_car  release];

    //最后调用【super delloc】

    [super delloc];

    }

    13、property默认生成的set和get方法是直接赋值assign

    14、property(retain)就是相当于在set方法中先release旧值,再retain新值

    15、property的参数有四个

    1>set方法内存管理的相关参数

    *retain:先release旧值,再retain新值

    *assign:直接赋值

    *copy

    2>多线程管理

    nonatomic :(性能高,set方法的时候不加线程锁)

    atomic :(性能低,加线程锁)

    3>是否只生成set和get方法

    *readwrite:同时生成set和get方法的声明与实现

    *readolny:只生成get的声明与实现

    4>set和get的方法名称

    setter:决定了set:的方法名,一般是需要带冒号的

    getter:决定了getter方法名(一般用在bool类型)

    例如:

    @property(nonatomic,retain,readwrite,setter=setCarrrrr:,getter=carrrrr)Car *car;

    16、

    定义一个枚举

    typedefenum{

        SexMan,

        SexWoman

    } Sex;

    定义一个结构体

    typedefstruct{

        intyear;

        intmonth;

        intday;

    } Date;

    17、@class只是告诉对象 这是一个类 , 但是不包含.h中的属性与方法

     1>@class只是告诉编译器某个名称是一个类,例如@class person仅仅告诉编译器person是一个类

    2>开发中引用一个类的规范

    在.h中用@class来声明

    在.m中用import来包含类的声明

    3>两端循环引用的解决方案

    一端用retain一端用assign

    在.h中用@class来声明可以解决循环包含的错误 

    例如

    #import <Foundation/Foundation.h>

    #import "Person.h"

    @interfaceCar : NSObject

    @property(nonatomic,retain)Person *p;

    @end

    #import <Foundation/Foundation.h>

    #import "Car.h"

    @interfacePerson : NSObject

    @property(nonatomic,retain)Car *car;

    @end

    这样会报错,编译不通过

    #import <Foundation/Foundation.h>

    @classPerson;

    @interfaceCar : NSObject

    @property(nonatomic,retain)Person *p;

    @end

    #import <Foundation/Foundation.h>

    @classCar;

    @interfacePerson : NSObject

    @property(nonatomic,retain)Car *car;

    @end

    这样才不会报错,编译可以通过,然后在.m文件中用#import “”来包含头文件

    这样做编译效率也比较高,如果按照第一种方式,一旦包含的类种的属性或者方法修改过了,就会造成每一个只要#import “”的.h都会重新编译一遍,造成效率比较低

    18、autorelease的使用

    autorelease方法会返回对象本身

    autorelease 会把对象放在一个自动释放池中,当自动释放池被销毁的时候,会对池中的对象做一次release操作

        @autoreleasepool{

            Person *p =  [[[Person alloc]init]autorelease];

        }

    会在池子结束的时候,对person对象调用一次release操作

    autoreleasepool是可以嵌套使用的,而且autoreleasepool是放在栈内存中的,先进后出。

    autoreleasepool的基本用法:

    1>会将对象放入一个自动释放池中

    2>当自动释放池销毁的时候,会对池中的对象进行一次release操作

    3>会返回对象本身

    4>调用完autorelease方法,对象的引用计数器不变,只有当autoreleasepool执行完毕之后,才会对池中的所有对象进行一次release操作

    autorelease的好用:

    1>不用关心对象释放的时间

    2>不用关心什么调用对象的release

    autorelease的使用注意:

    1>占用较大内存的对象,不要随便的使用autorelease

    2>占用较小内存的对象使用autorelease没有太大的影响

    池子autoreleasepool的错误写法:

    1>    @autoreleasepool{

            Person *p =  [[[[Person alloc]init]autorelease]autorelease];//调用两次autorelease会出现野指针错误

        }

    2>    @autoreleasepool{

            Person *p =  [[[Person alloc]init]autorelease];

            [p release];

        }        也会出现也指针错误

    3>        Person *p =  [[[Person alloc]init]autorelease];这样写没有任何的意义,因为autorelease不放到autoreleasepool中是不会调用release

    自动释放池:

    1>ios程序中也会创建自动释放池,这些自动释放池以栈的形式存在

    2>当一个对象调用autorelease方法的时候,会将这个对象放到栈顶的释放池

    自动释放池的创建方法:

    #pragma mark ios5之前

        NSAutoreleasePool *pool =[[NSAutoreleasePool alloc]init];

        Person *p =  [[[Person alloc]init]autorelease];

        [pool release];

    #pragma mark ios5之后

        @autoreleasepool{

            Person *p =  [[[Person alloc]init]autorelease];

            [p release];

        }

    }

    19、

    1>系统自带的方法没有alloc/new/copy,说明返回的对象都是autorelease的

    例如  NSString *str =[NSString stringWithFormat:@"string"];

        NSString *str1 =[NSString stringWithString:@"string"];

    2>开发中经常提供一些类方法快速创建一个已经autorelease的对象

    创建对象的时候,不要直接用类名,要用self,以满足子类的需求

    例如

    +(id)person{

        return  [[[selfalloc]init]autorelease];

    }

    内存管理的总结:

    一、计数器操作:

    1>retain +1;

    2>release -1;

    3>retaincount  :获取计数器

    二、set方法的内存管理

    1>set的实现

    -(void)setCar:(Car *)car{

        if(_car != car){

            [_car release];

            _car =[car retain];

        }

    }

    2>delloc方法的实现(不要直接调用delloc)

    -(void)dealloc{

        [_car dealloc];

        [superdealloc];

    }

    三、property参数

    oc对象:

    @property(nonatomic,retain)类名*属性名;

    非oc对象:

    @property(nonatomic,assign)类型名称属性名;

    四、@autoreleasepool

    1、系统的方法中如果不带有alloc、new、copy,这些方法返回的对象都是已经autorelease过的

    2、在开发过程中用类方法创建对象的时候,不要直接使用类名。可以用self(利用类方法快速创建一个autorelease对象)

    ARC

    ARC的判断准则:只要没有强指针指向对象,就会释放对象

    1>ARC的特点是不允许调用retain/release/retainCount

    2>允许重写delloc,但是不允许调用【super delloc】

    3>property参数

    *strong 成员变量是强指针 ,相当于之前retain(只适用于oc对象)

    *weak成员变量是弱指针,相当于之前的assign(适用于oc对象)

    *assign:适用于非oc对象

    指针分为两种:

    强指针:__strong 默认情况下就是强指针

    弱指针:__weak  当弱指针指向的对象消失后,默认会指向nil

    ARCMRC的转换

    1>mrc项目 arc

    xcode -> edit ->convert -> to object ARC

    2>查看项目当前是否为 ARC

    buildSetting ->搜索auto -> objectc automatic reference counting ->yes代表arc No代表mrc

    3>将项目中某个文件改为 arc或者mrc

    build phases ->compile source ->-fno-objc-arc不需要arc或者-f-objc-arc需要ARC

    当两端循环引用的时候,解决的方案

    1、ARC

    一端用strong一端用weak

    2、非arc

    一端用retain一端用assign

    Block:

    intsum(inta,intb){

        returna+b;

    }

    -(void)viewDidLoad {

        [superviewDidLoad];

        int(*p)(int,int)= sum;

        intsum = p(10,12);

        NSLog(@"%d",sum);

    }

    指向函数的指针

    int(^sumBlock)(int,int)=^(inta,intb){

            returna+b;

        };

        sumBlock(100,100);

    1>如何定义block变量

    Int(^SumBlock)(int,int);

    void(^myBlock)();

    2>如何利用block封装代码块

    ^(int a,int b){

    return a-b

    };

    ^(){

    NSLog(@“111111”);

    };

    3>block访问外部变量

    *block内部可以访问外面的变量

    *默认情况下,block内部不能修改外面的变量

    *给局部变量上加上__block关键字,这个局部变量才可以在block内部修改

    4>利用typedef定义block类型

    typedefint(^MyBlock)(int,int);

    以后就可以利用MyBlock这种类型来定义block变量

    协议Protocol

    1>基本用途

    *可以用来声明一大堆方法

    *只要某各类遵守了这个协议,就相当于拥有了这个协议的所有方法

    *只要父类遵守了某个协议,就相当于子类也遵守了

    2>格式

    @protocol协议名称

    //协议方法

    @end

    某个类遵守某个协议

    @interface类名:父类<协议名称1,协议名称2>

    @end

    3>关键字

    @required:这个方法是必须实现的(若不实现,编译器会发出警告)

    @optional这个方法不一定实现

    4>协议遵守协议

    *一个协议可以遵守其他多个协议,多个协议之间用逗号隔开

    *一个协议遵守了其他协议,就相当于拥有了其他以协议中的方法声明

    @protocol协议名称<协议1,协议2>

    @end

    5>基协议

    *nsobject是一个基类,最根本最基础的类,热河其他类都要继承它

    *其实还有一个协议,名字也叫NSobject,它是一个基协议,最根本的协议

    *nsobject协议中声明很多最基本的方法,比如description、retain、release等

    *建议每一个新协议都要遵守nsobject协议

    6>定义一个变量的时候,限制这个变量保存的对象遵守某个协议

    类名<协议名称>*变量名;

    id<协议名称>变量名;

    NSObject*object;

    Idobj2;

    如果没有遵守协议,编译器会报警

    7>@propety中声明的属性也可以用作一个遵守协议的限制

    @property(nonatoimic,strong)类名<协议名称>*属性名

    8>协议可以定义在单独的.h中,也可以定义在某个类型中

    *如果这个协议只用在某个类中,应该定义在该类中

    *如果某个协议用在很多类中,就应该把协议定义在单独的文件中

    9>分类可以定义在单独.h和.m文件中,也可以定义在原类中

    1>一般情况下,都是定义在单独文件

    2>定义在原类中的分类,只要求看懂语法

    集合中的对象的内存管理

    1>当对象被添加到集合中的时候,对象的引用计数器会加一

    2>当集合被销毁的时候,会对集合中的对象发送一次release消息

    3>当对象被移除的时候,也会给对象发送一次release消息

    @property内存管理的策略

    1非ARC

    1>copy:只用于nsstring/block

    2>retain:除nsstring/block以外的oc对象

    3>assign:基本数据类型、枚举、结构体(非oc对象)当两个对象相互引用的时候,一端用retain一端用assign

    2ARC

    1>copy:只用于nsstring/block

    2>strong:除nsstring/block以外的oc对象

    3>assign:基本数据类型、枚举、结构体(非oc对象)

    4>weak当两个对象相互引用的时候,一端用retain一端用assign

    自定义类实现copy操作

    1>类遵守NSCopying协议

    2>实现-(id)copyWithZone:(nullableNSZone *)zone;方法

    3>在- (id)copyWithZone:(nullableNSZone *)zone方法中创建一个新的对象,并设置该对象数据与现有对象一致,并返回该对象

    单例模式

    alloc方法其实调用的是allocWithZone

    Static修饰局部变量,只创建一次

    static修饰的全局变量,只有当前文件才可以访问,并且只创建一次

    1>永远只分配一块内存来创建对象

    2>提供一个类方法,返回内部唯一的一个对象

    3>最好保证init方法也只初始化一次

    Dispath_once方法是线程枷锁的,能保证线程安全

    非ARC单例模式的实现比ARC单例模式的实现多了

    -(id)retain{

    }

    -(NSUInteger)retainCount{

    }

    -(onewayvoid)release{

    }

    -(instancetype)autorelease{

    }

    // ## : 连接字符串和参数

    #if __has_feature(objc_arc)判断编译环境是ARC还是MRC

    #define singleton_h(name)+(instancetype)shared##name;

    #if __has_feature(objc_arc)// ARC 

    #define singleton_m(name)\

    static id _instance;\

    +(id)allocWithZone:(struct _NSZone *)zone \

    { \

        static dispatch_once_t onceToken;\

        dispatch_once(&onceToken,^{ \ dispatch_once线程安全,枷锁了

            _instance =[super allocWithZone:zone];\

        });\

        return _instance;\

    } \

     \

    +(instancetype)shared##name \

    { \

        static dispatch_once_t onceToken;\

        dispatch_once(&onceToken,^{ \

            _instance =[[self alloc]init];\

        });\

        return _instance;\

    } \

    +(id)copyWithZone:(struct _NSZone *)zone \

    { \

        return _instance;\

    }

    #else// ARC

    #define singleton_m(name)\

    static id _instance;\

    +(id)allocWithZone:(struct _NSZone *)zone \

    { \

    static dispatch_once_t onceToken;\

    dispatch_once(&onceToken,^{ \

    _instance =[super allocWithZone:zone];\

    });\

    return _instance;\

    } \

    \

    +(instancetype)shared##name \

    { \

    static dispatch_once_t onceToken;\

    dispatch_once(&onceToken,^{ \

    _instance =[[self alloc]init];\

    });\

    return _instance;\

    } \

    \

    -(oneway void)release \

    { \

    \

    } \

    \

    -(id)autorelease \

    { \

    return _instance;\

    } \

    \

    -(id)retain \

    { \

    return _instance;\

    } \

    \

    -(NSUInteger)retainCount \

    { \

    return 1;\

    } \

    \

    +(id)copyWithZone:(struct _NSZone *)zone \

    { \

    return _instance;\

    }

    #endif

    控制器的生命周期

    -(void)loadView{

        [superloadView];

    }

    -(void)viewDidLoad{

        [superviewDidLoad];

    }

    -(void)viewWillAppear:(BOOL)animated{

        [superviewWillAppear:animated];

    }

    -(void)viewDidAppear:(BOOL)animated{

        [superviewDidAppear:animated];

    }

    -(void)viewWillDisappear:(BOOL)animated{

        [superviewWillDisappear:animated];

    }

    -(void)viewDidDisappear:(BOOL)animated{

        [superviewDidDisappear:animated];

    }

    -(void)viewWillUnload{

        [superviewWillUnload];

    }

    销毁view

    -(void)viewDidUnload{

        [superviewDidUnload];

    }

    -(void)didReceiveMemoryWarning{

        [superdidReceiveMemoryWarning];

    }

    Block可以使用在定义之前声明的局部变量

        inti=10;

        void(^myBlock)(void)= ^(){

            NSLog(@"%d",i);

        };

        i = 100;

        myBlock();

    打印的结果是 10;

    1>在定义block的时候,会在block中建立当前局部变量的内容的副本(拷贝)

    2>后续对该值进行修改不会影响block中的数值

    3>如果需要在block中保持局部变量的数值变化,需要使用————block关键字

    4>使用————block关键字之后,同样可以在block中修改变量的值

    runtime

    运行时机制:比较高级的特性,纯C语言

    平时的oc代码——>C语言的运行时代码

    默认情况下任何block的内存都是分配在栈中的,随时可能被回收。

    对block进行一次copy操作就会把内存分配到堆中。

    block会对代码块中的对象进行一次强引用,例如

    self.block = ^{

    self.age = 20;

    }

    这个时候,block会对self进行一次强引用,而block又是self的一个copy属性,也属于强引用,所以造成了循环引用;

    因此当block中使用了对象的时候,需要判断是否造成了循环引用。如果造成了循环引用,就需要用——weak 来重新用指针指向对象

    或者__unsafe_unretained  来重新修饰 

    例如

    __unsafe_unretainedPerson *p2 = p1;

        __weakPerson *p3 = p1;

        __weaktypeof(self)weakSelf =self;

        typeof(self) 就是用来判断类型,即等价于 self的类型*

    KVC

    利用kvc可以随意的修改一个对象的属性或者成员变量(并且私有的也可以)

    Person *p1 =[[Person alloc]init];

        Dog *dog =[[Dog alloc]init];

        [p1.dog setValue:@"dogname" forKey:@"name"];

        [p1.dog setValue:@"dogname" forKeyPath:@"dog.name"];

    人对象有一个狗的属性,狗又有name的属性;

        [p1.dog setValue:@"dogname" forKey:@"name”];中的key只能是属性名,否则会报错;

        [p1.dog setValue:@"dogname" forKeyPath:@"dog.name"];

    可以是路径,又可以是属性名

    forKeyPath包含了for的功能,以后用forkeyPath

    iOS中的多线程

    1>一个NSTread对象就代表一条线程

    2>创建和启动线程

    NSThread *thread =[[NSThread alloc]initWithTarget:selfselector:@selector(run:)object:@"hah"];

      [thread start];

    3>主线程相关的用法

    [NSThread mainThread];

    [NSThread isMainThread];

     [thread isMainThread];

    4>设置线程的优先级

        [NSThread threadPriority];查看当前线程的优先级

        [NSThread setThreadPriority:0.5];设置当前线程的优先级

    5>设置线程的名字

        thread.name = @"线程一";

    6>创建线程的时候就启动线程

     [NSThread detachNewThreadWithBlock:^{

        }];

        [NSThread detachNewThreadSelector:@selector(run:)toTarget:selfwithObject:@"哈哈"];

    7>隐shi创建

        [selfperformSelectorInBackground:@selector(run:)withObject:@"哈哈”];

    线程的状态

    当线程在阻塞和死亡状态的时候,会退出可调度线程池中。但是内存还在。

    线程状态的控制

    最后的参数是说这个performSelector方法是否等待执行完毕,才继续跳出子线程操作

    GCD

    会自动帮我们创建线程

    MRC中队列是需要释放的  当调用creat方法的时候,要调用dispatch_release(queue)方法去释放队列

    在ios中凡是函数带有create、copy/new/retain等字眼,都需要在不需要使用这个数据的时候调用release操作

     CFArrayRef array = CFArrayCreate(0,0,11,0);

        CFRelease(array);在调用CF函数的时候,即使是在arc环境下也需要调用一下release操作

    CF(core Facation)框架在ARC中也需要手动release

    同步函数:

    *串行队列:不开启新的线程,任务串行执行

    *串行队列(主队列)会出现死锁

    *并行队列:不开启新线程,任务串行执行

    异步函数:

    *串行队列 :开启一条新的线程,任务并行执行

    *串行队列(主队列)不会开启新的线程

    *并行队列: 开启新的线程,任务并行执行

        dispatch_queue_t mainqueue = dispatch_get_main_queue();

        dispatch_sync(mainqueue,^{

            NSLog(@"这样会死锁");

        });

    dispatch_queue_t mainqueue = dispatch_get_main_queue();

        dispatch_async(mainqueue,^{

            NSLog(@"这样不会发生死锁");

        });

    因为异步函数虽然也是放在了主线程中,但是比较特殊,会将block中的代码缓后执行,所以不会出现死锁

    延迟执行:

    第一GCD:

     //全局并发队列

        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

        //计算执行任务的时间

        dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW,(int64_t)(3.0 * NSEC_PER_SEC));

        //when的这个时间点去执行queue的任务

        dispatch_after(when,queue,^{

        });

    第二:

        [selfperformSelector:@selector(run:)withObject:@"hahha" afterDelay:3.0];

    一次性代码

     staticdispatch_once_t onceToken;

        dispatch_once(&onceToken,^{

        });

    #define global_queue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)

    #define main_queue dispatch_get_main_queue()

     dispatch_group_t group = dispatch_group_create();

        dispatch_group_async(group,global_queue,^{

            //下载图片1

        });

        dispatch_group_async(group,global_queue,^{

            //下载图片2

        });

        dispatch_group_notify(group,main_queue,^{

            //当任务一 任务二 完成之后,回到主线程中去操作

        });

    NSOpertion:

    多线程

    1.NSThread

    1> 开线程的几种方式

    * 先创建,后启动

    NSThread *thread =[[NSThread alloc]initWithTarget:selfselector:@selector(run)object:nil];

    [thread start];

    * 直接启动

    [NSThread detachNewThreadSelector:@selector(run)toTarget:selfwithObject:nil];

    [selfperformSelectorInBackground:@selector(run)withObject:nil];

    2> 其他用法

    NSThread *current =[NSThread currentThread];

    +(NSThread *)mainThread;// 获得主线程

    3> 线程间通信

    performSelectorOnMainThread.....

    2.GCD(重点)

    1> 队列的类型

    * 并发队列

    获得全局的并发队列: dispatch_get_global_queue

    * 串行队列

    a.自己创建

    dispatch_queue_create

    b.主队列

    dispatch_get_main_queue

    2> 执行任务的方法类型

    *同步(sync)执行

    *异步(async)执行

    3> 了解队列和方法的配合使用

    4> 线程间通信

    dispatch_async(

    dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{

       // 执行耗时的异步操作...

       dispatch_async(dispatch_get_main_queue(),^{

           // 回到主线程,执行UI刷新操作

       });

    });

    5> 其他用法

    dispatch_once

    dispatch_after

    dispatch_group_async\dispatch_group_notify

    3.NSOperation

    1> 基本使用

    NSInvocationOperation

    NSBlockOperation

    2> NSOperationQueue(重点)

    * 最大并发数设置

    -(void)setMaxConcurrentOperationCount:(NSInteger)cnt;

    * 设置依赖(面试题)

    [operationB addDependency:operationA];// 操作B依赖于操作A

    3>自定义Operation(了解基本流程)

    4> 如何解决一张图片(一个url)重复下载的问题(面试题)

    相关文章

      网友评论

          本文标题:OC基础

          本文链接:https://www.haomeiwen.com/subject/ryqotqtx.html