沃梦达 / IT编程 / 移动开发 / 正文

Objective-C 入门篇(推荐)

由C语言和Smalltalk扩展出来的,是C语言的超集,最大的区别是OC是面向对象的,其火星文写法对于之前从事Java开发的同学颇感蛋疼,OC最大特点之一是使用消息结构而不是函数调用

结果:内存地址相同

方法

通过”+“、”-“分别声明类方法和实例方法,方法如果带有多个参数,参数在方法名之后接冒号定义,多个参数由空格隔开,如果参数个数可变,使用逗号接省略号。例如:


//无参数
- (void)print;
//有参数
- (void)print:(int)a andB:(int)b;

构造方法

第一种是重写init方法,第二种是自定义。


/**
 重写初始化方法
 **/
- (instancetype)init
{
    self = [super init];
    if (self) {
        _peopleName = @"hello ios";
    }
    return self;
}

/**
 自定义初始化方法
 **/
- (instancetype)initWithNameAndAge:(NSString *)name andAge:(int)age
{
    self = [super init];
    if (self) {
        _peopleName = name;
        _peopleAge = age;
    }
    return self;
}

创建类对象

所有对象和类的引用都是通过指针实现,严格地说指针就是一个地址,是一个常量,而指针变量可以被赋值不同的指针值,创建的对象就是一个指针变量,通过[People alloc]创建一个People对象,分配了内存,init是初始化对象。构造方法有两种方式,第一种是重写init方法,第二种是自定义。


People *p1 = [[People alloc] init];
//调用自定义的构造方法
People *p3 = [[People alloc] initWithNameAndAge:@"mingzi" andAge:12];
//调用方法
[p3 print];

在OC 2.0中,如果创建的对象不需要参数,可以直接使用new:

People *p1 = [People new];

self

作为OC的一个关键字,代表当前类的对象,类似Java中的this,最大的作用是让类中的一个方法调用该类另外一个方法或者成员变量,可以理解”当前类谁调用了这个方法,self就代表谁“。

继承

同Java一样只能单继承,只允许最多有一个直接父类。例如:定义一个父类Computer和子类MacBook。注意方法重写类似Java,子类要重写父类方法不需要重新声明重写方法,在实现部分直接重写目标方法即可。如果需要子类调用父类的方法,可以通过super关键字调用。


//Computer.h文件
#import <Foundation/Foundation.h>
@interface Computer : NSObject
@property(nonatomic,strong)NSString *name;
-(void)calculate;
@end

//  Computer.m
#import "Computer.h"
@implementation Computer
@synthesize name;
-(void) calculate{
    NSLog(@"i can calculate");
}
@end

//  MacBook.h
#import "Computer.h"
@interface MacBook : Computer
@end

//  MacBook.m
#import "MacBook.h"
@implementation MacBook
@end

//main.m
int main(int argc, char *argv[])
{
    @autoreleasepool
    {
        MacBook *macBook = [[MacBook alloc] init];
        macBook.name = @"mac";
        [macBook calculate];
    }
}

多态

封装、继承和多态是面向对象编程语言的三大特性,OC的多态是不同对象对同一消息的不同响应方式,实际过程主要分为三种:

  • 继承
  • 重写
  • 指向子类的指针指向父类

可以看出跟Java的多态类似,理解起来应该比较容易,注意是没有方法重载的,在OC中不允许。

Runtime

实例:用Runtime新增一个类Person, person有name属性,有sayHi方法


#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import <objc/runtime.h>
#import <objc/message.h>
void sayHi(id self, IMP _cmd, id some)
{
    //self指的是调用该方法传过来的类
    NSLog(@"%@说:%@,我%@岁", [self valueForKey:@"name"], some, object_getIvar(self, class_getInstanceVariable([self class], "_age")));
}

int main(int argc, char *argv[])
{
    @autoreleasepool
    {
        //该方法动态创建一个类,arg1:继承自哪个类 arg2:新建类的名称 arg3:extraBytes
        Class Person = objc_allocateClassPair([NSObject class], "Person", 0);
        //添加两个实例变量name和age,arg2:变量名称,arg3:内存地址大小,arg5:变量类型
        class_addIvar(Person, "_name", sizeof(NSString *), log2(sizeof(NSString *)), @encode(NSString *));
        class_addIvar(Person, "_age", sizeof(int), sizeof(int), @encode(int));
        //注册方法名
        SEL s = sel_registerName("say:");
        //arg3:IMP是“implementation”的缩写,这个函数指针决定了最终执行哪段代码
        //arg4:方法的参数及返回值
        class_addMethod(Person, s, (IMP)sayHi, "v@:@");
        //通过该类创建一个实体的对象
        id peopleInstance = [[Person alloc]init];
        //给对象的 name 实例变量赋值,下面是第二种赋值方式
        [peopleInstance setValue:@"XQM" forKey:@"name"];
        //Ivar nameIvar = class_getInstanceVariable(Person, "_name");
        //object_setIvar(peopleInstance, nameIvar, @"XQM");
        //获取实例变量
        Ivar ageIvar = class_getInstanceVariable(Person, "_age");
        //为变量赋值
        object_setIvar(peopleInstance, ageIvar, @21);
        //调用sayHi方法,arg2:注册指定的方法;arg3:带上有一个字符串的参数
        ((void(*)(id, SEL, id))objc_msgSend)(peopleInstance, s, @"大家好");
        //调用完成,将对象置为空
        peopleInstance = nil;
        //通过 objc 销毁类,销毁的是一个类不是对象
        objc_disposeClassPair(Person);
    }
}

主要流程是:

定义类的方法->objc_allocateClassPair创建类->class_addIvar给类添加成员变量->sel_registerName注册方法名->class_addMethod给类添加定义的方法->注册该类->创建类对象->class_getInstanceVariable获取成员变量,并通过object_setIvar赋值->objc_msgSend调用方法->释放对象,销毁类

Category(类别)

Objective-C借用并扩展了Smalltalk实现中的"分类"概念,用以帮助达到分解代码的目的。类别主要特点是不能增加属性或者成员变量、增加类功能和分离类实现,举个例子: 在UIImageView增加了图片异步加载的功能


@interface UIImageView (ImageViewLoader) <AysncImageDownloaderDelegate>
- (void)setOnlineImage:(NSString *)url placeholderImage:(UIImage *)image withRow:(NSNumber *)row;
@end

@implementation UIImageView (ImageViewLoader)

- (void)setOnlineImage:(NSString *)url placeholderImage:(UIImage *)image withRow:(NSNumber *)row;
{
    self.image = image;
    AsyncImageDownLoader *downloader = [AsyncImageDownLoader sharedImageDownloader];
    [downloader startWithUrl:url delegate:self withRow:row];
}
@end

Extension(拓展)

拓展也经常用到,主要特点是增加ivar、用于接口分离等。例如:ViewController的实现文件增加@interface ViewController (),支持定义属性等。


@interface ViewController ()
@property (nonatomic, copy) block b;
@end

@implementation ViewController
@end

异常处理

OC的异常处理极其类似Java中的,包括4个指示符,分别是@try、@catch、@throw、@finally。可能存在异常的代码写在@try块,异常处理逻辑写在@catch,@finally块的代码总是要执行的,@throw作用是抛出异常。

协议

类似Java中的接口(interface),类似多重继承功能,支持协议继承协议,通过定义一系列方法,然后由遵从协议的类实现这些方法,协议方法可以用@optional关键字标记为可选,@required关键字标记为必选,编译器会出现检查警告,一般来说还是可以编译通过。下面看下语法:


@protocol ClickDelegate
- (void)click;
- (void)unclick;
@end

协议最常应用在委托,分为委托方和代理方,委托方负责定义协议、通过id类型持有协议和调用协议的方法,而代理方则遵从协议、设置协议代理对象以及实现协议方法即可。

block

类似Java中的Lambda表达式,比较复杂,笔者的理解还未达到一定解说程度,所以这里先不做解释,放到后续的文章中介绍。

内存管理

Java的内存管理是由垃圾回收器负责,OC中引入自动引用计数(ARC),内存管理交由编译器决定。引用计数是每个对象都有一个计数器,如果对象继续存活,计数器递增其引用计数,用完之后递减引用计数,如果计数变为0,表示对象可以被释放了。NSObject协议声明了Retain、release和autorelease方法用于操作计数器,分别是递增、递减、自动释放操作,所有的对象都是收集器的工作对象。

ARC:自动引用计数,编译器自动生成retain/release
MRC:手动管理引用计数,旧版本使用
autoreleasepool:延迟自动释放

strong/weak/assgin最佳实践

基本类型:assgin;
delegate->week;
集合和block用copy;
其他用strong;
block中的self用weak打破循环引用。

参考资料

https://www.jianshu.com/p/eb713b1f22dc
https://www.jianshu.com/p/6ebda3cd8052
https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtForwarding.html

本文标题为:Objective-C 入门篇(推荐)