从oc到swift


Objective-C

介绍


OC程序的源文件后缀名为.m。 m代表message,代表OC的消息机制。OC语言完全兼容C语言,所以在.m文件中可以写C语言的任何代码,甚至C++的代 码也可以写在.m文件下。

#import 指令:同一个文件,无论被#import多少次,在预编译的时候只会包含1次。包括#import ""#import <>

.m是Objective-C的源文件。.mm是Objective-C++的源文件。

.h是头文件,包含方法,属性的声明。.m是类的实现文件,参与编译的文件,用来实现类中声明的方法。

Boolean类型数据允许取值true或false,也可以是0或非0的整数替代true和false。

@try { 
    //可能出错的代码块 
} 
@catch (NSException *exception) { 
    // 一旦出了错 可以的补救代码。
}
@finally { 
    //无论出错不出错都会执行的代 
}

面向对象设计四个个主要特征: 抽象性、 封装性、 多态性、 继承性。


NSLog


NSLog 是printf函数的增强版

NSLog(@"Hello World!"); 

@""这个符号表示将一个C的字符串转化为OC中的字符串对象NSString。

OC字符串必须使用@符号开头。@符号放置的位置在双引号外面。在打印OC字符串的时候使用格式控制符%@ 。NSString类型的指针变量只能存储OC字符串常量的地址。

OC中大部分的关键字都是以@开头的,比如@interface,@implementation,@end



.h文件为类的声明文件,用于声明成员变量、方法。类的声明使用关键字 @interface@end.h中的方法只是做一个声明,并不对方法进行实现。

.m:类的实现文件,用于实现.h中声明的方法。类的实现使用关键字 @implementation@end

方法的声明和实现,都必须以+ 或者 - 开头。 +表示类方法(静态方法), -表示对象方法(动态方法)。

.h中声明的所有方法作用域都是public类型,不能更改。

成员变量的常用作用域有3种:

@public 全局都可以访问 
@protected	只能在类内部和子类中访问 
@private 只能在类内部访问

创建对象


创建对象的写法

类名 * 对象名 = [类名 new]; 

只要是用new操作符定义的实体就会在堆内存中开辟一个新的空间。1)在堆中开辟一段存储空间 2)初始化成员变量(写在类声明大括号中的属性就叫做成员变量,也叫做实例变量) 3)返回开辟空间的首地址。

访问对象的属性可以用:

对象->对象成员;

方法


oc没有严格的方法重载. 在oc中方法名不允许相同。

OC中的方法分为:对象方法和类方法。

对象方法:对象方法以-开头如-(void)xx ;对象方法只能由对象来调用;对象方法中可以访问当前对象的成员变量;调用格式[对象名 对象方法名] 。凡事类型都用()括起来。

类方法:以+开头如 +(void)xxx;类方法只能由类来调用;类方法中不能访问实例变量(成员变量),因为类方法由类来调用,并没有创建存储空间来存储类中的成员变量;调用格式:[类名 类方法名]

有参方法,冒号也是方法名的一部分

//返回值类型 void ; 方法名run: : :,参数有3个,都是int型的,参数的参数名 steps  km times
 - (void)run:(int)steps :(int)km :(int)times;

// 声明三个带参数的方法,方法名是 runWith: andWith: andWith:
 - (void)runWith:(int)steps andWith:(int)km andWith:(int)times;

有参方法的调用:

//[对象名 方法名 参数]
[zhansgan eat:@"辣条"];

// [对象名 方法名:参数:参数];
[zhansgan run:3 :10 :5];

[zhansgan runWith:5 andWith:20 andWith:2];

对象的存储


类创建对象,每个对象在内存中都占据一定的存储空间,每个对象都有一份属于自己的单独的成员变量,所有的对象公用类的成员方法,方法在整个内存中只有一份,类本身在内存中占据一份存储空间,类的方法存储于此。

每一个对象都包含一个isa指针,这个指针指向当前对象所属的类。 当调用方法的时候如[p eat] 表示给p所指向的对象发送一条eat消息,表示要调用对象的eat方法,此时对象会顺着内部的isa指针找到存储于类中的方法,开始执行,此时方法所修改的所有的信息,都是对应的对象的。


pragma mark


#pragma mark -//“-”后面不能随手敲个空格 
#pragma mark 分组(标识)名称

常见错误


@interface @end@implementation @end不能嵌套包含。

只有类的声明没有类的实现,会报错。


NSString


NSString是Objective-C中核心处理字符串的类之一。

创建常量字符串,注意使用“@”符号:

NSString * str = @"hahahaha"; 

NSString * str2 = [[NSString alloc] initWithString:str];

创建空字符串,给予赋值:

NSString * str1 = [NSString new ];
str1 = @"啊哈哈哈";

创建格式化字符串:占位符(由一个%加一个字符组成):

NSString * str3 = [NSString stringWithFormat:@"图片 xxxx %02d- %02d.jpg",9,1];
NSLog(@"Hello, World! %@ ",str3);//图片 xxxx %09- %01.jpg

计算字符串长度

NSLog(@"字符串的长度  %ld", [str2 length]);

对象与对象之间的关系


一个对象作为另外1个对象的成员变量

//女孩有一部手机
@interface Girl :NSObject
{
Phone *_phone;
}
@end
//女孩用手机打电话
@interface Girl :NSObject
 - (void)callWithPhone:(Phone *)phone;
@end

组合关系:1个对象是由多个对象组合起来的。比如:计算机对象,是由主板对象、CPU对象、内存对象、硬盘对象组合起来的。

依赖关系:就是一个对象要1件事情的时候必须有另一个对象。比如,B类是A类⽅方法的参数,我们就说A类依赖于B类。

关联关系:当一个对象拥有另外一个对象的时候, 当B对象为A对象的成员变量的时候,B对象与A对象之间存在一种关联关系。


面向对象设计原则


单一职责原则、开放封闭原则


类方法

OC中的方法分为两种

对象方法


以“-”减号开头的方法就是对象方法。对象方法的调用,必须创建对象,然后通过对象名去调用。

声明

 -(返回值类型)方法名:(参数类型)参数名称; 
 - (void)square : (int)num;

类方法


+加号开头的方法叫做类方法。这个方法不依赖于对象,不需要创建对象来调用,而是直接使用类名调用。

 +(返回值类型)方法名:(参数类型)参数名称; 
 + (void)square : (int)num;

类方法使用注意事项: 1. 类方法中不能访问成员变量。 2. 类方法和对象方法可以同名。 3. 类方法中不能通过self调用同名方法,会死循环。 4. 对象方法当中可以通过类名直接调用类方法。 5. 在没有属性时,建议使用类方法。


匿名对象

没有名字的对象。

self关键字


self是一个指针变量,用于在方法中指向调用该方法的对象。

self的应用场景 1)用在类方法中,代表当前类。 2)用在对象方法中,代表当前对象。 3)访问成员变量:self->成员变量

self使用注意

在对象方法中
【self 对象方法】;  // 死循环 

在类方法中
【self 对象方法】;  // 死循环

封装

封装:把复杂的数据或者操作进行隐藏,只操作数据或者方法的接口。封装的好处:隐藏成员变量,不让外部直接访问,提高安全性。控制外界访问成员权限。

set方法


命名规范:方法名必须以set开头。set后面跟成员变量名称,成员变量的首字母必须大写。返回值一定是void。一定要接收一个参数,而且参数类型跟成员变量类型一致。形参的名称不能跟成员变量名一样。

 - (void)set成员变量名 (首字母大写,去掉下划线) : (成员变量类型)成员变量名称 (去掉下划线);
 - (void)setAge:(int)age;

get方法


命名规范:肯定有返回值,返回值类型肯定与成员变量类型一致。方法名跟成员变量名一样。不需要接收任何参数

格式:

 - (成员变量类型)成员变量名称(去掉下划线); 
 - (int) age;

使用

// Student类的声明
@interface Student : NSObject
{
   int _age;
}
- (void)setAge:(int)newAge;//set方法
- (int)age;//get方法

@end

//Student类实现:
@implementation Student //setter方法实现

- (void)setAge:(int)newAge
{
_age = newAge;
}
//getter方法
- (int)age
{
return _age;
}
@end

调用:
Student *s = [Student new];

// 设置age的值
[s setAge:10];

// 取出age的值
int age = [s age];

// 输出
NSLog(@"age is %d", age);

类的继承和派生


OC中的继承是单继承:也就是说一个类只能一个父类,不能继承多个父类。子类不能定义和父类同名的成员变量,但是可以继承父类的变量。

基类的私有属性@private能被继承,不能被使用。@public公有成员能被继承,也能被外部方法访问。@protected 保护成员能够被继承、在子类中使用,但不能够被外部函数访问。在@interface @end之间声明的成员变量如果不做特别的说明,那么其默认是 protected的。

在类的实现即.m文件,@implementation中也可以声明成员变量,是@private的。

方法的重写:从父类继承的方法,可能这个方法并不适合子类,可以在子类中重写父类的方法。

@interface Animal : NSObject
{
  int _tuiNum; //腿的个数
  int _eyeNum; //眼睛个数
}
-(void)setTuiNum:(int)tuiNum;
-(int)tuiNum;

-(void)setEyeNum:(int)eyeNum;
-(int)eyeNum;

-(void)eat;
-(void)run;
@end

#import "Animal.h"
@interface Dog : Animal
{

}
-(void)eat; //覆盖父类的eat的方法
@end

重写之后,父类的对象调用父类的方法;子类的对象,调用子类的方法,不会引起冲突。从父类继承的方法,不适用于子类时,可以直接声明父类的同名方法,并定义。不用考虑父类中,方法已存在的问题。

重写后,子类可以通过super调用父类的方法。

- (void)eat
{
 [super eat];
}

私有方法


OC中并没有像Java中提供的私有方法,OC中的私有,可以理解为相对私有。

方法在.m中实现,不在.h中声明,此时该方法被称为私有方法:私有方法不可以被子类继承

@interface MyClass
{
// 添加变量
}
- (void)PublicMethod;//公共方法,可以被继承类继承
@end

// 而在类的.m文件中,直接实现的方法:
- (void)PrivateMethod
{
 ////
}

@end

多态


多态:同一种行为,对于不同的事物具有不同的表现形式。多态的条件:有继承关系、有方法重写。

父类的声明变量指向子类对象。如果存在多态,父类是可以访问子类特有的方法。


类对象的用法


可以用来调用方法

Dog *d = [Dog new];
Dog *d1 = [Dog new];
Class c = [d1 class];

// 用类名调用类方法
[Dog test];
[c test];

可以用来初始化对象

Class c = [Dog class];
[c test];

// 用类对象创建对象
Dog *d = [c new];
[d eat];

SEL方法选择器


SEL类型作用:可以定义变量;可以用来作为方法的形参;可以用来作为方法的实参。


Static关键


static修饰局部变量:延长布局变量的生命周期。

static修饰全局变量:当前变量只能在当前文件中使用。

static修饰函数:函数只能在当前文件中使用。

使用static修饰实例变量是不被允许的。

使用static修饰方法也是不被允许的。

使用static@interface和@end之间是不被允许的(写局部变量本来就不被允许)。

//狗类的声明
static int m=10; //此句话也可以放到.m中
@interface Dog:NSObject
{
  int _speed;
}
-(void)run;
@end

// Dog.m文件内容
#import "Dog.h"
#pragma mark 狗类的实现
@implementation Dog

-(void)run{
//定义局部静态变量 m
static int m=10;
NSLog(@"m = %d",m);
m++;
}
@end

注意Static型全局变量的可见性,局限于当前.m文件,其他的文件中的类,无法访问到该变量。


点语法


点语法是编译器特性,当编译器看到对象使用点语法,会自动把点语法转换为调用set或get方法的形式。

Student *stu = [Student new];

// 设置age的值
stu.age = 10; // 等价于[stu setAge:10];

// 取出age的值
int age = stu.age; // 等价于int age = [stu age];

// 输出
NSLog(@"age is %i", age);

self在set方法中使用

- (void)setAge:(int)age
{
self.age = age;
}
错误用法,会死循环。
上面的代码相当于:
- (void)setAge:(int)age
{
[self setAge:age];
}

self在get方法中使用

- (int)age
{
return self.age;
}

错误用法:死循环。
相当于:
- (int)age
{
return [self age];
}

property关键字


@property是编译器的指令。它告诉编译器,声明属性的set、get方法。好处是:免去我们手工书写get和set方法繁琐的代码。

格式:

@property 类型 方法名
@property int age;

//相当于进行了age的set和get方法的声明
-(void)setAge:(int)age;
-(int)age;

synthesize关键字


如果@synthesize变量名要先在.h文件中声明

@property int age;
.h
-(void)setAge:(int)age;
-(int)age;

.m
@synthesize age;展开形式如下:
-(void)setAge:(int)age
{
self->age = age;
}
-(int)age
{
return age;
}

@property和@synthesize搭配使用,用于简化set和get方法的定义和实现。


id类型


id类型应用场景

//狗的类,狗继承自动物
Dog *dog = [Dog new];
[dog run];

//定义多态类型
NSObjcet *dog1 = [Dog new];
[(Dog *)dog run];

//定义id类型
id dog2 = [Dog new];
[obj run];

id作为成员变量

// 类的声明
@interface Person : NSObject
@property int age;
@property id obj; // 可以接收任何对象
@end

// 类的实现
@implementation Person
@end

// main函数
int main()
{
   Person *p = [Person new];
   [p setObj:@"小王子"];
   NSLog(@"%d",[p obj]);
   return 0;
}

构造方法


构造方法:用来初始化对象实例变量值的方法,是个对象方法,-开头。

完整的创建一个可用的对象:

Person *p=[Person new];

new方法的内部会分别调用两个方法来完成3件事情:使用alloc方法来分配存储空间(返回分配的对象);使用init方法来对对象进行初始化;返回对象的首地址。

可以把new方法拆开如下:调用类方法+alloc分配存储空间,返回未经初始化的对象

Person *p1=[Person alloc];

调用对象方法-init进行初始化,返回对象本身

Person *p2=[p1 init];

以上两个过程整合为一句:

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

init方法就是构造方法,是用来初始化对象的方法,注意这是一个对象方法,以减号开头。默认初始化完毕后,所有成员变量的值都为0。

自定义构造方法的代码实现

问题1:给Hero类定义一个构造方法,自定义名字信息。

// 声明
@property NSString *name;

- (instancetype)initWithName:(NSString *)name;

// 实现
- (instancetype)initWithName:(NSString *)name
{
    if (self = [super init])
    {
        _name = name;
    }
    return self;
}
@end

问题2:给Hero类定义一个构造方法,自定义名字信息和年龄。

// 声明
@property NSString *name;
@property int age;

- (instancetype)initWithName:(NSString *)name andAge:(int)age;

// 实现
- (instancetype)initWithName:(NSString *)name andAge:(int)age
{
    if (self = [super init])
    {
        _name = name;
        _age = age;
    }
    return self;
}
@end

NSMutableArray


数组长度不固定,可以随便往里面添加或者删除元素。

创建可变数组

NSMutableArray *arrayM = [NSMutableArray array];

数组添加成员

- (void)addObject:(ObjectType)object;

删除数组成员

// 用成员名进行删除
- (void)removeObject:(ObjectType)object;

// 删除指定位置的元素
- (void)removeObjectAtIndex:(NSUInteger)index;

swift

处理值缺失的情况


?代表可能值确实,可以使用if let或者??来处理。

var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
//如果不是 nil,会将值解包并赋给 let 后面的常量
if let name = optionalName {
    greeting = "Hello, \(name)"
}
print(greeting)//Hello, John Appleseed

??前面的变量为nil,则用??后面的变量;??前面的不为nil,则用??前面的变量。

let nickName: String? = nil
let fullName: String = "John Appleseed"
let informalGreeting = "Hi \(nickName ?? fullName)"
print(informalGreeting)//Hi John Appleseed

快速上手


使用let来声明常量,使用var来声明变量。

int转String

let label = "The width is"
let width = 94
let widthLabel = label + String(width)

或者

let apples = 3
let appleSummary = "I have \(apples) apples."

使用方括号[]来创建数组和字典。数组添加元素用append

初始化一个空数组或者空字典:

let emptyArray = [String]()
let emptyDictionary = [String: Float]()

使用 for-inwhilerepeat-while 来进行循环。

switch:不需要在每个子句结尾写 break。

let vegetable = "red pepper"
switch vegetable {
case "celery":
    print("Add some raisins and make ants on a log.")
case "cucumber", "watercress":
    print("That would make a good tea sandwich.")
case let x where x.hasSuffix("pepper"):
    print("Is it a spicy \(x)?")
default:
    print("Everything tastes good in soup.")
}

字典是一个无序的集合,所以他们的键和值以任意顺序迭代结束。

let interestingNumbers = [
    "Prime": [2, 3, 5, 7, 11, 13],
    "Fibonacci": [1, 1, 2, 3, 5, 8],
    "Square": [1, 4, 9, 16, 25],
]
var largest = 0
for (kind, numbers) in interestingNumbers {
    for number in numbers {
        if number > largest {
            largest = number
        }
    }
}
print(largest)

while

var n = 2
while n < 100 {
    n *= 2
}
print(n)//128

var m = 2
repeat {
    m *= 2
} while m < 100
print(m)//128

..< 来表示下标范围,不包含上界。如果想包含的话需要使用 ...

var total = 0
for i in 0..<4 {
    total += i
}
print(total)//6

条件循环:

for i in 1...10 where i % 2 == 0{
    print(i)
}

倒叙遍历:

for i in (1...10).reversed(){
    print(i)
}

生成随机数:

Int.random(in: 1...6)//1-6之间随机数

四舍五入double,返回double:

round(4.44) //4.0

使用func来声明一个函数,使用->来指定函数返回值的类型。

func greet(person: String, day: String) -> String {
    return "Hello \(person), today is \(day)."
}
print(greet(person:"Bob", day: "Tuesday"))//Hello Bob, today is Tuesday.

使用元组来生成复合值,比如让一个函数返回多个值。该元组的元素可以用名称或数字来获取:

func calculateStatistics(scores: [Int]) -> (min: Int, max: Int, sum: Int) {
    var min = scores[0]
    var max = scores[0]
    var sum = 0

    for score in scores {
        if score > max {
            max = score
        } else if score < min {
            min = score
        }
        sum += score
    }

    return (min, max, sum)
}
let statistics = calculateStatistics(scores:[5, 3, 100, 3, 9])
print(statistics.sum)//120
print(statistics.2)//120

两个参数名时候的外部参数和内部参数:

func numSun(wai nei : Int) {
    print(nei)
}
numSun(wai: 3)

func numSun(_ nei : Int) {
    print(nei)
}
numSun(3)

函数可以嵌套。

函数可以作为另一个函数的返回值:

func makeIncrementer() -> ((Int) -> Int) {
    func addOne(number: Int) -> Int {
        return 1 + number
    }
    return addOne
}
var increment = makeIncrementer()
increment(7)//8

函数也可以当做参数传入另一个函数:

func hasAnyMatches(list: [Int], condition: (Int) -> Bool) -> Bool {
    for item in list {
        if condition(item) {
            return true
        }
    }
    return false
}
func lessThanTen(number: Int) -> Bool {
    return number < 10
}
var numbers = [20, 19, 7, 12]
var bol = hasAnyMatches(list: numbers, condition: lessThanTen)
print("\(bol)")

闭包:

numbers.map({
    (number: Int) -> Int in
    let result = 3 * number
    return result
})
//或者
var numbers = [20, 19, 7, 12]
let mappedNumbers = numbers.map({ number in 3 * number })
print(mappedNumbers)//[60, 57, 21, 36]

使用 class 和类名来创建一个类。

要创建一个类的实例,在类名后面加上括号。使用点语法来访问实例的属性和方法。

使用 init 来创建一个构造器。

class NamedShape {
    var numberOfSides: Int = 0
    var name: String

    init(name: String) {
        self.name = name
    }

    func simpleDescription() -> String {
        return "A shape with \(numberOfSides) sides."
    }
}

如果需要在对象释放之前进行一些清理工作,使用 deinit 创建一个析构函数。

使用 enum 来创建一个枚举。

使用 struct 来创建一个结构体。结构体是传值,类是传引用。

使用 protocol 来声明一个协议。mutating。extension。


其他


Image View重新设置Assets资源图片:

@IBOutlet weak var leftImage: UIImageView!
leftImage.image = UIImage(named: "show2")

摇晃手机结束:

override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {  
}

设置view的tag,然后同一个方法中区分:

@IBAction func tagBtn(_ sender: UIButton) {
    print(sender.tag)
}

播放音频:

import AVFoundation
var player: AVAudioPlayer!
func palyAV() {
    let url = Bundle.main.url(forResource: "文件名", withExtension: "格式名")
    do{
        player = try AVAudioPlayer(contentsOf: url!)
        player.play()
    } catch {
        
    }
}

弹出框:

let alter = UIAlertController(title: "title", message: "message", preferredStyle: .alert)
alter.addAction(UIAlertAction(title: "ok", style: .default, handler: {(_) in
    print("点击了ok")
}))
present(alter,animated: true,completion: nil)

动态设置宽度:

rightImage.frame.size.width = view.frame.width / 10

UI


control拖拽控件,可以选择Outlet和Action。

Autoresizing:设置上下左右宽高六个属性。

stackview

页面控件:View Controller。输入框:Text Field


技巧


图标制作网站:

https://appicon.co/

快捷键

command + R		编译运行
command + m		最小化
command + z		撤销

command + d		复制粘贴出来一个选中的ui控件
command + 删除键	光标在一行的最后位置,删除整行/删除光标前的内容

局域网真机调试:

Window-Devices and Simulators

iOS

https://github.com/dequan1331/HybridPageKit



文章作者:
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 !
  目录