OC语言和ios学习

OC-Hello world

两种输出Hello world 的形式:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSLog(@"Hello, World!");
    }
    return 0;
}
@interface	创建接口
(void)	声明方法
@implementation 实现接口
``` 
@interface SampleClass : NSObject
(void)sampleMethod;
@end

@implementation SampleClass
(void)sampleMethod{
    NSLog(@"Hello world!");
}

@end

int main(){
    SampleClass * sampleClass = [[SampleClass alloc] init];
    [sampleClass sampleMethod];
    return 0;
} 

生命周期

APP生命周期

UIViewController生命周期

方法调用

求两数之间的最大值

@interface SampleClass : NSObject
(int) max:(int)num1 secondNumber:(int)num2;
@end

@implementation SampleClass
(int) max:(int)num1 secondNumber:(int)num2{
    int num;
    if(num1 > num2){
        num = num1;
    }else {
        num = num2;
    }
    return num;
}
@end

int main(){
    int a = 10;
    int b = 20;
    int res;
    
    SampleClass *sampleClass = [[SampleClass alloc]init];
    res = [sampleClass max:a secondNumber:b];
    NSLog(@"res is", res);
    return 0;
} 

属性

读写修饰符

(1)readwrite

表明这个属性是可读可写,默认属性,系统创建setter和getter方法。

(2)readonly

表明这个属性只读不写,系统只创建getter方法,不创建setter方法。

原子性修饰符

(1)atomic

原子属性,线程安全但可能降低性能。

(2)nonatomic

非原子属性,提高性能但非线程安全。

内存管理语义: assign、week、strong、copy、retain、unsafe_unretained

assgin和weak

weak与assgin都表示了一种非持有关系,也不被称为弱引用,在调用时,不会增加引用对象的引用计数。weak在引用对象被销毁时,会被指向nil;而assgin不会被置为nil。

(1)assign

如果用assgin修饰对象,当assgin指向的对象被销毁时,assgin就会指向一块无效内存,这时给assgin发送消息,程序一不定会发生崩溃,这个取决于你发送消息时,那块内存还是否存在。

assgin一般修饰的基础数据类型(NSInteger,int,float,double,char,bool等),因为基础数据类型是被分配到栈上,栈的内存会由系统自动处理,所以不会造成悬空指针。

(2)weak

只用于修饰对象,修饰的对象会通过一个hash表保存。

strong和copy

使用strong和copy是都会使引用对象引用计数+1。但是使用copy修饰的属性在某些情况下赋值的时候会创建对象的副本,就是深拷贝。strong是两个指针指向同一个内存地址,copy是在内存中拷贝一份对象,两个指针指向不同的内存地址。

(1)copy

copy修饰NSString、NSArray、block属性。系统在当源字符串为不可变类型时,你的属性copy其实就是进行浅拷贝,当源字符串为可变类型时,才进行深拷贝。但是我们建议在使用NSString属性的时候用copy,避免可变字符额修改导致一些非预期问题。与strong类似,设置方法会拷贝一份副本。一般用于修饰字符串和集合类的不可变版, block用copy修饰。

(2)strong

表示指向并拥有该对象。其修饰的对象引用计数会 +1 ,该对象只要引用计数不为 0 就不会销毁,强行置空可以销毁它。一般用于修饰对象类型、字符串和集合类的可变版本。只能用于ARC环境,strong修饰NSString、block以外的OC对象。

retain

retain修饰NSArray,NSDate,对应的setter方法。

修饰对象类型,强引用对象,并使对象引用计数加1,可用于MRC环境中。

与assign相对,我们要解决对象被其他对象引用后释放造成的问题,就要用retain来声明。retain声明后的对象每次被引用,引用计数会+1,释放后会-1,即使这个对象本身释放了,只要还有对象在引用它,就会持有,只有当引用计数为0时,就被dealloc析构函数回收内存了。

unsafe_unretained

与weak类似,但是销毁时不自动清空,容易形成野指针。

Block

块是C,Objective-C和C++等编程语言中的高级功能,它允许创建不同的代码段,这些代码段可以传递给方法或函数,就像它们是值一样。 块是Objective-C对象,因此它们可以添加到NSArray或NSDictionary等集合中。 它们还能够从封闭范围中捕获值,使其类似于其他编程语言中的闭包或lambda。

block本质上也是一个OC对象,内部也有个isa指针,并且内部封装了函数调用。

BLock的实现

1、无参数无返回值

 void(^ZSBlockOne)(void) = ^(void){
	NSLog(@"无参数,无返回值")
};

ZSBlockOne();

2、有参数无返回值

 void(^ZSBlockTwo)(int value) = ^(int value){
	NSLog(@"有参数,无返回值的Block:%d",value);
};

ZSBlockTwo()

3、有参数有返回值

 int(^ZSBlockThree)(int value) = ^(int value){
	NSLog(@"有参数,有返回值的block:%d",value);
	return value + 10;
};
int a = ZSBlockThree(5);

4、无参数有返回值

 int(^ZSBlockFour)(void) = ^{
	NSLog(@"无参数,有返回值的block");
	return 100;
};
int a = ZSBlockFour();

5、开发中常用typedef定义block

typedef void (^ZSBlock)(int value);

// ZSBlock是一个种block类型,可以直接创建属性
@property(nonatomic, copy) ZSBlock myBlock;

self.myBlock = ^(int value){
	NSLog(@"调用了block,传了一个值为:%d",value);
};

self.myBlock(10);

例如:

typedef void (^CompletionBlock)();
@interface SampleClass : NSObject
-(vid)performActionWithCompletion:(CompletionBlock)completionBlock;
@end

@implementation SampleClass
-(vid)performActionWithCompletion:(CompletionBlock)completionBlock{
    NSLog(@"Action perform:");
    completionBlock();
}
@end

int main(){
    SampleClass *sampleClass = [[SampleClass alloc]init];
    [sampleClass performActionWithCompletion:^{
        NSLog(@"is performed");
    }];
    return 0;
} 

Block的类型

协议

关键字@required下的方法必须在符合协议的类中实现,并且@optional关键字下的方法是可选的。

@protocol PrintProtocolDelegate
-(void)processCompleted;
@end

@interface PrintClass : NSObject{
    id delegate;
}
-(void)printDetails;
-(void)setDelegate:(id)newDelegate;
@end

@implementation PrintClass
-(void)printDetails{
    NSLog(@"Print details");
    [delegate processCompleted];
}

-(void)setDelegate:(id)newDelegate{
    delegate = newDelegate;
}
@end

@interface SampleClass : NSObject<PrintProtocolDelegate>
-(void)startAction;
@end

@implementation SampleClass
-(void)startAction{
    PrintClass *printClass = [[PrintClass alloc]init];
    [printClass setDelegate:self];
    [printClass printDetails];
}

-(void)processCompleted{
    NSLog(@"print process completed");
}
@end

int main(int args, const char * argv[]){
    SampleClass *sampleClass = [[SampleClass alloc]init];
    [sampleClass startAction];
    return 0;
} 

快速枚举

集合的主要目的是提供一种有效存储和检索对象的通用方法。

有几种不同类型的集合。 虽然它们都能实现能够容纳其他对象的相同目的,但它们的主要区别在于检索对象的方式。

int main(){
    NSArray * array = [[NSArray alloc] initWithObjects:@"1", @"2", @"3", nil];
	//1 2 3
    for(NSString * aString in array){
        NSLog(@"VAlue: %@", aString);
    }
	//3 2 1 
    for(NSString * bString in [array reverseObjectEnumerator]){
        NSLog(@"Value: %@", bString);
    }

    return 0;
} 

内存管理

Objective-C内存管理技术可分为两类:“手动保留或释放”或“自动参考计数”。

在MRR中,通过跟踪自己的对象来明确管理内存。这是使用一个称为引用计数的模型实现的,Foundation类NSObject与运行时环境一起提供。

MRR和ARC之间的唯一区别是保留和释放,前者是手动处理,而后者则自动处理。

手动保留释放

@interface SampleClass:NSObject
- (id)sampleMethod;
@end

@implementation SampleClass
- (id)sampleMethod {
   NSLog(@"Hello, World! \n");
}

- (id)dealloc  {
  NSLog(@"Object deallocated");
  [super dealloc];
}
@end

int main() {
   SampleClass *sampleClass = [[SampleClass alloc]init];
   [sampleClass sampleMethod];

   NSLog(@"Retain Count after initial allocation: %d", 
   [sampleClass retainCount]);
   [sampleClass retain];

   NSLog(@"Retain Count after retain: %d", [sampleClass retainCount]);
   [sampleClass release];
   NSLog(@"Retain Count after release: %d", [sampleClass retainCount]);
   [sampleClass release];
   NSLog(@"SampleClass dealloc will be called before this");

   sampleClass = nil;
   return 0;
}

自动参考计数

@interface SampleClass:NSObject
- (id)sampleMethod;
@end

@implementation SampleClass
- (id)sampleMethod {
   NSLog(@"Hello, World! \n");
}

- (id)dealloc  {
  NSLog(@"Object deallocated");
}
@end
int main() {

   @autoreleasepool {
      SampleClass *sampleClass = [[SampleClass alloc]init];
      [sampleClass sampleMethod];
      sampleClass = nil;
   }
   return 0;
}

Runtime

消息发送以及转发机制

调用[receiver selector];后,进行的流程:

1.  编译阶段:[receiver selector];方法被编译器转换为:

Objc_msgSend(receiver, selector) --- 不带参数

Objc_msdSend(receiver, selector, org1, org2, ...) --- 带参数

2.  运行时阶段:消息接受者receiver寻找对应的selector

通过receiver的isa 指针找到receiver的Class (类);

在Class (类)的cache (方法缓存)的散列表中寻找对应的IMP (方法实现);

如果在cache (方法缓存)中没有找到对应的IMP (方法实现)的话,就继续在Class (类)method list (方法列表)中找对应的selector,如果找到,填充到cache (方法缓存)中,并返回selector;

如果在class (类)中没有找到这个selector,就继续在它的superclass (父类)中寻找;

一旦找到对应的selector,直接执行receiver对应的selector方法实现的IMP (方法实现)。

若找不到对应的selector,Runtime系统进入消息转发机制。

3.  运行时消息转发阶段

动态解析

通过重写+resolveInstanceMethod: 或者 +resolveClassMethod:方法,利用class_addMethod方法添加其他函数实现;

消息接受者重定向

如果上一步没有添加其他函数实现,可在当前对象中利用forwardingTargetForSelector:方法将消息的接受者转发给其他对象;

消息重定向

如果上一步返回值是nil,则利用methodSignatureForSelector:方法获取函数的参数和返回值类型。如果methodSignatureForSelector:返回了一个NSMethodSignature对象(函数签名),Runtime系统就会创建一个NSInvocation对象,并通过forwardInvocation:消息通知当前对象,给予此次消息发送最后一次寻找IMP的机会。

如果methodSignatureForSelector:返回nil。则Runtime系统会发出doesNotRecognizeSelector:消息,程序也就崩溃了。

Method Swizzling黑魔法

Method Swizzling修改了method list (方法列表),使得不同Method (方法)中的键值对发生了交换。比如交换前两个键值分别为:SEL A : IMP A、SEL B : IMP B,交换之后变成了SEL A : IMP B、SEL B : IMP A。

需注意:

1、应该只在+load中执行 ·Method Swizzling·

程序在启动的时候,会先加载所有的类,这时会调用每个类的+load方法。而且在整个程序运行周期中只会调用一次(不包括外部显示调用)。所以在+load方法中进行是最好的选择

2、Method Swizzling 在+load中执行时,不要调用[super load];。

程序在启动的时候,会先加载所有类。如果在+(void)load方法中调用[super load]方法,就会导致父类的Method Swizzling被重复执行两次,而方法交换也被执行了两次,相当于互转了一次方法后,第二次又换回来了,从而是的父类的Method Swizzling失效。

3、Method Swizzling应该总在dispatch_once中执行。

Method Swizzling不是原子操作,dispatch_once可以保证即使在不同的线程中也能确保代码只执行一次。所以,我们应该总在dispatch_once中执行 ·Method Swizzling· 操作,保证方法替换只被执行一次。

4、使用 Method Swizzling 后要记得调用原生方法的实现。

在交换方法实现后记得要调用原生方法的实现。APIs 提供了输入输出的规则,而在输入输出中间的方法实现就是一个看不见的黑盒。交换了方法实现并且一些回调方法不会调用原生方法的实现,这可能造成底层实现的崩溃。

5、避免命名冲突和参数_cmd被篡改。

6、谨慎对待 Method Swizzling

使用 Method Swizzling,会改变非自己拥有的代码。我么使用 Method Swizzling通常会更改一些系统框架的对象方法,或是类方法。我们改变的不只是一个对象实例,而是改变了项目中所有的该类的对象实例,以及所有子类的对象实例。所以,在使用 ·Method Swizzling· 的时候,应该保持足够的谨慎。

7、对于 Method Swizzling 来说,调用顺序很重要

+load方法的调用规则为:

1.  先调用主类,按照编译顺序,顺序的根据继承关系由父类向子类调用;

2.  在调用分类,按照编译顺序,依次调用;

3.  +load方法除非主动调用,否则只会调用一次。

这样的调用规则导致了+load方法调用顺序不一定正确。一个顺序可能是:父类 -> 子类 -> 父类类别 -> 子类类别,也可能是父类 -> 子类 -> 子类类别 -> 父类类别。所以 ·Method Swizzling· 的顺序不能保证,那么就不能保证 ·Method Swizzling· 后方法的调用顺序是正确的。

所以被用于 ·Method Swizzling· 的方法必须是当前类自身的方法,如果把继承自父类的IMP复制到自身上面,可能存在问题。如果+load方法调用顺序为:父类 -> 子类 -> 父类类别 -> 子类类别,那么造成的影响就是·调用子类的替换方法并不能正确调起父类分类的替换方法。

Category分类

主要作用是为已经存在的类添加方法。category可以做到在既不子类化,又不侵入一个类的源码,为原有的类添加新的方法,从而实现扩展一个类或者分离一个类的目的。

category分类和extension扩展

看起来两者有些相似,但实质上是不同的东西。Extension是在编译阶段与该类同时编译,是类的一部分;且Extension中声明的方法只能在该类的@implementation中实现,也就是说,对于系统类(如NSArray类)是无法使用扩展的。

Extension不仅可以声明方法,还可以声明成员变量,是Category做不到的。Category的本质是_category_t结构体,可以为类添加对象方法、类方法、协议、属性

Category的特性是可以在运行时阶段动态的为已有类添加新行为,成员变量的内存布局在编译阶段就确定分配好了,添加成员变量,会破坏原有类的内存布局。

Category中方法、协议、属性附加到类上的操作,是在+load方法执行前进行的。Category分类和class类的+load方法调用顺序为:

1.  先调用主类,按照编译顺序,依次根据继承关系,由父类向子类调用

2.  调用分类,按照编译顺序,依次调用

3.  +load方法除了主动调用,否则只会调用一次

Core Animation(核心动画)

在创建UIView对象时,UIView内部会自动创建一个图层(即CALayer对象),通过UIView的layer属性可以访问这个层。

@property(nonatomic,readonly,retain) CALayer *layer;

当UIView需要显示到屏幕上时,会调用drawRect:方法进行绘图,并且会将所有内容绘制在自己的图层上,绘图完毕后,系统会将图层拷贝到屏幕上,于是就完成了UIView的显示。

换句话说,UIView本身不具备显示的功能,是它内部的层才有显示功能。

CALayer的基本属性:

//宽度和高度:
@property CGRect bounds;
//位置(默认指中点,具体由anchorPoint决定):
@property CGPoint position;
//锚点(x,y的范围都是0-1),决定了position的含义:
@property CGPoint anchorPoint;
//背景颜色(CGColorRef类型):
@property CGColorRef backgroundColor;
//形变属性:
@property CATransform3D transform;

CGPoint position 用来设置CALayer在父层的位置,以父层的左上角为原点

CGPoint anchorPoint 称为定位点、锚点,决定着CALayer身上的某点会在position所指的位置,以自己的左上角为原点,默认中心点为(0.5,0.5)。

隐式动画

根层与非根层:

● 每一个UIView内部都默认关联着一个CALayer,我们可用称这个Layer为Root Layer

● 所有的非Root Layer,也就是手动创建的CALayer对象,都存在着隐式动画

当对非Root Layer的部分属性进行修改时,默认会自动产生一些动画效果,而这些属性称为Animatable Properties(可动画属性)。

常见的几个可动画属性:

● bounds:用于设置CALayer的宽度和高度。修改这个属性会产生缩放动画

● backgroundColor:用于设置CALayer的背景色。修改这个属性会产生背景色的渐变动画

● position:用于设置CALayer的位置。修改这个属性会产生平移动画

可以通过事务关闭隐式动画:

[CATransaction begin]; 
// 关闭隐式动画 
[CATransaction setDisableActions:YES];  
self.myview.layer.position = CGPointMake(10, 10); 
[CATransaction commit];

CALayer和UIView都可以实现相同的效果,但UIView多一个事件处理功能,UIView可以处理用户的触摸事件。CALayer不可以直接使用UIColor、UIImage。

layer.backgroundColor = [UIColor redColor].CGColor;

CAAnimation

是所有动画对象的父类,负责控制动画的持续时间和速度,是个抽象类,不能直接使用

属性

说明

duration

动画的持续时间

repeatCount

重复次数,无限循环(HUGE_VALF或MAXFLOAT)

repeatDuration

重复时间

removedOnCompletion

默认YES,动画执行完就从图层上移除,图形恢复到执行前的状态。保持显示动画执行后的状态,就设为NO

fillMode

决定当前对象在非action时间段的行为

beginTime

可以设置延迟执行时间,CACurrentMediaTime()为图层的当前时间

timingFunction

速度控制函数

delagate

动画代理

CAPropertyAnimation

CAAnimation的子类,也是个抽象类,要想创建动画对象,应该使用它的两个子类:CABasicAnimation和CAKeyframeAnimation。

属性说明,keyPath:通过指定CALayer的一个属性名称为keyPath(NSString类型),并且对CALayer的这个属性的值进行修改,达到相应的动画效果。比如,指定@“position”为keyPath,就修改CALayer的position属性的值,以达到平移的动画效果

CABasicAnimation——基本动画

属性

说明

fromValue

keyPath相应属性的初始值

toValue

keyPath相应属性的结束值

CAKeyframeAnimation——关键帧动画

CAPropertyAnimation的子类,与CABasicAnimation的区别是:

CABasicAnimation只能从一个数值(fromValue)变到另一个数值(toValue),而CAKeyframeAnimation会使用一个NSArray保存这些数值;CABasicAnimation可看做是只有2个关键帧的CAKeyframeAnimation

属性

说明

values

NSArray对象。里面的元素称为“关键帧”(keyframe)。动画对象会在指定的时间(duration)内,依次显示values数组中的每一个关键帧

path

可以设置一个CGPathRef、CGMutablePathRef,让图层按照路径轨迹移动。path只对CALayer的anchorPoint和position起作用。如果设置了path,那么values将被忽略

keyTimes

可以为对应的关键帧指定对应的时间点,其取值范围为0到1.0,keyTimes中的每一个时间值都对应values中的每一帧。如果没有设置keyTimes,各个关键帧的时间是平分的

CAAnimationGroup——动画组

CAAnimation的子类,可以保存一组动画对象,将CAAnimationGroup对象加入层后,组中所有动画对象可以同时并发运行。默认情况下,一组动画对象是同时运行的,也可以通过设置动画对象的beginTime属性来更改动画的开始时间。

属性

说明

animations

用来保存一组动画对象的NSArray

CATransition——转场动画

CATransition是CAAnimation的子类,用于做转场动画,能够为层提供移出屏幕和移入屏幕的动画效果。iOS比Mac OS X的转场动画效果少一点。

UINavigationController就是通过CATransition实现了将控制器的视图推入屏幕的动画效果。

属性

说明

type

动画过渡类型

subtype

动画过度方向

startProgress

动画起点(在整体动画的百分比)

endProgress

动画终点(在整体动画的百分比)

CADisplayLink

是一种以屏幕刷新频率触发的时钟机制,每秒执行大概60次,可以是绘图与视图的刷新频率保持同步,NSTimer无法确保计时器实际被触发的时间。

使用方法:

// 定义
CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(rotationChange)];
// 添加到主循环队列
[link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];

//暂停
link.paused = YES;
//开始
link.paused = NO;

多线程

NSThread

两种创建方式:

通过NSThread的对象方法动态创建

通过NSThread的类方法静态创建

线程创建->线程开启->线程取消->线程关闭->线程暂停->设置线程优先级

NSOperation

NSOperation是对GCD的封装,其中有两个核心概念【队列+操作】

1.NSOperation本身是抽象类,只能有它的子类。

2.两大子类分别是:NSBlockOperation、NSInvocationOperation,当然你也可以自定义继承自NSOperation的类。

基本使用

实例化NSOperation的子类,绑定执行的操作。

创建NSOperationQueue队列,将NSOperation实例添加进来。

系统会自动将NSOperationQueue队列中检测取出和执行NSOperation的操作。

GCD

GCD是苹果公司为多核的并行运算提出的解决方案,会自动利用更多的CPU内核(比如双核、四核),会自动管理线程的生命周期(创建线程、调度任务、销毁线程)

使用步骤

指定任务:确定想要做的事

将任务添加到队列中:GCD会自动将队列中的任务取出,放到对应的线程中执行,任务的取出遵循队列的FIFO原则:先进先出,后进后出。

队列

可以分为2大类型:1.并发队列 2.串行队列

使用dispatch_queue_create函数创建队列

dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);
      //const char *label 列队列的名称
      //dispatch_queue_attr_t attr 队列的类型

//创建一个并发队列
dispatch_queue_t queue1 = dispatch_queue_create("myQueue1", DISPATCH_QUEUE_CONCURRENT);
//创建一个串行队列
 dispatch_queue_t queue2 = dispatch_queue_create("myQueue2", DISPATCH_QUEUE_SERIAL);
 
     //获得全局的并发队列
     dispatch_queue_t queue3 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//     其中:第一个参数代表全局并发队列的优先级
     #define DISPATCH_QUEUE_PRIORITY_HIGH 2 --》 高
     #define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 --》 默认(中)
     #define DISPATCH_QUEUE_PRIORITY_LOW (-2) --》 低
     #define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN --》 后台

GCD获得串行有2种途径

1.  使用dispatch_queue_create 创建串行队列,创建串行队列(队列类型传递NULL或者DISPATCH_QUEUE_SERIAL)

dispatch_queue_t queue4 = dispatch_queue_create("com.520.queue", NULL);

2.  使用主队列 (跟主线程相关联的队列),主队列时GCD自带的一种特殊的串行队列,放在主队列中的任务,都会放在主线程中执行,dispatch_get_main_queue()获得主队列

 dispatch_queue_t queue5 = dispatch_get_main_queue();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值